From 8baf82532c3a4114a054bbee7a8ecc790fc40f45 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Tue, 22 Apr 2014 16:47:48 -0500 Subject: [PATCH] SEC-2015: Add spring-security-test --- samples/hellomvc-jc/build.gradle | 2 + samples/hellomvc-jc/pom.xml | 6 + .../samples/config/SecurityConfigTests.java | 113 +++- samples/messages-jc/pom.xml | 12 +- settings.gradle | 3 +- test/pom.xml | 132 +++++ .../context/TestSecurityContextHolder.java | 110 ++++ .../test/context/support/WithMockUser.java | 83 +++ .../WithMockUserSecurityContextFactory.java | 57 ++ .../context/support/WithSecurityContext.java | 62 +++ .../support/WithSecurityContextFactory.java | 47 ++ ...SecurityContextTestExcecutionListener.java | 107 ++++ .../test/context/support/WithUserDetails.java | 65 +++ ...WithUserDetailsSecurityContextFactory.java | 54 ++ .../SecurityMockMvcRequestBuilders.java | 213 ++++++++ .../SecurityMockMvcRequestPostProcessors.java | 491 ++++++++++++++++++ .../SecurityMockMvcResultMatchers.java | 226 ++++++++ .../test/web/support/WebTestUtils.java | 105 ++++ .../TestSecurityContextHolderTests.java | 64 +++ ...thMockUserSecurityContextFactoryTests.java | 81 +++ .../context/support/WithMockUserTests.java | 36 ++ ...ityContextTestExcecutionListenerTests.java | 73 +++ ...serDetailsSecurityContextFactoryTests.java | 70 +++ .../context/support/WithUserDetailsTests.java | 33 ++ ...yMockMvcRequestBuildersFormLoginTests.java | 91 ++++ ...MockMvcRequestBuildersFormLogoutTests.java | 84 +++ ...uestPostProcessorsAuthenticationTests.java | 81 +++ ...MockMvcRequestPostProcessorsCsrfTests.java | 87 ++++ ...estPostProcessorsSecurityContextTests.java | 80 +++ ...ostProcessorsTestSecurityContextTests.java | 81 +++ ...RequestPostProcessorsUserDetailsTests.java | 83 +++ ...MockMvcRequestPostProcessorsUserTests.java | 142 +++++ .../showcase/csrf/CsrfShowcaseTests.java | 98 ++++ .../csrf/CustomCsrfShowcaseTests.java | 108 ++++ .../csrf/DefaultCsrfShowcaseTests.java | 92 ++++ .../showcase/login/AuthenticationTests.java | 107 ++++ .../CustomConfigAuthenticationTests.java | 132 +++++ ...oginRequestBuilderAuthenticationTests.java | 112 ++++ .../DefaultfSecurityRequestsTests.java | 106 ++++ .../secured/SecurityRequestsTests.java | 141 +++++ .../secured/WithUserAuthenticationTests.java | 120 +++++ ...WithUserClassLevelAuthenticationTests.java | 119 +++++ .../WithUserDetailsAuthenticationTests.java | 129 +++++ ...rDetailsClassLevelAuthenticationTests.java | 128 +++++ .../test/web/support/WebTestUtilsTests.java | 152 ++++++ test/template.mf | 18 + test/test.gradle | 13 + .../web/savedrequest/RequestCacheAdapter.java | 108 ++++ 48 files changed, 4647 insertions(+), 10 deletions(-) create mode 100644 test/pom.xml create mode 100644 test/src/main/java/org/springframework/security/test/context/TestSecurityContextHolder.java create mode 100644 test/src/main/java/org/springframework/security/test/context/support/WithMockUser.java create mode 100644 test/src/main/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactory.java create mode 100644 test/src/main/java/org/springframework/security/test/context/support/WithSecurityContext.java create mode 100644 test/src/main/java/org/springframework/security/test/context/support/WithSecurityContextFactory.java create mode 100644 test/src/main/java/org/springframework/security/test/context/support/WithSecurityContextTestExcecutionListener.java create mode 100644 test/src/main/java/org/springframework/security/test/context/support/WithUserDetails.java create mode 100644 test/src/main/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactory.java create mode 100644 test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuilders.java create mode 100644 test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java create mode 100644 test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java create mode 100644 test/src/main/java/org/springframework/security/test/web/support/WebTestUtils.java create mode 100644 test/src/test/java/org/springframework/security/test/context/TestSecurityContextHolderTests.java create mode 100644 test/src/test/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactoryTests.java create mode 100644 test/src/test/java/org/springframework/security/test/context/support/WithMockUserTests.java create mode 100644 test/src/test/java/org/springframework/security/test/context/support/WithSecurityContextTestExcecutionListenerTests.java create mode 100644 test/src/test/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactoryTests.java create mode 100644 test/src/test/java/org/springframework/security/test/context/support/WithUserDetailsTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuildersFormLoginTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuildersFormLogoutTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsAuthenticationTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsCsrfTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsSecurityContextTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsTestSecurityContextTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserDetailsTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/CsrfShowcaseTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/CustomCsrfShowcaseTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/DefaultCsrfShowcaseTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/AuthenticationTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/CustomConfigAuthenticationTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/CustomLoginRequestBuilderAuthenticationTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/DefaultfSecurityRequestsTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/SecurityRequestsTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserAuthenticationTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserClassLevelAuthenticationTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserDetailsAuthenticationTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserDetailsClassLevelAuthenticationTests.java create mode 100644 test/src/test/java/org/springframework/security/test/web/support/WebTestUtilsTests.java create mode 100644 test/template.mf create mode 100644 test/test.gradle create mode 100644 web/src/main/java/org/springframework/security/web/savedrequest/RequestCacheAdapter.java diff --git a/samples/hellomvc-jc/build.gradle b/samples/hellomvc-jc/build.gradle index f2696c79ca..2624e30f81 100644 --- a/samples/hellomvc-jc/build.gradle +++ b/samples/hellomvc-jc/build.gradle @@ -22,4 +22,6 @@ dependencies { runtime "opensymphony:sitemesh:2.4.2", 'cglib:cglib-nodep:2.2.2', 'ch.qos.logback:logback-classic:0.9.30' + + testCompile project(":spring-security-test") } \ No newline at end of file diff --git a/samples/hellomvc-jc/pom.xml b/samples/hellomvc-jc/pom.xml index 76d2a6ba2f..a8c990162b 100644 --- a/samples/hellomvc-jc/pom.xml +++ b/samples/hellomvc-jc/pom.xml @@ -208,6 +208,12 @@ 1.9.5 test + + org.springframework.security + spring-security-test + 4.0.0.CI-SNAPSHOT + test + org.springframework spring-test diff --git a/samples/hellomvc-jc/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java b/samples/hellomvc-jc/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java index 53e0c04696..98efb079d7 100644 --- a/samples/hellomvc-jc/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java +++ b/samples/hellomvc-jc/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -15,19 +15,126 @@ */ package org.springframework.security.samples.config; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import javax.servlet.Filter; + +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.samples.mvc.config.WebMvcConfiguration; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.security.test.context.support.WithSecurityContextTestExcecutionListener; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; /** * @author Rob Winch * */ @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes=SecurityConfig.class) +@ContextConfiguration(classes={RootConfiguration.class, WebMvcConfiguration.class}) +@WebAppConfiguration +@TestExecutionListeners(listeners={ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExcecutionListener.class}) public class SecurityConfigTests { + private MockMvc mvc; + + @Autowired + private WebApplicationContext context; + + @Autowired + private Filter springSecurityFilterChain; + + @Before + public void setup() { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .addFilters(springSecurityFilterChain) + .defaultRequest(get("/").with(testSecurityContext())) + .build(); + } @Test - public void securityConfigurationLoads() {} + public void requestProtectedResourceRequiresAuthentication() throws Exception { + mvc.perform(get("/")) + .andExpect(redirectedUrl("http://localhost/login")); + } + + @Test + public void loginSuccess() throws Exception { + mvc.perform(formLogin()) + .andExpect(redirectedUrl("/")); + } + + @Test + public void loginFailure() throws Exception { + mvc.perform(formLogin().password("invalid")) + .andExpect(redirectedUrl("/login?error")); + } + + @Test + @WithMockUser + public void requestProtectedResourceWithUser() throws Exception { + mvc.perform(get("/")) + .andExpect(status().isOk()); + } + + @Test + @WithMockUser + public void composeMessageRequiresCsrfToken() throws Exception { + MockHttpServletRequestBuilder composeMessage = + post("/") + .param("summary", "New Message") + .param("text", "This is a new message"); + + mvc.perform(composeMessage) + .andExpect(status().isForbidden()); + } + + @Test + @WithMockUser + public void composeMessage() throws Exception { + MockHttpServletRequestBuilder composeMessage = + post("/") + .param("summary", "New Message") + .param("text", "This is a new message") + .with(csrf()); + + mvc.perform(composeMessage) + .andExpect(redirectedUrlPattern("/*")); + } + + @Test + @WithMockUser + public void logoutRequiresCsrfToken() throws Exception { + mvc.perform(post("/logout")) + .andExpect(status().isForbidden()); + } + + @Test + @WithMockUser + public void logoutSuccess() throws Exception { + mvc.perform(logout()) + .andExpect(redirectedUrl("/login?logout")) + .andExpect(unauthenticated()); + } } diff --git a/samples/messages-jc/pom.xml b/samples/messages-jc/pom.xml index ab135a0606..9aa670527d 100644 --- a/samples/messages-jc/pom.xml +++ b/samples/messages-jc/pom.xml @@ -133,12 +133,6 @@ 4.0.2.RELEASE compile - - org.springframework - spring-core - 4.0.2.RELEASE - compile - org.springframework spring-core @@ -151,6 +145,12 @@ + + org.springframework + spring-core + 4.0.2.RELEASE + compile + org.springframework spring-instrument diff --git a/settings.gradle b/settings.gradle index e4ba8a0b67..974809509f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,7 +9,8 @@ def String[] modules = [ 'openid', 'taglibs', 'aspects', - 'crypto' + 'crypto', + 'test' ] def String[] samples = [ diff --git a/test/pom.xml b/test/pom.xml new file mode 100644 index 0000000000..1c7c89ed20 --- /dev/null +++ b/test/pom.xml @@ -0,0 +1,132 @@ + + + 4.0.0 + org.springframework.security + spring-security-test + 4.0.0.CI-SNAPSHOT + spring-security-test + spring-security-test + http://spring.io/spring-security + + spring.io + http://spring.io/ + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + rwinch + Rob Winch + rwinch@gopivotal.com + + + + scm:git:git://github.com/spring-projects/spring-security + scm:git:git://github.com/spring-projects/spring-security + https://github.com/spring-projects/spring-security + + + + + maven-compiler-plugin + + 1.7 + 1.7 + + + + + + + spring-snasphot + https://repo.spring.io/snapshot + + + + + org.springframework.security + spring-security-core + 4.0.0.CI-SNAPSHOT + compile + + + org.springframework.security + spring-security-web + 4.0.0.CI-SNAPSHOT + compile + + + org.springframework + spring-core + 4.0.2.RELEASE + compile + + + commons-logging + commons-logging + + + + + org.springframework + spring-test + 4.0.2.RELEASE + compile + + + commons-logging + commons-logging + 1.1.1 + compile + true + + + org.apache.tomcat + tomcat-servlet-api + 3.0.1 + provided + + + ch.qos.logback + logback-classic + 0.9.29 + test + + + junit + junit + 4.11 + test + + + org.easytesting + fest-assert + 1.4 + test + + + org.mockito + mockito-core + 1.9.5 + test + + + org.slf4j + jcl-over-slf4j + 1.7.5 + test + + + org.springframework.security + spring-security-config + 4.0.0.CI-SNAPSHOT + test + + + diff --git a/test/src/main/java/org/springframework/security/test/context/TestSecurityContextHolder.java b/test/src/main/java/org/springframework/security/test/context/TestSecurityContextHolder.java new file mode 100644 index 0000000000..92e6c65db0 --- /dev/null +++ b/test/src/main/java/org/springframework/security/test/context/TestSecurityContextHolder.java @@ -0,0 +1,110 @@ +/* + * Copyright 2002-2014 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.test.context; + +import javax.servlet.FilterChain; + +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.support.WithSecurityContextTestExcecutionListener; +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; +import org.springframework.security.web.context.SecurityContextPersistenceFilter; +import org.springframework.security.web.context.SecurityContextRepository; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.Assert; + +/** + * The {@link TestSecurityContextHolder} is very similar to + * {@link SecurityContextHolder}, but is necessary for testing. For example, we + * cannot populate the desired {@link SecurityContext} in + * {@link SecurityContextHolder} for web based testing. In a web request, the + * {@link SecurityContextPersistenceFilter} will override the + * {@link SecurityContextHolder} with the value returned by the + * {@link SecurityContextRepository}. At the end of the {@link FilterChain} the + * {@link SecurityContextPersistenceFilter} will clear out the + * {@link SecurityContextHolder}. This means if we make multiple web requests, + * we will not know which {@link SecurityContext} to use on subsequent requests. + * + * Typical usage is as follows: + * + * + * + * @author Rob Winch + * @since 4.0 + * + */ +public final class TestSecurityContextHolder { + + private static final ThreadLocal contextHolder = new ThreadLocal(); + + /** + * Clears the {@link SecurityContext} from {@link TestSecurityContextHolder} + * and {@link SecurityContextHolder}. + */ + public static void clearContext() { + contextHolder.remove(); + SecurityContextHolder.clearContext(); + } + + /** + * Gets the {@link SecurityContext} from {@link TestSecurityContextHolder}. + * + * @return the {@link SecurityContext} from {@link TestSecurityContextHolder}. + */ + public static SecurityContext getContext() { + SecurityContext ctx = contextHolder.get(); + + if (ctx == null) { + ctx = getDefaultContext(); + contextHolder.set(ctx); + } + + return ctx; + } + + /** + * Sets the {@link SecurityContext} on {@link TestSecurityContextHolder} and {@link SecurityContextHolder}. + * @param context the {@link SecurityContext} to use + */ + public static void setContext(SecurityContext context) { + Assert.notNull(context, + "Only non-null SecurityContext instances are permitted"); + contextHolder.set(context); + SecurityContextHolder.setContext(context); + } + + /** + * Gets the default {@link SecurityContext} by delegating to the {@link SecurityContextHolder} + * + * @return the default {@link SecurityContext} + */ + private static SecurityContext getDefaultContext() { + return SecurityContextHolder.getContext(); + } + + private TestSecurityContextHolder() { + } +} diff --git a/test/src/main/java/org/springframework/security/test/context/support/WithMockUser.java b/test/src/main/java/org/springframework/security/test/context/support/WithMockUser.java new file mode 100644 index 0000000000..9b88488cc5 --- /dev/null +++ b/test/src/main/java/org/springframework/security/test/context/support/WithMockUser.java @@ -0,0 +1,83 @@ +/* + * Copyright 2002-2014 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.test.context.support; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.web.servlet.MockMvc; + +/** + * When used with {@link WithSecurityContextTestExcecutionListener} this annotation can be + * added to a test method to emulate running with a mocked user. In order to work with {@link MockMvc} The + * {@link SecurityContext} that is used will have the following properties: + * + *
    + *
  • The {@link SecurityContext} created with be that of + * {@link SecurityContextHolder#createEmptyContext()}
  • + *
  • It will be populated with an {@link UsernamePasswordAuthenticationToken} + * that uses the username of either {@link #value()} or {@link #username()}, + * {@link GrantedAuthority} that are specified by {@link #roles()}, and a + * password specified by {@link #password()}. + *
+ * + * @see WithUserDetails + * + * @author Rob Winch + * @since 4.0 + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +@WithSecurityContext(factory = WithMockUserSecurityContextFactory.class) +public @interface WithMockUser { + /** + * Convenience mechanism for specifying the username. The default is "user". If {@link #username()} is specified it will be used instead of {@link #value()} + * @return + */ + String value() default "user"; + + /** + * The username to be used. Note that {@link #value()} is a synonym for {@link #username()}, but if {@link #username()} is specified it will take precedence. + * @return + */ + String username() default ""; + + /** + * The roles to use. The default is "USER". A {@link GrantedAuthority} will + * be created for each value within roles. Each value in roles will + * automatically be prefixed with "ROLE_". For example, the default will + * result in "ROLE_USER" being used. + * + * @return + */ + String[] roles() default { "USER" }; + + /** + * The password to be used. The default is "password". + * @return + */ + String password() default "password"; +} \ No newline at end of file diff --git a/test/src/main/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactory.java b/test/src/main/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactory.java new file mode 100644 index 0000000000..336e7e1132 --- /dev/null +++ b/test/src/main/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002-2014 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.test.context.support; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; +import org.springframework.util.StringUtils; + +/** + * A {@link WithUserDetailsSecurityContextFactory} that works with {@link WithMockUser}. + * + * @author Rob Winch + * @since 4.0 + * @see WithMockUser + */ +final class WithMockUserSecurityContextFactory implements WithSecurityContextFactory { + + public SecurityContext createSecurityContext(WithMockUser withUser) { + String username = StringUtils.hasLength(withUser.username()) ? withUser.username() : withUser.value(); + if(username == null) { + throw new IllegalArgumentException(withUser + " cannot have null username on both username and value properites"); + } + List authorities = new ArrayList(); + for(String role : withUser.roles()) { + if(role.startsWith("ROLE_")) { + throw new IllegalArgumentException("roles cannot start with ROLE_ Got " + role); + } + authorities.add(new SimpleGrantedAuthority("ROLE_"+role)); + } + User principal = new User(username, withUser.password(), true, true, true, true, authorities); + Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(), principal.getAuthorities()); + SecurityContext context = SecurityContextHolder.createEmptyContext(); + context.setAuthentication(authentication); + return context; + } +} \ No newline at end of file diff --git a/test/src/main/java/org/springframework/security/test/context/support/WithSecurityContext.java b/test/src/main/java/org/springframework/security/test/context/support/WithSecurityContext.java new file mode 100644 index 0000000000..81e8345e5c --- /dev/null +++ b/test/src/main/java/org/springframework/security/test/context/support/WithSecurityContext.java @@ -0,0 +1,62 @@ +/* + * Copyright 2002-2014 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.test.context.support; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContext; + +/** + *

+ * An annotation to determine what {@link SecurityContext} to use. The + * {@link #factory()} attribute must be provided with an instance of + * {@link WithUserDetailsSecurityContextFactory}. + *

+ * + *

+ * Typically this annotation will be used as an meta-annotation as done with + * {@link WithMockUser} and {@link WithUserDetails}. + *

+ * + *

+ * If you would like to create your own implementation of + * {@link WithSecurityContextFactory} you can do so by implementing the + * interface. You can also use {@link Autowired} and other Spring semantics on + * the {@link WithSecurityContextFactory} implementation. + *

+ * + * @author Rob Winch + * @since 4.0 + */ +@Target({ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface WithSecurityContext { + /** + * The {@link WithUserDetailsSecurityContextFactory} to use to create the {@link SecurityContext}. It can contain {@link Autowired} and other Spring annotations. + * + * @return + */ + Class> factory(); +} \ No newline at end of file diff --git a/test/src/main/java/org/springframework/security/test/context/support/WithSecurityContextFactory.java b/test/src/main/java/org/springframework/security/test/context/support/WithSecurityContextFactory.java new file mode 100644 index 0000000000..5cbd656c6d --- /dev/null +++ b/test/src/main/java/org/springframework/security/test/context/support/WithSecurityContextFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-2014 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.test.context.support; + +import java.lang.annotation.Annotation; + +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.test.context.TestSecurityContextHolder; + +/** + * An API that works with WithUserTestExcecutionListener for creating a + * {@link SecurityContext} that is populated in the + * {@link TestSecurityContextHolder}. + * + * @author Rob Winch + * + * @param + * @see WithSecurityContext + * @see WithMockUser + * @see WithUserDetails + * @since 4.0 + */ +public interface WithSecurityContextFactory { + + /** + * Create a {@link SecurityContext} given an Annotation. + * + * @param annotation + * the {@link Annotation} to create the {@link SecurityContext} + * from. Cannot be null. + * @return the {@link SecurityContext} to use. Cannot be null. + */ + SecurityContext createSecurityContext(A annotation); +} \ No newline at end of file diff --git a/test/src/main/java/org/springframework/security/test/context/support/WithSecurityContextTestExcecutionListener.java b/test/src/main/java/org/springframework/security/test/context/support/WithSecurityContextTestExcecutionListener.java new file mode 100644 index 0000000000..8e64727c40 --- /dev/null +++ b/test/src/main/java/org/springframework/security/test/context/support/WithSecurityContextTestExcecutionListener.java @@ -0,0 +1,107 @@ +/* + * Copyright 2002-2014 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.test.context.support; + +import java.lang.annotation.Annotation; + +import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.TestSecurityContextHolder; +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.TestExecutionListener; +import org.springframework.test.context.support.AbstractTestExecutionListener; +import org.springframework.test.web.servlet.MockMvc; + +/** + * A {@link TestExecutionListener} that will find annotations that are annotated + * with {@link WithSecurityContext} on a test method or at the class level. If found, the + * {@link WithSecurityContext#factory()} is used to create a {@link SecurityContext} that + * will be used with this test. If using with {@link MockMvc} the + * {@link SecurityMockMvcRequestPostProcessors#testSecurityContext()} needs to be used + * too. + * + * @author Rob Winch + * @since 4.0 + */ +public class WithSecurityContextTestExcecutionListener extends + AbstractTestExecutionListener { + + /** + * Sets up the {@link SecurityContext} for each test method. First the + * specific method is inspected for a {@link WithSecurityContext} or {@link Annotation} + * that has {@link WithSecurityContext} on it. If that is not found, the class is + * inspected. If still not found, then no {@link SecurityContext} is + * populated. + */ + @Override + public void beforeTestMethod(TestContext testContext) throws Exception { + Annotation[] methodAnnotations = AnnotationUtils + .getAnnotations(testContext.getTestMethod()); + ApplicationContext context = testContext.getApplicationContext(); + SecurityContext securityContext = createSecurityContext( + methodAnnotations, context); + if (securityContext == null) { + Annotation[] classAnnotations = testContext.getTestClass() + .getAnnotations(); + securityContext = createSecurityContext(classAnnotations, context); + } + if (securityContext != null) { + TestSecurityContextHolder.setContext(securityContext); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private SecurityContext createSecurityContext(Annotation[] annotations, + ApplicationContext context) { + for (Annotation a : annotations) { + WithSecurityContext withUser = AnnotationUtils.findAnnotation( + a.annotationType(), WithSecurityContext.class); + if (withUser != null) { + WithSecurityContextFactory factory = createFactory( + withUser, context); + try { + return factory.createSecurityContext(a); + } catch (RuntimeException e) { + throw new IllegalStateException("Unable to create SecurityContext using "+ a, e); + } + } + } + return null; + } + + private WithSecurityContextFactory createFactory( + WithSecurityContext withUser, ApplicationContext context) { + Class> clazz = withUser + .factory(); + try { + return context.getAutowireCapableBeanFactory().createBean(clazz); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Clears out the {@link TestSecurityContextHolder} and the + * {@link SecurityContextHolder} after each test method. + */ + @Override + public void afterTestMethod(TestContext testContext) throws Exception { + TestSecurityContextHolder.clearContext(); + } +} \ No newline at end of file diff --git a/test/src/main/java/org/springframework/security/test/context/support/WithUserDetails.java b/test/src/main/java/org/springframework/security/test/context/support/WithUserDetails.java new file mode 100644 index 0000000000..15c4eb6eb2 --- /dev/null +++ b/test/src/main/java/org/springframework/security/test/context/support/WithUserDetails.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-2014 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.test.context.support; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.test.web.servlet.MockMvc; + +/** + * When used with {@link WithSecurityContextTestExcecutionListener} this annotation can be + * added to a test method to emulate running with a {@link UserDetails} returned + * from the {@link UserDetailsService}. In order to work with {@link MockMvc} + * The {@link SecurityContext} that is used will have the following properties: + * + *
    + *
  • The {@link SecurityContext} created with be that of + * {@link SecurityContextHolder#createEmptyContext()}
  • + *
  • It will be populated with an {@link UsernamePasswordAuthenticationToken} + * that uses the username of either {@link #value()} or {@link #username()}, + * {@link GrantedAuthority} that are specified by {@link #roles()}, and a + * password specified by {@link #password()}. + *
+ * + * @see WithMockUser + * + * @author Rob Winch + * @since 4.0 + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +@WithSecurityContext(factory=WithUserDetailsSecurityContextFactory.class) +public @interface WithUserDetails { + /** + * The username to look up in the {@link UserDetailsService} + * + * @return + */ + String value() default "user"; +} \ No newline at end of file diff --git a/test/src/main/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactory.java b/test/src/main/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactory.java new file mode 100644 index 0000000000..b3b05bb5af --- /dev/null +++ b/test/src/main/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactory.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2014 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.test.context.support; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.util.Assert; + +/** + * A {@link WithUserDetailsSecurityContextFactory} that works with {@link WithUserDetails}. + * + * @see WithUserDetails + * + * @author Rob Winch + * @since 4.0 + */ + +final class WithUserDetailsSecurityContextFactory implements WithSecurityContextFactory { + + private UserDetailsService userDetailsService; + + @Autowired + public WithUserDetailsSecurityContextFactory(UserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + + public SecurityContext createSecurityContext(WithUserDetails withUser) { + String username = withUser.value(); + Assert.hasLength(username, "value() must be non empty String"); + UserDetails principal = userDetailsService.loadUserByUsername(username); + Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(), principal.getAuthorities()); + SecurityContext context = SecurityContextHolder.createEmptyContext(); + context.setAuthentication(authentication); + return context; + } +} \ No newline at end of file diff --git a/test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuilders.java b/test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuilders.java new file mode 100644 index 0000000000..92e0979790 --- /dev/null +++ b/test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuilders.java @@ -0,0 +1,213 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.request; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; + +import javax.servlet.ServletContext; + +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.RequestBuilder; +import org.springframework.test.web.servlet.request.RequestPostProcessor; + +/** + * Contains Spring Security related {@link MockMvc} {@link RequestBuilder}s. + * + * @author Rob Winch + * @since 4.0 + * + */ +public final class SecurityMockMvcRequestBuilders { + + /** + * Creates a request (including any necessary {@link CsrfToken}) that will + * submit a form based login to POST "/login". + * + * @return the FormLoginRequestBuilder for further customizations + */ + public static FormLoginRequestBuilder formLogin() { + return new FormLoginRequestBuilder(); + } + + /** + * Creates a request (including any necessary {@link CsrfToken}) that will + * submit a form based login to POST {@code loginProcessingUrl}. + * + * @param loginProcessingUrl + * the URL to POST to + * + * @return the FormLoginRequestBuilder for further customizations + */ + public static FormLoginRequestBuilder formLogin(String loginProcessingUrl) { + return formLogin().loginProcessingUrl(loginProcessingUrl); + } + + /** + * Creates a logout request. + * + * @return the LogoutRequestBuilder for additional customizations + */ + public static LogoutRequestBuilder logout() { + return new LogoutRequestBuilder(); + } + + /** + * Creates a logout request (including any necessary {@link CsrfToken}) to + * the specified {@code logoutUrl} + * + * @return the LogoutRequestBuilder for additional customizations + */ + public static LogoutRequestBuilder logout(String logoutUrl) { + return new LogoutRequestBuilder().logoutUrl(logoutUrl); + } + + /** + * Creates a logout request (including any necessary {@link CsrfToken}) + * + * @author Rob Winch + * @since 4.0 + */ + public static final class LogoutRequestBuilder implements RequestBuilder { + private String logoutUrl = "/logout"; + private RequestPostProcessor postProcessor = csrf(); + + public MockHttpServletRequest buildRequest(ServletContext servletContext) { + MockHttpServletRequest request = post(logoutUrl) + .buildRequest(servletContext); + return postProcessor.postProcessRequest(request); + } + + /** + * Specifies the logout URL to POST to. Defaults to "/logout". + * + * @param logoutUrl the logout URL to POST to. Defaults to "/logout". + * @return the {@link LogoutRequestBuilder} for additional customizations + */ + public LogoutRequestBuilder logoutUrl(String logoutUrl) { + this.logoutUrl = logoutUrl; + return this; + } + + private LogoutRequestBuilder() {} + } + + /** + * Creates a form based login request including any necessary {@link CsrfToken}. + * + * @author Rob Winch + * @since 4.0 + */ + public static final class FormLoginRequestBuilder implements RequestBuilder { + private String usernameParam = "username"; + private String passwordParam = "password"; + private String username = "user"; + private String password = "password"; + private String loginProcessingUrl = "/login"; + private RequestPostProcessor postProcessor = csrf(); + + public MockHttpServletRequest buildRequest(ServletContext servletContext) { + MockHttpServletRequest request = post(loginProcessingUrl) + .param(usernameParam,username) + .param(passwordParam, password) + .buildRequest(servletContext); + return postProcessor.postProcessRequest(request); + } + + /** + * Specifies the URL to POST to. Default is "/login" + * + * @param loginProcessingUrl the URL to POST to. Default is "/login" + * @return + */ + public FormLoginRequestBuilder loginProcessingUrl(String loginProcessingUrl) { + this.loginProcessingUrl = loginProcessingUrl; + return this; + } + + /** + * The HTTP parameter to place the username. Default is "username". + * @param usernameParameter the HTTP parameter to place the username. Default is "username". + * @return the {@link FormLoginRequestBuilder} for additional customizations + */ + public FormLoginRequestBuilder userParameter(String usernameParameter) { + this.usernameParam = usernameParameter; + return this; + } + + /** + * The HTTP parameter to place the password. Default is "password". + * @param passwordParameter the HTTP parameter to place the password. Default is "password". + * @return the {@link FormLoginRequestBuilder} for additional customizations + */ + public FormLoginRequestBuilder passwordParam(String passwordParameter) { + this.passwordParam = passwordParameter; + return this; + } + + /** + * The value of the password parameter. Default is "password". + * @param password the value of the password parameter. Default is "password". + * @return the {@link FormLoginRequestBuilder} for additional customizations + */ + public FormLoginRequestBuilder password(String password) { + this.password = password; + return this; + } + + /** + * The value of the username parameter. Default is "user". + * @param username the value of the username parameter. Default is "user". + * @return the {@link FormLoginRequestBuilder} for additional customizations + */ + public FormLoginRequestBuilder user(String username) { + this.username = username; + return this; + } + + /** + * Specify both the password parameter name and the password. + * + * @param passwordParameter the HTTP parameter to place the password. Default is "password". + * @param password the value of the password parameter. Default is "password". + * @return the {@link FormLoginRequestBuilder} for additional customizations + */ + public FormLoginRequestBuilder password(String passwordParameter, String password) { + passwordParam(passwordParameter); + this.password = password; + return this; + } + + /** + * Specify both the password parameter name and the password. + * + * @param usernameParameter the HTTP parameter to place the username. Default is "username". + * @param username the value of the username parameter. Default is "user". + * @return the {@link FormLoginRequestBuilder} for additional customizations + */ + public FormLoginRequestBuilder user(String usernameParameter, String username) { + userParameter(usernameParameter); + this.username = username; + return this; + } + + private FormLoginRequestBuilder() {} + } + + private SecurityMockMvcRequestBuilders() {} +} diff --git a/test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java b/test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java new file mode 100644 index 0000000000..f783a5db36 --- /dev/null +++ b/test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java @@ -0,0 +1,491 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.request; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.codec.Base64; +import org.springframework.security.test.context.TestSecurityContextHolder; +import org.springframework.security.test.web.support.WebTestUtils; +import org.springframework.security.web.context.HttpRequestResponseHolder; +import org.springframework.security.web.context.SecurityContextRepository; +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import org.springframework.util.Assert; + +/** + * Contains {@link MockMvc} {@link RequestPostProcessor} implementations for + * Spring Security. + * + * @author Rob Winch + * @since 4.0 + */ +public final class SecurityMockMvcRequestPostProcessors { + + /** + * Creates a {@link RequestPostProcessor} that will automatically populate a + * valid {@link CsrfToken} in the request. + * + * @return the {@link CsrfRequestPostProcessor} for further customizations. + */ + public static CsrfRequestPostProcessor csrf() { + return new CsrfRequestPostProcessor(); + } + + /** + * Creates a {@link RequestPostProcessor} that can be used to ensure that + * the resulting request is ran with the user in the + * {@link TestSecurityContextHolder}. + * + * @return the {@link RequestPostProcessor} to sue + */ + public static RequestPostProcessor testSecurityContext() { + return new TestSecurityContextHolderPostProcessor(); + } + + /** + * Establish a {@link SecurityContext} that has a + * {@link UsernamePasswordAuthenticationToken} for the + * {@link Authentication#getPrincipal()} and a {@link User} for the + * {@link UsernamePasswordAuthenticationToken#getPrincipal()}. All details + * are declarative and do not require that the user actually exists. + * + * @param username + * the username to populate + * @return the {@link UserRequestPostProcessor} for additional customization + */ + public static UserRequestPostProcessor user(String username) { + return new UserRequestPostProcessor(username); + } + + /** + * Establish a {@link SecurityContext} that has a + * {@link UsernamePasswordAuthenticationToken} for the + * {@link Authentication#getPrincipal()} and a custom {@link UserDetails} + * for the {@link UsernamePasswordAuthenticationToken#getPrincipal()}. All + * details are declarative and do not require that the user actually exists. + * + * @param user + * the UserDetails to populate + * @return the {@link RequestPostProcessor} to use + */ + public static RequestPostProcessor user(UserDetails user) { + return new UserDetailsRequestPostProcessor(user); + } + + /** + * Establish a {@link SecurityContext} that uses the specified {@link Authentication} for the + * {@link Authentication#getPrincipal()} and a custom {@link UserDetails}. All + * details are declarative and do not require that the user actually exists. + * + * @param user + * the UserDetails to populate + * @return the {@link RequestPostProcessor} to use + */ + public static RequestPostProcessor authentication( + Authentication authentication) { + return new AuthenticationRequestPostProcessor(authentication); + } + + /** + * Establish the specified {@link SecurityContext} to be used. + */ + public static RequestPostProcessor securityContext( + SecurityContext securityContext) { + return new SecurityContextRequestPostProcessor(securityContext); + } + + /** + * Convenience mechanism for setting the Authorization header to use HTTP + * Basic with the given username and password. This method will + * automatically perform the necessary Base64 encoding. + * + * @param username + * the username to include in the Authorization header. + * @param password the password to include in the Authorization header. + * @return the {@link RequestPostProcessor} to use + */ + public static RequestPostProcessor httpBasic(String username, String password) { + return new HttpBasicRequestPostProcessor(username, password); + } + + /** + * Populates a valid {@link CsrfToken} into the request. + * + * @author Rob Winch + * @since 4.0 + */ + public static class CsrfRequestPostProcessor implements + RequestPostProcessor { + + private boolean asHeader; + + private boolean useInvalidToken; + + /* + * (non-Javadoc) + * + * @see + * org.springframework.test.web.servlet.request.RequestPostProcessor + * #postProcessRequest + * (org.springframework.mock.web.MockHttpServletRequest) + */ + public MockHttpServletRequest postProcessRequest( + MockHttpServletRequest request) { + + CsrfTokenRepository repository = WebTestUtils + .getCsrfTokenRepository(request); + CsrfToken token = repository.generateToken(request); + repository.saveToken(token, request, new MockHttpServletResponse()); + String tokenValue = useInvalidToken ? "invalid" + token.getToken() : token.getToken(); + if(asHeader) { + request.addHeader(token.getHeaderName(), tokenValue); + } else { + request.setParameter(token.getParameterName(), tokenValue); + } + return request; + } + + /** + * Instead of using the {@link CsrfToken} as a request parameter + * (default) will populate the {@link CsrfToken} as a header. + * + * @return the {@link CsrfRequestPostProcessor} for additional customizations + */ + public CsrfRequestPostProcessor asHeader() { + this.asHeader = true; + return this; + } + + /** + * Populates an invalid token value on the request. + * + * @return the {@link CsrfRequestPostProcessor} for additional customizations + */ + public CsrfRequestPostProcessor useInvalidToken() { + this.useInvalidToken = true; + return this; + } + + private CsrfRequestPostProcessor() {} + } + + /** + * Support class for {@link RequestPostProcessor}'s that establish a Spring + * Security context + */ + private static abstract class SecurityContextRequestPostProcessorSupport { + + /** + * Saves the specified {@link Authentication} into an empty + * {@link SecurityContext} using the {@link SecurityContextRepository}. + * + * @param authentication the {@link Authentication} to save + * @param request the {@link HttpServletRequest} to use + */ + final void save(Authentication authentication, + HttpServletRequest request) { + SecurityContext securityContext = SecurityContextHolder + .createEmptyContext(); + securityContext.setAuthentication(authentication); + save(securityContext, request); + } + + /** + * Saves the {@link SecurityContext} using the + * {@link SecurityContextRepository} + * + * @param securityContext the {@link SecurityContext} to save + * @param request the {@link HttpServletRequest} to use + */ + final void save(SecurityContext securityContext, + HttpServletRequest request) { + HttpServletResponse response = new MockHttpServletResponse(); + + HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder( + request, response); + SecurityContextRepository securityContextRepository = WebTestUtils + .getSecurityContextRepository(request); + securityContextRepository.loadContext(requestResponseHolder); + + request = requestResponseHolder.getRequest(); + response = requestResponseHolder.getResponse(); + + securityContextRepository.saveContext(securityContext, request, + response); + } + } + + /** + * Associates the {@link SecurityContext} found in + * {@link TestSecurityContextHolder#getContext()} with the + * {@link MockHttpServletRequest}. + * + * @author Rob Winch + * @since 4.0 + */ + private final static class TestSecurityContextHolderPostProcessor extends + SecurityContextRequestPostProcessorSupport implements + RequestPostProcessor { + + public MockHttpServletRequest postProcessRequest( + MockHttpServletRequest request) { + save(TestSecurityContextHolder.getContext(), request); + return request; + } + } + + /** + * Associates the specified {@link SecurityContext} with the + * {@link MockHttpServletRequest}. + * + * @author Rob Winch + * @since 4.0 + */ + private final static class SecurityContextRequestPostProcessor extends + SecurityContextRequestPostProcessorSupport implements + RequestPostProcessor { + + private final SecurityContext securityContext; + + private SecurityContextRequestPostProcessor( + SecurityContext securityContext) { + this.securityContext = securityContext; + } + + public MockHttpServletRequest postProcessRequest( + MockHttpServletRequest request) { + save(this.securityContext, request); + return request; + } + } + + /** + * Sets the specified {@link Authentication} on an empty + * {@link SecurityContext} and associates it to the + * {@link MockHttpServletRequest} + * + * @author Rob Winch + * @since 4.0 + * + */ + private final static class AuthenticationRequestPostProcessor extends + SecurityContextRequestPostProcessorSupport implements + RequestPostProcessor { + private final Authentication authentication; + + private AuthenticationRequestPostProcessor(Authentication authentication) { + this.authentication = authentication; + } + + public MockHttpServletRequest postProcessRequest( + MockHttpServletRequest request) { + SecurityContext context = SecurityContextHolder.getContext(); + context.setAuthentication(authentication); + save(authentication, request); + return request; + } + } + + /** + * Creates a {@link UsernamePasswordAuthenticationToken} and sets the + * {@link UserDetails} as the principal and associates it to the + * {@link MockHttpServletRequest}. + * + * @author Rob Winch + * @since 4.0 + */ + private final static class UserDetailsRequestPostProcessor implements + RequestPostProcessor { + private final RequestPostProcessor delegate; + + public UserDetailsRequestPostProcessor(UserDetails user) { + Authentication token = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities()); + + delegate = new AuthenticationRequestPostProcessor(token); + } + + public MockHttpServletRequest postProcessRequest( + MockHttpServletRequest request) { + return delegate.postProcessRequest(request); + } + } + + /** + * Creates a {@link UsernamePasswordAuthenticationToken} and sets the + * principal to be a {@link User} and associates it to the + * {@link MockHttpServletRequest}. + * + * @author Rob Winch + * @since 4.0 + */ + public final static class UserRequestPostProcessor extends + SecurityContextRequestPostProcessorSupport implements + RequestPostProcessor { + + private String username; + + private String password = "password"; + + private static final String ROLE_PREFIX = "ROLE_"; + + private Collection authorities = AuthorityUtils + .createAuthorityList("ROLE_USER"); + + private boolean enabled = true; + + private boolean accountNonExpired = true; + + private boolean credentialsNonExpired = true; + + private boolean accountNonLocked = true; + + /** + * Creates a new instance with the given username + * @param username the username to use + */ + private UserRequestPostProcessor(String username) { + Assert.notNull(username, "username cannot be null"); + this.username = username; + } + + /** + * Specify the roles of the user to authenticate as. This method is + * similar to {@link #authorities(GrantedAuthority...)}, but just not as + * flexible. + * + * @param roles + * The roles to populate. Note that if the role does not + * start with {@link #rolePrefix(String)} it will + * automatically be prepended. This means by default + * {@code roles("ROLE_USER")} and {@code roles("USER")} are + * equivalent. + * @see #authorities(GrantedAuthority...) + * @see #rolePrefix(String) + * @return the UserRequestPostProcessor for further customizations + */ + public UserRequestPostProcessor roles(String... roles) { + List authorities = new ArrayList( + roles.length); + for (String role : roles) { + if (role.startsWith(ROLE_PREFIX)) { + throw new IllegalArgumentException("Role should not start with "+ROLE_PREFIX + " since this method automatically prefixes with this value. Got "+ role); + } else { + authorities.add(new SimpleGrantedAuthority(ROLE_PREFIX + + role)); + } + } + this.authorities = authorities; + return this; + } + + /** + * Populates the user's {@link GrantedAuthority}'s. The default is + * ROLE_USER. + * + * @param authorities + * @see #roles(String...) + * @return the UserRequestPostProcessor for further customizations + */ + public UserRequestPostProcessor authorities( + GrantedAuthority... authorities) { + return authorities(Arrays.asList(authorities)); + } + + /** + * Populates the user's {@link GrantedAuthority}'s. The default is + * ROLE_USER. + * + * @param authorities + * @see #roles(String...) + * @return the UserRequestPostProcessor for further customizations + */ + public UserRequestPostProcessor authorities( + Collection authorities) { + this.authorities = authorities; + return this; + } + + /** + * Populates the user's password. The default is "password" + * + * @param password + * the user's password + * @return the UserRequestPostProcessor for further customizations + */ + public UserRequestPostProcessor password(String password) { + this.password = password; + return this; + } + + public MockHttpServletRequest postProcessRequest( + MockHttpServletRequest request) { + UserDetailsRequestPostProcessor delegate = new UserDetailsRequestPostProcessor(createUser()); + return delegate.postProcessRequest(request); + } + + /** + * Creates a new {@link User} + * @return the {@link User} for the principal + */ + private User createUser() { + return new User(username, password, enabled, accountNonExpired, + credentialsNonExpired, accountNonLocked, authorities); + } + } + + private static class HttpBasicRequestPostProcessor implements RequestPostProcessor { + private String headerValue; + + private HttpBasicRequestPostProcessor(String username, String password) { + byte[] toEncode; + try { + toEncode = (username + ":" + password).getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + this.headerValue = "Basic " + new String(Base64.encode(toEncode)); + } + + public MockHttpServletRequest postProcessRequest( + MockHttpServletRequest request) { + request.addHeader("Authorization", headerValue); + return request; + } + } + + private SecurityMockMvcRequestPostProcessors() { } +} \ No newline at end of file diff --git a/test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java b/test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java new file mode 100644 index 0000000000..342e5fc9c3 --- /dev/null +++ b/test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java @@ -0,0 +1,226 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.response; + +import static org.springframework.test.util.AssertionErrors.assertEquals; +import static org.springframework.test.util.AssertionErrors.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.test.web.support.WebTestUtils; +import org.springframework.security.web.context.HttpRequestResponseHolder; +import org.springframework.security.web.context.SecurityContextRepository; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultMatcher; + +/** + * Security related {@link MockMvc} {@link ResultMatcher}s. + * + * @author Rob Winch + * @since 4.0 + */ +public final class SecurityMockMvcResultMatchers { + + /** + * {@link ResultMatcher} that verifies that a specified user is + * authenticated. + * + * @return the {@link AuthenticatedMatcher} to use + */ + public static AuthenticatedMatcher authenticated() { + return new AuthenticatedMatcher(); + } + + /** + * {@link ResultMatcher} that verifies that no user is authenticated. + * + * @return the {@link AuthenticatedMatcher} to use + */ + public static ResultMatcher unauthenticated() { + return new UnAuthenticatedMatcher(); + } + + private static abstract class AuthenticationMatcher> implements ResultMatcher { + + protected SecurityContext load(MvcResult result) { + HttpRequestResponseHolder holder = new HttpRequestResponseHolder(result.getRequest(), result.getResponse()); + SecurityContextRepository repository = WebTestUtils.getSecurityContextRepository(result.getRequest()); + return repository.loadContext(holder); + } + } + + /** + * A {@link MockMvc} {@link ResultMatcher} that verifies a specific user is + * associated to the {@link MvcResult}. + * + * @author Rob Winch + * @since 4.0 + */ + public static final class AuthenticatedMatcher extends AuthenticationMatcher { + + private SecurityContext expectedContext; + private Authentication expectedAuthentication; + private Object expectedAuthenticationPrincipal; + private String expectedAuthenticationName; + private Collection expectedGrantedAuthorities; + + + public void match(MvcResult result) throws Exception { + SecurityContext context = load(result); + + Authentication auth = context.getAuthentication(); + + assertTrue("Authentication should not be null", auth != null); + + if(this.expectedContext != null) { + assertEquals(this.expectedContext + " does not equal " + context, this.expectedContext, context); + } + + if(this.expectedAuthentication != null) { + assertEquals(this.expectedAuthentication + " does not equal " + context.getAuthentication(), this.expectedAuthentication, context.getAuthentication()); + } + + if(this.expectedAuthenticationPrincipal != null) { + assertTrue("Authentication cannot be null", context.getAuthentication() != null); + assertEquals(this.expectedAuthenticationPrincipal + " does not equal " + context.getAuthentication().getPrincipal(), this.expectedAuthenticationPrincipal, context.getAuthentication().getPrincipal()); + } + + if(this.expectedAuthenticationName != null) { + assertTrue("Authentication cannot be null", auth != null); + String name = auth.getName(); + assertEquals(this.expectedAuthenticationName + " does not equal " + name, this.expectedAuthenticationName, name); + } + + if(this.expectedGrantedAuthorities != null) { + assertTrue("Authentication cannot be null", auth != null); + Collection authorities = auth.getAuthorities(); + assertEquals(this.expectedGrantedAuthorities + " does not equal " + authorities, this.expectedGrantedAuthorities, authorities); + } + } + + /** + * Specifies the expected username + * + * @param expected + * the expected username + * @return the {@link AuthenticatedMatcher} for further customization + */ + public AuthenticatedMatcher withUsername(String expected) { + return withAuthenticationName(expected); + } + + /** + * Specifies the expected {@link SecurityContext} + * + * @param expected + * the expected {@link SecurityContext} + * @return the {@link AuthenticatedMatcher} for further customization + */ + public AuthenticatedMatcher withSecurityContext(SecurityContext expected) { + this.expectedContext = expected; + return this; + } + + /** + * Specifies the expected {@link Authentication} + * + * @param expected + * the expected {@link Authentication} + * @return the {@link AuthenticatedMatcher} for further customization + */ + public AuthenticatedMatcher withAuthentication(Authentication expected) { + this.expectedAuthentication = expected; + return this; + } + + /** + * Specifies the expected principal + * + * @param expected + * the expected principal + * @return the {@link AuthenticatedMatcher} for further customization + */ + public AuthenticatedMatcher withAuthenticationPrincipal(Object expected) { + this.expectedAuthenticationPrincipal = expected; + return this; + } + + /** + * Specifies the expected {@link Authentication#getName()} + * + * @param expected + * the expected {@link Authentication#getName()} + * @return the {@link AuthenticatedMatcher} for further customization + */ + public AuthenticatedMatcher withAuthenticationName(String expected) { + this.expectedAuthenticationName = expected; + return this; + } + + /** + * Specifies the {@link Authentication#getAuthorities()} + * + * @param expected the {@link Authentication#getAuthorities()} + * @return the {@link AuthenticatedMatcher} for further customization + */ + public AuthenticatedMatcher withAuthorities(Collection expected) { + this.expectedGrantedAuthorities = expected; + return this; + } + + /** + * Specifies the {@link Authentication#getAuthorities()} + * + * @param expected the roles. Each value is automatically prefixed with "ROLE_" + * @return the {@link AuthenticatedMatcher} for further customization + */ + public AuthenticatedMatcher withRoles(String... roles) { + Collection authorities = new ArrayList(); + for(String role : roles) { + authorities.add(new SimpleGrantedAuthority("ROLE_"+role)); + } + return withAuthorities(authorities); + } + + AuthenticatedMatcher() {} + } + + /** + * A {@link MockMvc} {@link ResultMatcher} that verifies no + * {@link Authentication} is associated with the {@link MvcResult}. + * + * @author Rob Winch + * @since 4.0 + */ + private static final class UnAuthenticatedMatcher extends AuthenticationMatcher{ + + public void match(MvcResult result) throws Exception { + SecurityContext context = load(result); + + assertEquals("",null,context.getAuthentication()); + } + + private UnAuthenticatedMatcher() {} + } + + private SecurityMockMvcResultMatchers() {} +} \ No newline at end of file diff --git a/test/src/main/java/org/springframework/security/test/web/support/WebTestUtils.java b/test/src/main/java/org/springframework/security/test/web/support/WebTestUtils.java new file mode 100644 index 0000000000..a98c06a2b5 --- /dev/null +++ b/test/src/main/java/org/springframework/security/test/web/support/WebTestUtils.java @@ -0,0 +1,105 @@ +/* + * Copyright 2002-2014 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.test.web.support; + +import java.util.List; + +import javax.servlet.Filter; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.security.web.context.SecurityContextPersistenceFilter; +import org.springframework.security.web.context.SecurityContextRepository; +import org.springframework.security.web.csrf.CsrfFilter; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +/** + * A utility class for testing spring security + * + * @author Rob Winch + * @since 4.0 + */ +public abstract class WebTestUtils { + private static final SecurityContextRepository DEFAULT_CONTEXT_REPO = new HttpSessionSecurityContextRepository(); + private static final CsrfTokenRepository DEFAULT_TOKEN_REPO = new HttpSessionCsrfTokenRepository(); + + /** + * Gets the {@link SecurityContextRepository} for the specified + * {@link HttpServletRequest}. If one is not found, a default + * {@link HttpSessionSecurityContextRepository} is used. + * + * @param request + * the {@link HttpServletRequest} to obtain the + * {@link SecurityContextRepository} + * @return the {@link SecurityContextRepository} for the specified + * {@link HttpServletRequest} + */ + public static SecurityContextRepository getSecurityContextRepository(HttpServletRequest request) { + SecurityContextPersistenceFilter filter = findFilter(request, SecurityContextPersistenceFilter.class); + if(filter == null) { + return DEFAULT_CONTEXT_REPO; + } + return (SecurityContextRepository) ReflectionTestUtils.getField(filter, "repo"); + } + + /** + * Gets the {@link CsrfTokenRepository} for the specified + * {@link HttpServletRequest}. If one is not found, the default + * {@link HttpSessionCsrfTokenRepository} is used. + * + * @param request + * the {@link HttpServletRequest} to obtain the + * {@link CsrfTokenRepository} + * @return the {@link CsrfTokenRepository} for the specified + * {@link HttpServletRequest} + */ + public static CsrfTokenRepository getCsrfTokenRepository(HttpServletRequest request) { + CsrfFilter filter = findFilter(request, CsrfFilter.class); + if(filter == null) { + return DEFAULT_TOKEN_REPO; + } + return (CsrfTokenRepository) ReflectionTestUtils.getField(filter, "tokenRepository"); + } + + @SuppressWarnings("unchecked") + private static T findFilter(HttpServletRequest request, Class filterClass) { + WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext()); + if(webApplicationContext == null) { + return null; + } + FilterChainProxy springSecurityFilterChain = null; + try { + springSecurityFilterChain = webApplicationContext.getBean(FilterChainProxy.class); + } catch(NoSuchBeanDefinitionException notFound) { + return null; + } + List filters = (List) ReflectionTestUtils.invokeMethod(springSecurityFilterChain,"getFilters", request); + for(Filter filter : filters) { + if(filterClass.isAssignableFrom(filter.getClass())) { + return (T) filter; + } + } + return null; + } + + private WebTestUtils() {} +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/context/TestSecurityContextHolderTests.java b/test/src/test/java/org/springframework/security/test/context/TestSecurityContextHolderTests.java new file mode 100644 index 0000000000..663df85542 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/context/TestSecurityContextHolderTests.java @@ -0,0 +1,64 @@ +/* + * Copyright 2002-2014 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.test.context; + +import static org.fest.assertions.Assertions.assertThat; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +public class TestSecurityContextHolderTests { + + private SecurityContext context; + + @Before + public void setup() { + context = SecurityContextHolder.createEmptyContext(); + } + + @After + public void cleanup() { + TestSecurityContextHolder.clearContext(); + } + + @Test + public void clearContextClearsBoth() { + SecurityContextHolder.setContext(context); + TestSecurityContextHolder.setContext(context); + + TestSecurityContextHolder.clearContext(); + + assertThat(SecurityContextHolder.getContext()).isNotSameAs(context); + assertThat(TestSecurityContextHolder.getContext()).isNotSameAs(context); + } + + @Test + public void getContextDefaultsNonNull() { + assertThat(TestSecurityContextHolder.getContext()).isNotNull(); + assertThat(SecurityContextHolder.getContext()).isNotNull(); + } + + @Test + public void setContextSetsBoth() { + TestSecurityContextHolder.setContext(context); + + assertThat(TestSecurityContextHolder.getContext()).isSameAs(context); + assertThat(SecurityContextHolder.getContext()).isSameAs(context); + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactoryTests.java b/test/src/test/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactoryTests.java new file mode 100644 index 0000000000..a9422c29c1 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactoryTests.java @@ -0,0 +1,81 @@ +/* + * Copyright 2002-2014 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.test.context.support; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.*; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class WithMockUserSecurityContextFactoryTests { + + @Mock + private WithMockUser withUser; + + private WithMockUserSecurityContextFactory factory; + + @Before + public void setup() { + factory = new WithMockUserSecurityContextFactory(); + } + + @Test(expected=IllegalArgumentException.class) + public void usernameNull() { + factory.createSecurityContext(withUser); + } + + @Test + public void valueDefaultsUsername() { + when(withUser.value()).thenReturn("valueUser"); + when(withUser.password()).thenReturn("password"); + when(withUser.roles()).thenReturn(new String[] { "USER"}); + + assertThat(factory.createSecurityContext(withUser).getAuthentication().getName()).isEqualTo(withUser.value()); + } + + @Test + public void usernamePrioritizedOverValue() { + when(withUser.value()).thenReturn("valueUser"); + when(withUser.username()).thenReturn("customUser"); + when(withUser.password()).thenReturn("password"); + when(withUser.roles()).thenReturn(new String[] { "USER"}); + + assertThat(factory.createSecurityContext(withUser).getAuthentication().getName()).isEqualTo(withUser.username()); + } + + @Test + public void rolesWorks() { + when(withUser.value()).thenReturn("valueUser"); + when(withUser.password()).thenReturn("password"); + when(withUser.roles()).thenReturn(new String[] { "USER", "CUSTOM"}); + + assertThat(factory.createSecurityContext(withUser).getAuthentication().getAuthorities()).onProperty("authority").containsOnly("ROLE_USER","ROLE_CUSTOM"); + } + + @Test(expected = IllegalArgumentException.class) + public void rolesWithRolePrefixFails() { + when(withUser.value()).thenReturn("valueUser"); + when(withUser.password()).thenReturn("password"); + when(withUser.roles()).thenReturn(new String[] { "ROLE_FAIL"}); + + factory.createSecurityContext(withUser); + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/context/support/WithMockUserTests.java b/test/src/test/java/org/springframework/security/test/context/support/WithMockUserTests.java new file mode 100644 index 0000000000..4e8103dfab --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/context/support/WithMockUserTests.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2014 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.test.context.support; + +import static org.fest.assertions.Assertions.assertThat; + +import org.junit.Test; +import org.springframework.core.annotation.AnnotationUtils; + +public class WithMockUserTests { + + @Test + public void defaults() { + WithMockUser mockUser = AnnotationUtils.findAnnotation(Annotated.class, WithMockUser.class); + assertThat(mockUser.value()).isEqualTo("user"); + assertThat(mockUser.username()).isEmpty(); + assertThat(mockUser.password()).isEqualTo("password"); + assertThat(mockUser.roles()).containsOnly("USER"); + } + + @WithMockUser + private class Annotated {} +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/context/support/WithSecurityContextTestExcecutionListenerTests.java b/test/src/test/java/org/springframework/security/test/context/support/WithSecurityContextTestExcecutionListenerTests.java new file mode 100644 index 0000000000..157de2b5bb --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/context/support/WithSecurityContextTestExcecutionListenerTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2002-2014 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.test.context.support; + +import static org.mockito.Mockito.when; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.test.context.TestSecurityContextHolder; +import org.springframework.test.context.TestContext; +import org.springframework.util.ReflectionUtils; + +@RunWith(MockitoJUnitRunner.class) +public class WithSecurityContextTestExcecutionListenerTests { + private ConfigurableApplicationContext context; + + @Mock + private TestContext testContext; + + private WithSecurityContextTestExcecutionListener listener; + + @Before + public void setup() { + listener = new WithSecurityContextTestExcecutionListener(); + context = new AnnotationConfigApplicationContext(Config.class); + } + + @After + public void cleanup() { + TestSecurityContextHolder.clearContext(); + if(context != null) { + context.close(); + } + } + + @Test + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void beforeTestMethodNullSecurityContextNoError() throws Exception { + Class testClass = FakeTest.class; + when(testContext.getApplicationContext()).thenReturn(context); + when(testContext.getTestClass()).thenReturn(testClass); + when(testContext.getTestMethod()).thenReturn(ReflectionUtils.findMethod(testClass, "testNoAnnotation")); + + listener.beforeTestMethod(testContext); + } + + static class FakeTest { + public void testNoAnnotation() {} + } + + @Configuration + static class Config {} +} diff --git a/test/src/test/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactoryTests.java b/test/src/test/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactoryTests.java new file mode 100644 index 0000000000..b1b6fb141a --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactoryTests.java @@ -0,0 +1,70 @@ +/* + * Copyright 2002-2014 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.test.context.support; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; + +@RunWith(MockitoJUnitRunner.class) +public class WithUserDetailsSecurityContextFactoryTests { + + @Mock + private UserDetailsService userDetailsService; + @Mock + private UserDetails userDetails; + + @Mock + private WithUserDetails withUserDetails; + + private WithUserDetailsSecurityContextFactory factory; + + @Before + public void setup() { + factory = new WithUserDetailsSecurityContextFactory(userDetailsService); + } + + @Test(expected=IllegalArgumentException.class) + public void createSecurityContextNullValue() { + factory.createSecurityContext(withUserDetails); + } + + @Test(expected=IllegalArgumentException.class) + public void createSecurityContextEmptyValue() { + when(withUserDetails.value()).thenReturn(""); + factory.createSecurityContext(withUserDetails); + } + + @Test + public void createSecurityContextWithExistingUser() { + String username = "user"; + when(withUserDetails.value()).thenReturn(username); + when(userDetailsService.loadUserByUsername(username)).thenReturn(userDetails); + + SecurityContext context = factory.createSecurityContext(withUserDetails); + assertThat(context.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class); + assertThat(context.getAuthentication().getPrincipal()).isEqualTo(userDetails); + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/context/support/WithUserDetailsTests.java b/test/src/test/java/org/springframework/security/test/context/support/WithUserDetailsTests.java new file mode 100644 index 0000000000..f159bca8a7 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/context/support/WithUserDetailsTests.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002-2014 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.test.context.support; + +import static org.fest.assertions.Assertions.assertThat; + +import org.junit.Test; +import org.springframework.core.annotation.AnnotationUtils; + +public class WithUserDetailsTests { + + @Test + public void defaults() { + WithUserDetails userDetails = AnnotationUtils.findAnnotation(Annotated.class, WithUserDetails.class); + assertThat(userDetails.value()).isEqualTo("user"); + } + + @WithUserDetails + private static class Annotated {} +} diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuildersFormLoginTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuildersFormLoginTests.java new file mode 100644 index 0000000000..6159f5326b --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuildersFormLoginTests.java @@ -0,0 +1,91 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.request; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.fest.assertions.Assertions.assertThat; +import static org.powermock.api.mockito.PowerMockito.spy; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.powermock.api.mockito.PowerMockito.doReturn; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockServletContext; +import org.springframework.security.test.web.support.WebTestUtils; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.security.web.csrf.DefaultCsrfToken; + + +@RunWith(PowerMockRunner.class) +@PrepareForTest({WebTestUtils.class,SecurityMockMvcRequestBuildersFormLoginTests.class}) +public class SecurityMockMvcRequestBuildersFormLoginTests { + @Mock + private CsrfTokenRepository repository; + private DefaultCsrfToken token; + private MockServletContext servletContext; + + @Before + public void setup() throws Exception { + token = new DefaultCsrfToken("header", "param", "token"); + servletContext = new MockServletContext(); + mockWebTestUtils(); + } + + @Test + public void defaults() throws Exception { + MockHttpServletRequest request = formLogin().buildRequest(servletContext); + + assertThat(request.getParameter("username")).isEqualTo("user"); + assertThat(request.getParameter("password")).isEqualTo("password"); + assertThat(request.getMethod()).isEqualTo("POST"); + assertThat(request.getParameter(token.getParameterName())).isEqualTo(token.getToken()); + assertThat(request.getRequestURI()).isEqualTo("/login"); + verify(repository).saveToken(eq(token), any(HttpServletRequest.class), any(HttpServletResponse.class)); + } + + + @Test + public void custom() throws Exception { + MockHttpServletRequest request = formLogin("/j_spring_security_login") + .user("j_username","admin") + .password("j_password","secret") + .buildRequest(servletContext); + + assertThat(request.getParameter("j_username")).isEqualTo("admin"); + assertThat(request.getParameter("j_password")).isEqualTo("secret"); + assertThat(request.getMethod()).isEqualTo("POST"); + assertThat(request.getParameter(token.getParameterName())).isEqualTo(token.getToken()); + assertThat(request.getRequestURI()).isEqualTo("/j_spring_security_login"); + verify(repository).saveToken(eq(token), any(HttpServletRequest.class), any(HttpServletResponse.class)); + } + + private void mockWebTestUtils() throws Exception { + spy(WebTestUtils.class); + doReturn(repository).when(WebTestUtils.class,"getCsrfTokenRepository",any(HttpServletRequest.class)); + when(repository.generateToken(any(HttpServletRequest.class))).thenReturn(token); + } +} diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuildersFormLogoutTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuildersFormLogoutTests.java new file mode 100644 index 0000000000..313e3db8a6 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuildersFormLogoutTests.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.request; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.fest.assertions.Assertions.assertThat; +import static org.powermock.api.mockito.PowerMockito.spy; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.powermock.api.mockito.PowerMockito.doReturn; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.logout; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockServletContext; +import org.springframework.security.test.web.support.WebTestUtils; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.security.web.csrf.DefaultCsrfToken; + + +@RunWith(PowerMockRunner.class) +@PrepareForTest({WebTestUtils.class,SecurityMockMvcRequestBuildersFormLogoutTests.class}) +public class SecurityMockMvcRequestBuildersFormLogoutTests { + @Mock + private CsrfTokenRepository repository; + private DefaultCsrfToken token; + private MockServletContext servletContext; + + @Before + public void setup() { + token = new DefaultCsrfToken("header", "param", "token"); + servletContext = new MockServletContext(); + } + + @Test + public void defaults() throws Exception { + mockWebTestUtils(); + MockHttpServletRequest request = logout().buildRequest(servletContext); + + assertThat(request.getMethod()).isEqualTo("POST"); + assertThat(request.getParameter(token.getParameterName())).isEqualTo(token.getToken()); + assertThat(request.getRequestURI()).isEqualTo("/logout"); + verify(repository).saveToken(eq(token), any(HttpServletRequest.class), any(HttpServletResponse.class)); + } + + @Test + public void custom() throws Exception { + mockWebTestUtils(); + MockHttpServletRequest request = logout("/admin/logout").buildRequest(servletContext); + + assertThat(request.getMethod()).isEqualTo("POST"); + assertThat(request.getParameter(token.getParameterName())).isEqualTo(token.getToken()); + assertThat(request.getRequestURI()).isEqualTo("/admin/logout"); + verify(repository).saveToken(eq(token), any(HttpServletRequest.class), any(HttpServletResponse.class)); + } + + private void mockWebTestUtils() throws Exception { + spy(WebTestUtils.class); + doReturn(repository).when(WebTestUtils.class,"getCsrfTokenRepository",any(HttpServletRequest.class)); + when(repository.generateToken(any(HttpServletRequest.class))).thenReturn(token); + } +} diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsAuthenticationTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsAuthenticationTests.java new file mode 100644 index 0000000000..681ffc8c22 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsAuthenticationTests.java @@ -0,0 +1,81 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.request; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.spy; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; + +import javax.servlet.http.HttpServletResponse; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.test.context.TestSecurityContextHolder; +import org.springframework.security.test.web.support.WebTestUtils; +import org.springframework.security.web.context.SecurityContextRepository; + +@RunWith(PowerMockRunner.class) +@PrepareOnlyThisForTest(WebTestUtils.class) +public class SecurityMockMvcRequestPostProcessorsAuthenticationTests { + @Captor + private ArgumentCaptor contextCaptor; + @Mock + private SecurityContextRepository repository; + + private MockHttpServletRequest request; + + @Mock + private Authentication authentication; + + @Before + public void setup() { + request = new MockHttpServletRequest(); + mockWebTestUtils(); + } + + @After + public void cleanup() { + TestSecurityContextHolder.clearContext(); + } + + @Test + public void userDetails() { + authentication(authentication).postProcessRequest(request); + + verify(repository).saveContext(contextCaptor.capture(), eq(request), any(HttpServletResponse.class)); + SecurityContext context = contextCaptor.getValue(); + assertThat(context.getAuthentication()).isSameAs(authentication); + } + + private void mockWebTestUtils() { + spy(WebTestUtils.class); + when(WebTestUtils.getSecurityContextRepository(request)).thenReturn(repository); + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsCsrfTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsCsrfTests.java new file mode 100644 index 0000000000..d09064cdf0 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsCsrfTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.request; + +import static org.fest.assertions.Assertions.assertThat; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.powermock.api.mockito.PowerMockito.*; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.security.test.web.support.WebTestUtils; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.security.web.csrf.DefaultCsrfToken; + +@RunWith(PowerMockRunner.class) +@PrepareOnlyThisForTest(WebTestUtils.class) +public class SecurityMockMvcRequestPostProcessorsCsrfTests { + @Mock + private CsrfTokenRepository repository; + private DefaultCsrfToken token; + + private MockHttpServletRequest request; + + @Before + public void setup() { + token = new DefaultCsrfToken("header", "param", "token"); + request = new MockHttpServletRequest(); + mockWebTestUtils(); + } + + @Test + public void csrfWithParam() { + MockHttpServletRequest postProcessedRequest = csrf().postProcessRequest(request); + + assertThat(postProcessedRequest.getParameter(token.getParameterName())).isEqualTo(token.getToken()); + assertThat(postProcessedRequest.getHeader(token.getHeaderName())).isNull(); + } + + @Test + public void csrfWithHeader() { + MockHttpServletRequest postProcessedRequest = csrf().asHeader().postProcessRequest(request); + + assertThat(postProcessedRequest.getParameter(token.getParameterName())).isNull(); + assertThat(postProcessedRequest.getHeader(token.getHeaderName())).isEqualTo(token.getToken()); + } + + @Test + public void csrfWithInvalidParam() { + MockHttpServletRequest postProcessedRequest = csrf().useInvalidToken().postProcessRequest(request); + + assertThat(postProcessedRequest.getParameter(token.getParameterName())).isNotEmpty().isNotEqualTo(token.getToken()); + assertThat(postProcessedRequest.getHeader(token.getHeaderName())).isNull(); + } + + @Test + public void csrfWithInvalidHeader() { + MockHttpServletRequest postProcessedRequest = csrf().asHeader().useInvalidToken().postProcessRequest(request); + + assertThat(postProcessedRequest.getParameter(token.getParameterName())).isNull(); + assertThat(postProcessedRequest.getHeader(token.getHeaderName())).isNotEmpty().isNotEqualTo(token.getToken()); + } + + private void mockWebTestUtils() { + spy(WebTestUtils.class); + when(WebTestUtils.getCsrfTokenRepository(request)).thenReturn(repository); + when(repository.loadToken(request)).thenReturn(token); + when(repository.generateToken(request)).thenReturn(token); + } +} diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsSecurityContextTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsSecurityContextTests.java new file mode 100644 index 0000000000..f6081b2580 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsSecurityContextTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.request; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.spy; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.securityContext; + +import javax.servlet.http.HttpServletResponse; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.test.context.TestSecurityContextHolder; +import org.springframework.security.test.web.support.WebTestUtils; +import org.springframework.security.web.context.SecurityContextRepository; + +@RunWith(PowerMockRunner.class) +@PrepareOnlyThisForTest(WebTestUtils.class) +public class SecurityMockMvcRequestPostProcessorsSecurityContextTests { + @Captor + private ArgumentCaptor contextCaptor; + @Mock + private SecurityContextRepository repository; + + private MockHttpServletRequest request; + + @Mock + private SecurityContext expectedContext; + + @Before + public void setup() { + request = new MockHttpServletRequest(); + mockWebTestUtils(); + } + + @After + public void cleanup() { + TestSecurityContextHolder.clearContext(); + } + + @Test + public void userDetails() { + securityContext(expectedContext).postProcessRequest(request); + + verify(repository).saveContext(contextCaptor.capture(), eq(request), any(HttpServletResponse.class)); + SecurityContext context = contextCaptor.getValue(); + assertThat(context).isSameAs(this.expectedContext); + } + + private void mockWebTestUtils() { + spy(WebTestUtils.class); + when(WebTestUtils.getSecurityContextRepository(request)).thenReturn(repository); + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsTestSecurityContextTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsTestSecurityContextTests.java new file mode 100644 index 0000000000..39b1457f22 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsTestSecurityContextTests.java @@ -0,0 +1,81 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.request; + +import static org.powermock.api.mockito.PowerMockito.*; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.any; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.testSecurityContext; + +import javax.servlet.http.HttpServletResponse; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.test.context.TestSecurityContextHolder; +import org.springframework.security.test.web.support.WebTestUtils; +import org.springframework.security.web.context.SecurityContextRepository; + +@RunWith(PowerMockRunner.class) +@PrepareOnlyThisForTest(WebTestUtils.class) +public class SecurityMockMvcRequestPostProcessorsTestSecurityContextTests { + @Mock + private SecurityContext context; + @Mock + private SecurityContextRepository repository; + + private MockHttpServletRequest request; + + @Before + public void setup() { + request = new MockHttpServletRequest(); + mockWebTestUtils(); + } + + @After + public void cleanup() { + TestSecurityContextHolder.clearContext(); + } + + @Test + public void testSecurityContextSaves() { + TestSecurityContextHolder.setContext(context); + + testSecurityContext().postProcessRequest(request); + + verify(repository).saveContext(eq(context), eq(request), any(HttpServletResponse.class)); + } + + // Ensure it does not fail if TestSecurityContextHolder is not initialized + @Test + public void testSecurityContextNoContext() { + testSecurityContext().postProcessRequest(request); + + verify(repository).saveContext(any(SecurityContext.class), eq(request), any(HttpServletResponse.class)); + } + + private void mockWebTestUtils() { + spy(WebTestUtils.class); + when(WebTestUtils.getSecurityContextRepository(request)).thenReturn(repository); + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserDetailsTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserDetailsTests.java new file mode 100644 index 0000000000..2576e33d2e --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserDetailsTests.java @@ -0,0 +1,83 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.request; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.spy; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; + +import javax.servlet.http.HttpServletResponse; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.test.context.TestSecurityContextHolder; +import org.springframework.security.test.web.support.WebTestUtils; +import org.springframework.security.web.context.SecurityContextRepository; + +@RunWith(PowerMockRunner.class) +@PrepareOnlyThisForTest(WebTestUtils.class) +public class SecurityMockMvcRequestPostProcessorsUserDetailsTests { + @Captor + private ArgumentCaptor contextCaptor; + @Mock + private SecurityContextRepository repository; + + private MockHttpServletRequest request; + + @Mock + private UserDetails userDetails; + + @Before + public void setup() { + request = new MockHttpServletRequest(); + mockWebTestUtils(); + } + + @After + public void cleanup() { + TestSecurityContextHolder.clearContext(); + } + + @Test + public void userDetails() { + user(userDetails).postProcessRequest(request); + + verify(repository).saveContext(contextCaptor.capture(), eq(request), any(HttpServletResponse.class)); + SecurityContext context = contextCaptor.getValue(); + assertThat(context.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class); + assertThat(context.getAuthentication().getPrincipal()).isSameAs(userDetails); + } + + private void mockWebTestUtils() { + spy(WebTestUtils.class); + when(WebTestUtils.getSecurityContextRepository(request)).thenReturn(repository); + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserTests.java new file mode 100644 index 0000000000..fe5caa6eb5 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserTests.java @@ -0,0 +1,142 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.request; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.spy; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; + +import java.util.Arrays; + +import javax.servlet.http.HttpServletResponse; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.test.context.TestSecurityContextHolder; +import org.springframework.security.test.web.support.WebTestUtils; +import org.springframework.security.web.context.SecurityContextRepository; + +@RunWith(PowerMockRunner.class) +@PrepareOnlyThisForTest(WebTestUtils.class) +public class SecurityMockMvcRequestPostProcessorsUserTests { + @Captor + private ArgumentCaptor contextCaptor; + @Mock + private SecurityContextRepository repository; + + private MockHttpServletRequest request; + + @Mock + private GrantedAuthority authority1; + @Mock + private GrantedAuthority authority2; + + @Before + public void setup() { + request = new MockHttpServletRequest(); + mockWebTestUtils(); + } + + @After + public void cleanup() { + TestSecurityContextHolder.clearContext(); + } + + @Test + public void userWithDefaults() { + String username = "userabc"; + + user(username).postProcessRequest(request); + + verify(repository).saveContext(contextCaptor.capture(), eq(request), any(HttpServletResponse.class)); + SecurityContext context = contextCaptor.getValue(); + assertThat(context.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class); + assertThat(context.getAuthentication().getName()).isEqualTo(username); + assertThat(context.getAuthentication().getCredentials()).isEqualTo("password"); + assertThat(context.getAuthentication().getAuthorities()).onProperty("authority").containsOnly("ROLE_USER"); + } + + + @Test + public void userWithCustom() { + String username = "customuser"; + + user(username) + .roles("CUSTOM","ADMIN") + .password("newpass") + .postProcessRequest(request); + + verify(repository).saveContext(contextCaptor.capture(), eq(request), any(HttpServletResponse.class)); + SecurityContext context = contextCaptor.getValue(); + assertThat(context.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class); + assertThat(context.getAuthentication().getName()).isEqualTo(username); + assertThat(context.getAuthentication().getCredentials()).isEqualTo("newpass"); + assertThat(context.getAuthentication().getAuthorities()).onProperty("authority").containsOnly("ROLE_CUSTOM","ROLE_ADMIN"); + } + + @Test + public void userCustomAuthoritiesVarargs() { + String username = "customuser"; + + user(username) + .authorities(authority1,authority2) + .postProcessRequest(request); + + verify(repository).saveContext(contextCaptor.capture(), eq(request), any(HttpServletResponse.class)); + SecurityContext context = contextCaptor.getValue(); + assertThat(context.getAuthentication().getAuthorities()).containsOnly(authority1,authority2); + } + + @Test(expected = IllegalArgumentException.class) + public void userRolesWithRolePrefixErrors() { + user("user") + .roles("ROLE_INVALID") + .postProcessRequest(request); + } + + @Test + public void userCustomAuthoritiesList() { + String username = "customuser"; + + user(username) + .authorities(Arrays.asList(authority1,authority2)) + .postProcessRequest(request); + + verify(repository).saveContext(contextCaptor.capture(), eq(request), any(HttpServletResponse.class)); + SecurityContext context = contextCaptor.getValue(); + assertThat(context.getAuthentication().getAuthorities()).containsOnly(authority1,authority2); + } + + private void mockWebTestUtils() { + spy(WebTestUtils.class); + when(WebTestUtils.getSecurityContextRepository(request)).thenReturn(repository); + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/CsrfShowcaseTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/CsrfShowcaseTests.java new file mode 100644 index 0000000000..d6a338be02 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/CsrfShowcaseTests.java @@ -0,0 +1,98 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.showcase.csrf; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import javax.servlet.Filter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=CsrfShowcaseTests.Config.class) +@WebAppConfiguration +public class CsrfShowcaseTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private Filter springSecurityFilterChain; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .addFilters(springSecurityFilterChain) + .build(); + } + + @Test + public void postWithCsrfWorks() throws Exception { + mvc + .perform(post("/").with(csrf())) + .andExpect(status().isNotFound()); + } + + @Test + public void postWithCsrfWorksWithPut() throws Exception { + mvc + .perform(put("/").with(csrf())) + .andExpect(status().isNotFound()); + } + + @Test + public void postWithNoCsrfForbidden() throws Exception { + mvc + .perform(post("/")) + .andExpect(status().isForbidden()); + } + + @Configuration + @EnableWebMvcSecurity + @EnableWebMvc + static class Config extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + } + } +} diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/CustomCsrfShowcaseTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/CustomCsrfShowcaseTests.java new file mode 100644 index 0000000000..262285dc6c --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/CustomCsrfShowcaseTests.java @@ -0,0 +1,108 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.showcase.csrf; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import javax.servlet.Filter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=CustomCsrfShowcaseTests.Config.class) +@WebAppConfiguration +public class CustomCsrfShowcaseTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private Filter springSecurityFilterChain; + + @Autowired + private CsrfTokenRepository repository; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .defaultRequest(get("/").with(csrf())) + .addFilters(springSecurityFilterChain) + .build(); + } + + @Test + public void postWithCsrfWorks() throws Exception { + mvc + .perform(post("/").with(csrf())) + .andExpect(status().isNotFound()); + } + + @Test + public void postWithCsrfWorksWithPut() throws Exception { + mvc + .perform(put("/").with(csrf())) + .andExpect(status().isNotFound()); + } + + @Configuration + @EnableWebMvcSecurity + @EnableWebMvc + static class Config extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .csrf() + .csrfTokenRepository(repo()); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + } + + @Bean + public CsrfTokenRepository repo() { + HttpSessionCsrfTokenRepository repo = new HttpSessionCsrfTokenRepository(); + repo.setParameterName("custom_csrf"); + return repo; + } + } +} diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/DefaultCsrfShowcaseTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/DefaultCsrfShowcaseTests.java new file mode 100644 index 0000000000..43d898fdf8 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/DefaultCsrfShowcaseTests.java @@ -0,0 +1,92 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.showcase.csrf; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import javax.servlet.Filter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=DefaultCsrfShowcaseTests.Config.class) +@WebAppConfiguration +public class DefaultCsrfShowcaseTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private Filter springSecurityFilterChain; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .defaultRequest(get("/").with(csrf())) + .addFilters(springSecurityFilterChain) + .build(); + } + + @Test + public void postWithCsrfWorks() throws Exception { + mvc + .perform(post("/")) + .andExpect(status().isNotFound()); + } + + @Test + public void postWithCsrfWorksWithPut() throws Exception { + mvc + .perform(put("/")) + .andExpect(status().isNotFound()); + } + + @Configuration + @EnableWebMvcSecurity + @EnableWebMvc + static class Config extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + } + } +} diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/AuthenticationTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/AuthenticationTests.java new file mode 100644 index 0000000000..8d02d52abe --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/AuthenticationTests.java @@ -0,0 +1,107 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.showcase.login; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; + +import javax.servlet.Filter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=AuthenticationTests.Config.class) +@WebAppConfiguration +public class AuthenticationTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private Filter springSecurityFilterChain; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .addFilters(springSecurityFilterChain) + .build(); + } + + @Test + public void requiresAuthentication() throws Exception { + mvc + .perform(get("/")) + .andExpect(status().isMovedTemporarily()); + } + + @Test + public void httpBasicAuthenticationSuccess() throws Exception { + mvc + .perform(get("/secured/butnotfound").with(httpBasic("user","password"))) + .andExpect(status().isNotFound()) + .andExpect(authenticated().withUsername("user")); + } + + @Test + public void authenticationSuccess() throws Exception { + mvc + .perform(formLogin()) + .andExpect(status().isMovedTemporarily()) + .andExpect(redirectedUrl("/")) + .andExpect(authenticated().withUsername("user")); + } + + @Test + public void authenticationFailed() throws Exception { + mvc + .perform(formLogin().user("user").password("invalid")) + .andExpect(status().isMovedTemporarily()) + .andExpect(redirectedUrl("/login?error")) + .andExpect(unauthenticated()); + } + + @Configuration + @EnableWebMvcSecurity + @EnableWebMvc + static class Config extends WebSecurityConfigurerAdapter { + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + } + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/CustomConfigAuthenticationTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/CustomConfigAuthenticationTests.java new file mode 100644 index 0000000000..22e5d83fec --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/CustomConfigAuthenticationTests.java @@ -0,0 +1,132 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.showcase.login; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; + +import javax.servlet.Filter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.security.web.context.SecurityContextRepository; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=CustomConfigAuthenticationTests.Config.class) +@WebAppConfiguration +public class CustomConfigAuthenticationTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private Filter springSecurityFilterChain; + + @Autowired + private SecurityContextRepository securityContextRepository; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .defaultRequest(get("/")) + .addFilters(springSecurityFilterChain) + .build(); + } + + @Test + public void authenticationSuccess() throws Exception { + mvc + .perform(formLogin("/authenticate").user("user","user").password("pass","password")) + .andExpect(status().isMovedTemporarily()) + .andExpect(redirectedUrl("/")) + .andExpect(authenticated().withUsername("user")); + } + + + @Test + public void withUserSuccess() throws Exception { + mvc + .perform(get("/").with(user("user"))) + .andExpect(status().isNotFound()) + .andExpect(authenticated().withUsername("user")); + } + + @Test + public void authenticationFailed() throws Exception { + mvc + .perform(formLogin("/authenticate").user("user","notfound").password("pass","invalid")) + .andExpect(status().isMovedTemporarily()) + .andExpect(redirectedUrl("/authenticate?error")) + .andExpect(unauthenticated()); + } + + @Configuration + @EnableWebMvcSecurity + @EnableWebMvc + static class Config extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .securityContext() + .securityContextRepository(securityContextRepository()) + .and() + .formLogin() + .usernameParameter("user") + .passwordParameter("pass") + .loginPage("/authenticate"); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + } + + @Bean + public SecurityContextRepository securityContextRepository() { + HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository(); + repo.setSpringSecurityContextKey("CUSTOM"); + return repo; + } + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/CustomLoginRequestBuilderAuthenticationTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/CustomLoginRequestBuilderAuthenticationTests.java new file mode 100644 index 0000000000..9952b78296 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/CustomLoginRequestBuilderAuthenticationTests.java @@ -0,0 +1,112 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.showcase.login; + +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import javax.servlet.Filter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders; +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.FormLoginRequestBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=CustomLoginRequestBuilderAuthenticationTests.Config.class) +@WebAppConfiguration +public class CustomLoginRequestBuilderAuthenticationTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private Filter springSecurityFilterChain; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .addFilters(springSecurityFilterChain) + .build(); + } + + @Test + public void authenticationSuccess() throws Exception { + mvc + .perform(login()) + .andExpect(status().isMovedTemporarily()) + .andExpect(redirectedUrl("/")) + .andExpect(authenticated().withUsername("user")); + } + + @Test + public void authenticationFailed() throws Exception { + mvc + .perform(login().user("notfound").password("invalid")) + .andExpect(status().isMovedTemporarily()) + .andExpect(redirectedUrl("/authenticate?error")) + .andExpect(unauthenticated()); + } + + static FormLoginRequestBuilder login() { + return SecurityMockMvcRequestBuilders + .formLogin("/authenticate") + .userParameter("user") + .passwordParam("pass"); + } + + @Configuration + @EnableWebMvcSecurity + @EnableWebMvc + static class Config extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .formLogin() + .usernameParameter("user") + .passwordParameter("pass") + .loginPage("/authenticate"); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + } + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/DefaultfSecurityRequestsTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/DefaultfSecurityRequestsTests.java new file mode 100644 index 0000000000..8224ba096b --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/DefaultfSecurityRequestsTests.java @@ -0,0 +1,106 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.showcase.secured; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import javax.servlet.Filter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=DefaultfSecurityRequestsTests.Config.class) +@WebAppConfiguration +public class DefaultfSecurityRequestsTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private Filter springSecurityFilterChain; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .defaultRequest(get("/").with(user("user").roles("ADMIN"))) + .addFilters(springSecurityFilterChain) + .build(); + } + + @Test + public void requestProtectedUrlWithUser() throws Exception { + mvc + .perform(get("/")) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with user + .andExpect(authenticated().withUsername("user")); + } + + @Test + public void requestProtectedUrlWithAdmin() throws Exception { + mvc + .perform(get("/admin")) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with user + .andExpect(authenticated().withUsername("user")); + } + + @Configuration + @EnableWebMvcSecurity + @EnableWebMvc + static class Config extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + .and() + .formLogin(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + } + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/SecurityRequestsTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/SecurityRequestsTests.java new file mode 100644 index 0000000000..82af6745f5 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/SecurityRequestsTests.java @@ -0,0 +1,141 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.showcase.secured; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import javax.servlet.Filter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=SecurityRequestsTests.Config.class) +@WebAppConfiguration +public class SecurityRequestsTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private Filter springSecurityFilterChain; + + @Autowired + private UserDetailsService userDetailsService; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .addFilters(springSecurityFilterChain) + .build(); + } + + @Test + public void requestProtectedUrlWithUser() throws Exception { + mvc + .perform(get("/").with(user("user"))) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with user + .andExpect(authenticated().withUsername("user")); + } + + @Test + public void requestProtectedUrlWithAdmin() throws Exception { + mvc + .perform(get("/admin").with(user("admin").roles("ADMIN"))) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with admin + .andExpect(authenticated().withUsername("admin")); + } + + @Test + public void requestProtectedUrlWithUserDetails() throws Exception { + UserDetails user = userDetailsService.loadUserByUsername("user"); + mvc + .perform(get("/").with(user(user))) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with user + .andExpect(authenticated().withAuthenticationPrincipal(user)); + } + + @Test + public void requestProtectedUrlWithAuthentication() throws Exception { + Authentication authentication = new TestingAuthenticationToken("test", "notused", "ROLE_USER"); + mvc + .perform(get("/").with(authentication(authentication))) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with user + .andExpect(authenticated().withAuthentication(authentication)); + } + + @Configuration + @EnableWebMvcSecurity + @EnableWebMvc + static class Config extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + .and() + .formLogin(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + } + + @Override + @Bean + public UserDetailsService userDetailsServiceBean() throws Exception { + return super.userDetailsServiceBean(); + } + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserAuthenticationTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserAuthenticationTests.java new file mode 100644 index 0000000000..dcc6a75ced --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserAuthenticationTests.java @@ -0,0 +1,120 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.showcase.secured; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import javax.servlet.Filter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.security.test.context.support.WithSecurityContextTestExcecutionListener; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=WithUserAuthenticationTests.Config.class) +@WebAppConfiguration +@TestExecutionListeners(listeners={ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExcecutionListener.class}) +public class WithUserAuthenticationTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private Filter springSecurityFilterChain; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .addFilters(springSecurityFilterChain) + .defaultRequest(get("/").with(testSecurityContext())) + .build(); + } + + @Test + @WithMockUser + public void requestProtectedUrlWithUser() throws Exception { + mvc + .perform(get("/")) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with user + .andExpect(authenticated().withUsername("user")); + } + + @Test + @WithMockUser(roles="ADMIN") + public void requestProtectedUrlWithAdmin() throws Exception { + mvc + .perform(get("/admin")) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with user + .andExpect(authenticated().withUsername("user").withRoles("ADMIN")); + } + + @Configuration + @EnableWebMvcSecurity + @EnableWebMvc + static class Config extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + .and() + .formLogin(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + } + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserClassLevelAuthenticationTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserClassLevelAuthenticationTests.java new file mode 100644 index 0000000000..99a49861a4 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserClassLevelAuthenticationTests.java @@ -0,0 +1,119 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.showcase.secured; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import javax.servlet.Filter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.security.test.context.support.WithSecurityContextTestExcecutionListener; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=WithUserClassLevelAuthenticationTests.Config.class) +@WebAppConfiguration +@TestExecutionListeners(listeners={ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExcecutionListener.class}) +@WithMockUser(roles="ADMIN") +public class WithUserClassLevelAuthenticationTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private Filter springSecurityFilterChain; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .addFilters(springSecurityFilterChain) + .defaultRequest(get("/").with(testSecurityContext())) + .build(); + } + + @Test + public void requestProtectedUrlWithUser() throws Exception { + mvc + .perform(get("/")) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with user + .andExpect(authenticated().withUsername("user")); + } + + @Test + public void requestProtectedUrlWithAdmin() throws Exception { + mvc + .perform(get("/admin")) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with user + .andExpect(authenticated().withUsername("user").withRoles("ADMIN")); + } + + @Configuration + @EnableWebMvcSecurity + @EnableWebMvc + static class Config extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + .and() + .formLogin(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + } + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserDetailsAuthenticationTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserDetailsAuthenticationTests.java new file mode 100644 index 0000000000..66ef676989 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserDetailsAuthenticationTests.java @@ -0,0 +1,129 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.showcase.secured; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.testSecurityContext; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import javax.servlet.Filter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.test.context.support.WithUserDetails; +import org.springframework.security.test.context.support.WithSecurityContextTestExcecutionListener; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=WithUserDetailsAuthenticationTests.Config.class) +@WebAppConfiguration +@TestExecutionListeners(listeners={ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExcecutionListener.class}) +public class WithUserDetailsAuthenticationTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private Filter springSecurityFilterChain; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .addFilters(springSecurityFilterChain) + .defaultRequest(get("/").with(testSecurityContext())) + .build(); + } + + @Test + @WithUserDetails + public void requestProtectedUrlWithUser() throws Exception { + mvc + .perform(get("/")) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with user + .andExpect(authenticated().withUsername("user")); + } + + @Test + @WithUserDetails("admin") + public void requestProtectedUrlWithAdmin() throws Exception { + mvc + .perform(get("/admin")) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with user + .andExpect(authenticated().withUsername("admin").withRoles("ADMIN","USER")); + } + + @Configuration + @EnableWebMvcSecurity + @EnableWebMvc + static class Config extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + .and() + .formLogin(); + } + + @Bean + @Override + public UserDetailsService userDetailsServiceBean() throws Exception { + return super.userDetailsServiceBean(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER").and() + .withUser("admin").password("password").roles("USER","ADMIN"); + } + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserDetailsClassLevelAuthenticationTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserDetailsClassLevelAuthenticationTests.java new file mode 100644 index 0000000000..4dbf3871f2 --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserDetailsClassLevelAuthenticationTests.java @@ -0,0 +1,128 @@ +/* + * Copyright 2002-2014 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.test.web.servlet.showcase.secured; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.testSecurityContext; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import javax.servlet.Filter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.test.context.support.WithUserDetails; +import org.springframework.security.test.context.support.WithSecurityContextTestExcecutionListener; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.web.ServletTestExecutionListener; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=WithUserDetailsClassLevelAuthenticationTests.Config.class) +@WebAppConfiguration +@TestExecutionListeners(listeners={ServletTestExecutionListener.class, + DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, + WithSecurityContextTestExcecutionListener.class}) +@WithUserDetails("admin") +public class WithUserDetailsClassLevelAuthenticationTests { + + @Autowired + private WebApplicationContext context; + + @Autowired + private Filter springSecurityFilterChain; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders + .webAppContextSetup(context) + .addFilters(springSecurityFilterChain) + .defaultRequest(get("/").with(testSecurityContext())) + .build(); + } + + @Test + public void requestRootUrlWithAdmin() throws Exception { + mvc + .perform(get("/")) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with user + .andExpect(authenticated().withUsername("admin").withRoles("ADMIN","USER")); + } + + @Test + public void requestProtectedUrlWithAdmin() throws Exception { + mvc + .perform(get("/admin")) + // Ensure we got past Security + .andExpect(status().isNotFound()) + // Ensure it appears we are authenticated with user + .andExpect(authenticated().withUsername("admin").withRoles("ADMIN","USER")); + } + + @Configuration + @EnableWebMvcSecurity + @EnableWebMvc + static class Config extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + .and() + .formLogin(); + } + + @Bean + @Override + public UserDetailsService userDetailsServiceBean() throws Exception { + return super.userDetailsServiceBean(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER").and() + .withUser("admin").password("password").roles("USER","ADMIN"); + } + } +} \ No newline at end of file diff --git a/test/src/test/java/org/springframework/security/test/web/support/WebTestUtilsTests.java b/test/src/test/java/org/springframework/security/test/web/support/WebTestUtilsTests.java new file mode 100644 index 0000000000..055ef8270d --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/web/support/WebTestUtilsTests.java @@ -0,0 +1,152 @@ +/* + * Copyright 2002-2014 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.test.web.support; + +import static org.fest.assertions.Assertions.assertThat; +import static org.springframework.security.test.web.support.WebTestUtils.getCsrfTokenRepository; +import static org.springframework.security.test.web.support.WebTestUtils.getSecurityContextRepository; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.security.web.context.SecurityContextRepository; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; + +@RunWith(MockitoJUnitRunner.class) +public class WebTestUtilsTests { + @Mock + private SecurityContextRepository contextRepo; + @Mock + private CsrfTokenRepository csrfRepo; + + private MockHttpServletRequest request; + private ConfigurableApplicationContext context; + + @Before + public void setup() { + request = new MockHttpServletRequest(); + } + + @After + public void cleanup() { + if(context != null) { + context.close(); + } + } + + @Test + public void getCsrfTokenRepositorytNoWac() { + assertThat(getCsrfTokenRepository(request)).isInstanceOf(HttpSessionCsrfTokenRepository.class); + } + + @Test + public void getCsrfTokenRepositorytNoSecurity() { + loadConfig(Config.class); + assertThat(getCsrfTokenRepository(request)).isInstanceOf(HttpSessionCsrfTokenRepository.class); + } + + @Test + public void getCsrfTokenRepositorytSecurityNoCsrf() { + loadConfig(SecurityNoCsrfConfig.class); + assertThat(getCsrfTokenRepository(request)).isInstanceOf(HttpSessionCsrfTokenRepository.class); + } + + @Test + public void getCsrfTokenRepositorytSecurityCustomRepo() { + CustomSecurityConfig.CONTEXT_REPO = contextRepo; + CustomSecurityConfig.CSRF_REPO = csrfRepo; + loadConfig(CustomSecurityConfig.class); + assertThat(getCsrfTokenRepository(request)).isSameAs(csrfRepo); + } + + // getSecurityContextRepository + + @Test + public void getSecurityContextRepositoryNoWac() { + assertThat(getSecurityContextRepository(request)).isInstanceOf(HttpSessionSecurityContextRepository.class); + } + + @Test + public void getSecurityContextRepositoryNoSecurity() { + loadConfig(Config.class); + assertThat(getSecurityContextRepository(request)).isInstanceOf(HttpSessionSecurityContextRepository.class); + } + + @Test + public void getSecurityContextRepositorySecurityNoCsrf() { + loadConfig(SecurityNoCsrfConfig.class); + assertThat(getSecurityContextRepository(request)).isInstanceOf(HttpSessionSecurityContextRepository.class); + } + + @Test + public void getSecurityContextRepositorySecurityCustomRepo() { + CustomSecurityConfig.CONTEXT_REPO = contextRepo; + CustomSecurityConfig.CSRF_REPO = csrfRepo; + loadConfig(CustomSecurityConfig.class); + assertThat(getSecurityContextRepository(request)).isSameAs(contextRepo); + } + + private void loadConfig(Class config) { + AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); + context.register(config); + context.refresh(); + this.context = context; + request.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context); + } + + @Configuration + static class Config {} + + @Configuration + @EnableWebSecurity + static class SecurityNoCsrfConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable(); + } + } + + @Configuration + @EnableWebSecurity + static class CustomSecurityConfig extends WebSecurityConfigurerAdapter { + static CsrfTokenRepository CSRF_REPO; + static SecurityContextRepository CONTEXT_REPO; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .csrf() + .csrfTokenRepository(CSRF_REPO) + .and() + .securityContext() + .securityContextRepository(CONTEXT_REPO); + } + } +} \ No newline at end of file diff --git a/test/template.mf b/test/template.mf new file mode 100644 index 0000000000..d2a39fe73c --- /dev/null +++ b/test/template.mf @@ -0,0 +1,18 @@ +Implementation-Title: org.springframework.security.test +Implementation-Version: ${version} +Bundle-SymbolicName: org.springframework.security.test +Bundle-Name: Spring Security Test +Bundle-Vendor: SpringSource +Bundle-Version: ${version} +Bundle-ManifestVersion: 2 +Ignored-Existing-Headers: + Import-Package, + Export-Package +Import-Template: + org.apache.commons.logging.*;version="${cloggingRange}", + org.springframework.security.core.*;version="${secRange}", + org.springframework.security.authentication.*;version="${secRange}", + org.springframework.security.web.*;version="${secRange}", + org.springframework.beans.factory;version="${springRange}", + org.springframework.util;version="${springRange}", + javax.servlet.*;version="0" diff --git a/test/test.gradle b/test/test.gradle new file mode 100644 index 0000000000..df85d572bc --- /dev/null +++ b/test/test.gradle @@ -0,0 +1,13 @@ +// OpenID Module build file + +dependencies { + compile project(':spring-security-core'), + project(':spring-security-web'), + "org.springframework:spring-test:$springVersion" + + provided "javax.servlet:javax.servlet-api:$servletApiVersion" + + testCompile project(':spring-security-config'), + "org.springframework:spring-webmvc:$springVersion", + powerMockDependencies +} diff --git a/web/src/main/java/org/springframework/security/web/savedrequest/RequestCacheAdapter.java b/web/src/main/java/org/springframework/security/web/savedrequest/RequestCacheAdapter.java new file mode 100644 index 0000000000..85df5c1f91 --- /dev/null +++ b/web/src/main/java/org/springframework/security/web/savedrequest/RequestCacheAdapter.java @@ -0,0 +1,108 @@ +/* + * Copyright 2002-2014 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.web.savedrequest; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.util.Assert; + +public class RequestCacheAdapter implements RequestCache { + + private final RequestCache delegate; + + public RequestCacheAdapter() { + this(new HttpSessionRequestCache()); + } + + public RequestCacheAdapter(RequestCache delegate) { + Assert.notNull(delegate, "delegate cannot be null"); + this.delegate = delegate; + } + + public void saveRequest(HttpServletRequest request, + HttpServletResponse response) { + delegate.saveRequest(request, response); + } + + public SavedRequest getRequest(HttpServletRequest request, + HttpServletResponse response) { + SavedRequest result = delegate.getRequest(request, response); + Cookie[] cookies = request.getCookies(); + return new SavedRequestAdapter(result, cookies == null ? null : Arrays.asList(cookies)); + } + + public HttpServletRequest getMatchingRequest(HttpServletRequest request, + HttpServletResponse response) { + return delegate.getMatchingRequest(request, response); + } + + public void removeRequest(HttpServletRequest request, + HttpServletResponse response) { + delegate.removeRequest(request, response); + } + + private static class SavedRequestAdapter implements SavedRequest { + private SavedRequest delegate; + private List cookies; + + public SavedRequestAdapter(SavedRequest delegate, List cookies) { + this.delegate = delegate; + this.cookies = cookies; + } + + public String getRedirectUrl() { + return delegate.getRedirectUrl(); + } + + public List getCookies() { + return cookies; + } + + public String getMethod() { + return delegate.getMethod(); + } + + public List getHeaderValues(String name) { + return delegate.getHeaderValues(name); + } + + public Collection getHeaderNames() { + return delegate.getHeaderNames(); + } + + public List getLocales() { + return delegate.getLocales(); + } + + public String[] getParameterValues(String name) { + return delegate.getParameterValues(name); + } + + public Map getParameterMap() { + return delegate.getParameterMap(); + } + + private static final long serialVersionUID = 1184951442151447331L; + } +}