Merge pull request #1222 from Smartling/KEYCLOAK-1273

Improve Spring Security adapter client to client authorization
This commit is contained in:
Stian Thorgersen 2015-05-08 07:01:14 +02:00
commit 53716697ca
12 changed files with 575 additions and 15 deletions

View file

@ -77,7 +77,7 @@ public class SpringSecurityRequestAuthenticator extends RequestAuthenticator {
Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
final KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, securityContext);
logger.warn("Completing bearer authentication. Bearer roles: {} ",roles);
logger.debug("Completing bearer authentication. Bearer roles: {} ",roles);
SecurityContextHolder.getContext().setAuthentication(new KeycloakAuthenticationToken(account));
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);

View file

@ -36,10 +36,35 @@ public class KeycloakClientRequestFactory extends HttpComponentsClientHttpReques
@Override
protected void postProcessHttpRequest(HttpUriRequest request) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) authentication;
KeycloakSecurityContext context = token.getAccount().getKeycloakSecurityContext();
KeycloakSecurityContext context = this.getKeycloakSecurityContext();
request.setHeader(AUTHORIZATION_HEADER, "Bearer " + context.getTokenString());
}
/**
* Returns the {@link KeycloakSecurityContext} from the Spring {@link SecurityContextHolder}'s {@link Authentication}.
*
* @return the current <code>KeycloakSecurityContext</code>
*/
protected KeycloakSecurityContext getKeycloakSecurityContext() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
KeycloakAuthenticationToken token;
KeycloakSecurityContext context;
if (authentication == null) {
throw new IllegalStateException("Cannot set authorization header because there is no authenticated principal");
}
if (!KeycloakAuthenticationToken.class.isAssignableFrom(authentication.getClass())) {
throw new IllegalStateException(
String.format(
"Cannot set authorization header because Authentication is of type %s but %s is required",
authentication.getClass(), KeycloakAuthenticationToken.class)
);
}
token = (KeycloakAuthenticationToken) authentication;
context = token.getAccount().getKeycloakSecurityContext();
return context;
}
}

View file

@ -1,15 +1,32 @@
package org.keycloak.adapters.springsecurity.client;
import org.keycloak.adapters.springsecurity.client.KeycloakClientRequestFactory;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
/**
* Created by scott on 4/22/15.
* Extends Spring's central class for client-side HTTP access, {@link RestTemplate}, adding
* automatic authentication for service to service calls using the currently authenticated Keycloak principal.
* This class is designed to work with other services secured by Keycloak.
*
* <p>
* The main advantage to using this class over Spring's <code>RestTemplate</code> is that authentication
* is handled automatically when both the service making the API call and the service being called are
* protected by Keycloak authentication.
* </p>
*
* @see RestOperations
* @see RestTemplate
*
* @author Scott Rossillo
* @version $Revision: 1 $
*/
//@Service
//@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class KeycloakRestTemplate extends RestTemplate implements RestOperations {
/**
* Create a new instance based on the given {@link KeycloakClientRequestFactory}.
*
* @param factory the <code>KeycloakClientRequestFactory</code> to use when creating new requests
*/
public KeycloakRestTemplate(KeycloakClientRequestFactory factory) {
super(factory);
}

View file

@ -1,19 +1,23 @@
package org.keycloak.adapters.springsecurity.filter;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.AuthChallenge;
import org.keycloak.adapters.AuthOutcome;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.RequestAuthenticator;
import org.keycloak.adapters.springsecurity.AdapterDeploymentContextBean;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationEntryPoint;
import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticator;
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
import org.keycloak.adapters.springsecurity.token.SpringSecurityTokenStore;
import org.keycloak.adapters.springsecurity.token.AdapterTokenStoreFactory;
import org.keycloak.adapters.springsecurity.token.SpringSecurityAdapterTokenStoreFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
@ -24,6 +28,7 @@ import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -37,6 +42,8 @@ import java.io.IOException;
*/
public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter implements ApplicationContextAware {
public static final String AUTHORIZATION_HEADER = "Authorization";
/**
* Request matcher that matches requests to the {@link KeycloakAuthenticationEntryPoint#DEFAULT_LOGIN_URI default login URI}
* and any request with a <code>Authorization</code> header.
@ -48,6 +55,7 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
private ApplicationContext applicationContext;
private AdapterDeploymentContextBean adapterDeploymentContextBean;
private AdapterTokenStoreFactory adapterTokenStoreFactory = new SpringSecurityAdapterTokenStoreFactory();
private AuthenticationManager authenticationManager;
/**
@ -83,11 +91,13 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
Assert.notNull(authenticationManager, "authenticationManager cannot be null");
this.authenticationManager = authenticationManager;
super.setAuthenticationManager(authenticationManager);
super.setAllowSessionCreation(false);
super.setContinueChainBeforeSuccessfulAuthentication(false);
}
@Override
public void afterPropertiesSet() {
adapterDeploymentContextBean= applicationContext.getBean(AdapterDeploymentContextBean.class);
adapterDeploymentContextBean = applicationContext.getBean(AdapterDeploymentContextBean.class);
super.afterPropertiesSet();
}
@ -99,8 +109,8 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
KeycloakDeployment deployment = adapterDeploymentContextBean.getDeployment();
SimpleHttpFacade facade = new SimpleHttpFacade(request, response);
SpringSecurityTokenStore tokenStore = new SpringSecurityTokenStore(deployment, request);
SpringSecurityRequestAuthenticator authenticator
AdapterTokenStore tokenStore = adapterTokenStoreFactory.createAdapterTokenStore(deployment, request);
RequestAuthenticator authenticator
= new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, -1);
AuthOutcome result = authenticator.authenticate();
@ -121,8 +131,92 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
return null;
}
/**
* Returns true if the request was made with a bearer token authorization header.
*
* @param request the current <code>HttpServletRequest</code>
*
* @return <code>true</code> if the <code>request</code> was made with a bearer token authorization header;
* <code>false</code> otherwise.
*/
protected boolean isBearerTokenRequest(HttpServletRequest request) {
String authValue = request.getHeader(AUTHORIZATION_HEADER);
return authValue != null && authValue.startsWith("Bearer");
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
if (!this.isBearerTokenRequest(request)) {
super.successfulAuthentication(request, response, chain, authResult);
return;
}
if (log.isDebugEnabled()) {
log.debug("Authentication success using bearer token. Updating SecurityContextHolder to contain: {}", authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
try {
chain.doFilter(request, response);
} finally {
SecurityContextHolder.clearContext();
}
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
if (this.isBearerTokenRequest(request)) {
SecurityContextHolder.clearContext();
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Unable to authenticate bearer token");
return;
}
super.unsuccessfulAuthentication(request, response, failed);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* Sets the adapter token store factory to use when creating per-request adapter token stores.
*
* @param adapterTokenStoreFactory the <code>AdapterTokenStoreFactory</code> to use
*/
public void setAdapterTokenStoreFactory(AdapterTokenStoreFactory adapterTokenStoreFactory) {
Assert.notNull(adapterTokenStoreFactory, "AdapterTokenStoreFactory cannot be null");
this.adapterTokenStoreFactory = adapterTokenStoreFactory;
}
/**
* This filter does not support explicitly enabling session creation.
*
* @throws UnsupportedOperationException this filter does not support explicitly enabling session creation.
*/
@Override
public final void setAllowSessionCreation(boolean allowSessionCreation) {
throw new UnsupportedOperationException("This filter does not support explicitly setting a session creation policy");
}
/**
* This filter does not support explicitly setting a continue chain before success policy
*
* @throws UnsupportedOperationException this filter does not support explicitly setting a continue chain before success policy
*/
@Override
public final void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
throw new UnsupportedOperationException("This filter does not support explicitly setting a continue chain before success policy");
}
}

View file

@ -0,0 +1,26 @@
package org.keycloak.adapters.springsecurity.token;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import javax.servlet.http.HttpServletRequest;
/**
* Creates a per-request adapter token store.
*
* @author <a href="mailto:srossillo@smartling.com">Scott Rossillo</a>
*/
public interface AdapterTokenStoreFactory {
/**
* Returns a new {@link AdapterTokenStore} for the given {@link KeycloakDeployment} and {@link HttpServletRequest request}.
*
* @param deployment the <code>KeycloakDeployment</code> (required)
* @param request the current <code>HttpServletRequest</code> (required)
*
* @return a new <code>AdapterTokenStore</code> for the given <code>deployment</code> and <code>request</code>
* @throws IllegalArgumentException if either the <code>deployment</code> or <code>request</code> is <code>null</code>
*/
AdapterTokenStore createAdapterTokenStore(KeycloakDeployment deployment, HttpServletRequest request);
}

View file

@ -34,6 +34,8 @@ public class KeycloakAuthenticationToken extends AbstractAuthenticationToken imp
public KeycloakAuthenticationToken(KeycloakAccount account, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
Assert.notNull(account, "KeycloakAccount cannot be null");
Assert.notNull(account.getPrincipal(), "KeycloakAccount.getPrincipal() cannot be null");
this.principal = account.getPrincipal();
this.setDetails(account);
setAuthenticated(true);

View file

@ -0,0 +1,19 @@
package org.keycloak.adapters.springsecurity.token;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import javax.servlet.http.HttpServletRequest;
/**
* {@link AdapterTokenStoreFactory} that returns a new {@link SpringSecurityTokenStore} for each request.
*
* @author <a href="mailto:srossillo@smartling.com">Scott Rossillo</a>
*/
public class SpringSecurityAdapterTokenStoreFactory implements AdapterTokenStoreFactory {
@Override
public AdapterTokenStore createAdapterTokenStore(KeycloakDeployment deployment, HttpServletRequest request) {
return new SpringSecurityTokenStore(deployment, request);
}
}

View file

@ -53,7 +53,7 @@ public class SpringSecurityTokenStore implements AdapterTokenStore {
return false;
}
if (KeycloakAuthenticationToken.class.isAssignableFrom(context.getAuthentication().getClass())) {
if (!KeycloakAuthenticationToken.class.isAssignableFrom(context.getAuthentication().getClass())) {
logger.warn("Expected a KeycloakAuthenticationToken, but found {}", context.getAuthentication());
return false;
}
@ -84,7 +84,7 @@ public class SpringSecurityTokenStore implements AdapterTokenStore {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
throw new IllegalStateException("Went to save Keycloak account {}, but already have {}");
throw new IllegalStateException(String.format("Went to save Keycloak account %s, but already have %s", account, authentication));
}
logger.debug("Saving account info {}", account);
@ -99,6 +99,7 @@ public class SpringSecurityTokenStore implements AdapterTokenStore {
if (session != null) {
session.setAttribute(KeycloakSecurityContext.class.getName(), null);
session.invalidate();
}
SecurityContextHolder.clearContext();

View file

@ -0,0 +1,81 @@
package org.keycloak.adapters.springsecurity.client;
import org.apache.http.client.methods.HttpUriRequest;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.KeycloakAccount;
import org.keycloak.adapters.springsecurity.account.KeycloakRole;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import java.util.Collections;
import java.util.UUID;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* Keycloak client request factory tests.
*/
public class KeycloakClientRequestFactoryTest {
@Spy
private KeycloakClientRequestFactory factory;
@Mock
private KeycloakAccount account;
@Mock
private KeycloakAuthenticationToken keycloakAuthenticationToken;
@Mock
private KeycloakSecurityContext keycloakSecurityContext;
@Mock
private HttpUriRequest request;
private String bearerTokenString;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
bearerTokenString = UUID.randomUUID().toString();
SecurityContextHolder.getContext().setAuthentication(keycloakAuthenticationToken);
when(keycloakAuthenticationToken.getAccount()).thenReturn(account);
when(account.getKeycloakSecurityContext()).thenReturn(keycloakSecurityContext);
when(keycloakSecurityContext.getTokenString()).thenReturn(bearerTokenString);
}
@Test
public void testPostProcessHttpRequest() throws Exception {
factory.postProcessHttpRequest(request);
verify(factory).getKeycloakSecurityContext();
verify(request).setHeader(eq(KeycloakClientRequestFactory.AUTHORIZATION_HEADER), eq("Bearer " + bearerTokenString));
}
@Test
public void testGetKeycloakSecurityContext() throws Exception {
KeycloakSecurityContext context = factory.getKeycloakSecurityContext();
assertNotNull(context);
assertEquals(keycloakSecurityContext, context);
}
@Test(expected = IllegalStateException.class)
public void testGetKeycloakSecurityContextInvalidAuthentication() throws Exception {
SecurityContextHolder.getContext().setAuthentication(
new PreAuthenticatedAuthenticationToken("foo", "bar", Collections.singleton(new KeycloakRole("baz"))));
factory.getKeycloakSecurityContext();
}
@Test(expected = IllegalStateException.class)
public void testGetKeycloakSecurityContextNullAuthentication() throws Exception {
SecurityContextHolder.clearContext();
factory.getKeycloakSecurityContext();
}
}

View file

@ -0,0 +1,152 @@
package org.keycloak.adapters.springsecurity.filter;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.KeycloakAccount;
import org.keycloak.adapters.KeycloakDeployment;
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.context.ApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* Keycloak authentication process filter test cases.
*/
public class KeycloakAuthenticationProcessingFilterTest {
private KeycloakAuthenticationProcessingFilter filter;
@Mock
private AuthenticationManager authenticationManager;
@Mock
private AdapterDeploymentContextBean adapterDeploymentContextBean;
@Mock
private FilterChain chain;
private MockHttpServletRequest request;
@Mock
private HttpServletResponse response;
@Mock
private ApplicationContext applicationContext;
@Mock
private AuthenticationSuccessHandler successHandler;
@Mock
private AuthenticationFailureHandler failureHandler;
@Mock
private KeycloakAccount keycloakAccount;
@Mock
private KeycloakDeployment keycloakDeployment;
@Mock
private KeycloakSecurityContext keycloakSecurityContext;
private final List<? extends GrantedAuthority> authorities = Collections.singletonList(new KeycloakRole("ROLE_USER"));
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
request = spy(new MockHttpServletRequest());
filter = new KeycloakAuthenticationProcessingFilter(authenticationManager);
filter.setApplicationContext(applicationContext);
filter.setAuthenticationSuccessHandler(successHandler);
filter.setAuthenticationFailureHandler(failureHandler);
when(applicationContext.getBean(eq(AdapterDeploymentContextBean.class))).thenReturn(adapterDeploymentContextBean);
when(adapterDeploymentContextBean.getDeployment()).thenReturn(keycloakDeployment);
when(keycloakAccount.getPrincipal()).thenReturn(
new KeycloakPrincipal<KeycloakSecurityContext>(UUID.randomUUID().toString(), keycloakSecurityContext));
filter.afterPropertiesSet();
}
@Test
public void testIsBearerTokenRequest() throws Exception {
assertFalse(filter.isBearerTokenRequest(request));
this.setAuthHeader(request);
assertTrue(filter.isBearerTokenRequest(request));
}
@Test
public void testSuccessfulAuthenticationInteractive() throws Exception {
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, authorities);
filter.successfulAuthentication(request, response, chain, authentication);
verify(successHandler).onAuthenticationSuccess(eq(request), eq(response), eq(authentication));
verify(chain, never()).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));
}
@Test
public void testSuccessfulAuthenticationBearer() throws Exception {
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, authorities);
this.setAuthHeader(request);
filter.successfulAuthentication(request, response, chain, authentication);
verify(chain).doFilter(eq(request), eq(response));
verify(successHandler, never()).onAuthenticationSuccess(any(HttpServletRequest.class), any(HttpServletResponse.class),
any(Authentication.class));
}
@Test
public void testUnsuccessfulAuthenticationInteractive() throws Exception {
AuthenticationException exception = new BadCredentialsException("OOPS");
filter.unsuccessfulAuthentication(request, response, exception);
verify(failureHandler).onAuthenticationFailure(eq(request), eq(response), eq(exception));
}
@Test
public void testUnsuccessfulAuthenticatioBearer() throws Exception {
AuthenticationException exception = new BadCredentialsException("OOPS");
this.setAuthHeader(request);
filter.unsuccessfulAuthentication(request, response, exception);
verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString());
verify(failureHandler, never()).onAuthenticationFailure(any(HttpServletRequest.class), any(HttpServletResponse.class),
any(AuthenticationException.class));
}
@Test(expected = UnsupportedOperationException.class)
public void testSetAllowSessionCreation() throws Exception {
filter.setAllowSessionCreation(true);
}
@Test(expected = UnsupportedOperationException.class)
public void testSetContinueChainBeforeSuccessfulAuthentication() throws Exception {
filter.setContinueChainBeforeSuccessfulAuthentication(true);
}
private void setAuthHeader(MockHttpServletRequest request) {
request.addHeader(KeycloakAuthenticationProcessingFilter.AUTHORIZATION_HEADER, "Bearer " + UUID.randomUUID().toString());
}
}

View file

@ -0,0 +1,49 @@
package org.keycloak.adapters.springsecurity.token;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import javax.servlet.http.HttpServletRequest;
import static org.junit.Assert.*;
/**
* Spring Security adapter token store factory tests.
*/
public class SpringSecurityAdapterTokenStoreFactoryTest {
private AdapterTokenStoreFactory factory = new SpringSecurityAdapterTokenStoreFactory();
@Mock
private KeycloakDeployment deployment;
@Mock
private HttpServletRequest request;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
public void testCreateAdapterTokenStore() throws Exception {
AdapterTokenStore store = factory.createAdapterTokenStore(deployment, request);
assertNotNull(store);
assertTrue(store instanceof SpringSecurityTokenStore);
}
@Test(expected = IllegalArgumentException.class)
public void testCreateAdapterTokenStoreNullDeployment() throws Exception {
factory.createAdapterTokenStore(null, request);
}
@Test(expected = IllegalArgumentException.class)
public void testCreateAdapterTokenStoreNullRequest() throws Exception {
factory.createAdapterTokenStore(deployment, null);
}
}

View file

@ -0,0 +1,94 @@
package org.keycloak.adapters.springsecurity.token;
import org.junit.After;
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.RequestAuthenticator;
import org.keycloak.adapters.springsecurity.account.KeycloakRole;
import org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import javax.servlet.http.HttpSession;
import java.security.Principal;
import java.util.Collections;
import static org.junit.Assert.*;
/**
* Spring Security token store tests.
*/
public class SpringSecurityTokenStoreTest {
private SpringSecurityTokenStore store;
@Mock
private KeycloakDeployment deployment;
@Mock
private Principal principal;
@Mock
private RequestAuthenticator requestAuthenticator;
@Mock
private RefreshableKeycloakSecurityContext keycloakSecurityContext;
private MockHttpServletRequest request;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
request = new MockHttpServletRequest();
store = new SpringSecurityTokenStore(deployment, request);
}
@After
public void tearDown() throws Exception {
SecurityContextHolder.clearContext();
}
@Test
public void testIsCached() throws Exception {
Authentication authentication = new PreAuthenticatedAuthenticationToken("foo", "bar", Collections.singleton(new KeycloakRole("ROLE_FOO")));
SecurityContextHolder.getContext().setAuthentication(authentication);
assertFalse(store.isCached(requestAuthenticator));
}
@Test
public void testSaveAccountInfo() throws Exception {
KeycloakAccount account = new SimpleKeycloakAccount(principal, Collections.singleton("FOO"), keycloakSecurityContext);
Authentication authentication;
store.saveAccountInfo(account);
authentication = SecurityContextHolder.getContext().getAuthentication();
assertNotNull(authentication);
assertTrue(authentication instanceof KeycloakAuthenticationToken);
}
@Test(expected = IllegalStateException.class)
public void testSaveAccountInfoInvalidAuthenticationType() throws Exception {
KeycloakAccount account = new SimpleKeycloakAccount(principal, Collections.singleton("FOO"), keycloakSecurityContext);
Authentication authentication = new PreAuthenticatedAuthenticationToken("foo", "bar", Collections.singleton(new KeycloakRole("ROLE_FOO")));
SecurityContextHolder.getContext().setAuthentication(authentication);
store.saveAccountInfo(account);
}
@Test
public void testLogout() throws Exception {
MockHttpSession session = (MockHttpSession) request.getSession(true);
assertFalse(session.isInvalid());
store.logout();
assertTrue(session.isInvalid());
}
}