From abfec234040463afbbcd7c2c6d0427910ac908d0 Mon Sep 17 00:00:00 2001 From: Scott Rossillo Date: Wed, 10 Jun 2015 12:21:43 -0400 Subject: [PATCH] Fix Spring Security adapter logout handling Stops KeycloakLogoutHandler from throwing an exception if the authentication is not of type KeycloakAuthenticationToken. Fixes KEYCLOAK-1438. --- .../authentication/KeycloakLogoutHandler.java | 27 ++---- .../KeycloakLogoutHandlerTest.java | 96 +++++++++++++++++++ 2 files changed, 102 insertions(+), 21 deletions(-) create mode 100644 integration/spring-security/src/test/java/org/keycloak/adapters/springsecurity/authentication/KeycloakLogoutHandlerTest.java diff --git a/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/authentication/KeycloakLogoutHandler.java b/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/authentication/KeycloakLogoutHandler.java index 6a64765dd6..d843aa7d41 100644 --- a/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/authentication/KeycloakLogoutHandler.java +++ b/integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/authentication/KeycloakLogoutHandler.java @@ -6,15 +6,12 @@ import org.keycloak.adapters.springsecurity.AdapterDeploymentContextBean; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.util.Assert; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; /** * Logs the current user out of Keycloak. @@ -36,29 +33,17 @@ public class KeycloakLogoutHandler implements LogoutHandler { @Override public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { - if (authentication instanceof AnonymousAuthenticationToken) { - log.warn("Attempt to log out an anonymous authentication"); + if (!KeycloakAuthenticationToken.class.isAssignableFrom(authentication.getClass())) { + log.warn("Cannot log out a non-Keycloak authentication: {}", authentication); return; } - try { - handleSingleSignOut(request, response); - } catch (IOException e) { - throw new IllegalStateException("Unable to make logout admin request!", e); - } - + handleSingleSignOut(request, response, (KeycloakAuthenticationToken) authentication); } - protected void handleSingleSignOut(HttpServletRequest request, HttpServletResponse response) throws IOException { - - KeycloakAuthenticationToken authentication = (KeycloakAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + protected void handleSingleSignOut(HttpServletRequest request, HttpServletResponse response, KeycloakAuthenticationToken authenticationToken) { KeycloakDeployment deployment = deploymentContextBean.getDeployment(); - RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) authentication.getAccount().getKeycloakSecurityContext(); - - try { - session.logout(deployment); - } catch (Exception e) { - log.error("Unable to complete Keycloak single sign out", e); - } + RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) authenticationToken.getAccount().getKeycloakSecurityContext(); + session.logout(deployment); } } diff --git a/integration/spring-security/src/test/java/org/keycloak/adapters/springsecurity/authentication/KeycloakLogoutHandlerTest.java b/integration/spring-security/src/test/java/org/keycloak/adapters/springsecurity/authentication/KeycloakLogoutHandlerTest.java new file mode 100644 index 0000000000..2ee32af85e --- /dev/null +++ b/integration/spring-security/src/test/java/org/keycloak/adapters/springsecurity/authentication/KeycloakLogoutHandlerTest.java @@ -0,0 +1,96 @@ +package org.keycloak.adapters.springsecurity.authentication; + +import org.junit.Before; +import org.junit.Test; +import org.keycloak.adapters.KeycloakAccount; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.adapters.RefreshableKeycloakSecurityContext; +import org.keycloak.adapters.springsecurity.AdapterDeploymentContextBean; +import org.keycloak.adapters.springsecurity.account.KeycloakRole; +import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.RememberMeAuthenticationToken; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; + +import java.util.Collection; +import java.util.Collections; +import java.util.UUID; + +import static org.mockito.Mockito.*; + +/** + * Keycloak logout handler tests. + */ +public class KeycloakLogoutHandlerTest { + + private KeycloakAuthenticationToken keycloakAuthenticationToken; + private KeycloakLogoutHandler keycloakLogoutHandler; + + private MockHttpServletRequest request; + private MockHttpServletResponse response; + + @Mock + private AdapterDeploymentContextBean adapterDeploymentContextBean; + + @Mock + private KeycloakAccount keycloakAccount; + + @Mock + private KeycloakDeployment keycloakDeployment; + + @Mock + private RefreshableKeycloakSecurityContext session; + + private Collection authorities = Collections.singleton(new KeycloakRole(UUID.randomUUID().toString())); + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + keycloakAuthenticationToken = mock(KeycloakAuthenticationToken.class); + keycloakLogoutHandler = new KeycloakLogoutHandler(adapterDeploymentContextBean); + request = new MockHttpServletRequest(); + response = new MockHttpServletResponse(); + + when(adapterDeploymentContextBean.getDeployment()).thenReturn(keycloakDeployment); + when(keycloakAuthenticationToken.getAccount()).thenReturn(keycloakAccount); + when(keycloakAccount.getKeycloakSecurityContext()).thenReturn(session); + } + + @Test + public void testLogout() throws Exception { + keycloakLogoutHandler.logout(request, response, keycloakAuthenticationToken); + verify(session).logout(eq(keycloakDeployment)); + } + + @Test + public void testLogoutAnonymousAuthentication() throws Exception { + Authentication authentication = new AnonymousAuthenticationToken(UUID.randomUUID().toString(), UUID.randomUUID().toString(), authorities); + keycloakLogoutHandler.logout(request, response, authentication); + verifyZeroInteractions(session); + } + + @Test + public void testLogoutUsernamePasswordAuthentication() throws Exception { + Authentication authentication = new UsernamePasswordAuthenticationToken(UUID.randomUUID().toString(), UUID.randomUUID().toString(), authorities); + keycloakLogoutHandler.logout(request, response, authentication); + verifyZeroInteractions(session); + } + + @Test + public void testLogoutRememberMeAuthentication() throws Exception { + Authentication authentication = new RememberMeAuthenticationToken(UUID.randomUUID().toString(), UUID.randomUUID().toString(), authorities); + keycloakLogoutHandler.logout(request, response, authentication); + verifyZeroInteractions(session); + } + + @Test + public void testHandleSingleSignOut() throws Exception { + keycloakLogoutHandler.handleSingleSignOut(request, response, keycloakAuthenticationToken); + verify(session).logout(eq(keycloakDeployment)); + } +}