/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.cms.itest.security; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.enonic.cms.api.plugin.ext.userstore.RemoteGroup; import com.enonic.cms.api.plugin.ext.userstore.RemoteUser; import com.enonic.cms.api.plugin.ext.userstore.UserStoreConfig; import com.enonic.cms.core.config.ConfigProperties; import com.enonic.cms.core.security.AdminSecurityHolder; import com.enonic.cms.core.security.ImpersonateCommand; import com.enonic.cms.core.security.LoginAdminUserCommand; import com.enonic.cms.core.security.SecurityService; import com.enonic.cms.core.security.group.AddMembershipsCommand; import com.enonic.cms.core.security.group.GroupSpecification; import com.enonic.cms.core.security.group.GroupType; import com.enonic.cms.core.security.user.QualifiedUsername; import com.enonic.cms.core.security.user.User; import com.enonic.cms.core.security.user.UserKey; import com.enonic.cms.core.security.user.UserNotFoundException; import com.enonic.cms.core.security.userstore.UserStoreEntity; import com.enonic.cms.core.security.userstore.UserStoreKey; import com.enonic.cms.core.security.userstore.UserStoreService; import com.enonic.cms.core.security.userstore.connector.config.UserStoreConnectorConfigLoader; import com.enonic.cms.core.security.userstore.connector.synchronize.SynchronizeUserStoreJob; import com.enonic.cms.core.security.userstore.connector.synchronize.SynchronizeUserStoreJobFactory; import com.enonic.cms.core.security.userstore.connector.synchronize.SynchronizeUserStoreType; import com.enonic.cms.core.servlet.ServletRequestAccessor; import com.enonic.cms.itest.AbstractSpringTest; import com.enonic.cms.itest.userstore.MemUserDatabase; import com.enonic.cms.itest.util.DomainFactory; import com.enonic.cms.itest.util.DomainFixture; import static org.junit.Assert.*; public class SecurityServiceImplTest extends AbstractSpringTest { @Autowired private SynchronizeUserStoreJobFactory synchronizeUserStoreJobFactory; @Autowired private UserStoreConnectorConfigLoader userStoreConnectorConfigLoader; @Autowired private UserStoreService userStoreService; @Autowired private SecurityService securityService; @Autowired private MemUserDatabase userDatabase; @Autowired private DomainFixture fixture; @Before public void before() throws Exception { this.fixture.initSystemData(); MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); ServletRequestAttributes servletRequestAttributes = new ServletRequestAttributes( httpServletRequest ); RequestContextHolder.setRequestAttributes( servletRequestAttributes ); httpServletRequest.setRemoteAddr( "127.0.0.1" ); ServletRequestAccessor.setRequest( httpServletRequest ); setupRemoteUserStore(); } @Test public void impersonate_impersonating_admin_user_throws_exception() throws Exception { synchronizeUserStoreJobFactory.createSynchronizeUserStoreJob( fixture.findUserStoreByName( "myRemoteUserStore" ).getKey(), SynchronizeUserStoreType.USERS_ONLY, 10 ).start(); ImpersonateCommand command = new ImpersonateCommand( false, fixture.findUserByName( User.ROOT_UID ).getKey() ); try { securityService.impersonatePortalUser( command ); fail( "Expected exception" ); } catch ( Exception e ) { assertTrue( e instanceof IllegalArgumentException ); System.out.println( e.getMessage() ); } } @Test public void getLoggedInPortalUser_returns_logged_in_portal_user() throws Exception { addUser( "arn" ); synchronizeUserStoreJobFactory.createSynchronizeUserStoreJob( fixture.findUserStoreByName( "myRemoteUserStore" ).getKey(), SynchronizeUserStoreType.USERS_ONLY, 10 ).start(); securityService.loginPortalUser( new QualifiedUsername( "myRemoteUserStore", "arn" ), "mypassword" ); assertEquals( "arn", securityService.getLoggedInPortalUserAsEntity().getName() ); } @Test public void getLoggedInPortalUser_returns_anonymous_user_when_no_user_have_logged_in() throws Exception { synchronizeUserStoreJobFactory.createSynchronizeUserStoreJob( fixture.findUserStoreByName( "myRemoteUserStore" ).getKey(), SynchronizeUserStoreType.USERS_ONLY, 10 ).start(); assertEquals( "anonymous", securityService.getLoggedInPortalUserAsEntity().getName() ); } @Test public void impersonatePortalUser_with_required_access_check_throws_exception_when_current_logged_in_user_is_not_admin() throws Exception { addUser( "arn" ); addUser( "jvs" ); // setup synchronizeUserStoreJobFactory.createSynchronizeUserStoreJob( fixture.findUserStoreByName( "myRemoteUserStore" ).getKey(), SynchronizeUserStoreType.USERS_ONLY, 10 ).start(); securityService.loginPortalUser( new QualifiedUsername( "myRemoteUserStore", "arn" ), "mypassword" ); // exercise ImpersonateCommand impersonateCommand = new ImpersonateCommand( true, fixture.findUserByName( "jvs" ).getKey() ); try { securityService.impersonatePortalUser( impersonateCommand ); fail( "Expected exception" ); } catch ( Exception e ) { assertTrue( e instanceof IllegalArgumentException ); assertEquals( "Impersonate not allowed", e.getMessage() ); } } @Test public void impersonatePortalUser_throws_exception_when_trying_impersonate_anonymous() throws Exception { addUser( "arn" ); // setup securityService.loginPortalUser( new QualifiedUsername( "myRemoteUserStore", "arn" ), "mypassword" ); synchronizeUserStoreJobFactory.createSynchronizeUserStoreJob( fixture.findUserStoreByName( "myRemoteUserStore" ).getKey(), SynchronizeUserStoreType.USERS_ONLY, 10 ).start(); // exercise ImpersonateCommand impersonateCommand = new ImpersonateCommand( false, fixture.findUserByName( User.ANONYMOUS_UID ).getKey() ); try { securityService.impersonatePortalUser( impersonateCommand ); fail( "Expected exception" ); } catch ( Exception e ) { assertTrue( e instanceof IllegalArgumentException ); assertEquals( "Not allowed to impersonate anonymous user, use method removePortalImpersonation instead", e.getMessage() ); } } @Test public void impersonatePortalUser_throws_exception_when_trying_impersonate_admin() throws Exception { addUser( "arn" ); // setup synchronizeUserStoreJobFactory.createSynchronizeUserStoreJob( fixture.findUserStoreByName( "myRemoteUserStore" ).getKey(), SynchronizeUserStoreType.USERS_ONLY, 10 ).start(); securityService.loginPortalUser( new QualifiedUsername( "myRemoteUserStore", "arn" ), "mypassword" ); // exercise ImpersonateCommand impersonateCommand = new ImpersonateCommand( false, fixture.findUserByName( User.ROOT_UID ).getKey() ); try { securityService.impersonatePortalUser( impersonateCommand ); fail( "Expected exception" ); } catch ( Exception e ) { assertTrue( e instanceof IllegalArgumentException ); assertEquals( "Not allowed to impersonate the admin user", e.getMessage() ); } } @Test public void impersonatePortalUser_throws_exception_when_trying_impersonate_none_existing_user() throws Exception { addUser( "arn" ); // setup synchronizeUserStoreJobFactory.createSynchronizeUserStoreJob( fixture.findUserStoreByName( "myRemoteUserStore" ).getKey(), SynchronizeUserStoreType.USERS_ONLY, 10 ).start(); securityService.loginPortalUser( new QualifiedUsername( "myRemoteUserStore", "arn" ), "mypassword" ); // exercise ImpersonateCommand impersonateCommand = new ImpersonateCommand( false, new UserKey( "NONEXISTING" ) ); try { securityService.impersonatePortalUser( impersonateCommand ); fail( "Expected exception" ); } catch ( Exception e ) { assertTrue( e instanceof UserNotFoundException ); assertEquals( "User not found, key: 'NONEXISTING'", e.getMessage() ); } } @Test public void impersonatePortalUser_with_required_access_check_passes_when_current_logged_in_user_is_admin() throws Exception { addUser( "jvs" ); // setup synchronizeUserStoreJobFactory.createSynchronizeUserStoreJob( fixture.findUserStoreByName( "myRemoteUserStore" ).getKey(), SynchronizeUserStoreType.USERS_ONLY, 10 ).start(); securityService.loginPortalUser( new QualifiedUsername( "admin" ), "password" ); // exercise ImpersonateCommand impersonateCommand = new ImpersonateCommand( true, fixture.findUserByName( "jvs" ).getKey() ); securityService.impersonatePortalUser( impersonateCommand ); assertEquals( "jvs", securityService.getImpersonatedPortalUser().getName() ); } @Test public void getImpersonatedPortalUser_returns_impersonated_user() throws Exception { addUser( "arn" ); addUser( "jvs" ); // setup synchronizeUserStoreJobFactory.createSynchronizeUserStoreJob( fixture.findUserStoreByName( "myRemoteUserStore" ).getKey(), SynchronizeUserStoreType.USERS_ONLY, 10 ).start(); // setup: login as jvs securityService.loginPortalUser( new QualifiedUsername( "myRemoteUserStore", "jvs" ), "mypassword" ); // setup: impersonate arn ImpersonateCommand impersonateCommand = new ImpersonateCommand( false, fixture.findUserByName( "arn" ).getKey() ); securityService.impersonatePortalUser( impersonateCommand ); // exercise and verify assertEquals( "arn", securityService.getImpersonatedPortalUser().getName() ); } @Test public void removePortalImpersonation_removes_currently_active_impersonation() throws Exception { addUser( "jvs" ); addUser( "arn" ); // setup synchronizeUserStoreJobFactory.createSynchronizeUserStoreJob( fixture.findUserStoreByName( "myRemoteUserStore" ).getKey(), SynchronizeUserStoreType.USERS_ONLY, 10 ).start(); // setup: login as jvs securityService.loginPortalUser( new QualifiedUsername( "myRemoteUserStore", "jvs" ), "mypassword" ); // setup: impersonate arn ImpersonateCommand impersonateCommand = new ImpersonateCommand( false, fixture.findUserByName( "arn" ).getKey() ); securityService.impersonatePortalUser( impersonateCommand ); // setup: verify current impersonation assertEquals( "arn", securityService.getImpersonatedPortalUser().getName() ); securityService.removePortalImpersonation(); // verify assertEquals( "jvs", securityService.getImpersonatedPortalUser().getName() ); } @Test public void loginAdminUser_locally_existing_user_is_deleted_and_new_created_when_user_have_been_renamed_remotely() throws Exception { final RemoteUser arnUser = addUser( "arn" ); final RemoteGroup editorsGroup = addGroup( "editors" ); this.userDatabase.addMember( editorsGroup, arnUser ); // verify: arn must not exist in db assertNull( fixture.findUserByName( "arn" ) ); // setup UserStoreKey userStoreKey = fixture.findUserStoreByName( "myRemoteUserStore" ).getKey(); // setup: synchronize with remote SynchronizeUserStoreJob job = synchronizeUserStoreJobFactory.createSynchronizeUserStoreJob( userStoreKey, SynchronizeUserStoreType.USERS_AND_GROUPS, 100 ); job.start(); // after synchronization we have: // local (arn) - remote (arn) // setup: make the remote group editors a member of the built-in contributors group (to enable admin) createMembershipToGroupOfType( "editors", GroupType.CONTRIBUTORS ); // setup: verify login with arn works securityService.loginAdminUser( new LoginAdminUserCommand( new QualifiedUsername( userStoreKey, "arn" ), "mypassword" ) ); assertEquals( fixture.findUserByName( "arn" ).getKey(), AdminSecurityHolder.getUser() ); securityService.logoutAdminUser(); assertNull( AdminSecurityHolder.getUser() ); // setup: rename remote user arn to archer this.userDatabase.removeMember( editorsGroup, arnUser ); this.userDatabase.removeUser( arnUser ); final RemoteUser archerUser = addUser( "archer" ); this.userDatabase.addMember( editorsGroup, archerUser ); // after renaming remote user we have: // local (arn) - remote (n/a) // local (n/a) - remote (archer) assertNull( this.userDatabase.getUser( "arn" ) ); assertNotNull( fixture.findUserByName( "arn" ) ); assertNotNull( this.userDatabase.getUser( "archer" ) ); assertNull( fixture.findUserByName( "archer" ) ); // exercise: provoke synchronization of user archer. securityService.loginAdminUser( new LoginAdminUserCommand( new QualifiedUsername( userStoreKey, "archer" ), "mypassword" ) ); // verify: archer is created with same email assertNotNull( fixture.findUserByName( "archer" ) ); assertEquals( "archer@test.com", fixture.findUserByName( "archer" ).getEmail() ); // verify: user archer can login to admin securityService.loginAdminUser( new LoginAdminUserCommand( new QualifiedUsername( userStoreKey, "archer" ), "mypassword" ) ); assertEquals( fixture.findUserByName( "archer" ).getKey(), AdminSecurityHolder.getUser() ); securityService.logoutAdminUser(); assertNull( AdminSecurityHolder.getUser() ); } private void setupRemoteUserStore() throws Exception { this.userDatabase.clear(); // setup vertical properties final ConfigProperties propsForVP = new ConfigProperties(); propsForVP.setProperty( "cms.userstore.connector.myRemoteUserStore.userPolicy", "all" ); propsForVP.setProperty( "cms.userstore.connector.myRemoteUserStore.groupPolicy", "all" ); propsForVP.setProperty( "cms.userstore.connector.myRemoteUserStore.plugin", "mem" ); userStoreConnectorConfigLoader.setProperties( propsForVP ); final DomainFactory factory = this.fixture.getFactory(); final UserStoreEntity userStore = factory.createUserStore( "myRemoteUserStore", "myRemoteUserStore", true ); final UserStoreConfig userStoreConfig = new UserStoreConfig(); userStore.setConfig( userStoreConfig ); fixture.save( userStore ); fixture.save( factory.createGroupInUserstore( GroupType.AUTHENTICATED_USERS.getName(), GroupType.AUTHENTICATED_USERS, "myRemoteUserStore" ) ); fixture.save( factory.createGroupInUserstore( GroupType.USERSTORE_ADMINS.getName(), GroupType.USERSTORE_ADMINS, "myRemoteUserStore" ) ); fixture.flushAndClearHibernateSession(); } private void createMembershipToGroupOfType( String memberName, GroupType groupType ) { GroupSpecification contributorsSpec = new GroupSpecification(); contributorsSpec.setName( memberName ); AddMembershipsCommand addMembershipsCommand = new AddMembershipsCommand( contributorsSpec, fixture.findUserByName( User.ROOT_UID ).getKey() ); addMembershipsCommand.addGroupToAddTo( fixture.findGroupByType( groupType ).getGroupKey() ); userStoreService.addMembershipsToGroup( addMembershipsCommand ); fixture.flushAndClearHibernateSession(); } private RemoteUser addUser( final String id ) { final RemoteUser user = new RemoteUser( id ); user.setEmail( id + "@test.com" ); this.userDatabase.addUser( user ); this.userDatabase.setPassword( user.getId(), "mypassword" ); return user; } private RemoteGroup addGroup( final String name ) { final RemoteGroup group = new RemoteGroup( name ); this.userDatabase.addGroup( group ); return group; } }