/* * Copyright 2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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.AdapterDeploymentContext; import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.OidcKeycloakAccount; import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.adapters.springsecurity.KeycloakAuthenticationException; import org.keycloak.adapters.springsecurity.account.KeycloakRole; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.keycloak.common.enums.SslRequired; import org.keycloak.common.util.KeycloakUriBuilder; 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.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.startsWith; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * Keycloak authentication process filter test cases. */ public class KeycloakAuthenticationProcessingFilterTest { private KeycloakAuthenticationProcessingFilter filter; @Mock private AuthenticationManager authenticationManager; @Mock private AdapterDeploymentContext adapterDeploymentContext; @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 OidcKeycloakAccount 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(AdapterDeploymentContext.class))).thenReturn(adapterDeploymentContext); when(adapterDeploymentContext.resolveDeployment(any(HttpFacade.class))).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.setBearerAuthHeader(request); assertTrue(filter.isBearerTokenRequest(request)); } @Test public void testIsBearerTokenRequestCaseInsensitive() throws Exception { assertFalse(filter.isBearerTokenRequest(request)); this.setAuthorizationHeader(request, "bearer"); assertTrue(filter.isBearerTokenRequest(request)); } @Test public void testIsBasicAuthRequest() throws Exception { assertFalse(filter.isBasicAuthRequest(request)); this.setBasicAuthHeader(request); assertTrue(filter.isBasicAuthRequest(request)); } @Test public void testIsBasicAuthRequestCaseInsensitive() throws Exception { assertFalse(filter.isBasicAuthRequest(request)); this.setAuthorizationHeader(request, "basic"); assertTrue(filter.isBasicAuthRequest(request)); } @Test public void testAttemptAuthenticationExpectRedirect() throws Exception { when(keycloakDeployment.getAuthUrl()).thenReturn(KeycloakUriBuilder.fromUri("http://localhost:8080/auth")); when(keycloakDeployment.getResourceName()).thenReturn("resource-name"); when(keycloakDeployment.getStateCookieName()).thenReturn("kc-cookie"); when(keycloakDeployment.getSslRequired()).thenReturn(SslRequired.NONE); when(keycloakDeployment.isBearerOnly()).thenReturn(Boolean.FALSE); filter.attemptAuthentication(request, response); verify(response).setStatus(302); verify(response).setHeader(eq("Location"), startsWith("http://localhost:8080/auth")); } @Test(expected = KeycloakAuthenticationException.class) public void testAttemptAuthenticationWithInvalidToken() throws Exception { request.addHeader("Authorization", "Bearer xxx"); filter.attemptAuthentication(request, response); } @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.setBearerAuthHeader(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 testSuccessfulAuthenticationBasicAuth() throws Exception { Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, authorities); this.setBasicAuthHeader(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.setBearerAuthHeader(request); filter.unsuccessfulAuthentication(request, response, exception); verify(response).sendError(eq(HttpServletResponse.SC_UNAUTHORIZED), anyString()); verify(failureHandler, never()).onAuthenticationFailure(any(HttpServletRequest.class), any(HttpServletResponse.class), any(AuthenticationException.class)); } @Test public void testUnsuccessfulAuthenticatioBasicAuth() throws Exception { AuthenticationException exception = new BadCredentialsException("OOPS"); this.setBasicAuthHeader(request); filter.unsuccessfulAuthentication(request, response, exception); verify(response).sendError(eq(HttpServletResponse.SC_UNAUTHORIZED), 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 setBearerAuthHeader(MockHttpServletRequest request) { setAuthorizationHeader(request, "Bearer"); } private void setBasicAuthHeader(MockHttpServletRequest request) { setAuthorizationHeader(request, "Basic"); } private void setAuthorizationHeader(MockHttpServletRequest request, String scheme) { request.addHeader(KeycloakAuthenticationProcessingFilter.AUTHORIZATION_HEADER, scheme + " " + UUID.randomUUID().toString()); } }