diff --git a/servlet/spring-boot/java/saml2/custom-urls/.idea/.gitignore b/servlet/spring-boot/java/saml2/custom-urls/.idea/.gitignore
new file mode 100644
index 000000000..13566b81b
--- /dev/null
+++ b/servlet/spring-boot/java/saml2/custom-urls/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/servlet/spring-boot/java/saml2/custom-urls/.idea/checkstyle-idea.xml b/servlet/spring-boot/java/saml2/custom-urls/.idea/checkstyle-idea.xml
new file mode 100644
index 000000000..0389c4061
--- /dev/null
+++ b/servlet/spring-boot/java/saml2/custom-urls/.idea/checkstyle-idea.xml
@@ -0,0 +1,15 @@
+
+
+
+ 10.18.1
+ JavaOnly
+
+
+
+
+
\ No newline at end of file
diff --git a/servlet/spring-boot/java/saml2/custom-urls/.idea/codeStyles b/servlet/spring-boot/java/saml2/custom-urls/.idea/codeStyles
new file mode 100644
index 000000000..72a9bbb58
--- /dev/null
+++ b/servlet/spring-boot/java/saml2/custom-urls/.idea/codeStyles
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/servlet/spring-boot/java/saml2/custom-urls/.idea/compiler.xml b/servlet/spring-boot/java/saml2/custom-urls/.idea/compiler.xml
new file mode 100644
index 000000000..b86273d94
--- /dev/null
+++ b/servlet/spring-boot/java/saml2/custom-urls/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/servlet/spring-boot/java/saml2/custom-urls/.idea/gradle.xml b/servlet/spring-boot/java/saml2/custom-urls/.idea/gradle.xml
new file mode 100644
index 000000000..f9163b40e
--- /dev/null
+++ b/servlet/spring-boot/java/saml2/custom-urls/.idea/gradle.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/servlet/spring-boot/java/saml2/custom-urls/.idea/jarRepositories.xml b/servlet/spring-boot/java/saml2/custom-urls/.idea/jarRepositories.xml
new file mode 100644
index 000000000..5938bdd69
--- /dev/null
+++ b/servlet/spring-boot/java/saml2/custom-urls/.idea/jarRepositories.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/servlet/spring-boot/java/saml2/custom-urls/.idea/misc.xml b/servlet/spring-boot/java/saml2/custom-urls/.idea/misc.xml
new file mode 100644
index 000000000..7c47dd48d
--- /dev/null
+++ b/servlet/spring-boot/java/saml2/custom-urls/.idea/misc.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/servlet/spring-boot/java/saml2/custom-urls/.idea/vcs.xml b/servlet/spring-boot/java/saml2/custom-urls/.idea/vcs.xml
new file mode 100644
index 000000000..bc5997070
--- /dev/null
+++ b/servlet/spring-boot/java/saml2/custom-urls/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/servlet/spring-boot/java/saml2/custom-urls/build.gradle b/servlet/spring-boot/java/saml2/custom-urls/build.gradle
index f2484a768..8767d557e 100644
--- a/servlet/spring-boot/java/saml2/custom-urls/build.gradle
+++ b/servlet/spring-boot/java/saml2/custom-urls/build.gradle
@@ -12,6 +12,15 @@ 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"
+
+if (plugins.hasPlugin("io.spring.javaformat")) {
+ tasks.formatMain {
+ dependsOn(":servlet:spring-boot:java:saml2:identity-provider:formatMain")
+ }
+}
+
dependencies {
constraints {
implementation "org.opensaml:opensaml-saml-api:5.1.3"
@@ -26,6 +35,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 {
diff --git a/servlet/spring-boot/java/saml2/custom-urls/src/integTest/java/example/CustomUrlsApplicationITests.java b/servlet/spring-boot/java/saml2/custom-urls/src/integTest/java/example/CustomUrlsApplicationITests.java
index fb4c78e39..f65671e23 100644
--- a/servlet/spring-boot/java/saml2/custom-urls/src/integTest/java/example/CustomUrlsApplicationITests.java
+++ b/servlet/spring-boot/java/saml2/custom-urls/src/integTest/java/example/CustomUrlsApplicationITests.java
@@ -21,18 +21,19 @@
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.boot.test.web.server.LocalServerPort;
import org.springframework.test.web.servlet.MockMvc;
import static org.assertj.core.api.Assertions.assertThat;
@@ -40,10 +41,13 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-@SpringBootTest
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@AutoConfigureMockMvc
public class CustomUrlsApplicationITests {
+ @LocalServerPort
+ int port;
+
@Autowired
MockMvc mvc;
@@ -59,7 +63,7 @@ void setup() {
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
@@ -83,14 +87,14 @@ void metadataWhenGetThenForwardToUrl() throws Exception {
}
private void performLogin() throws Exception {
- HtmlPage login = this.webClient.getPage("/");
+ HtmlPage login = this.webClient.getPage("http://localhost:" + this.port + "/saml/login");
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);
}
@@ -98,7 +102,7 @@ private void performLogin() throws Exception {
private HtmlForm findForm(HtmlPage login) {
for (HtmlForm form : login.getForms()) {
try {
- if (form.getId().equals("form19")) {
+ if (form.getNameAttribute().equals("f")) {
return form;
}
}
diff --git a/servlet/spring-boot/java/saml2/custom-urls/src/integTest/java/example/PreDockerComposeServerPortInitializer.java b/servlet/spring-boot/java/saml2/custom-urls/src/integTest/java/example/PreDockerComposeServerPortInitializer.java
new file mode 100644
index 000000000..96e373dba
--- /dev/null
+++ b/servlet/spring-boot/java/saml2/custom-urls/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 {
+
+ 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;
+ }
+
+ }
+
+}
diff --git a/servlet/spring-boot/java/saml2/custom-urls/src/integTest/resources/META-INF/spring.factories b/servlet/spring-boot/java/saml2/custom-urls/src/integTest/resources/META-INF/spring.factories
new file mode 100644
index 000000000..f4d2ae40c
--- /dev/null
+++ b/servlet/spring-boot/java/saml2/custom-urls/src/integTest/resources/META-INF/spring.factories
@@ -0,0 +1 @@
+org.springframework.boot.env.EnvironmentPostProcessor=example.PreDockerComposeServerPortInitializer
\ No newline at end of file
diff --git a/servlet/spring-boot/java/saml2/custom-urls/src/main/java/example/SecurityConfiguration.java b/servlet/spring-boot/java/saml2/custom-urls/src/main/java/example/SecurityConfiguration.java
index ea58cac7f..97b41f446 100644
--- a/servlet/spring-boot/java/saml2/custom-urls/src/main/java/example/SecurityConfiguration.java
+++ b/servlet/spring-boot/java/saml2/custom-urls/src/main/java/example/SecurityConfiguration.java
@@ -21,12 +21,6 @@
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataResolver;
-import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
-import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
-import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
-import org.springframework.security.saml2.provider.service.web.Saml2MetadataFilter;
-import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@@ -34,12 +28,7 @@
public class SecurityConfiguration {
@Bean
- SecurityFilterChain securityFilterChain(HttpSecurity http,
- RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) throws Exception {
- RelyingPartyRegistrationResolver relyingPartyRegistrationResolver = new DefaultRelyingPartyRegistrationResolver(
- relyingPartyRegistrationRepository);
- Saml2MetadataFilter metadataFilter = new Saml2MetadataFilter(relyingPartyRegistrationResolver,
- new OpenSamlMetadataResolver());
+ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize
@@ -48,7 +37,7 @@ SecurityFilterChain securityFilterChain(HttpSecurity http,
)
.saml2Login(Customizer.withDefaults())
.saml2Logout(Customizer.withDefaults())
- .addFilterBefore(metadataFilter, Saml2WebSsoAuthenticationFilter.class);
+ .saml2Metadata(Customizer.withDefaults());
// @formatter:on
return http.build();
}
diff --git a/servlet/spring-boot/java/saml2/custom-urls/src/main/resources/application.yml b/servlet/spring-boot/java/saml2/custom-urls/src/main/resources/application.yml
index f32f09ac6..b004b612e 100644
--- a/servlet/spring-boot/java/saml2/custom-urls/src/main/resources/application.yml
+++ b/servlet/spring-boot/java/saml2/custom-urls/src/main/resources/application.yml
@@ -1,4 +1,14 @@
+logging.level:
+ org.springframework.security: TRACE
+
spring:
+ docker:
+ compose:
+ file: docker:docker/compose.yml
+ readiness:
+ wait: never
+ skip:
+ in-tests: false
security:
filter:
dispatcher-types: async, error, request, forward
@@ -6,12 +16,13 @@ spring:
relyingparty:
registration:
one:
+ entity-id: "{baseUrl}/saml/metadata"
signing.credentials:
- private-key-location: classpath:credentials/rp-private.key
certificate-location: classpath:credentials/rp-certificate.crt
- assertingparty.metadata-uri: https://dev-05937739.okta.com/app/exk598vc9bHhwoTXM5d7/sso/saml/metadata
+ assertingparty.metadata-uri: http://idp-one.7f000001.nip.io/simplesaml/saml2/idp/metadata.php
singlelogout:
- binding: POST
+ binding: REDIRECT
url: "{baseUrl}/saml/logout"
responseUrl: "{baseUrl}/saml/SingleLogout"
acs:
diff --git a/servlet/spring-boot/java/saml2/identity-provider/src/main/resources/docker/metadata/one-relyingparties.php b/servlet/spring-boot/java/saml2/identity-provider/src/main/resources/docker/metadata/one-relyingparties.php
index a08fa8c4d..46894211f 100644
--- a/servlet/spring-boot/java/saml2/identity-provider/src/main/resources/docker/metadata/one-relyingparties.php
+++ b/servlet/spring-boot/java/saml2/identity-provider/src/main/resources/docker/metadata/one-relyingparties.php
@@ -1,5 +1,8 @@
"https://localhost:$port/login/saml2/sso",
'SingleLogoutService' => "https://localhost:$port/logout/saml2/slo",
@@ -10,4 +13,23 @@
'validate.authnrequest' => FALSE,
'redirect.sign' => TRUE,
);
+
+// Spring Security SAML SP
+
+$metadata["http://localhost:$port/saml/metadata"] = array(
+ 'AssertionConsumerService' => "http://localhost:$port/saml/SSO",
+ 'SingleLogoutService' => [
+ [
+ 'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
+ 'Location' => "http://localhost:$port/saml/logout",
+ 'ResponseLocation' => "http://localhost:$port/saml/SingleLogout"
+ ]
+ ],
+ 'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
+ 'simplesaml.nameidattribute' => 'emailAddress',
+ 'assertion.encryption' => FALSE,
+ 'nameid.encryption' => FALSE,
+ 'validate.authnrequest' => FALSE,
+ 'redirect.sign' => TRUE,
+);
?>
diff --git a/servlet/spring-boot/java/saml2/identity-provider/src/main/resources/docker/metadata/two-relyingparties.php b/servlet/spring-boot/java/saml2/identity-provider/src/main/resources/docker/metadata/two-relyingparties.php
index a82934eaf..9cf7f3152 100644
--- a/servlet/spring-boot/java/saml2/identity-provider/src/main/resources/docker/metadata/two-relyingparties.php
+++ b/servlet/spring-boot/java/saml2/identity-provider/src/main/resources/docker/metadata/two-relyingparties.php
@@ -1,5 +1,8 @@
"http://localhost:$port/login/saml2/sso",
'SingleLogoutService' => "http://localhost:$port/logout/saml2/slo",
@@ -10,4 +13,23 @@
'validate.authnrequest' => FALSE,
'redirect.sign' => TRUE,
);
+
+// Spring Security SAML SP
+
+$metadata["http://localhost:$port/saml/metadata"] = array(
+ 'AssertionConsumerService' => "http://localhost:$port/saml/SSO",
+ 'SingleLogoutService' => [
+ [
+ 'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
+ 'Location' => "http://localhost:$port/saml/logout",
+ 'ResponseLocation' => "http://localhost:$port/saml/SingleLogout"
+ ]
+ ],
+ 'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
+ 'simplesaml.nameidattribute' => 'emailAddress',
+ 'assertion.encryption' => FALSE,
+ 'nameid.encryption' => FALSE,
+ 'validate.authnrequest' => FALSE,
+ 'redirect.sign' => TRUE,
+);
?>