diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/jaas/CasLoginModule.java b/cas-client-core/src/main/java/org/jasig/cas/client/jaas/CasLoginModule.java index d181dd9..c76a58a 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/jaas/CasLoginModule.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/jaas/CasLoginModule.java @@ -446,6 +446,14 @@ public class CasLoginModule implements LoginModule { return false; } + // Remove cache entry if assertion caching is enabled + if (this.cacheAssertions) { + for (TicketCredential ticket : this.subject.getPrivateCredentials(TicketCredential.class)) { + logger.debug("Removing cached assertion for {}", ticket); + ASSERTION_CACHE.remove(ticket); + } + } + // Remove all CAS principals removePrincipalsOfType(AssertionPrincipal.class); removePrincipalsOfType(SimplePrincipal.class); diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHandler.java b/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHandler.java index ebd3323..fc530e9 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHandler.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHandler.java @@ -23,6 +23,7 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.jasig.cas.client.util.CommonUtils; +import org.jasig.cas.client.util.ReflectUtils; import org.jasig.cas.client.util.XmlUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -172,6 +173,7 @@ public final class SingleSignOutHandler { try { session.invalidate(); + // TODO: Add request.logout() upon bump to Servlet 3.0 API dependency } catch (final IllegalStateException e) { logger.debug("Error invalidating session.", e); } diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/jaas/CasLoginModuleTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/jaas/CasLoginModuleTests.java index 0952c9e..d255f39 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/jaas/CasLoginModuleTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/jaas/CasLoginModuleTests.java @@ -28,6 +28,7 @@ import javax.security.auth.Subject; import javax.security.auth.login.LoginException; import org.jasig.cas.client.PublicTestHttpServer; import org.jasig.cas.client.validation.TicketValidationException; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -84,7 +85,10 @@ public class CasLoginModuleTests { + ""; server.content = RESPONSE.getBytes(server.encoding); - module.initialize(subject, new ServiceAndTicketCallbackHandler(SERVICE, TICKET), new HashMap(), + module.initialize( + subject, + new ServiceAndTicketCallbackHandler(SERVICE, TICKET), + new HashMap(), options); module.login(); module.commit(); @@ -105,13 +109,16 @@ public class CasLoginModuleTests { final String TICKET = "ST-200000-aA5Yuvrxzpv8Tau1cYQ7-srv1"; final String RESPONSE = "Ticket ST-200000-aA5Yuvrxzpv8Tau1cYQ7-srv1 not recognized"; server.content = RESPONSE.getBytes(server.encoding); - module.initialize(subject, new ServiceAndTicketCallbackHandler(SERVICE, TICKET), new HashMap(), + module.initialize( + subject, + new ServiceAndTicketCallbackHandler(SERVICE, TICKET), + new HashMap(), options); try { module.login(); - fail("Login did not throw LoginException as expected."); - } catch (Exception e) { - assertTrue(e instanceof LoginException); + fail("Login did not throw FailedLoginException as expected."); + } catch (LoginException e) { + assertEquals(TicketValidationException.class, e.getCause().getClass()); } module.commit(); assertNull(module.ticket); @@ -131,7 +138,7 @@ public class CasLoginModuleTests { } /** - * Test assertion cache allows successive logins with same ticket to succeed. + * Confirm that CasLoginModule#logout() destroys cached data and prevents subsequent login w/expired ticket. * @throws Exception On errors. */ @Test @@ -148,24 +155,37 @@ public class CasLoginModuleTests { options.put("cacheTimeout", "1"); server.content = SUCCESS_RESPONSE.getBytes(server.encoding); - module.initialize(subject, new ServiceAndTicketCallbackHandler(SERVICE, TICKET), new HashMap(), + module.initialize( + subject, + new ServiceAndTicketCallbackHandler(SERVICE, TICKET), + new HashMap(), options); module.login(); module.commit(); assertEquals(this.subject.getPrincipals().size(), 3); assertEquals(TICKET, this.subject.getPrivateCredentials().iterator().next().toString()); - Thread.sleep(2000); + // Logout should destroy all authenticated state data including assertion cache entries module.logout(); assertEquals(0, subject.getPrincipals().size()); assertEquals(0, subject.getPrivateCredentials().size()); server.content = FAILURE_RESPONSE.getBytes(server.encoding); - module.initialize(subject, new ServiceAndTicketCallbackHandler(SERVICE, TICKET), new HashMap(), + + // Verify we can't log in again with same ticket + module.initialize( + subject, + new ServiceAndTicketCallbackHandler(SERVICE, TICKET), + new HashMap(), options); - module.login(); - module.commit(); - assertEquals(this.subject.getPrincipals().size(), 3); - assertEquals(TICKET, this.subject.getPrivateCredentials().iterator().next().toString()); + try { + module.login(); + module.commit(); + Assert.fail("Login should have failed."); + } catch (LoginException e) { + assertEquals(TicketValidationException.class, e.getCause().getClass()); + } + assertEquals(0, this.subject.getPrincipals().size()); + assertEquals(0, this.subject.getPrivateCredentials().size()); } /** @@ -190,7 +210,10 @@ public class CasLoginModuleTests { options.put("cacheTimeout", "1"); server.content = SUCCESS_RESPONSE.getBytes(server.encoding); - module.initialize(subject, new ServiceAndTicketCallbackHandler(SERVICE, TICKET), new HashMap(), + module.initialize( + subject, + new ServiceAndTicketCallbackHandler(SERVICE, TICKET), + new HashMap(), options); assertTrue(module.login()); module.commit(); @@ -198,13 +221,16 @@ public class CasLoginModuleTests { Thread.sleep(1100); // Assertion should now be expired from cache server.content = FAILURE_RESPONSE.getBytes(server.encoding); - module.initialize(subject, new ServiceAndTicketCallbackHandler(SERVICE, TICKET), new HashMap(), + module.initialize( + subject, + new ServiceAndTicketCallbackHandler(SERVICE, TICKET), + new HashMap(), options); try { module.login(); - fail("Should have thrown login exception."); + fail("Should have thrown FailedLoginException."); } catch (LoginException e) { - assertTrue(e.getCause() instanceof TicketValidationException); + assertEquals(TicketValidationException.class, e.getCause().getClass()); } }