/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License, version 2 as published by the Free Software * Foundation. * * You should have received a copy of the GNU General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * * Copyright 2006 - 2016 Pentaho Corporation. All rights reserved. */ package org.pentaho.platform.engine.security; import static org.junit.Assert.*; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.AdditionalMatchers; import org.mockito.Matchers; import org.pentaho.platform.api.engine.IPentahoObjectFactory; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.ISystemSettings; import org.pentaho.platform.api.engine.IUserRoleListService; import org.pentaho.platform.api.mt.ITenant; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.core.system.boot.PentahoSystemBoot; import org.pentaho.platform.engine.core.system.objfac.references.SingletonPentahoObjectReference; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.userdetails.User; public class SecurityHelperTest { private static final String PENTAHO_OBJECT_FACTORY_MOCK_NAME = "Mock of IPentahoObjectFactory"; private static final String SINGLE_TENANT_ADMIN_USER_NAME = "singleTenantAdminUserName"; private static final String ADMIN_USER_NAME = "super_admin"; private static final String CALLABLE_RETURNED_VALUE_OK = "ok"; private static final String DEF_USERNAME = "myuser"; private static final String[] ALL_ROLES_ARRAY = { "role1", "role2" }; private static final String[] ADMIN_ROLES_ARRAY = { "adm_role1", "adm_role2" }; private static final String ANONIMOUS_USER; private static final String ANONIMOUS_ROLE; private static ISystemSettings oldSystemSettingsService; private SecurityHelper emptySecurityHelper; static { setSystemSettingsService( null ); ANONIMOUS_USER = PentahoSystem.getSystemSetting( "anonymous-authentication/anonymous-user", "anonymousUser" ); ANONIMOUS_ROLE = PentahoSystem.getSystemSetting( "anonymous-authentication/anonymous-role", "Anonymous" ); rollbackSystemSettingsService(); } @Before public void init() { setSystemSettingsService( null ); PentahoSystemBoot boot = new PentahoSystemBoot(); boot.setFilePath( "test-src/solution" ); emptySecurityHelper = spy( new SecurityHelper() ); } @After public void destroy() { rollbackSystemSettingsService(); } @Test public void createAuthentificationTest() { Authentication authentication = getAuthorizedSecurityHelper().createAuthentication( DEF_USERNAME ); Collection<? extends GrantedAuthority> autorities = authentication.getAuthorities(); // check for the all inner roles from ALL_ROLES_ARRAY that they are present in authentication authorities for ( String sourceRole : ALL_ROLES_ARRAY ) { boolean roleWasFound = false; for ( GrantedAuthority authRole : autorities ) { if ( sourceRole.equals( authRole.getAuthority() ) ) { roleWasFound = true; break; } } if ( !roleWasFound ) { fail( "not whole of required roles are present in created authentication authorities" ); return; } } } @Test public void createAnonimousAuthentificationTest() { Authentication auth = getAuthorizedSecurityHelper().createAuthentication( ANONIMOUS_USER ); boolean roleWasFound = false; for ( GrantedAuthority authElem : auth.getAuthorities() ) { if ( authElem != null && ANONIMOUS_ROLE.equals( authElem.getAuthority() ) ) { roleWasFound = true; break; } } assertTrue( "not granted access for anonimous user", roleWasFound ); } @Test @SuppressWarnings( "unchecked" ) public void runAsSystemTest() throws Exception { // creating environment PentahoSystemBoot boot = new PentahoSystemBoot(); boot.setFilePath( "test-src/solution" ); IPentahoObjectFactory pentahoObjectFactory = mock( IPentahoObjectFactory.class, PENTAHO_OBJECT_FACTORY_MOCK_NAME ); when( pentahoObjectFactory.objectDefined( eq( SINGLE_TENANT_ADMIN_USER_NAME ) ) ).thenReturn( true ); when( pentahoObjectFactory.get( eq( String.class ), eq( SINGLE_TENANT_ADMIN_USER_NAME ), Matchers.<IPentahoSession>any() ) ).thenReturn( ADMIN_USER_NAME ); when( pentahoObjectFactory.getName() ).thenReturn( PENTAHO_OBJECT_FACTORY_MOCK_NAME ); boot.setFactory( pentahoObjectFactory ); IUserRoleListService mockUserRoleListService = getUserRoleListServiceMock( ADMIN_USER_NAME, ADMIN_ROLES_ARRAY ); doReturn( mockUserRoleListService ).when( emptySecurityHelper ).getUserRoleListService(); // test for call Callable<String> callable = (Callable<String>) mock( Callable.class ); when( callable.call() ).thenReturn( CALLABLE_RETURNED_VALUE_OK ); String runningResult = emptySecurityHelper.runAsSystem( callable ); assertEquals( CALLABLE_RETURNED_VALUE_OK, runningResult ); } @Test @SuppressWarnings( "unchecked" ) public void runAsAnonymousTest() throws Exception { Callable<String> callable = (Callable<String>) mock( Callable.class ); when( callable.call() ).thenReturn( CALLABLE_RETURNED_VALUE_OK ); String runningResult = emptySecurityHelper.runAsAnonymous( callable ); assertEquals( CALLABLE_RETURNED_VALUE_OK, runningResult ); } @Test @SuppressWarnings( "unchecked" ) public void runAsUserTest() throws Exception { Callable<String> callable = (Callable<String>) mock( Callable.class ); when( callable.call() ).thenReturn( CALLABLE_RETURNED_VALUE_OK ); String runningResult = getAuthorizedSecurityHelper().runAsUser( DEF_USERNAME, callable ); assertEquals( CALLABLE_RETURNED_VALUE_OK, runningResult ); } // Temporarily leaving this test out until http://jira.pentaho.com/browse/BISERVER-13627 is addressed //@Test /** * Verification for BISERVER-12365 where a Threads are sharing a SecurityContext and making concurrent calls to * runAsSytem() and runAsUser() */ public void testWithSharedSecurityContext() throws InterruptedException { IUserRoleListService userRoleListService = getUserRoleListServiceMock( "admin", new String[]{"authenticated"} ); when( userRoleListService.getRolesForUser( Matchers.<ITenant>any(), eq( "suzy" ) ) ).thenReturn( Collections.singletonList( "authenticated" ) ); PentahoSystem.registerObject( userRoleListService ); PentahoSystem.registerReference( new SingletonPentahoObjectReference.Builder<String>( String.class ).object( "admin" ) .attributes( Collections.<String, Object>singletonMap( "id", "singleTenantAdminUserName" ) ).build() ); SecurityContextHolder.setStrategyName( PentahoSecurityContextHolderStrategy.class.getName() ); UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( "suzy", "password" ); final SecurityContext context = new SecurityContextImpl(); SecurityContextHolder.setContext( context ); SecurityContextHolder.getContext().setAuthentication( token ); final AtomicBoolean lock = new AtomicBoolean( true ); final AtomicBoolean lock2 = new AtomicBoolean( true ); final Thread t2 = new Thread( new Runnable() { @Override public void run() { try { SecurityContextHolder.setContext( context ); SecurityHelper.getInstance().runAsSystem( new Callable<Void>() { @Override public Void call() throws Exception { synchronized ( lock ) { System.out.println( "Starting Thread 2" ); lock.notify(); } synchronized ( lock2 ) { lock2.wait(); } System.out.println( "Finishing Thread 2" ); return null; } } ); } catch ( Exception e ) { e.printStackTrace(); fail( e.getMessage() ); } } } ); final Thread t1 = new Thread( new Runnable() { @Override public void run() { try { SecurityContextHolder.setContext( context ); SecurityHelper.getInstance().runAsSystem( new Callable<Void>() { @Override public Void call() throws Exception { System.out.println( "Starting Thread 1" ); t2.start(); synchronized ( lock ) { lock.wait(); } System.out.println( "Finishing Thread 1" ); return null; } } ); } catch ( Exception e ) { e.printStackTrace(); fail( e.getMessage() ); } } } ); t1.start(); t1.join(); synchronized ( lock2 ) { lock2.notify(); } t2.join(); assertSame( token.getPrincipal(), SecurityContextHolder.getContext().getAuthentication().getPrincipal() ); } @Test /** * Authenticate as Suzy, make a runAsSystem() call with an embedded runAsUser(), verify that Authentication is * restored successfully. */ public void testNestedCalls() throws Exception { IUserRoleListService userRoleListService = getUserRoleListServiceMock( "admin", new String[]{"authenticated"} ); when( userRoleListService.getRolesForUser( Matchers.<ITenant>any(), eq( "suzy" ) ) ).thenReturn( Collections.singletonList( "authenticated" ) ); PentahoSystem.registerObject( userRoleListService ); PentahoSystem.registerReference( new SingletonPentahoObjectReference.Builder<String>( String.class ).object( "admin" ) .attributes( Collections.<String, Object>singletonMap( "id", "singleTenantAdminUserName" ) ).build() ); SecurityContextHolder.setStrategyName( PentahoSecurityContextHolderStrategy.class.getName() ); UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( "suzy", "password" ); SecurityContextHolder.getContext().setAuthentication( token ); SecurityHelper.getInstance().runAsSystem( new Callable<Void>() { @Override public Void call() throws Exception { try { SecurityHelper.getInstance().runAsUser( "suzy", new Callable<Void>() { @Override public Void call() throws Exception { assertEquals( ( (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal() ).getUsername(), "suzy" ); throw new NullPointerException(); } } ); } catch ( Exception e ) { /* No-op */ } assertEquals( ( (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal() ).getUsername(), "admin" ); return null; } } ); assertSame( SecurityContextHolder.getContext().getAuthentication(), token ); } private static void setSystemSettingsService( ISystemSettings service ) { oldSystemSettingsService = PentahoSystem.getSystemSettings(); PentahoSystem.setSystemSettingsService( service ); } private static void rollbackSystemSettingsService() { PentahoSystem.setSystemSettingsService( oldSystemSettingsService ); } private SecurityHelper getAuthorizedSecurityHelper() { SecurityHelper authorizedSecurityHelper = spy( new SecurityHelper() ); IUserRoleListService userRoleListServiceMock = getUserRoleListServiceMock( DEF_USERNAME, ALL_ROLES_ARRAY ); doReturn( userRoleListServiceMock ).when( authorizedSecurityHelper ).getUserRoleListService(); return authorizedSecurityHelper; } private IUserRoleListService getUserRoleListServiceMock( String userName, String[] roles ) { IUserRoleListService mockUserRoleListService = mock( IUserRoleListService.class ); List<String> noRoles = new ArrayList<String>(); List<String> allRoles = new ArrayList<String>( Arrays.asList( roles ) ); when( mockUserRoleListService.getRolesForUser( Matchers.<ITenant>any(), eq( userName ) ) ).thenReturn( allRoles ); when( mockUserRoleListService.getRolesForUser( Matchers.<ITenant>any(), AdditionalMatchers.not( eq( userName ) ) ) ) .thenReturn( noRoles ); return mockUserRoleListService; } }