/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.test.authentication.jaas; import java.lang.reflect.Method; import java.security.Principal; import java.util.Arrays; import java.util.HashMap; import javax.security.auth.Subject; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jboss.security.SecurityContext; import org.jboss.security.SecurityContextAssociation; import org.jboss.security.SecurityContextFactory; import org.jboss.security.SimplePrincipal; import org.jboss.security.SubjectInfo; import org.jboss.security.auth.callback.UsernamePasswordHandler; /** ClientLoginModuleUnitTestCase/SecurityContextAssociation interaction tests @author Scott.Stark@jboss.org @version $Revision: 68075 $ */ public class ClientLoginModuleUnitTestCase extends TestCase { static TestConfig jaasConfig = new TestConfig(); static class TestConfig extends Configuration { public void refresh() { } public AppConfigurationEntry[] getAppConfigurationEntry(String name) { AppConfigurationEntry[] entry = null; try { Class<?>[] parameterTypes = {}; Method m = getClass().getDeclaredMethod(name, parameterTypes); Object[] args = {}; entry = (AppConfigurationEntry[]) m.invoke(this, args); } catch(Exception e) { } return entry; } AppConfigurationEntry[] testSingleThreaded() { String name = "org.jboss.security.ClientLoginModule"; HashMap<String,String> options = new HashMap<String,String>(); options.put("multi-threaded", "false"); AppConfigurationEntry ace = new AppConfigurationEntry(name, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); AppConfigurationEntry[] entry = {ace}; return entry; } AppConfigurationEntry[] testSingleThreadedRestoreIdentity() { String name = "org.jboss.security.ClientLoginModule"; HashMap<String,String> options = new HashMap<String,String>(); options.put("multi-threaded", "false"); options.put("restore-login-identity", "true"); AppConfigurationEntry ace = new AppConfigurationEntry(name, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); AppConfigurationEntry[] entry = {ace}; return entry; } AppConfigurationEntry[] testSingleThreadedRestoreStack() { String name = "org.jboss.security.ClientLoginModule"; HashMap<String,String> options = new HashMap<String,String>(); options.put("multi-threaded", "false"); options.put("restore-login-identity", "true"); AppConfigurationEntry ace = new AppConfigurationEntry(name, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); AppConfigurationEntry[] entry = {ace}; return entry; } AppConfigurationEntry[] testMultiThreaded() { String name = "org.jboss.security.ClientLoginModule"; HashMap<String,String> options = new HashMap<String,String>(); options.put("multi-threaded", "true"); AppConfigurationEntry ace = new AppConfigurationEntry(name, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); AppConfigurationEntry[] entry = {ace}; return entry; } AppConfigurationEntry[] testMultiThreadedRestoreIdentity() { String name = "org.jboss.security.ClientLoginModule"; HashMap<String,String> options = new HashMap<String,String>(); options.put("multi-threaded", "true"); options.put("restore-login-identity", "true"); AppConfigurationEntry ace = new AppConfigurationEntry(name, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); AppConfigurationEntry[] entry = {ace}; return entry; } AppConfigurationEntry[] testMultiThreadedRestoreStack() { String name = "org.jboss.security.ClientLoginModule"; HashMap<String,String> options = new HashMap<String,String>(); options.put("multi-threaded", "true"); options.put("restore-login-identity", "true"); AppConfigurationEntry ace = new AppConfigurationEntry(name, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); AppConfigurationEntry[] entry = {ace}; return entry; } AppConfigurationEntry[] testAbortWithRestore() { String name1 = "org.jboss.security.auth.spi.SimpleServerLoginModule"; AppConfigurationEntry ace1 = new AppConfigurationEntry(name1, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, new HashMap<String,String>()); String name2 = "org.jboss.security.ClientLoginModule"; HashMap<String,String> options = new HashMap<String,String>(); options.put("multi-threaded", "true"); options.put("restore-login-identity", "true"); AppConfigurationEntry ace2 = new AppConfigurationEntry(name2, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); AppConfigurationEntry[] entry = {ace1,ace2}; return entry; } AppConfigurationEntry[] testAbortWithNoRestore() { String name1 = "org.jboss.security.auth.spi.SimpleServerLoginModule"; AppConfigurationEntry ace1 = new AppConfigurationEntry(name1, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, new HashMap<String,String>()); String name2 = "org.jboss.security.ClientLoginModule"; HashMap<String,String> options = new HashMap<String,String>(); options.put("multi-threaded", "true"); AppConfigurationEntry ace2 = new AppConfigurationEntry(name2, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); AppConfigurationEntry[] entry = {ace1,ace2}; return entry; } } public static Test suite() throws Exception { TestSuite suite = new TestSuite(); suite.addTest(new ClientLoginModuleUnitTestCase("testSingleThreaded")); suite.addTest(new ClientLoginModuleUnitTestCase("testSingleThreadedRestoreIdentity")); suite.addTest(new ClientLoginModuleUnitTestCase("testMultiThreaded")); suite.addTest(new ClientLoginModuleUnitTestCase("testMultiThreadedRestoreIdentity")); suite.addTest(new ClientLoginModuleUnitTestCase("testAbortWithRestore")); suite.addTest(new ClientLoginModuleUnitTestCase("testAbortWithNoRestore")); return suite; } public ClientLoginModuleUnitTestCase(String name) { super(name); } protected void setUp() throws Exception { Configuration.setConfiguration(jaasConfig); //Clear SecurityContextAssociation SecurityContextAssociation.clearSecurityContext(); } protected void tearDown() { } public void testSingleThreaded() throws Exception { System.out.println("+++ testSingleThreaded"); UsernamePasswordHandler handler = new UsernamePasswordHandler("jduke", "theduke"); LoginContext lc = new LoginContext("testSingleThreaded", handler); lc.login(); Subject subject = lc.getSubject(); System.out.println("LC.Subject: "+subject); Principal theduke = new SimplePrincipal("jduke"); assertTrue("Principals contains theduke", subject.getPrincipals().contains(theduke)); Principal saPrincipal = SecurityContextAssociation.getPrincipal(); assertTrue("SecurityContextAssociation.getPrincipal == theduke", saPrincipal.equals(theduke)); char[] password = (char[]) SecurityContextAssociation.getCredential(); assertTrue("password == theduke", Arrays.equals(password, "theduke".toCharArray())); assertTrue("Client side association?", SecurityContextAssociation.isClient()); SecurityContext sc = SecurityContextAssociation.getSecurityContext(); assertNotNull("SecurityContext not null", sc); assertEquals("jduke", sc.getUtil().getUserPrincipal().getName()); } public void testSingleThreadedRestoreIdentity() throws Exception { System.out.println("+++ testSingleThreadedRestoreIdentity"); Principal jduke1 = new SimplePrincipal("jduke1"); SecurityContext sc = SecurityContextAssociation.getSecurityContext(); if (sc == null) sc = SecurityContextFactory.createSecurityContext("test"); sc.getUtil().createSubjectInfo(jduke1, "theduke1", new Subject()); SecurityContextAssociation.setSecurityContext(sc); UsernamePasswordHandler handler = new UsernamePasswordHandler("jduke2", "theduke2"); LoginContext lc = new LoginContext("testSingleThreadedRestoreIdentity", handler); lc.login(); Subject subject = lc.getSubject(); System.out.println("LC.Subject: "+subject); Principal jduke2 = new SimplePrincipal("jduke2"); assertTrue("Principals contains jduke2", subject.getPrincipals().contains(jduke2)); Principal saPrincipal = SecurityContextAssociation.getPrincipal(); assertTrue("SecurityContextAssociation.getPrincipal == jduke2", saPrincipal.equals(jduke2)); char[] password = (char[]) SecurityContextAssociation.getCredential(); assertTrue("password == theduke2", Arrays.equals(password, "theduke2".toCharArray())); lc.logout(); // Validate restored state saPrincipal = SecurityContextAssociation.getPrincipal(); assertTrue("SecurityContextAssociation.getPrincipal == jduke1", saPrincipal.equals(jduke1)); String theduke1 = (String) SecurityContextAssociation.getCredential(); assertTrue("password == theduke1", theduke1.equals("theduke1")); } public void testMultiThreaded() throws Exception { TestMultiThreaded r0 = new TestMultiThreaded(); Thread t0 = new Thread(r0, "testMultiThreaded#0"); t0.start(); TestMultiThreaded r1 = new TestMultiThreaded(); Thread t1 = new Thread(r1, "testMultiThreaded#1"); t1.start(); t0.join(); assertTrue(r0.failure == null); t1.join(); assertTrue(r1.failure == null); } static class TestMultiThreaded implements Runnable { Exception failure; public void run() { try { System.out.println("+++ testMultiThreadedRunnable"); UsernamePasswordHandler handler = new UsernamePasswordHandler("jduke", "theduke"); LoginContext lc = new LoginContext("testMultiThreaded", handler); lc.login(); Subject subject = lc.getSubject(); System.out.println("LC.Subject: "+subject); Principal theduke = new SimplePrincipal("jduke"); assertTrue("Principals contains theduke", subject.getPrincipals().contains(theduke)); Principal saPrincipal = SecurityContextAssociation.getPrincipal(); assertTrue("SecurityContextAssociation.getPrincipal == theduke", saPrincipal.equals(theduke)); char[] password = (char[]) SecurityContextAssociation.getCredential(); assertTrue("password == theduke", Arrays.equals(password, "theduke".toCharArray())); } catch(Exception e) { failure = e; } } } public void testMultiThreadedRestoreIdentity() throws Exception { TestMultiThreadedRestoreIdentity r0 = new TestMultiThreadedRestoreIdentity(); Thread t0 = new Thread(r0, "testMultiThreadedRestoreIdentity#0"); t0.start(); TestMultiThreadedRestoreIdentity r1 = new TestMultiThreadedRestoreIdentity(); Thread t1 = new Thread(r1, "testMultiThreadedRestoreIdentity#1"); t1.start(); t0.join(); assertTrue(r0.failure == null); t1.join(); assertTrue(r1.failure == null); } static class TestMultiThreadedRestoreIdentity implements Runnable { Exception failure; public void run() { try { System.out.println("+++ testMultiThreadedRestoreIdentity"); Principal jduke1 = new SimplePrincipal("jduke1"); SecurityContext sc = SecurityContextAssociation.getSecurityContext(); if (sc == null) sc = SecurityContextFactory.createSecurityContext("test"); sc.getUtil().createSubjectInfo(jduke1, "theduke1", new Subject()); SecurityContextAssociation.setSecurityContext(sc); UsernamePasswordHandler handler = new UsernamePasswordHandler("jduke2", "theduke2"); LoginContext lc = new LoginContext("testMultiThreadedRestoreIdentity", handler); lc.login(); Subject subject = lc.getSubject(); System.out.println("LC.Subject: "+subject); Principal jduke2 = new SimplePrincipal("jduke2"); assertTrue("Principals contains jduke2", subject.getPrincipals().contains(jduke2)); Principal saPrincipal = SecurityContextAssociation.getPrincipal(); assertTrue("SecurityContextAssociation.getPrincipal == jduke2", saPrincipal.equals(jduke2)); char[] password = (char[]) SecurityContextAssociation.getCredential(); assertTrue("password == theduke2", Arrays.equals(password, "theduke2".toCharArray())); lc.logout(); // Validate restored state saPrincipal = SecurityContextAssociation.getPrincipal(); assertTrue("SecurityContextAssociation.getPrincipal == jduke1", saPrincipal.equals(jduke1)); String theduke1 = (String) SecurityContextAssociation.getCredential(); assertTrue("password == theduke1", theduke1.equals("theduke1")); } catch(Exception e) { failure = e; } } } //SECURITY-339: ClientLoginModule abort should not clear security context public void testAbortWithRestore() throws Exception { SecurityContext sc = SecurityContextFactory.createSecurityContext("test"); SecurityContextAssociation.setSecurityContext(sc); //Start with successful login. Then a failed login UsernamePasswordHandler handler = new UsernamePasswordHandler("jduke", "jduke"); LoginContext lc = new LoginContext("testAbortWithRestore", handler); lc.login(); Subject subject = lc.getSubject(); assertNotNull("Subject is not null", subject); SecurityContext currentSC = SecurityContextAssociation.getSecurityContext(); assertNotNull("Current Security Context is not null", currentSC); verifySubjectInfo(currentSC); //Failed Login handler = new UsernamePasswordHandler("jduke", "BAD_PASSWORD"); lc = new LoginContext("testAbortWithRestore", handler); try { lc.login(); fail("Should have failed"); } catch(LoginException le) { //pass } subject = lc.getSubject(); assertNull("Subject from login context is null", subject); currentSC = SecurityContextAssociation.getSecurityContext(); assertNotNull("Current Security Context is not null", currentSC); verifySubjectInfo(currentSC); //Successful Login SecurityContextAssociation.setSecurityContext(sc); handler = new UsernamePasswordHandler("jduke", "jduke"); lc = new LoginContext("testAbortWithRestore", handler); lc.login(); subject = lc.getSubject(); assertNotNull("Subject is not null", subject); currentSC = SecurityContextAssociation.getSecurityContext(); assertNotNull("Current Security Context is not null", currentSC); verifySubjectInfo(currentSC); //Failed Login handler = new UsernamePasswordHandler("jduke", "BAD_PASSWORD"); lc = new LoginContext("testAbortWithRestore", handler); try { lc.login(); fail("Should have failed"); } catch(LoginException le) { //pass } subject = lc.getSubject(); assertNull("Subject is null", subject); currentSC = SecurityContextAssociation.getSecurityContext(); assertNotNull("Current Security Context is not null", currentSC); verifySubjectInfo(currentSC); lc.logout(); subject = lc.getSubject(); assertNull("Subject from login context is null", subject); } //SECURITY-339: ClientLoginModule abort should not clear security context public void testAbortWithNoRestore() throws Exception { SecurityContext sc = SecurityContextFactory.createSecurityContext("test"); SecurityContextAssociation.setSecurityContext(sc); //Successful Login SecurityContextAssociation.setSecurityContext(sc); UsernamePasswordHandler handler = new UsernamePasswordHandler("jduke", "jduke"); LoginContext lc = new LoginContext("testAbortWithNoRestore", handler); lc.login(); Subject subject = lc.getSubject(); assertNotNull("Subject is not null", subject); SecurityContext currentSC = SecurityContextAssociation.getSecurityContext(); assertNotNull("Current Security Context is not null", currentSC); this.verifySubjectInfo(currentSC); //Failed Login - calls abort on the login modules handler = new UsernamePasswordHandler("BAD_USER", "BAD_PASSWORD"); lc = new LoginContext("testAbortWithNoRestore", handler); try { lc.login(); fail("Should have failed"); } catch(LoginException le) { //pass } //Ensure that the failed login context does not return a subject subject = lc.getSubject(); assertNull("Subject is null", subject); //We have to ensure that the security context was cleared after abort currentSC = SecurityContextAssociation.getSecurityContext(); assertNull("Current Security Context is not null", currentSC); //Let us go through some logout cycles handler = new UsernamePasswordHandler("jduke", "jduke"); lc = new LoginContext("testAbortWithNoRestore", handler); lc.login(); subject = lc.getSubject(); assertNotNull("Subject is not null", subject); currentSC = SecurityContextAssociation.getSecurityContext(); assertNotNull("Current Security Context is not null", currentSC); this.verifySubjectInfo(currentSC); lc.logout(); assertNull("Current Security Context is null", SecurityContextAssociation.getSecurityContext()); subject = lc.getSubject(); assertEquals("Subject from login context has no principals", 0, subject.getPrincipals().size()); sc = SecurityContextFactory.createSecurityContext("test"); SecurityContextAssociation.setSecurityContext(sc); //Failed Login - calls abort on the login modules handler = new UsernamePasswordHandler("BAD_USER", "BAD_PASSWORD"); lc = new LoginContext("testAbortWithNoRestore", handler); try { lc.login(); fail("Should have failed"); } catch(LoginException le) { //pass } //Ensure that the failed login context does not return a subject subject = lc.getSubject(); assertNull("Subject is null", subject); //We have to ensure that the security context was cleared after abort currentSC = SecurityContextAssociation.getSecurityContext(); assertNull("Current Security Context is not null", currentSC); } private void verifySubjectInfo(SecurityContext currentSC) { SubjectInfo subjectInfo = currentSC.getSubjectInfo(); assertNotNull("SubjectInfo", subjectInfo); Subject subject = subjectInfo.getAuthenticatedSubject(); assertNotNull("Subject is not null", subject); Principal jduke = new SimplePrincipal("jduke"); assertTrue("jduke exists in the subject",subject.getPrincipals().contains(jduke)); assertEquals("jduke exists", jduke, currentSC.getUtil().getUserPrincipal()); assertEquals("jduke exists", jduke, SecurityContextAssociation.getPrincipal()); } }