// $HeadURL$ // $Id$ // // Copyright © 2006, 2010, 2011, 2012 by the President and Fellows of Harvard College. // // Screensaver is an open-source project developed by the ICCB-L and NSRB labs // at Harvard Medical School. This software is distributed under the terms of // the GNU General Public License. package edu.harvard.med.screensaver.ui.arch.auth; import java.io.IOException; import java.util.HashMap; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.FailedLoginException; import javax.security.auth.login.LoginException; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import edu.harvard.med.authentication.AuthenticationClient; import edu.harvard.med.authentication.AuthenticationRequestException; import edu.harvard.med.authentication.AuthenticationResponseException; import edu.harvard.med.authentication.AuthenticationResult; import edu.harvard.med.authentication.Credentials; import edu.harvard.med.screensaver.db.DAOTransaction; import edu.harvard.med.screensaver.db.GenericEntityDAO; import edu.harvard.med.screensaver.db.SchemaUtil; import edu.harvard.med.screensaver.model.users.ScreeningRoomUser; import edu.harvard.med.screensaver.model.users.ScreensaverUser; import edu.harvard.med.screensaver.model.users.ScreensaverUserRole; import edu.harvard.med.screensaver.test.AbstractSpringTest; @ContextConfiguration(locations = { "/spring-context-test-security.xml", "/spring-context-authentication.xml" }, inheritLocations = false) public class ScreensaverLoginModuleTest extends AbstractSpringTest { // static data members private static final Logger log = Logger.getLogger(ScreensaverLoginModuleTest.class); private static final String TEST_VALID_ECOMMONS_USER_LOGIN = "ecom"; private static final String TEST_VALID_SCREENSAVER_USER_LOGIN = "screensaverId"; private static final String TEST_VALID_SCREENSAVER_USER_EMAIL = "test.user@screensaver.com"; private static final String TEST_VALID_ECOMMONS_PASSWORD = "eCommonsPassword"; private static final String TEST_VALID_SCREENSAVER_PASSWORD = "screensaverPassword"; private static final String TEST_INVALID_USER_LOGIN = "!testUser"; private static final String TEST_INVALID_PASSWORD = "!testPassword"; // Spring-injected data members (must have @Autowired protected access) @Autowired protected ScreensaverLoginModule screensaverLoginModule; @Autowired protected SchemaUtil schemaUtil; @Autowired protected GenericEntityDAO genericEntityDao; // instance data private CallbackHandler _mockCallbackHandlerForValidEcommonsUserAndPassword; private CallbackHandler _mockCallbackHandlerForValidEcommonsUserInvalidPassword; private CallbackHandler _mockCallbackHandlerForValidScreensaverUserAndPassword; private CallbackHandler _mockCallbackHandlerForValidScreensaverUserInvalidPassword; private CallbackHandler _mockCallbackHandlerForInvalidUser; private AuthenticationClient _mockECommonsAuthenticationClient; private Subject _subject; private ScreensaverUser _validUser; // test setup methods @Override protected void setUp() throws Exception { super.setUp(); _subject = new Subject(); _mockCallbackHandlerForValidEcommonsUserAndPassword = new CallbackHandler() { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { NameCallback nameCallback = (NameCallback) callbacks[0]; PasswordCallback passwordCallback = (PasswordCallback) callbacks[1]; nameCallback.setName(TEST_VALID_ECOMMONS_USER_LOGIN); passwordCallback.setPassword(TEST_VALID_ECOMMONS_PASSWORD.toCharArray()); } }; _mockCallbackHandlerForValidEcommonsUserInvalidPassword = new CallbackHandler() { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { NameCallback nameCallback = (NameCallback) callbacks[0]; PasswordCallback passwordCallback = (PasswordCallback) callbacks[1]; nameCallback.setName(TEST_VALID_ECOMMONS_USER_LOGIN); passwordCallback.setPassword(TEST_INVALID_PASSWORD.toCharArray()); } }; _mockCallbackHandlerForValidScreensaverUserAndPassword = new CallbackHandler() { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { NameCallback nameCallback = (NameCallback) callbacks[0]; PasswordCallback passwordCallback = (PasswordCallback) callbacks[1]; nameCallback.setName(TEST_VALID_SCREENSAVER_USER_LOGIN); passwordCallback.setPassword(TEST_VALID_SCREENSAVER_PASSWORD.toCharArray()); } }; _mockCallbackHandlerForValidScreensaverUserInvalidPassword = new CallbackHandler() { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { NameCallback nameCallback = (NameCallback) callbacks[0]; PasswordCallback passwordCallback = (PasswordCallback) callbacks[1]; nameCallback.setName(TEST_VALID_SCREENSAVER_USER_LOGIN); passwordCallback.setPassword(TEST_INVALID_PASSWORD.toCharArray()); } }; _mockCallbackHandlerForInvalidUser = new CallbackHandler() { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { NameCallback nameCallback = (NameCallback) callbacks[0]; PasswordCallback passwordCallback = (PasswordCallback) callbacks[1]; nameCallback.setName(TEST_INVALID_USER_LOGIN); passwordCallback.setPassword(TEST_INVALID_PASSWORD.toCharArray()); } }; _mockECommonsAuthenticationClient = new AuthenticationClient() { public AuthenticationResult authenticate(Credentials credentials) throws AuthenticationRequestException, AuthenticationResponseException { return new TestAuthenticationResult(credentials, credentials.getUserId().equals(TEST_VALID_ECOMMONS_USER_LOGIN) && credentials.getPassword().equals(TEST_VALID_ECOMMONS_PASSWORD)); } }; screensaverLoginModule.setAuthenticationClient(_mockECommonsAuthenticationClient); schemaUtil.truncateTables(); // create a user _validUser = new ScreeningRoomUser("Iam", "Authorized"); _validUser.setEmail(TEST_VALID_SCREENSAVER_USER_EMAIL); _validUser.setECommonsId(TEST_VALID_ECOMMONS_USER_LOGIN); _validUser.setLoginId(TEST_VALID_SCREENSAVER_USER_LOGIN); _validUser.updateScreensaverPassword(TEST_VALID_SCREENSAVER_PASSWORD); _validUser.addScreensaverUserRole(ScreensaverUserRole.SCREENSAVER_USER); _validUser.addScreensaverUserRole(ScreensaverUserRole.SM_DSL_LEVEL3_SHARED_SCREENS); _validUser.addScreensaverUserRole(ScreensaverUserRole.RNAI_DSL_LEVEL3_SHARED_SCREENS); genericEntityDao.saveOrUpdateEntity(_validUser); } // test methods public void testInitialize() { screensaverLoginModule.initialize(_subject, _mockCallbackHandlerForValidEcommonsUserAndPassword, new HashMap(), new HashMap()); // hmmm...nothing we can assert...well, that's an easy test to pass! (simply don't throw an exception!) } public void testLoginLogoutValidScreensaverUserAndPassword() { try { screensaverLoginModule.initialize(_subject, _mockCallbackHandlerForValidScreensaverUserAndPassword, new HashMap(), new HashMap()); assertSuccessfulLoginAndLogout(); } catch (LoginException e) { e.printStackTrace(); fail("login failed due to exception: " + e.getMessage()); } } public void testLoginLogoutValidEcommonsUserAndPassword() { try { screensaverLoginModule.initialize(_subject, _mockCallbackHandlerForValidEcommonsUserAndPassword, new HashMap(), new HashMap()); assertSuccessfulLoginAndLogout(); } catch (LoginException e) { e.printStackTrace(); fail("login failed due to exception: " + e.getMessage()); } } public void testLoginValidEcommonsUserInvalidPassword() { try { screensaverLoginModule.initialize(_subject, _mockCallbackHandlerForValidEcommonsUserInvalidPassword, new HashMap(), new HashMap()); screensaverLoginModule.login(); fail("expected login failure"); } catch (FailedLoginException e) { assertEquals("principals count", 0, _subject.getPrincipals().size()); // test passed! } catch (LoginException e) { e.printStackTrace(); fail("login failed due to exception " + e.getMessage()); } } public void testLoginValidScreensaverUserInvalidPassword() { try { screensaverLoginModule.initialize(_subject, _mockCallbackHandlerForValidScreensaverUserInvalidPassword, new HashMap(), new HashMap()); screensaverLoginModule.login(); fail("expected login failure"); } catch (FailedLoginException e) { assertEquals("principals count", 0, _subject.getPrincipals().size()); // test passed! } catch (LoginException e) { e.printStackTrace(); fail("login failed due to exception " + e.getMessage()); } } public void testLoginInvalidUser() { try { screensaverLoginModule.initialize(_subject, _mockCallbackHandlerForInvalidUser, new HashMap(), new HashMap()); screensaverLoginModule.login(); fail("expected login failure"); } catch (FailedLoginException e) { assertEquals("principals count", 0, _subject.getPrincipals().size()); // test passed! } catch (LoginException e) { e.printStackTrace(); fail("login failed due to exception " + e.getMessage()); } } public void testLoginValidUserWithoutLoginPrivileges() { genericEntityDao.doInTransaction(new DAOTransaction() { public void runTransaction() { ScreensaverUser user = genericEntityDao.findEntityByProperty(ScreensaverUser.class, "email", _validUser.getEmail()); user.getScreensaverUserRoles().remove(ScreensaverUserRole.SCREENSAVER_USER); } }); try { screensaverLoginModule.initialize(_subject, _mockCallbackHandlerForValidScreensaverUserAndPassword, new HashMap(), new HashMap()); screensaverLoginModule.login(); fail("expected login failure"); } catch (FailedLoginException e) { assertEquals("principals count", 0, _subject.getPrincipals().size()); // test passed! } catch (LoginException e) { e.printStackTrace(); fail("login failed due to exception " + e.getMessage()); } } public void testLoginWithAbort() { // TODO: implement (but code can practically be verified by inspection alone...) } private void assertSuccessfulLoginAndLogout() throws LoginException { screensaverLoginModule.login(); assertEquals("principals count", 0, _subject.getPrincipals().size()); boolean loginResult = screensaverLoginModule.login(); assertTrue("LoginModule's login method is \"in use\"", loginResult); assertEquals("principals count", 0, _subject.getPrincipals().size()); boolean commitResult = screensaverLoginModule.commit(); assertTrue("LoginModule's commit succeeded", commitResult); assertEquals("principals count", 4, _subject.getPrincipals().size()); assertTrue("subject contains \"user\" Principal", _subject.getPrincipals().contains(new ScreensaverUserPrincipal(_validUser))); assertTrue("subject contains user role screensaverUser Principal", _subject.getPrincipals().contains(ScreensaverUserRole.SCREENSAVER_USER)); assertTrue("subject contains user role smallMoleculeScreeningRoomUser Principal", _subject.getPrincipals().contains(ScreensaverUserRole.SM_DSL_LEVEL3_SHARED_SCREENS)); assertTrue("subject contains user role rnaiScreeningRoomUser Principal", _subject.getPrincipals().contains(ScreensaverUserRole.RNAI_DSL_LEVEL3_SHARED_SCREENS)); assertFalse("subject does not contain user role userAdmin Principal", _subject.getPrincipals().contains(ScreensaverUserRole.USERS_ADMIN)); boolean logoutResult = screensaverLoginModule.logout(); assertTrue("LoginModule's logout method is \"in use\"", logoutResult); assertEquals("principals count reset", 0, _subject.getPrincipals().size()); } private static class TestAuthenticationResult implements AuthenticationResult { private boolean _isAuthenticated; private Credentials _credentials; public TestAuthenticationResult( Credentials credentials, boolean isAuthenticated) { _credentials = credentials; _isAuthenticated = isAuthenticated; } public Credentials getCredentials() { return _credentials; } public boolean isAuthenticated() throws AuthenticationResponseException { return _isAuthenticated; } public int getStatusCode() throws AuthenticationResponseException { return _isAuthenticated ? 1 : -1; } public String getStatusCodeCategory() throws AuthenticationResponseException { return ""; } public String getStatusMessage() throws AuthenticationResponseException { return ""; } } }