MiscHttpConfigTests groovy->java
Issue: gh-4939
This commit is contained in:
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package org.springframework.security;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@@ -42,4 +43,12 @@ public class BeanNameCollectingPostProcessor implements BeanPostProcessor {
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Set<String> getBeforeInitPostProcessedBeans() {
|
||||
return beforeInitPostProcessedBeans;
|
||||
}
|
||||
|
||||
public Set<String> getAfterInitPostProcessedBeans() {
|
||||
return afterInitPostProcessedBeans;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,987 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* 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.springframework.security.config.http;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.AccessController;
|
||||
import java.security.Principal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.Appender;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.assertj.core.api.iterable.Extractor;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.security.BeanNameCollectingPostProcessor;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.InsufficientAuthenticationException;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authentication.jaas.AuthorityGranter;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.openid.OpenIDAuthenticationFilter;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
||||
import org.springframework.security.web.csrf.CsrfFilter;
|
||||
import org.springframework.security.web.firewall.FirewalledRequest;
|
||||
import org.springframework.security.web.firewall.HttpFirewall;
|
||||
import org.springframework.security.web.header.HeaderWriterFilter;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.support.XmlWebApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.x509;
|
||||
import static org.springframework.test.util.ReflectionTestUtils.getField;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class MiscHttpConfigTests {
|
||||
private static final String CONFIG_LOCATION_PREFIX =
|
||||
"classpath:org/springframework/security/config/http/MiscHttpConfigTests";
|
||||
|
||||
@Autowired
|
||||
MockMvc mvc;
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Test
|
||||
public void configureWhenUsingMinimalConfigurationThenParses() {
|
||||
this.spring.configLocations(xml("MinimalConfiguration")).autowire();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenUsingAutoConfigThenSetsUpCorrectFilterList() {
|
||||
this.spring.configLocations(xml("AutoConfig")).autowire();
|
||||
assertThatFiltersMatchExpectedAutoConfigList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenUsingSecurityNoneThenNoFiltersAreSetUp() {
|
||||
this.spring.configLocations(xml("NoSecurityForPattern")).autowire();
|
||||
assertThat(getFilters("/unprotected")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenUsingDebugFilterAndPatternIsNotConfigureForSecurityThenRespondsOk()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("NoSecurityForPattern")).autowire();
|
||||
|
||||
this.mvc.perform(get("/unprotected"))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
this.mvc.perform(get("/nomatch"))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenHttpPatternUsesRegexMatchingThenMatchesAccordingly()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("RegexSecurityPattern")).autowire();
|
||||
|
||||
this.mvc.perform(get("/protected"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
|
||||
this.mvc.perform(get("/unprotected"))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenHttpPatternUsesCiRegexMatchingThenMatchesAccordingly()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("CiRegexSecurityPattern")).autowire();
|
||||
|
||||
this.mvc.perform(get("/ProTectEd"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
|
||||
this.mvc.perform(get("/UnProTectEd"))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenHttpPatternUsesCustomRequestMatcherThenMatchesAccordingly()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("CustomRequestMatcher")).autowire();
|
||||
|
||||
this.mvc.perform(get("/protected"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
|
||||
this.mvc.perform(get("/unprotected"))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
/**
|
||||
* SEC-1152
|
||||
*/
|
||||
@Test
|
||||
public void requestWhenUsingMinimalConfigurationThenHonorsAnonymousEndpoints()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("AnonymousEndpoints")).autowire();
|
||||
|
||||
this.mvc.perform(get("/protected"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
|
||||
this.mvc.perform(get("/unprotected"))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
assertThat(getFilter(AnonymousAuthenticationFilter.class)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenAnonymousIsDisabledThenRejectsAnonymousEndpoints()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("AnonymousDisabled")).autowire();
|
||||
|
||||
this.mvc.perform(get("/protected"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
|
||||
this.mvc.perform(get("/unprotected"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
|
||||
assertThat(getFilter(AnonymousAuthenticationFilter.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenAnonymousUsesCustomAttributesThenRespondsWithThoseAttributes()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("AnonymousCustomAttributes")).autowire();
|
||||
|
||||
this.mvc.perform(get("/protected")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(status().isForbidden());
|
||||
|
||||
this.mvc.perform(get("/protected"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("josh"));
|
||||
|
||||
this.mvc.perform(get("/customKey"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(String.valueOf("myCustomKey".hashCode())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenAnonymousUsesMultipleGrantedAuthoritiesThenRespondsWithThoseAttributes()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("AnonymousMultipleAuthorities")).autowire();
|
||||
|
||||
this.mvc.perform(get("/protected")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(status().isForbidden());
|
||||
|
||||
this.mvc.perform(get("/protected"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("josh"));
|
||||
|
||||
this.mvc.perform(get("/customKey"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(String.valueOf("myCustomKey".hashCode())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenInterceptUrlMatchesMethodThenSecuresAccordingly()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("InterceptUrlMethod")).autowire();
|
||||
|
||||
this.mvc.perform(get("/protected")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
this.mvc.perform(post("/protected")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(status().isForbidden());
|
||||
|
||||
this.mvc.perform(post("/protected")
|
||||
.with(httpBasic("poster", "password")))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
this.mvc.perform(delete("/protected")
|
||||
.with(httpBasic("poster", "password")))
|
||||
.andExpect(status().isForbidden());
|
||||
|
||||
this.mvc.perform(delete("/protected")
|
||||
.with(httpBasic("admin", "password")))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenInterceptUrlMatchesMethodAndRequiresHttpsThenSecuresAccordingly()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("InterceptUrlMethodRequiresHttps")).autowire();
|
||||
|
||||
this.mvc.perform(post("/protected").with(csrf()))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
this.mvc.perform(get("/protected")
|
||||
.secure(true)
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(status().isForbidden());
|
||||
|
||||
this.mvc.perform(get("/protected")
|
||||
.secure(true)
|
||||
.with(httpBasic("admin", "password")))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenInterceptUrlMatchesAnyPatternAndRequiresHttpsThenSecuresAccordingly()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("InterceptUrlMethodRequiresHttpsAny")).autowire();
|
||||
|
||||
this.mvc.perform(post("/protected").with(csrf()))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
this.mvc.perform(get("/protected")
|
||||
.secure(true)
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(status().isForbidden());
|
||||
|
||||
this.mvc.perform(get("/protected")
|
||||
.secure(true)
|
||||
.with(httpBasic("admin", "password")))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenOncePerRequestIsFalseThenFilterSecurityInterceptorExercisedForForwards() {
|
||||
this.spring.configLocations(xml("OncePerRequest")).autowire();
|
||||
|
||||
FilterSecurityInterceptor filterSecurityInterceptor = getFilter(FilterSecurityInterceptor.class);
|
||||
assertThat(filterSecurityInterceptor.isObserveOncePerRequest()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenCustomHttpBasicEntryPointRefThenInvokesOnCommence()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("CustomHttpBasicEntryPointRef")).autowire();
|
||||
|
||||
AuthenticationEntryPoint entryPoint = this.spring.getContext().getBean(AuthenticationEntryPoint.class);
|
||||
|
||||
this.mvc.perform(get("/protected"))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
verify(entryPoint).commence(
|
||||
any(HttpServletRequest.class), any(HttpServletResponse.class), any(AuthenticationException.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenInterceptUrlWithRequiresChannelThenAddedChannelFilterToChain() {
|
||||
this.spring.configLocations(xml("InterceptUrlMethodRequiresHttpsAny")).autowire();
|
||||
assertThat(getFilter(ChannelProcessingFilter.class)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenPortsMappedThenRedirectedAccordingly() throws Exception {
|
||||
this.spring.configLocations(xml("PortsMappedInterceptUrlMethodRequiresAny")).autowire();
|
||||
|
||||
this.mvc.perform(get("http://localhost:9080/protected"))
|
||||
.andExpect(redirectedUrl("https://localhost:9443/protected"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenCustomFiltersThenAddedToChainInCorrectOrder() {
|
||||
System.setProperty("customFilterRef", "userFilter");
|
||||
this.spring.configLocations(xml("CustomFilters")).autowire();
|
||||
|
||||
List<Filter> filters = getFilters("/");
|
||||
|
||||
Class<?> userFilterClass = this.spring.getContext().getBean("userFilter").getClass();
|
||||
|
||||
assertThat(filters)
|
||||
.extracting((Extractor<Filter, Class<?>>) filter -> filter.getClass())
|
||||
.containsSubsequence(
|
||||
userFilterClass, userFilterClass,
|
||||
SecurityContextPersistenceFilter.class, LogoutFilter.class,
|
||||
userFilterClass);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenTwoFiltersWithSameOrderThenException() {
|
||||
assertThatCode(() -> this.spring.configLocations(xml("CollidingFilters")).autowire())
|
||||
.isInstanceOf(BeanDefinitionParsingException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenUsingX509ThenAddsX509FilterCorrectly() {
|
||||
this.spring.configLocations(xml("X509")).autowire();
|
||||
|
||||
assertThat(getFilters("/"))
|
||||
.extracting((Extractor<Filter, Class<?>>) filter -> filter.getClass())
|
||||
.containsSubsequence(
|
||||
CsrfFilter.class, X509AuthenticationFilter.class, ExceptionTranslationFilter.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void getWhenUsingX509AndPropertyPlaceholderThenSubjectPrincipalRegexIsConfigured() throws Exception {
|
||||
System.setProperty("subject_principal_regex", "OU=(.*?)(?:,|$)");
|
||||
this.spring.configLocations(xml("X509")).autowire();
|
||||
|
||||
this.mvc.perform(get("/protected")
|
||||
.with(x509("classpath:org/springframework/security/config/http/MiscHttpConfigTests-certificate.pem")))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenUsingInvalidLogoutSuccessUrlThenThrowsException() {
|
||||
assertThatCode(() -> this.spring.configLocations(xml("InvalidLogoutSuccessUrl")).autowire())
|
||||
.isInstanceOf(BeanCreationException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logoutWhenSpecifyingCookiesToDeleteThenSetCookieAdded() throws Exception {
|
||||
this.spring.configLocations(xml("DeleteCookies")).autowire();
|
||||
|
||||
MvcResult result =
|
||||
this.mvc.perform(post("/logout").with(csrf())).andReturn();
|
||||
|
||||
List<String> values = result.getResponse().getHeaders("Set-Cookie");
|
||||
assertThat(values.size()).isEqualTo(2);
|
||||
assertThat(values).extracting(value -> value.split("=")[0]).contains("JSESSIONID", "mycookie");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logoutWhenSpecifyingSuccessHandlerRefThenResponseHandledAccordingly() throws Exception {
|
||||
this.spring.configLocations(xml("LogoutSuccessHandlerRef")).autowire();
|
||||
|
||||
this.mvc.perform(post("/logout").with(csrf()))
|
||||
.andExpect(redirectedUrl("/logoutSuccessEndpoint"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenUnauthenticatedThenUsesConfiguredRequestCache() throws Exception {
|
||||
this.spring.configLocations(xml("RequestCache")).autowire();
|
||||
|
||||
RequestCache requestCache = this.spring.getContext().getBean(RequestCache.class);
|
||||
|
||||
this.mvc.perform(get("/"));
|
||||
|
||||
verify(requestCache).saveRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenUnauthenticatedThenUsesConfiguredAuthenticationEntryPoint() throws Exception {
|
||||
this.spring.configLocations(xml("EntryPoint")).autowire();
|
||||
|
||||
AuthenticationEntryPoint entryPoint = this.spring.getContext().getBean(AuthenticationEntryPoint.class);
|
||||
|
||||
this.mvc.perform(get("/"));
|
||||
|
||||
verify(entryPoint).commence(
|
||||
any(HttpServletRequest.class),
|
||||
any(HttpServletResponse.class),
|
||||
any(AuthenticationException.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* See SEC-750. If the http security post processor causes beans to be instantiated too eagerly, they way miss
|
||||
* additional processing. In this method we have a UserDetailsService which is referenced from the namespace
|
||||
* and also has a post processor registered which will modify it.
|
||||
*/
|
||||
@Test
|
||||
public void configureWhenUsingCustomUserDetailsServiceThenBeanPostProcessorsAreStillApplied() {
|
||||
this.spring.configLocations(xml("Sec750")).autowire();
|
||||
|
||||
BeanNameCollectingPostProcessor postProcessor =
|
||||
this.spring.getContext().getBean(BeanNameCollectingPostProcessor.class);
|
||||
|
||||
assertThat(postProcessor.getBeforeInitPostProcessedBeans())
|
||||
.contains("authenticationProvider", "userService");
|
||||
assertThat(postProcessor.getAfterInitPostProcessedBeans())
|
||||
.contains("authenticationProvider", "userService");
|
||||
|
||||
}
|
||||
|
||||
/* SEC-934 */
|
||||
@Test
|
||||
public void getWhenUsingTwoIdenticalInterceptUrlsThenTheSecondTakesPrecedence() throws Exception {
|
||||
this.spring.configLocations(xml("Sec934")).autowire();
|
||||
|
||||
this.mvc.perform(get("/protected")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
this.mvc.perform(get("/protected")
|
||||
.with(httpBasic("admin", "password")))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenAuthenticatingThenConsultsCustomSecurityContextRepository() throws Exception {
|
||||
this.spring.configLocations(xml("SecurityContextRepository")).autowire();
|
||||
|
||||
SecurityContextRepository repository = this.spring.getContext().getBean(SecurityContextRepository.class);
|
||||
SecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken("user", "password"));
|
||||
when(repository.loadContext(any(HttpRequestResponseHolder.class))).thenReturn(context);
|
||||
|
||||
MvcResult result =
|
||||
this.mvc.perform(get("/protected")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
|
||||
assertThat(result.getRequest().getSession(false)).isNotNull();
|
||||
|
||||
verify(repository, atLeastOnce()).saveContext(
|
||||
any(SecurityContext.class),
|
||||
any(HttpServletRequest.class),
|
||||
any(HttpServletResponse.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenUsingInterceptUrlExpressionsThenAuthorizesAccordingly() throws Exception {
|
||||
this.spring.configLocations(xml("InterceptUrlExpressions")).autowire();
|
||||
|
||||
this.mvc.perform(get("/protected")
|
||||
.with(httpBasic("admin", "password")))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
this.mvc.perform(get("/protected")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(status().isForbidden());
|
||||
|
||||
this.mvc.perform(get("/unprotected")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenUsingCustomExpressionHandlerThenAuthorizesAccordingly() throws Exception {
|
||||
this.spring.configLocations(xml("ExpressionHandler")).autowire();
|
||||
|
||||
PermissionEvaluator permissionEvaluator = this.spring.getContext().getBean(PermissionEvaluator.class);
|
||||
when(permissionEvaluator.hasPermission(any(Authentication.class), any(Object.class), any(Object.class)))
|
||||
.thenReturn(false);
|
||||
|
||||
this.mvc.perform(get("/")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(status().isForbidden());
|
||||
|
||||
verify(permissionEvaluator).hasPermission(any(Authentication.class), any(Object.class), any(Object.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenProtectingLoginPageThenWarningLogged() {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
redirectLogsTo(baos, DefaultFilterChainValidator.class);
|
||||
|
||||
this.spring.configLocations(xml("ProtectedLoginPage")).autowire();
|
||||
|
||||
assertThat(baos.toString()).contains("[WARN]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenUsingDisableUrlRewritingThenRedirectIsNotEncodedByResponse()
|
||||
throws IOException, ServletException {
|
||||
|
||||
this.spring.configLocations(xml("DisableUrlRewriting")).autowire();
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
FilterChainProxy proxy = this.spring.getContext().getBean(FilterChainProxy.class);
|
||||
|
||||
proxy.doFilter(
|
||||
request,
|
||||
new EncodeUrlDenyingHttpServletResponseWrapper(response),
|
||||
(req, resp) -> {});
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_MOVED_TEMPORARILY);
|
||||
assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/login");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenUserDetailsServiceInParentContextThenLocatesSuccessfully() {
|
||||
assertThatCode(() -> this.spring.configLocations(this.xml("MissingUserDetailsService")).autowire())
|
||||
.isInstanceOf(BeansException.class);
|
||||
|
||||
try ( XmlWebApplicationContext parent = new XmlWebApplicationContext() ) {
|
||||
parent.setConfigLocations(this.xml("AutoConfig"));
|
||||
parent.refresh();
|
||||
|
||||
try ( XmlWebApplicationContext child = new XmlWebApplicationContext() ) {
|
||||
child.setParent(parent);
|
||||
child.setConfigLocation(this.xml("MissingUserDetailsService"));
|
||||
child.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenConfiguredWithNoInternalAuthenticationProvidersThenSuccessfullyAuthenticates()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("NoInternalAuthenticationProviders")).autowire();
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "password"))
|
||||
.andExpect(redirectedUrl("/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenUsingDefaultsThenErasesCredentialsAfterAuthentication() throws Exception {
|
||||
this.spring.configLocations(xml("HttpBasic")).autowire();
|
||||
|
||||
this.mvc.perform(get("/password")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(content().string(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenAuthenticationManagerConfiguredToEraseCredentialsThenErasesCredentialsAfterAuthentication()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("AuthenticationManagerEraseCredentials")).autowire();
|
||||
|
||||
this.mvc.perform(get("/password")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(content().string(""));
|
||||
}
|
||||
|
||||
/**
|
||||
* SEC-2020
|
||||
*/
|
||||
@Test
|
||||
public void loginWhenAuthenticationManagerRefConfiguredToKeepCredentialsThenKeepsCredentialsAfterAuthentication()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("AuthenticationManagerRefKeepCredentials")).autowire();
|
||||
|
||||
this.mvc.perform(get("/password")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(content().string("password"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenAuthenticationManagerRefIsNotAProviderManagerThenKeepsCredentialsAccordingly()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("AuthenticationManagerRefNotProviderManager")).autowire();
|
||||
|
||||
this.mvc.perform(get("/password")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(content().string("password"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenJeeFilterThenExtractsRoles() throws Exception {
|
||||
this.spring.configLocations(xml("JeeFilter")).autowire();
|
||||
|
||||
Principal user = mock(Principal.class);
|
||||
when(user.getName()).thenReturn("joe");
|
||||
|
||||
this.mvc.perform(get("/roles")
|
||||
.principal(user)
|
||||
.with(request -> {
|
||||
request.addUserRole("admin");
|
||||
request.addUserRole("user");
|
||||
request.addUserRole("unmapped");
|
||||
return request;
|
||||
}))
|
||||
.andExpect(content().string("ROLE_admin,ROLE_user"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenUsingCustomAuthenticationDetailsSourceRefThenAuthenticationSourcesDetailsAccordingly()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(xml("CustomAuthenticationDetailsSourceRef")).autowire();
|
||||
|
||||
Object details = mock(Object.class);
|
||||
AuthenticationDetailsSource source = this.spring.getContext().getBean(AuthenticationDetailsSource.class);
|
||||
when(source.buildDetails(any(Object.class))).thenReturn(details);
|
||||
|
||||
this.mvc.perform(get("/details")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(content().string(details.getClass().getName()));
|
||||
|
||||
this.mvc.perform(get("/details")
|
||||
.with(x509("classpath:org/springframework/security/config/http/MiscHttpConfigTests-certificate.pem")))
|
||||
.andExpect(content().string(details.getClass().getName()));
|
||||
|
||||
MockHttpSession session = (MockHttpSession)
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "password")
|
||||
.with(csrf()))
|
||||
.andReturn().getRequest().getSession(false);
|
||||
|
||||
this.mvc.perform(get("/details")
|
||||
.session(session))
|
||||
.andExpect(content().string(details.getClass().getName()));
|
||||
|
||||
assertThat(getField(getFilter(OpenIDAuthenticationFilter.class), "authenticationDetailsSource"))
|
||||
.isEqualTo(source);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenUsingJaasApiProvisionThenJaasSubjectContainsUsername() throws Exception {
|
||||
this.spring.configLocations(xml("Jaas")).autowire();
|
||||
|
||||
AuthorityGranter granter = this.spring.getContext().getBean(AuthorityGranter.class);
|
||||
when(granter.grant(any(Principal.class))).thenReturn(new HashSet<>(Arrays.asList("USER")));
|
||||
|
||||
this.mvc.perform(get("/username")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(content().string("user"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenUsingCustomHttpFirewallThenFirewallIsInvoked() throws Exception {
|
||||
this.spring.configLocations(xml("HttpFirewall")).autowire();
|
||||
|
||||
FirewalledRequest request = new FirewalledRequest(new MockHttpServletRequest()) {
|
||||
@Override
|
||||
public void reset() { }
|
||||
};
|
||||
HttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
HttpFirewall firewall = this.spring.getContext().getBean(HttpFirewall.class);
|
||||
when(firewall.getFirewalledRequest(any(HttpServletRequest.class))).thenReturn(request);
|
||||
when(firewall.getFirewalledResponse(any(HttpServletResponse.class))).thenReturn(response);
|
||||
this.mvc.perform(get("/unprotected"));
|
||||
|
||||
verify(firewall).getFirewalledRequest(any(HttpServletRequest.class));
|
||||
verify(firewall).getFirewalledResponse(any(HttpServletResponse.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenUsingCustomAccessDecisionManagerThenAuthorizesAccordingly() throws Exception {
|
||||
this.spring.configLocations(xml("CustomAccessDecisionManager")).autowire();
|
||||
|
||||
this.mvc.perform(get("/unprotected")
|
||||
.with(httpBasic("user", "password")))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
/**
|
||||
* SEC-1893
|
||||
*/
|
||||
@Test
|
||||
public void authenticateWhenUsingPortMapperThenRedirectsAppropriately() throws Exception {
|
||||
this.spring.configLocations(xml("PortsMappedRequiresHttps")).autowire();
|
||||
|
||||
MockHttpSession session = (MockHttpSession)
|
||||
this.mvc.perform(get("https://localhost:9080/protected"))
|
||||
.andExpect(redirectedUrl("https://localhost:9443/login"))
|
||||
.andReturn().getRequest().getSession(false);
|
||||
|
||||
session = (MockHttpSession)
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "password")
|
||||
.session(session)
|
||||
.with(csrf()))
|
||||
.andExpect(redirectedUrl("https://localhost:9443/protected"))
|
||||
.andReturn().getRequest().getSession(false);
|
||||
|
||||
this.mvc.perform(get("http://localhost:9080/protected")
|
||||
.session(session))
|
||||
.andExpect(redirectedUrl("https://localhost:9443/protected"));
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class BasicController {
|
||||
@RequestMapping("/unprotected")
|
||||
public String unprotected() {
|
||||
return "ok";
|
||||
}
|
||||
|
||||
@RequestMapping("/protected")
|
||||
public String protectedMethod(@AuthenticationPrincipal String name) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class CustomKeyController {
|
||||
@GetMapping("/customKey")
|
||||
public String customKey() {
|
||||
Authentication authentication =
|
||||
SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if ( authentication != null &&
|
||||
authentication instanceof AnonymousAuthenticationToken ) {
|
||||
return String.valueOf(
|
||||
((AnonymousAuthenticationToken) authentication).getKeyHash());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class AuthenticationController {
|
||||
@GetMapping("/password")
|
||||
public String password(@AuthenticationPrincipal Authentication authentication) {
|
||||
return (String) authentication.getCredentials();
|
||||
}
|
||||
|
||||
@GetMapping("/roles")
|
||||
public String roles(@AuthenticationPrincipal Authentication authentication) {
|
||||
return authentication.getAuthorities().stream()
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
@GetMapping("/details")
|
||||
public String details(@AuthenticationPrincipal Authentication authentication) {
|
||||
return authentication.getDetails().getClass().getName();
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class JaasController {
|
||||
@GetMapping("/username")
|
||||
public String username() {
|
||||
Subject subject = Subject.getSubject(AccessController.getContext());
|
||||
return subject.getPrincipals().iterator().next().getName();
|
||||
}
|
||||
}
|
||||
|
||||
public static class JaasLoginModule implements LoginModule {
|
||||
private Subject subject;
|
||||
|
||||
@Override
|
||||
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean login() throws LoginException {
|
||||
return this.subject.getPrincipals().add(() -> "user");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commit() throws LoginException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean abort() throws LoginException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean logout() throws LoginException {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static class MockAccessDecisionManager implements AccessDecisionManager {
|
||||
|
||||
@Override
|
||||
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
|
||||
throw new AccessDeniedException("teapot");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static class MockAuthenticationManager implements AuthenticationManager {
|
||||
public Authentication authenticate(Authentication authentication) {
|
||||
return new TestingAuthenticationToken(authentication.getPrincipal(),
|
||||
authentication.getCredentials(),
|
||||
AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||
}
|
||||
}
|
||||
|
||||
static class EncodeUrlDenyingHttpServletResponseWrapper
|
||||
extends HttpServletResponseWrapper {
|
||||
|
||||
public EncodeUrlDenyingHttpServletResponseWrapper(HttpServletResponse response) {
|
||||
super(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeURL(String url) {
|
||||
throw new RuntimeException("Unexpected invocation of encodeURL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeRedirectURL(String url) {
|
||||
throw new RuntimeException("Unexpected invocation of encodeURL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeUrl(String url) {
|
||||
throw new RuntimeException("Unexpected invocation of encodeURL");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeRedirectUrl(String url) {
|
||||
throw new RuntimeException("Unexpected invocation of encodeURL");
|
||||
}
|
||||
}
|
||||
|
||||
private void redirectLogsTo(OutputStream os, Class<?> clazz) {
|
||||
Logger logger = (Logger) LoggerFactory.getLogger(clazz);
|
||||
Appender<ILoggingEvent> appender = mock(Appender.class);
|
||||
when(appender.isStarted()).thenReturn(true);
|
||||
doAnswer(writeTo(os)).when(appender).doAppend(any(ILoggingEvent.class));
|
||||
logger.addAppender(appender);
|
||||
}
|
||||
|
||||
private Answer<ILoggingEvent> writeTo(OutputStream os) {
|
||||
return invocation -> {
|
||||
os.write(invocation.getArgument(0).toString().getBytes());
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private void assertThatFiltersMatchExpectedAutoConfigList() {
|
||||
assertThatFiltersMatchExpectedAutoConfigList("/");
|
||||
}
|
||||
|
||||
private void assertThatFiltersMatchExpectedAutoConfigList(String url) {
|
||||
Iterator<Filter> filters = getFilters(url).iterator();
|
||||
|
||||
assertThat(filters.next()).isInstanceOf(SecurityContextPersistenceFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(WebAsyncManagerIntegrationFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(HeaderWriterFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(CsrfFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(LogoutFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(UsernamePasswordAuthenticationFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(DefaultLoginPageGeneratingFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(DefaultLogoutPageGeneratingFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(BasicAuthenticationFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(RequestCacheAwareFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(SecurityContextHolderAwareRequestFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(AnonymousAuthenticationFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(SessionManagementFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(ExceptionTranslationFilter.class);
|
||||
assertThat(filters.next()).isInstanceOf(FilterSecurityInterceptor.class)
|
||||
.hasFieldOrPropertyWithValue("observeOncePerRequest", true);
|
||||
}
|
||||
|
||||
private <T extends Filter> T getFilter(Class<T> filterClass) {
|
||||
return (T) getFilters("/").stream().filter(filterClass::isInstance).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private List<Filter> getFilters(String url) {
|
||||
FilterChainProxy proxy = this.spring.getContext().getBean(FilterChainProxy.class);
|
||||
return proxy.getFilters(url);
|
||||
}
|
||||
|
||||
private static String xml(String configName) {
|
||||
return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user