소스 검색

Move refreshable-metadata to use docker IdP

Issue gh-127
Josh Cummings 9 달 전
부모
커밋
84738c5940

+ 3 - 0
servlet/spring-boot/java/saml2/refreshable-metadata/build.gradle

@@ -13,6 +13,8 @@ repositories {
 	maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" }
 }
 
+sourceSets.main.java.srcDirs += "$projectDir/../identity-provider/src/main/java"
+sourceSets.main.resources.srcDirs += "$projectDir/../identity-provider/src/main/resources"
 
 dependencies {
 	constraints {
@@ -28,6 +30,7 @@ dependencies {
 	testImplementation 'org.htmlunit:htmlunit'
 	testImplementation 'org.springframework.boot:spring-boot-starter-test'
 	testImplementation 'org.springframework.security:spring-security-test'
+	runtimeOnly "org.springframework.boot:spring-boot-docker-compose"
 }
 
 tasks.withType(Test).configureEach {

+ 70 - 0
servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/PreDockerComposeServerPortInitializer.java

@@ -0,0 +1,70 @@
+/*
+ * Copyright 2002-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 java.net.ServerSocket;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.env.EnvironmentPostProcessor;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.PropertySource;
+
+/**
+ * Spring Boot doesn't determine the port before the docker containers are loaded, so
+ * we'll decide the test port here and override the associated properties.
+ *
+ * @author Josh Cummings
+ */
+public class PreDockerComposeServerPortInitializer implements EnvironmentPostProcessor {
+
+	private static final Integer port = getPort();
+
+	@Override
+	public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
+		environment.getPropertySources().addFirst(new ServerPortPropertySource(port));
+	}
+
+	private static Integer getPort() {
+		try (ServerSocket serverSocket = new ServerSocket(0)) {
+			return serverSocket.getLocalPort();
+		}
+		catch (IOException ex) {
+			throw new RuntimeException(ex);
+		}
+	}
+
+	private static class ServerPortPropertySource extends PropertySource<Integer> {
+
+		ServerPortPropertySource(Integer port) {
+			super("server.port.override", port);
+		}
+
+		@Override
+		public Object getProperty(String name) {
+			if ("server.port".equals(name)) {
+				return getSource();
+			}
+			if ("SERVER_PORT".equals(name)) {
+				return getSource();
+			}
+			return null;
+		}
+
+	}
+
+}

+ 38 - 11
servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/java/example/Saml2LoginApplicationITests.java

@@ -16,29 +16,34 @@
 
 package example;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.htmlunit.ElementNotFoundException;
 import org.htmlunit.WebClient;
+import org.htmlunit.html.HtmlButton;
+import org.htmlunit.html.HtmlElement;
 import org.htmlunit.html.HtmlForm;
 import org.htmlunit.html.HtmlInput;
 import org.htmlunit.html.HtmlPage;
 import org.htmlunit.html.HtmlPasswordInput;
-import org.htmlunit.html.HtmlSubmitInput;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.boot.test.web.server.LocalServerPort;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
-@SpringBootTest
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
 @AutoConfigureMockMvc
 public class Saml2LoginApplicationITests {
 
-	@Autowired
-	MockMvc mvc;
+
+	@LocalServerPort
+	int port;
 
 	@Autowired
 	WebClient webClient;
@@ -52,18 +57,40 @@ public class Saml2LoginApplicationITests {
 	void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception {
 		performLogin();
 		HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage();
-		assertThat(home.asNormalizedText()).contains("You're email address is testuser2@spring.security.saml");
+		assertThat(home.asNormalizedText()).contains("You're email address is user1@example.org");
+	}
+
+	@Test
+	void logoutWhenRelyingPartyInitiatedLogoutThenLoginPageWithLogoutParam() throws Exception {
+		performLogin();
+		HtmlPage home = (HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage();
+		HtmlElement rpLogoutButton = home.getHtmlElementById("rp_logout_button");
+		HtmlPage loginPage = rpLogoutButton.click();
+		this.webClient.waitForBackgroundJavaScript(10000);
+		List<String> urls = new ArrayList<>();
+		urls.add(loginPage.getUrl().getFile());
+		urls.add(((HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage()).getUrl().getFile());
+		assertThat(urls).withFailMessage(() -> {
+			// @formatter:off
+			String builder = loginPage.asXml()
+				+ "\n\n\n"
+				+ "Enclosing Page"
+				+ "\n\n\n"
+				+ ((HtmlPage) this.webClient.getCurrentWindow().getEnclosedPage()).asXml();
+			// @formatter:on
+			return builder;
+		}).contains("/login?logout");
 	}
 
 	private void performLogin() throws Exception {
-		HtmlPage login = this.webClient.getPage("/");
+		HtmlPage login = this.webClient.getPage("http://localhost:" + this.port + "/saml2/authenticate/one");
 		this.webClient.waitForBackgroundJavaScript(10000);
 		HtmlForm form = findForm(login);
 		HtmlInput username = form.getInputByName("username");
 		HtmlPasswordInput password = form.getInputByName("password");
-		HtmlSubmitInput submit = login.getHtmlElementById("okta-signin-submit");
-		username.type("testuser2@spring.security.saml");
-		password.type("12345678");
+		HtmlButton submit = (HtmlButton) form.getElementsByTagName("button").iterator().next();
+		username.type("user1");
+		password.type("user1pass");
 		submit.click();
 		this.webClient.waitForBackgroundJavaScript(10000);
 	}
@@ -71,7 +98,7 @@ public class Saml2LoginApplicationITests {
 	private HtmlForm findForm(HtmlPage login) {
 		for (HtmlForm form : login.getForms()) {
 			try {
-				if (form.getId().equals("form19")) {
+				if (form.getNameAttribute().equals("f")) {
 					return form;
 				}
 			}

+ 1 - 0
servlet/spring-boot/java/saml2/refreshable-metadata/src/integTest/resources/META-INF/spring.factories

@@ -0,0 +1 @@
+org.springframework.boot.env.EnvironmentPostProcessor=example.PreDockerComposeServerPortInitializer

+ 4 - 0
servlet/spring-boot/java/saml2/refreshable-metadata/src/main/java/example/RefreshableRelyingPartyRegistrationRepository.java

@@ -70,6 +70,10 @@ public class RefreshableRelyingPartyRegistrationRepository
 	private void fetchMetadata(String registrationId, Saml2RelyingPartyProperties.Registration registration) {
 		RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations
 			.fromMetadataLocation(registration.getAssertingparty().getMetadataUri())
+			.entityId(registration.getEntityId())
+			.assertionConsumerServiceLocation(registration.getAcs().getLocation())
+			.singleLogoutServiceLocation(registration.getSinglelogout().getUrl())
+			.singleLogoutServiceBinding(registration.getSinglelogout().getBinding())
 			.signingX509Credentials((credentials) -> registration.getSigning()
 				.getCredentials()
 				.stream()

+ 13 - 1
servlet/spring-boot/java/saml2/refreshable-metadata/src/main/resources/application.yml

@@ -1,14 +1,26 @@
 spring:
+  docker:
+    compose:
+      file: docker:docker/compose.yml
+      readiness:
+        wait: never
+      skip:
+        in-tests: false
   security:
     saml2:
       relyingparty:
         registration:
           one:
+            entity-id: "{baseUrl}/saml2/metadata"
+            acs.location: "{baseUrl}/login/saml2/sso"
             signing.credentials:
               - private-key-location: classpath:credentials/rp-private.key
                 certificate-location: classpath:credentials/rp-certificate.crt
+            singlelogout:
+              binding: REDIRECT
+              url: "{baseUrl}/logout/saml2/slo"
             assertingparty:
-              metadata-uri: https://dev-05937739.okta.com/app/exk46xofd8NZvFCpS5d7/sso/saml/metadata
+              metadata-uri: http://idp-one.7f000001.nip.io/simplesaml/saml2/idp/metadata.php
 
 logging.level:
   org.springframework.security: TRACE