diff --git a/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java b/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java index af79d71798..8efb1880ab 100644 --- a/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java +++ b/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java @@ -399,7 +399,7 @@ public abstract class CifsAuthenticatorBase extends CifsAuthenticator implements if (personName == null) { // Force creation of a person if possible - getPersonService().getPerson(userName); + authenticationComponent.setCurrentUser(userName); personName = getPersonService().getUserIdentifier(userName); } diff --git a/source/java/org/alfresco/filesys/auth/cifs/PassthruCifsAuthenticator.java b/source/java/org/alfresco/filesys/auth/cifs/PassthruCifsAuthenticator.java index 21dfe4eca8..8dc738447f 100644 --- a/source/java/org/alfresco/filesys/auth/cifs/PassthruCifsAuthenticator.java +++ b/source/java/org/alfresco/filesys/auth/cifs/PassthruCifsAuthenticator.java @@ -122,6 +122,11 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements m_sessions = new Hashtable(); } + public Hashtable getSessions() + { + return m_sessions; + } + public void setPassthruServers(PassthruServers servers) { m_passthruServers = servers; diff --git a/source/test-java/org/alfresco/Repository01TestSuite.java b/source/test-java/org/alfresco/Repository01TestSuite.java index 5168f59f6d..2ddc7701fe 100644 --- a/source/test-java/org/alfresco/Repository01TestSuite.java +++ b/source/test-java/org/alfresco/Repository01TestSuite.java @@ -436,7 +436,10 @@ public class Repository01TestSuite extends TestSuite suite.addTest(new JUnit4TestAdapter(org.alfresco.util.test.junitrules.ApplicationContextInitTest.class)); suite.addTest(new JUnit4TestAdapter(org.alfresco.util.test.junitrules.TemporaryNodesTest.class)); suite.addTest(new JUnit4TestAdapter(org.alfresco.util.test.junitrules.TemporarySitesTest.class)); + suite.addTest(new JUnit4TestAdapter(org.alfresco.filesys.auth.cifs.CifsAuthenticatorKerberosTest.class)); + suite.addTest(new JUnit4TestAdapter(org.alfresco.filesys.auth.cifs.CifsAuthenticatorPassthruTest.class)); suite.addTest(new JUnit4TestAdapter(org.alfresco.util.CronTriggerBeanTest.class)); suite.addTest(new JUnit4TestAdapter(org.alfresco.util.CronTriggerBeanSystemTest.class)); } + } diff --git a/source/test-java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorKerberosTest.java b/source/test-java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorKerberosTest.java new file mode 100644 index 0000000000..39659c3e34 --- /dev/null +++ b/source/test-java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorKerberosTest.java @@ -0,0 +1,157 @@ +package org.alfresco.filesys.auth.cifs; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AbstractAuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.sync.UserRegistrySynchronizer; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.GUID; +import org.alfresco.util.PropertyMap; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.context.ApplicationContext; + +public class CifsAuthenticatorKerberosTest +{ + public static final String[] CONFIG_LOCATIONS = new String[] { "classpath:alfresco/application-context.xml", + "classpath:alfresco/filesys/auth/cifs/test-kerberos-context.xml" + }; + private static ApplicationContext ctx = null; + + private PersonService personService; + private TransactionService transactionService; + private EnterpriseCifsAuthenticator cifsAuthenticator; + + private String userExistingLocal = "user1." + GUID.generate(); + private String userMissingLocal = "user2." + GUID.generate(); + + @BeforeClass + public static void init() + { + ApplicationContextHelper.setUseLazyLoading(false); + ApplicationContextHelper.setNoAutoStart(true); + ctx = ApplicationContextHelper.getApplicationContext(CONFIG_LOCATIONS); + } + + @Before + public void before() throws Exception + { + this.personService = (PersonService)ctx.getBean("personService"); + this.transactionService = (TransactionService)ctx.getBean("transactionService"); + this.cifsAuthenticator = (EnterpriseCifsAuthenticator)ctx.getBean("cifsAuthenticator"); + + // create only user1 in local repository + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @SuppressWarnings("synthetic-access") + public Void execute() throws Throwable + { + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + // create person properties + PropertyMap personProps = new PropertyMap(); + personProps.put(ContentModel.PROP_USERNAME, userExistingLocal); + personProps.put(ContentModel.PROP_FIRSTNAME, userExistingLocal); + personProps.put(ContentModel.PROP_LASTNAME, userExistingLocal); + personProps.put(ContentModel.PROP_EMAIL, userExistingLocal+"@email.com"); + personService.createPerson(personProps); + + AuthenticationUtil.popAuthentication(); + + return null; + } + }, false, true); + } + + private UserRegistrySynchronizer makeUserRegistrySynchronizerStub(final boolean autoCreatePeopleOnLogin) + { + UserRegistrySynchronizer stub = mock(UserRegistrySynchronizer.class); + when(stub.createMissingPerson(anyString())).thenAnswer(new Answer() + { + public Boolean answer(InvocationOnMock invocation) throws Throwable + { + Object[] args = invocation.getArguments(); + String userName = (String) args[0]; + + if (userName != null && !userName.equals(AuthenticationUtil.getSystemUserName())) + { + PersonService personServiceStub = mock(PersonService.class); + when(personServiceStub.createMissingPeople()).thenReturn(true); + if (autoCreatePeopleOnLogin && personServiceStub.createMissingPeople()) + { + AuthorityType authorityType = AuthorityType.getAuthorityType(userName); + if (authorityType == AuthorityType.USER) + { + personService.getPerson(userName); + return true; + } + } + } + return false; + } + }); + return stub; + } + + @Test + public void testExistingUserMappingWhenAutoCreateNotAllowed() + { + UserRegistrySynchronizer userRegistrySynchronizer = makeUserRegistrySynchronizerStub(false); + ((AbstractAuthenticationComponent) cifsAuthenticator.getAuthenticationComponent()).setUserRegistrySynchronizer(userRegistrySynchronizer); + String username = cifsAuthenticator.mapUserNameToPerson(userExistingLocal, false); + assertEquals("Existing local user should be mapped to authenticated AD user", username, userExistingLocal); + } + + @Test + public void testExistingUserMappingWhenAutoCreateAllowed() + { + UserRegistrySynchronizer userRegistrySynchronizer = makeUserRegistrySynchronizerStub(true); + ((AbstractAuthenticationComponent) cifsAuthenticator.getAuthenticationComponent()).setUserRegistrySynchronizer(userRegistrySynchronizer); + String username = cifsAuthenticator.mapUserNameToPerson(userExistingLocal, false); + assertEquals("Existing local user should be mapped to authenticated AD user", username, userExistingLocal); + } + + @Test + public void testMissingUserMappingWhenAutoCreateNotAllowed() + { + UserRegistrySynchronizer userRegistrySynchronizer = makeUserRegistrySynchronizerStub(false); + ((AbstractAuthenticationComponent) cifsAuthenticator.getAuthenticationComponent()).setUserRegistrySynchronizer(userRegistrySynchronizer); + try + { + cifsAuthenticator.mapUserNameToPerson(userMissingLocal, false); + fail("User that does not exist in repository should not login when autoCreatePeopleOnLogin is not allowed"); + } + catch (AuthenticationException expected) + { + } + } + + @Test + public void testMissingUserMappingWhenAutoCreateAllowed() + { + UserRegistrySynchronizer userRegistrySynchronizer = makeUserRegistrySynchronizerStub(true); + ((AbstractAuthenticationComponent) cifsAuthenticator.getAuthenticationComponent()).setUserRegistrySynchronizer(userRegistrySynchronizer); + String username = cifsAuthenticator.mapUserNameToPerson(userMissingLocal, false); + assertEquals("User that does not exist in repository can login when autoCreatePeopleOnLogin is allowed", username, userMissingLocal); + // personService.personExists requires RunAsUser to be set + AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName()); + assertTrue(personService.personExists(userMissingLocal)); + } + +} diff --git a/source/test-java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorPassthruTest.java b/source/test-java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorPassthruTest.java new file mode 100644 index 0000000000..33c9cce30d --- /dev/null +++ b/source/test-java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorPassthruTest.java @@ -0,0 +1,198 @@ +package org.alfresco.filesys.auth.cifs; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.alfresco.filesys.alfresco.AlfrescoClientInfo; +import org.alfresco.jlan.server.SrvSession; +import org.alfresco.jlan.server.auth.ClientInfo; +import org.alfresco.jlan.server.auth.ICifsAuthenticator; +import org.alfresco.jlan.server.auth.passthru.AuthenticateSession; +import org.alfresco.jlan.server.auth.passthru.PassthruDetails; +import org.alfresco.jlan.smb.server.SMBSrvSession; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AbstractAuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationContext; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.sync.UserRegistrySynchronizer; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.GUID; +import org.alfresco.util.PropertyMap; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.context.ApplicationContext; + +public class CifsAuthenticatorPassthruTest +{ + private static ApplicationContext ctx = null; + + private PersonService personService; + private TransactionService transactionService; + private NodeService nodeService; + private PassthruCifsAuthenticator cifsAuthenticator; + private AuthenticationContext authenticationContext; + + private String userExistingLocal = "user1." + GUID.generate(); + private String userMissingLocal = "user2." + GUID.generate(); + + @BeforeClass + public static void init() + { + ApplicationContextHelper.setUseLazyLoading(false); + ApplicationContextHelper.setNoAutoStart(true); + ctx = ApplicationContextHelper.getApplicationContext(); + } + + @Before + public void before() throws Exception + { + this.personService = (PersonService) ctx.getBean("personService"); + this.transactionService = (TransactionService) ctx.getBean("transactionService"); + this.nodeService = (NodeService) ctx.getBean("nodeService"); + this.authenticationContext = (AuthenticationContext) ctx.getBean("authenticationContext"); + + // cannot get bean from context directly because of side affects from passthruServers.afterPropertiesSet + this.cifsAuthenticator = new PassthruCifsAuthenticator(); + this.cifsAuthenticator.setTransactionService(transactionService); + // passthru-authentication-context.xml : NTLMAuthenticationComponentImpl is used for passthru + AbstractAuthenticationComponent ac = new org.alfresco.repo.security.authentication.ntlm.NTLMAuthenticationComponentImpl(); + ac.setPersonService(personService); + ac.setTransactionService(transactionService); + ac.setNodeService(nodeService); + ac.setAuthenticationContext(authenticationContext); + this.cifsAuthenticator.setAuthenticationComponent(ac); + this.cifsAuthenticator.setAuthenticationService(mock(org.alfresco.repo.security.authentication.AuthenticationServiceImpl.class)); + + // create only user1 in local repository + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @SuppressWarnings("synthetic-access") + public Void execute() throws Throwable + { + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + // create person properties + PropertyMap personProps = new PropertyMap(); + personProps.put(ContentModel.PROP_USERNAME, userExistingLocal); + personProps.put(ContentModel.PROP_FIRSTNAME, userExistingLocal); + personProps.put(ContentModel.PROP_LASTNAME, userExistingLocal); + personProps.put(ContentModel.PROP_EMAIL, userExistingLocal + "@email.com"); + personService.createPerson(personProps); + + AuthenticationUtil.popAuthentication(); + + return null; + } + }, false, true); + } + + private UserRegistrySynchronizer makeUserRegistrySynchronizerStub(final boolean autoCreatePeopleOnLogin) + { + UserRegistrySynchronizer stub = mock(UserRegistrySynchronizer.class); + when(stub.createMissingPerson(anyString())).thenAnswer(new Answer() + { + public Boolean answer(InvocationOnMock invocation) throws Throwable + { + Object[] args = invocation.getArguments(); + String userName = (String) args[0]; + + if (userName != null && !userName.equals(AuthenticationUtil.getSystemUserName())) + { + PersonService personServiceStub = mock(PersonService.class); + when(personServiceStub.createMissingPeople()).thenReturn(true); + if (autoCreatePeopleOnLogin && personServiceStub.createMissingPeople()) + { + AuthorityType authorityType = AuthorityType.getAuthorityType(userName); + if (authorityType == AuthorityType.USER) + { + personService.getPerson(userName); + return true; + } + } + } + return false; + } + }); + return stub; + } + + private class TestContext + { + protected ClientInfo client; + protected SrvSession sess; + + protected TestContext(ClientInfo client, SrvSession sess) + { + this.client = client; + this.sess = sess; + } + } + + private TestContext prepareTestConditions(boolean autoCreatePeopleOnLogin, String userName) throws Exception + { + UserRegistrySynchronizer userRegistrySynchronizer = makeUserRegistrySynchronizerStub(autoCreatePeopleOnLogin); + ((AbstractAuthenticationComponent) cifsAuthenticator.getAuthenticationComponent()).setUserRegistrySynchronizer(userRegistrySynchronizer); + + ClientInfo client = mock(AlfrescoClientInfo.class); + client.setUserName(userName); + SrvSession sess = mock(SMBSrvSession.class); + sess.setUniqueId(userName); + // add getter to the original class, otherwise reflection should be used to add session to private field + AuthenticateSession as = mock(AuthenticateSession.class); + // assume successful passthru authentication + doNothing().when(as).doSessionSetup(anyString(), anyString(), anyString(), any(byte[].class), any(byte[].class), anyInt()); + PassthruDetails pd = new PassthruDetails(sess, as); + cifsAuthenticator.getSessions().put(userName, pd); + + return new TestContext(client, sess); + } + + @Test + public void testExistingUserAuthenticationWhenAutoCreateNotAllowed() throws Exception + { + TestContext tc = prepareTestConditions(false, userExistingLocal); + int status = cifsAuthenticator.authenticateUser(tc.client, tc.sess, 0); + assertEquals("Access should be allowed if user exists in local repository", status, ICifsAuthenticator.AUTH_ALLOW); + } + + @Test + public void testExistingUserAuthenticationWhenAutoCreateAllowed() throws Exception + { + TestContext tc = prepareTestConditions(true, userExistingLocal); + int status = cifsAuthenticator.authenticateUser(tc.client, tc.sess, 0); + assertEquals("Access should be allowed if user exists in local repository", status, ICifsAuthenticator.AUTH_ALLOW); + } + + @Test + public void testMissingUserAuthenticationWhenAutoCreateNotAllowed() throws Exception + { + TestContext tc = prepareTestConditions(false, userMissingLocal); + int status = cifsAuthenticator.authenticateUser(tc.client, tc.sess, 0); + assertEquals("User that does not exist in repository should not login when autoCreatePeopleOnLogin is not allowed", + status, ICifsAuthenticator.AUTH_DISALLOW); + } + + @Test + public void testMissingUserAuthenticationWhenAutoCreateAllowed() throws Exception + { + TestContext tc = prepareTestConditions(true, userMissingLocal); + int status = cifsAuthenticator.authenticateUser(tc.client, tc.sess, 0); + assertEquals("User that does not exist in repository can login when autoCreatePeopleOnLogin is allowed", + status, ICifsAuthenticator.AUTH_ALLOW); + } + +} diff --git a/source/test-resources/alfresco/filesys/auth/cifs/test-kerberos-context.xml b/source/test-resources/alfresco/filesys/auth/cifs/test-kerberos-context.xml new file mode 100644 index 0000000000..2db5c952f4 --- /dev/null +++ b/source/test-resources/alfresco/filesys/auth/cifs/test-kerberos-context.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + ALFRESCO.ORG + Alfresco + + AlfrescoCIFS + secret + false + true + + + false + + + + false + + + + + + \ No newline at end of file