Ver Fonte

NamespaceHttpOpenIDLoginTests groovy->java

Issue gh-4939
Josh Cummings há 6 anos atrás
pai
commit
bf5b693549

+ 0 - 228
config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpOpenIDLoginTests.groovy

@@ -1,228 +0,0 @@
-
-
-/*
- * Copyright 2002-2013 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
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.security.config.annotation.web.configurers
-
-import org.springframework.context.annotation.Bean
-import org.springframework.context.annotation.Configuration
-import org.springframework.mock.web.MockFilterChain
-import org.springframework.mock.web.MockHttpServletRequest
-import org.springframework.mock.web.MockHttpServletResponse
-import org.springframework.security.authentication.AuthenticationManager
-import org.springframework.security.config.annotation.BaseSpringSpec
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
-import org.springframework.security.core.userdetails.AuthenticationUserDetailsService
-import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
-import org.springframework.security.openid.OpenID4JavaConsumer
-import org.springframework.security.openid.OpenIDAuthenticationFilter
-import org.springframework.security.openid.OpenIDAuthenticationProvider
-import org.springframework.security.openid.OpenIDAuthenticationToken;
-import org.springframework.security.web.FilterChainProxy
-import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler
-import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler
-import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
-
-/**
- * Tests to verify that all the functionality of <openid-login> attributes is present
- *
- * @author Rob Winch
- *
- */
-public class NamespaceHttpOpenIDLoginTests extends BaseSpringSpec {
-	def "http/openid-login"() {
-		when:
-			loadConfig(OpenIDLoginConfig)
-		then:
-			findFilter(OpenIDAuthenticationFilter).consumer.class == OpenID4JavaConsumer
-		when:
-			springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-			response.getRedirectedUrl() == "http://localhost/login"
-		when: "fail to log in"
-			super.setup()
-			request.servletPath = "/login/openid"
-			request.method = "POST"
-			springSecurityFilterChain.doFilter(request,response,chain)
-		then: "sent to login error page"
-			response.getRedirectedUrl() == "/login?error"
-	}
-
-	@Configuration
-	static class OpenIDLoginConfig extends BaseWebConfig {
-		protected void configure(HttpSecurity http) {
-			http
-				.authorizeRequests()
-					.anyRequest().hasRole("USER")
-					.and()
-				.openidLogin()
-					.permitAll();
-		}
-	}
-
-	def "http/openid-login/attribute-exchange"() {
-		when:
-			loadConfig(OpenIDLoginAttributeExchangeConfig)
-			OpenID4JavaConsumer consumer = findFilter(OpenIDAuthenticationFilter).consumer
-		then:
-			consumer.class == OpenID4JavaConsumer
-
-			def googleAttrs = consumer.attributesToFetchFactory.createAttributeList("https://www.google.com/1")
-			googleAttrs[0].name == "email"
-			googleAttrs[0].type == "https://axschema.org/contact/email"
-			googleAttrs[0].required
-			googleAttrs[1].name == "firstname"
-			googleAttrs[1].type == "https://axschema.org/namePerson/first"
-			googleAttrs[1].required
-			googleAttrs[2].name == "lastname"
-			googleAttrs[2].type == "https://axschema.org/namePerson/last"
-			googleAttrs[2].required
-
-			def yahooAttrs = consumer.attributesToFetchFactory.createAttributeList("https://rwinch.yahoo.com/rwinch/id")
-			yahooAttrs[0].name == "email"
-			yahooAttrs[0].type == "https://schema.openid.net/contact/email"
-			yahooAttrs[0].required
-			yahooAttrs[1].name == "fullname"
-			yahooAttrs[1].type == "https://axschema.org/namePerson"
-			yahooAttrs[1].required
-		when:
-			springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-			response.getRedirectedUrl() == "http://localhost/login"
-		when: "fail to log in"
-			super.setup()
-			request.servletPath = "/login/openid"
-			request.method = "POST"
-			springSecurityFilterChain.doFilter(request,response,chain)
-		then: "sent to login error page"
-			response.getRedirectedUrl() == "/login?error"
-	}
-
-	@Configuration
-	static class OpenIDLoginAttributeExchangeConfig extends BaseWebConfig {
-		protected void configure(HttpSecurity http) {
-			http
-				.authorizeRequests()
-					.anyRequest().hasRole("USER")
-					.and()
-				.openidLogin()
-					.attributeExchange("https://www.google.com/.*") // attribute-exchange@identifier-match
-						.attribute("email") // openid-attribute@name
-							.type("https://axschema.org/contact/email") // openid-attribute@type
-							.required(true) // openid-attribute@required
-							.count(1) // openid-attribute@count
-							.and()
-						.attribute("firstname")
-							.type("https://axschema.org/namePerson/first")
-							.required(true)
-							.and()
-						.attribute("lastname")
-							.type("https://axschema.org/namePerson/last")
-							.required(true)
-							.and()
-						.and()
-					.attributeExchange(".*yahoo.com.*")
-						.attribute("email")
-							.type("https://schema.openid.net/contact/email")
-							.required(true)
-							.and()
-						.attribute("fullname")
-							.type("https://axschema.org/namePerson")
-							.required(true)
-							.and()
-						.and()
-					.permitAll();
-		}
-	}
-
-	def "http/openid-login custom"() {
-		setup:
-			loadConfig(OpenIDLoginCustomConfig)
-		when:
-			springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-			response.getRedirectedUrl() == "http://localhost/authentication/login"
-		when: "fail to log in"
-			super.setup()
-			request.servletPath = "/authentication/login/process"
-			request.method = "POST"
-			springSecurityFilterChain.doFilter(request,response,chain)
-		then: "sent to login error page"
-			response.getRedirectedUrl() == "/authentication/login?failed"
-	}
-
-	@Configuration
-	static class OpenIDLoginCustomConfig extends BaseWebConfig {
-		protected void configure(HttpSecurity http) throws Exception {
-			boolean alwaysUseDefaultSuccess = true;
-			http
-				.authorizeRequests()
-					.anyRequest().hasRole("USER")
-					.and()
-				.openidLogin()
-					.permitAll()
-					.loginPage("/authentication/login") // openid-login@login-page
-					.failureUrl("/authentication/login?failed") // openid-login@authentication-failure-url
-					.loginProcessingUrl("/authentication/login/process") // openid-login@login-processing-url
-					.defaultSuccessUrl("/default", alwaysUseDefaultSuccess) // openid-login@default-target-url / openid-login@always-use-default-target
-		}
-	}
-
-	def "http/openid-login custom refs"() {
-		when:
-			OpenIDLoginCustomRefsConfig.AUDS = Mock(AuthenticationUserDetailsService)
-			loadConfig(OpenIDLoginCustomRefsConfig)
-		then: "CustomWebAuthenticationDetailsSource is used"
-			findFilter(OpenIDAuthenticationFilter).authenticationDetailsSource.class == CustomWebAuthenticationDetailsSource
-			findAuthenticationProvider(OpenIDAuthenticationProvider).userDetailsService == OpenIDLoginCustomRefsConfig.AUDS
-		when: "fail to log in"
-			request.servletPath = "/login/openid"
-			request.method = "POST"
-			springSecurityFilterChain.doFilter(request,response,chain)
-		then: "sent to login error page"
-			response.getRedirectedUrl() == "/custom/failure"
-	}
-
-	@Configuration
-	static class OpenIDLoginCustomRefsConfig extends BaseWebConfig {
-		static AuthenticationUserDetailsService AUDS
-
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.authorizeRequests()
-					.anyRequest().hasRole("USER")
-					.and()
-				.openidLogin()
-					// if using UserDetailsService wrap with new UserDetailsByNameServiceWrapper<OpenIDAuthenticationToken>()
-					.authenticationUserDetailsService(AUDS) // openid-login@user-service-ref
-					.failureHandler(new SimpleUrlAuthenticationFailureHandler("/custom/failure")) // openid-login@authentication-failure-handler-ref
-					.successHandler(new SavedRequestAwareAuthenticationSuccessHandler( defaultTargetUrl : "/custom/targetUrl" )) // openid-login@authentication-success-handler-ref
-					.authenticationDetailsSource(new CustomWebAuthenticationDetailsSource()); // openid-login@authentication-details-source-ref
-		}
-
-		// only necessary to have easy access to the AuthenticationManager for testing/verification
-		@Bean
-		@Override
-		public AuthenticationManager authenticationManagerBean()
-				throws Exception {
-			return super.authenticationManagerBean();
-		}
-
-	}
-
-	static class CustomWebAuthenticationDetailsSource extends WebAuthenticationDetailsSource {}
-}

+ 304 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpOpenIDLoginTests.java

@@ -0,0 +1,304 @@
+/*
+ * Copyright 2002-2019 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
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.config.annotation.web.configurers;
+
+import java.util.Arrays;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import org.junit.Rule;
+import org.junit.Test;
+import org.openid4java.consumer.ConsumerManager;
+import org.openid4java.discovery.DiscoveryInformation;
+import org.openid4java.message.AuthRequest;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationDetailsSource;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.config.annotation.ObjectPostProcessor;
+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.config.test.SpringTestRule;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.openid.OpenIDAttribute;
+import org.springframework.security.openid.OpenIDAuthenticationFilter;
+import org.springframework.security.openid.OpenIDAuthenticationStatus;
+import org.springframework.security.openid.OpenIDAuthenticationToken;
+import org.springframework.security.openid.OpenIDConsumer;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.openid4java.discovery.yadis.YadisResolver.YADIS_XRDS_LOCATION;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+/**
+ * Tests to verify that all the functionality of <openid-login> attributes is present
+ *
+ * @author Rob Winch
+ * @author Josh Cummings
+ */
+public class NamespaceHttpOpenIDLoginTests {
+
+	@Rule
+	public final SpringTestRule spring = new SpringTestRule();
+
+	@Autowired
+	MockMvc mvc;
+
+	@Test
+	public void openidLoginWhenUsingDefaultsThenMatchesNamespace() throws Exception {
+		this.spring.register(OpenIDLoginConfig.class).autowire();
+		this.mvc.perform(get("/"))
+				.andExpect(redirectedUrl("http://localhost/login"));
+		this.mvc.perform(post("/login/openid").with(csrf()))
+				.andExpect(redirectedUrl("/login?error"));
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	static class OpenIDLoginConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			http
+				.authorizeRequests()
+					.anyRequest().hasRole("USER")
+					.and()
+				.openidLogin()
+					.permitAll();
+		}
+	}
+
+	@Test
+	public void openidLoginWhenAttributeExchangeConfiguredThenFetchAttributesMatchAttributeList() throws Exception {
+		OpenIDLoginAttributeExchangeConfig.CONSUMER_MANAGER = mock(ConsumerManager.class);
+		AuthRequest mockAuthRequest = mock(AuthRequest.class);
+		DiscoveryInformation mockDiscoveryInformation = mock(DiscoveryInformation.class);
+		when(mockAuthRequest.getDestinationUrl(anyBoolean())).thenReturn("mockUrl");
+		when(OpenIDLoginAttributeExchangeConfig.CONSUMER_MANAGER.associate(any()))
+				.thenReturn(mockDiscoveryInformation);
+		when(OpenIDLoginAttributeExchangeConfig.CONSUMER_MANAGER.authenticate(any(DiscoveryInformation.class), any(), any()))
+				.thenReturn(mockAuthRequest);
+		this.spring.register(OpenIDLoginAttributeExchangeConfig.class).autowire();
+
+		try (MockWebServer server = new MockWebServer()) {
+			String endpoint = server.url("/").toString();
+
+			server.enqueue(new MockResponse()
+					.addHeader(YADIS_XRDS_LOCATION, endpoint));
+			server.enqueue(new MockResponse()
+					.setBody(String.format("<XRDS><XRD><Service><URI>%s</URI></Service></XRD></XRDS>", endpoint)));
+
+			MvcResult mvcResult = this.mvc.perform(get("/login/openid")
+					.param(OpenIDAuthenticationFilter.DEFAULT_CLAIMED_IDENTITY_FIELD, "https://www.google.com/1"))
+					.andExpect(status().isFound())
+					.andReturn();
+
+			Object attributeObject = mvcResult.getRequest().getSession().getAttribute("SPRING_SECURITY_OPEN_ID_ATTRIBUTES_FETCH_LIST");
+			assertThat(attributeObject).isInstanceOf(List.class);
+			List<OpenIDAttribute> attributeList = (List<OpenIDAttribute>) attributeObject;
+			assertThat(attributeList.stream().anyMatch(attribute ->
+					"firstname".equals(attribute.getName())
+							&& "https://axschema.org/namePerson/first".equals(attribute.getType())
+							&& attribute.isRequired()))
+					.isTrue();
+			assertThat(attributeList.stream().anyMatch(attribute ->
+					"lastname".equals(attribute.getName())
+							&& "https://axschema.org/namePerson/last".equals(attribute.getType())
+							&& attribute.isRequired()))
+					.isTrue();
+			assertThat(attributeList.stream().anyMatch(attribute ->
+					"email".equals(attribute.getName())
+							&& "https://axschema.org/contact/email".equals(attribute.getType())
+							&& attribute.isRequired()))
+					.isTrue();
+		}
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	static class OpenIDLoginAttributeExchangeConfig extends WebSecurityConfigurerAdapter {
+		static ConsumerManager CONSUMER_MANAGER;
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			http
+				.authorizeRequests()
+					.anyRequest().hasRole("USER")
+					.and()
+				.openidLogin()
+					.consumerManager(CONSUMER_MANAGER)
+					.attributeExchange("https://www.google.com/.*") // attribute-exchange@identifier-match
+						.attribute("email") // openid-attribute@name
+							.type("https://axschema.org/contact/email") // openid-attribute@type
+							.required(true) // openid-attribute@required
+							.count(1) // openid-attribute@count
+							.and()
+						.attribute("firstname")
+							.type("https://axschema.org/namePerson/first")
+							.required(true)
+							.and()
+						.attribute("lastname")
+							.type("https://axschema.org/namePerson/last")
+							.required(true)
+							.and()
+						.and()
+					.attributeExchange(".*yahoo.com.*")
+						.attribute("email")
+							.type("https://schema.openid.net/contact/email")
+							.required(true)
+							.and()
+						.attribute("fullname")
+							.type("https://axschema.org/namePerson")
+							.required(true)
+							.and()
+						.and()
+					.permitAll();
+		}
+	}
+
+	@Test
+	public void openidLoginWhenUsingCustomEndpointsThenMatchesNamespace() throws Exception {
+		this.spring.register(OpenIDLoginCustomConfig.class).autowire();
+		this.mvc.perform(get("/"))
+				.andExpect(redirectedUrl("http://localhost/authentication/login"));
+		this.mvc.perform(post("/authentication/login/process").with(csrf()))
+				.andExpect(redirectedUrl("/authentication/login?failed"));
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	static class OpenIDLoginCustomConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			boolean alwaysUseDefaultSuccess = true;
+			http
+				.authorizeRequests()
+					.anyRequest().hasRole("USER")
+					.and()
+				.openidLogin()
+					.permitAll()
+					.loginPage("/authentication/login") // openid-login@login-page
+					.failureUrl("/authentication/login?failed") // openid-login@authentication-failure-url
+					.loginProcessingUrl("/authentication/login/process") // openid-login@login-processing-url
+					.defaultSuccessUrl("/default", alwaysUseDefaultSuccess); // openid-login@default-target-url / openid-login@always-use-default-target
+		}
+	}
+
+	@Test
+	public void openidLoginWithCustomHandlersThenBehaviorMatchesNamespace() throws Exception {
+		OpenIDAuthenticationToken token = new OpenIDAuthenticationToken(
+				OpenIDAuthenticationStatus.SUCCESS,
+				"identityUrl",
+				"message",
+				Arrays.asList(new OpenIDAttribute("name", "type")));
+
+		OpenIDLoginCustomRefsConfig.AUDS = mock(AuthenticationUserDetailsService.class);
+		when(OpenIDLoginCustomRefsConfig.AUDS.loadUserDetails(any(Authentication.class)))
+				.thenReturn(new User("user", "password", AuthorityUtils.createAuthorityList("ROLE_USER")));
+		OpenIDLoginCustomRefsConfig.ADS = spy(new WebAuthenticationDetailsSource());
+		OpenIDLoginCustomRefsConfig.CONSUMER = mock(OpenIDConsumer.class);
+
+		this.spring.register(OpenIDLoginCustomRefsConfig.class, UserDetailsServiceConfig.class).autowire();
+
+		when(OpenIDLoginCustomRefsConfig.CONSUMER.endConsumption(any(HttpServletRequest.class)))
+				.thenThrow(new AuthenticationServiceException("boom"));
+		this.mvc.perform(post("/login/openid").with(csrf())
+				.param("openid.identity", "identity"))
+				.andExpect(redirectedUrl("/custom/failure"));
+		reset(OpenIDLoginCustomRefsConfig.CONSUMER);
+
+		when(OpenIDLoginCustomRefsConfig.CONSUMER.endConsumption(any(HttpServletRequest.class)))
+				.thenReturn(token);
+		this.mvc.perform(post("/login/openid").with(csrf())
+				.param("openid.identity", "identity"))
+				.andExpect(redirectedUrl("/custom/targetUrl"));
+
+		verify(OpenIDLoginCustomRefsConfig.AUDS).loadUserDetails(any(Authentication.class));
+		verify(OpenIDLoginCustomRefsConfig.ADS).buildDetails(any(Object.class));
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	static class OpenIDLoginCustomRefsConfig extends WebSecurityConfigurerAdapter {
+		static AuthenticationUserDetailsService AUDS;
+		static AuthenticationDetailsSource ADS;
+		static OpenIDConsumer CONSUMER;
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			SavedRequestAwareAuthenticationSuccessHandler handler =
+					new SavedRequestAwareAuthenticationSuccessHandler();
+			handler.setDefaultTargetUrl("/custom/targetUrl");
+
+			http
+				.authorizeRequests()
+					.anyRequest().hasRole("USER")
+					.and()
+				.openidLogin()
+					// if using UserDetailsService wrap with new UserDetailsByNameServiceWrapper<OpenIDAuthenticationToken>()
+					.authenticationUserDetailsService(AUDS) // openid-login@user-service-ref
+					.failureHandler(new SimpleUrlAuthenticationFailureHandler("/custom/failure")) // openid-login@authentication-failure-handler-ref
+					.successHandler(handler) // openid-login@authentication-success-handler-ref
+					.authenticationDetailsSource(ADS) // openid-login@authentication-details-source-ref
+					.withObjectPostProcessor(new ObjectPostProcessor<OpenIDAuthenticationFilter>() {
+						@Override
+						public <O extends OpenIDAuthenticationFilter> O postProcess(O filter) {
+							filter.setConsumer(CONSUMER);
+							return filter;
+						}
+					});
+
+		}
+	}
+
+	@Configuration
+	static class UserDetailsServiceConfig {
+		@Bean
+		public UserDetailsService userDetailsService() {
+			return new InMemoryUserDetailsManager(
+					User.withDefaultPasswordEncoder()
+							.username("user")
+							.password("password")
+							.roles("USER")
+							.build());
+		}
+	}
+}