Update custom-urls Sample to use Docker-based IdP
Issue gh-127
This commit is contained in:
8
servlet/spring-boot/java/saml2/custom-urls/.idea/.gitignore
generated
vendored
Normal file
8
servlet/spring-boot/java/saml2/custom-urls/.idea/.gitignore
generated
vendored
Normal file
@@ -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
|
||||
15
servlet/spring-boot/java/saml2/custom-urls/.idea/checkstyle-idea.xml
generated
Normal file
15
servlet/spring-boot/java/saml2/custom-urls/.idea/checkstyle-idea.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CheckStyle-IDEA" serialisationVersion="2">
|
||||
<checkstyleVersion>10.18.1</checkstyleVersion>
|
||||
<scanScope>JavaOnly</scanScope>
|
||||
<option name="thirdPartyClasspath" />
|
||||
<option name="activeLocationIds" />
|
||||
<option name="locations">
|
||||
<list>
|
||||
<ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">(bundled)</ConfigurationLocation>
|
||||
<ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">(bundled)</ConfigurationLocation>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
31
servlet/spring-boot/java/saml2/custom-urls/.idea/codeStyles
generated
Normal file
31
servlet/spring-boot/java/saml2/custom-urls/.idea/codeStyles
generated
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<DBN-PSQL>
|
||||
<case-options enabled="false">
|
||||
<option name="KEYWORD_CASE" value="lower" />
|
||||
<option name="FUNCTION_CASE" value="lower" />
|
||||
<option name="PARAMETER_CASE" value="lower" />
|
||||
<option name="DATATYPE_CASE" value="lower" />
|
||||
<option name="OBJECT_CASE" value="preserve" />
|
||||
</case-options>
|
||||
<formatting-settings enabled="false" />
|
||||
</DBN-PSQL>
|
||||
<DBN-SQL>
|
||||
<case-options enabled="false">
|
||||
<option name="KEYWORD_CASE" value="lower" />
|
||||
<option name="FUNCTION_CASE" value="lower" />
|
||||
<option name="PARAMETER_CASE" value="lower" />
|
||||
<option name="DATATYPE_CASE" value="lower" />
|
||||
<option name="OBJECT_CASE" value="preserve" />
|
||||
</case-options>
|
||||
<formatting-settings enabled="false">
|
||||
<option name="STATEMENT_SPACING" value="one_line" />
|
||||
<option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
|
||||
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
|
||||
</formatting-settings>
|
||||
</DBN-SQL>
|
||||
</code_scheme>
|
||||
</component>
|
||||
</project>
|
||||
6
servlet/spring-boot/java/saml2/custom-urls/.idea/compiler.xml
generated
Normal file
6
servlet/spring-boot/java/saml2/custom-urls/.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
</project>
|
||||
15
servlet/spring-boot/java/saml2/custom-urls/.idea/gradle.xml
generated
Normal file
15
servlet/spring-boot/java/saml2/custom-urls/.idea/gradle.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
35
servlet/spring-boot/java/saml2/custom-urls/.idea/jarRepositories.xml
generated
Normal file
35
servlet/spring-boot/java/saml2/custom-urls/.idea/jarRepositories.xml
generated
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenRepo" />
|
||||
<option name="name" value="MavenRepo" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven" />
|
||||
<option name="name" value="maven" />
|
||||
<option name="url" value="https://repo.spring.io/milestone" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven2" />
|
||||
<option name="name" value="maven2" />
|
||||
<option name="url" value="https://repo.spring.io/snapshot" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven3" />
|
||||
<option name="name" value="maven3" />
|
||||
<option name="url" value="https://build.shibboleth.net/nexus/content/repositories/releases/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
8
servlet/spring-boot/java/saml2/custom-urls/.idea/misc.xml
generated
Normal file
8
servlet/spring-boot/java/saml2/custom-urls/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK" />
|
||||
</project>
|
||||
6
servlet/spring-boot/java/saml2/custom-urls/.idea/vcs.xml
generated
Normal file
6
servlet/spring-boot/java/saml2/custom-urls/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../../../../.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -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 {
|
||||
|
||||
@@ -21,18 +21,19 @@ 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.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.request.MockMvcRequestBuilder
|
||||
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 @@ public class CustomUrlsApplicationITests {
|
||||
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 @@ public class CustomUrlsApplicationITests {
|
||||
}
|
||||
|
||||
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 @@ public class CustomUrlsApplicationITests {
|
||||
private HtmlForm findForm(HtmlPage login) {
|
||||
for (HtmlForm form : login.getForms()) {
|
||||
try {
|
||||
if (form.getId().equals("form19")) {
|
||||
if (form.getNameAttribute().equals("f")) {
|
||||
return form;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.boot.env.EnvironmentPostProcessor=example.PreDockerComposeServerPortInitializer
|
||||
@@ -21,12 +21,6 @@ import org.springframework.context.annotation.Configuration;
|
||||
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 @@ import org.springframework.security.web.SecurityFilterChain;
|
||||
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 @@ public class SecurityConfiguration {
|
||||
)
|
||||
.saml2Login(Customizer.withDefaults())
|
||||
.saml2Logout(Customizer.withDefaults())
|
||||
.addFilterBefore(metadataFilter, Saml2WebSsoAuthenticationFilter.class);
|
||||
.saml2Metadata(Customizer.withDefaults());
|
||||
// @formatter:on
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<?php
|
||||
$port = getenv("PORT");
|
||||
|
||||
// Spring Security SP
|
||||
|
||||
$metadata["http://localhost:$port/saml2/metadata"] = array(
|
||||
'AssertionConsumerService' => "https://localhost:$port/login/saml2/sso",
|
||||
'SingleLogoutService' => "https://localhost:$port/logout/saml2/slo",
|
||||
@@ -10,4 +13,23 @@ $metadata["http://localhost:$port/saml2/metadata"] = array(
|
||||
'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,
|
||||
);
|
||||
?>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<?php
|
||||
$port = getenv("PORT");
|
||||
|
||||
// Spring Security SP
|
||||
|
||||
$metadata["http://localhost:$port/saml2/metadata"] = array(
|
||||
'AssertionConsumerService' => "http://localhost:$port/login/saml2/sso",
|
||||
'SingleLogoutService' => "http://localhost:$port/logout/saml2/slo",
|
||||
@@ -10,4 +13,23 @@ $metadata["http://localhost:$port/saml2/metadata"] = array(
|
||||
'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,
|
||||
);
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user