diff --git a/servlet/java-configuration/saml2/login/build.gradle b/servlet/java-configuration/saml2/login/build.gradle index 2f02269..da355e3 100644 --- a/servlet/java-configuration/saml2/login/build.gradle +++ b/servlet/java-configuration/saml2/login/build.gradle @@ -55,10 +55,10 @@ dependencies { testImplementation "org.springframework:spring-test" testImplementation "org.springframework.security:spring-security-test" testImplementation("org.junit.jupiter:junit-jupiter-api") + testImplementation "org.seleniumhq.selenium:htmlunit-driver:2.44.0" + testImplementation 'org.hamcrest:hamcrest:2.2' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") - - integTestImplementation "org.seleniumhq.selenium:htmlunit-driver:2.44.0" } tasks.withType(Test).configureEach { diff --git a/servlet/java-configuration/saml2/login/src/integTest/java/example/LocalHostWebClient.java b/servlet/java-configuration/saml2/login/src/integTest/java/example/LocalHostWebClient.java new file mode 100644 index 0000000..8cefa23 --- /dev/null +++ b/servlet/java-configuration/saml2/login/src/integTest/java/example/LocalHostWebClient.java @@ -0,0 +1,53 @@ +/* + * Copyright 2021 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 example; + +import java.io.IOException; + +import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; +import com.gargoylesoftware.htmlunit.Page; +import com.gargoylesoftware.htmlunit.WebClient; + +import org.springframework.core.env.Environment; +import org.springframework.util.Assert; + +/** + * {@link WebClient} will automatically prefix relative URLs with + * localhost:${local.server.port}. + * + * @author Phillip Webb + * @since 1.4.0 + */ +public class LocalHostWebClient extends WebClient { + + private final Environment environment; + + public LocalHostWebClient(Environment environment) { + Assert.notNull(environment, "Environment must not be null"); + this.environment = environment; + } + + @Override + public

P getPage(String url) throws IOException, FailingHttpStatusCodeException { + if (url.startsWith("/")) { + String port = this.environment.getProperty("local.server.port", "8080"); + url = "http://localhost:" + port + url; + } + return super.getPage(url); + } + +} diff --git a/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java b/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java new file mode 100644 index 0000000..3cd5ac1 --- /dev/null +++ b/servlet/java-configuration/saml2/login/src/integTest/java/example/Saml2JavaConfigurationITests.java @@ -0,0 +1,100 @@ +/* + * Copyright 2021 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 example; + +import java.io.IOException; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlInput; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.htmlunit.MockMvcWebClientBuilder; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = ApplicationConfiguration.class) +@WebAppConfiguration +public class Saml2JavaConfigurationITests { + + private MockMvc mvc; + + private WebClient webClient; + + @Autowired + WebApplicationContext webApplicationContext; + + @Autowired + Environment environment; + + @BeforeEach + void setup() { + this.mvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext) + .apply(SecurityMockMvcConfigurers.springSecurity()).build(); + this.webClient = MockMvcWebClientBuilder.mockMvcSetup(this.mvc) + .withDelegate(new LocalHostWebClient(this.environment)).build(); + this.webClient.getCookieManager().clearCookies(); + } + + @Test + void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { + HtmlPage relyingParty = performLogin(); + Assertions.assertThat(relyingParty.asText()).contains("You're email address is testuser@spring.security.saml"); + } + + @Test + void logoutWhenRelyingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { + HtmlPage relyingParty = performLogin(); + HtmlElement rpLogoutButton = relyingParty.getHtmlElementById("rp_logout_button"); + HtmlPage loginPage = rpLogoutButton.click(); + Assertions.assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); + } + + @Test + void logoutWhenAssertingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception { + HtmlPage relyingParty = performLogin(); + HtmlElement apLogoutButton = relyingParty.getHtmlElementById("ap_logout_button"); + HtmlPage loginPage = apLogoutButton.click(); + Assertions.assertThat(loginPage.getUrl().getFile()).isEqualTo("/login?logout"); + } + + private HtmlPage performLogin() throws IOException { + HtmlPage login = this.webClient.getPage("/"); + HtmlForm form = login.getFormByName("f"); + HtmlInput username = form.getInputByName("username"); + HtmlInput password = form.getInputByName("password"); + HtmlSubmitInput submit = login.getHtmlElementById("submit_button"); + username.setValueAttribute("user"); + password.setValueAttribute("password"); + return submit.click(); + } + +} diff --git a/servlet/java-configuration/saml2/login/src/main/java/example/SecurityConfiguration.java b/servlet/java-configuration/saml2/login/src/main/java/example/SecurityConfiguration.java index fd05fc1..50daa56 100644 --- a/servlet/java-configuration/saml2/login/src/main/java/example/SecurityConfiguration.java +++ b/servlet/java-configuration/saml2/login/src/main/java/example/SecurityConfiguration.java @@ -32,8 +32,6 @@ import org.springframework.security.saml2.provider.service.registration.InMemory import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; -import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver; -import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; import org.springframework.security.web.SecurityFilterChain; @EnableWebSecurity @@ -56,12 +54,6 @@ public class SecurityConfiguration { return http.build(); } - @Bean - RelyingPartyRegistrationResolver relyingPartyRegistrationResolver( - RelyingPartyRegistrationRepository registrations) { - return new DefaultRelyingPartyRegistrationResolver(registrations); - } - @Bean RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() { RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations