From e6a71192d7268246b809521b3919fc4e8dd87be0 Mon Sep 17 00:00:00 2001
From: AB
Date: Tue, 2 Apr 2024 11:53:13 +0200
Subject: [PATCH 01/47] Init
---
.run/Run Demo.run.xml | 4 ++--
CONTRIBUTING.md | 4 ++--
README.md | 12 ++++++------
SECURITY.md | 2 +-
pom.xml | 6 +++---
.../pom.xml | 4 ++--
.../pom.xml | 12 ++++++------
7 files changed, 22 insertions(+), 22 deletions(-)
rename {standard-maven-template-demo => spring-security-advanced-login-demo}/pom.xml (96%)
rename {standard-maven-template => spring-security-advanced-login}/pom.xml (93%)
diff --git a/.run/Run Demo.run.xml b/.run/Run Demo.run.xml
index 764b3b9..58378e0 100644
--- a/.run/Run Demo.run.xml
+++ b/.run/Run Demo.run.xml
@@ -1,7 +1,7 @@
-
+
@@ -13,4 +13,4 @@
-
\ No newline at end of file
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cbb31c6..dd3f598 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -33,10 +33,10 @@ You should have the following things installed:
* Ensure that the JDK/Java-Version is correct
-## Releasing [![Build](https://img.shields.io/github/actions/workflow/status/xdev-software/standard-maven-template/release.yml?branch=master)](https://github.com/xdev-software/standard-maven-template/actions/workflows/release.yml)
+## Releasing [![Build](https://img.shields.io/github/actions/workflow/status/xdev-software/spring-security-advanced-login/release.yml?branch=master)](https://github.com/xdev-software/spring-security-advanced-login/actions/workflows/release.yml)
Before releasing:
-* Consider doing a [test-deployment](https://github.com/xdev-software/standard-maven-template/actions/workflows/test-deploy.yml?query=branch%3Adevelop) before actually releasing.
+* Consider doing a [test-deployment](https://github.com/xdev-software/spring-security-advanced-login/actions/workflows/test-deploy.yml?query=branch%3Adevelop) before actually releasing.
* Check the [changelog](CHANGELOG.md)
If the ``develop`` is ready for release, create a pull request to the ``master``-Branch and merge the changes
diff --git a/README.md b/README.md
index db9412b..305b34c 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,12 @@
-[![Latest version](https://img.shields.io/maven-central/v/software.xdev/standard-maven-template?logo=apache%20maven)](https://mvnrepository.com/artifact/software.xdev/standard-maven-template)
-[![Build](https://img.shields.io/github/actions/workflow/status/xdev-software/standard-maven-template/checkBuild.yml?branch=develop)](https://github.com/xdev-software/standard-maven-template/actions/workflows/checkBuild.yml?query=branch%3Adevelop)
-[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=xdev-software_standard-maven-template&metric=alert_status)](https://sonarcloud.io/dashboard?id=xdev-software_standard-maven-template)
+[![Latest version](https://img.shields.io/maven-central/v/software.xdev/spring-security-advanced-login?logo=apache%20maven)](https://mvnrepository.com/artifact/software.xdev/spring-security-advanced-login)
+[![Build](https://img.shields.io/github/actions/workflow/status/xdev-software/spring-security-advanced-login/checkBuild.yml?branch=develop)](https://github.com/xdev-software/spring-security-advanced-login/actions/workflows/checkBuild.yml?query=branch%3Adevelop)
+[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=xdev-software_spring-security-advanced-login&metric=alert_status)](https://sonarcloud.io/dashboard?id=xdev-software_spring-security-advanced-login)
-# standard-maven-template
+# spring-security-advanced-login
## Installation
-[Installation guide for the latest release](https://github.com/xdev-software/standard-maven-template/releases/latest#Installation)
+[Installation guide for the latest release](https://github.com/xdev-software/spring-security-advanced-login/releases/latest#Installation)
## Support
@@ -16,4 +16,4 @@ If you need support as soon as possible and you can't wait for any pull request,
See the [contributing guide](./CONTRIBUTING.md) for detailed instructions on how to get started with our project.
## Dependencies and Licenses
-View the [license of the current project](LICENSE) or the [summary including all dependencies](https://xdev-software.github.io/standard-maven-template/dependencies)
+View the [license of the current project](LICENSE) or the [summary including all dependencies](https://xdev-software.github.io/spring-security-advanced-login/dependencies)
diff --git a/SECURITY.md b/SECURITY.md
index 92188f3..96b03b1 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,4 +2,4 @@
## Reporting a Vulnerability
-Please report a security vulnerability [on GitHub Security Advisories](https://github.com/xdev-software/standard-maven-template/security/advisories/new).
+Please report a security vulnerability [on GitHub Security Advisories](https://github.com/xdev-software/spring-security-advanced-login/security/advisories/new).
diff --git a/pom.xml b/pom.xml
index 4e48f7a..d7b1a20 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
4.0.0
software.xdev
- standard-maven-template-root
+ spring-security-advanced-login-root
1.0.0-SNAPSHOT
pom
@@ -15,8 +15,8 @@
- standard-maven-template
- standard-maven-template-demo
+ spring-security-advanced-login
+ spring-security-advanced-login-demo
diff --git a/standard-maven-template-demo/pom.xml b/spring-security-advanced-login-demo/pom.xml
similarity index 96%
rename from standard-maven-template-demo/pom.xml
rename to spring-security-advanced-login-demo/pom.xml
index 2e466d4..6711f25 100644
--- a/standard-maven-template-demo/pom.xml
+++ b/spring-security-advanced-login-demo/pom.xml
@@ -5,7 +5,7 @@
4.0.0
software.xdev
- standard-maven-template-demo
+ spring-security-advanced-login-demo
1.0.0-SNAPSHOT
jar
@@ -29,7 +29,7 @@
software.xdev
- standard-maven-template
+ spring-security-advanced-login
${project.version}
diff --git a/standard-maven-template/pom.xml b/spring-security-advanced-login/pom.xml
similarity index 93%
rename from standard-maven-template/pom.xml
rename to spring-security-advanced-login/pom.xml
index e84ddff..408f9fc 100644
--- a/standard-maven-template/pom.xml
+++ b/spring-security-advanced-login/pom.xml
@@ -5,17 +5,17 @@
4.0.0
software.xdev
- standard-maven-template
+ spring-security-advanced-login
1.0.0-SNAPSHOT
jar
- standard-maven-template
- standard-maven-template
- https://github.com/xdev-software/standard-maven-template
+ spring-security-advanced-login
+ spring-security-advanced-login
+ https://github.com/xdev-software/spring-security-advanced-login
- https://github.com/xdev-software/standard-maven-template
- scm:git:https://github.com/xdev-software/standard-maven-template.git
+ https://github.com/xdev-software/spring-security-advanced-login
+ scm:git:https://github.com/xdev-software/spring-security-advanced-login.git
2023
From 90c5fc455c5e940c2df00741f145859996b724af Mon Sep 17 00:00:00 2001
From: AB
Date: Tue, 2 Apr 2024 13:29:46 +0200
Subject: [PATCH 02/47] Init
---
spring-security-advanced-login/pom.xml | 22 +
.../advanced/AdditionalRegistrationData.java | 40 ++
.../ui/advanced/AdvancedLoginPageAdapter.java | 51 +++
.../ui/advanced/StylingDefinition.java | 26 ++
.../AdditionalOAuth2ClientProperties.java | 25 ++
.../AdditionalSaml2ClientProperties.java | 25 ++
.../AdvancedLoginPageGeneratingFilter.java | 333 +++++++++++++++
.../AdvancedLogoutPageGeneratingFilter.java | 104 +++++
.../AdvancedSharedPageGeneratingFilter.java | 31 ++
.../ExtendableLoginPageAdapter.java | 288 +++++++++++++
.../ui/extendable/FieldAccessor.java | 45 ++
...dableDefaultLoginPageGeneratingFilter.java | 388 ++++++++++++++++++
...ableDefaultLogoutPageGeneratingFilter.java | 132 ++++++
...ExtendableDefaultPageGeneratingFilter.java | 5 +
.../filters/GeneratingFilterFillDataFrom.java | 27 ++
15 files changed, 1542 insertions(+)
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java
create mode 100644 spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java
diff --git a/spring-security-advanced-login/pom.xml b/spring-security-advanced-login/pom.xml
index 408f9fc..85ea478 100644
--- a/spring-security-advanced-login/pom.xml
+++ b/spring-security-advanced-login/pom.xml
@@ -84,6 +84,28 @@
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 3.2.4
+ provided
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+ 3.2.4
+ provided
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.10.2
+ test
+
+
+
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java
new file mode 100644
index 0000000..49c6f75
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java
@@ -0,0 +1,40 @@
+package software.xdev.spring.security.web.authentication.ui.advanced;
+
+public class AdditionalRegistrationData
+{
+ private String color;
+
+ private String iconSrc;
+
+ private boolean invertTextColor;
+
+ public String getColor()
+ {
+ return this.color;
+ }
+
+ public void setColor(final String color)
+ {
+ this.color = color;
+ }
+
+ public String getIconSrc()
+ {
+ return this.iconSrc;
+ }
+
+ public void setIconSrc(final String iconSrc)
+ {
+ this.iconSrc = iconSrc;
+ }
+
+ public boolean isInvertTextColor()
+ {
+ return this.invertTextColor;
+ }
+
+ public void setInvertTextColor(final boolean invertTextColor)
+ {
+ this.invertTextColor = invertTextColor;
+ }
+}
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java
new file mode 100644
index 0000000..219b7e8
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java
@@ -0,0 +1,51 @@
+package software.xdev.spring.security.web.authentication.ui.advanced;
+
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+
+import software.xdev.spring.security.web.authentication.ui.advanced.filters.AdvancedLoginPageGeneratingFilter;
+import software.xdev.spring.security.web.authentication.ui.advanced.filters.AdvancedLogoutPageGeneratingFilter;
+import software.xdev.spring.security.web.authentication.ui.advanced.filters.AdvancedSharedPageGeneratingFilter;
+import software.xdev.spring.security.web.authentication.ui.extendable.ExtendableLoginPageAdapter;
+
+
+public class AdvancedLoginPageAdapter>
+ extends ExtendableLoginPageAdapter<
+ AdvancedLoginPageAdapter,
+ AdvancedSharedPageGeneratingFilter>,
+ AdvancedLoginPageGeneratingFilter,
+ AdvancedLogoutPageGeneratingFilter,
+ H>
+{
+ public AdvancedLoginPageAdapter(
+ final Consumer> c)
+ {
+ super(() -> new InitConfiguration<>(
+ AdvancedLoginPageGeneratingFilter::new,
+ AdvancedLogoutPageGeneratingFilter::new), c);
+ }
+
+ public AdvancedLoginPageAdapter(
+ final InitConfiguration initConfig)
+ {
+ super(initConfig);
+ }
+
+ public AdvancedLoginPageAdapter(
+ final HttpSecurity httpSecurity,
+ final Supplier loginPageGeneratingFilterSupplier,
+ final Supplier logoutPageGeneratingFilterSupplier,
+ final boolean copyDataFromExistingFilters,
+ final InstallWith installWith)
+ {
+ super(
+ httpSecurity,
+ loginPageGeneratingFilterSupplier,
+ logoutPageGeneratingFilterSupplier,
+ copyDataFromExistingFilters,
+ installWith);
+ }
+}
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java
new file mode 100644
index 0000000..0c069b9
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java
@@ -0,0 +1,26 @@
+package software.xdev.spring.security.web.authentication.ui.advanced;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+
+public record StylingDefinition(
+ Set classNames,
+ String style
+)
+{
+ public String classNameString()
+ {
+ return this.classNames().stream().map(s -> " " + s).collect(Collectors.joining());
+ }
+
+ public static StylingDefinition classNames(final String... classNames)
+ {
+ return new StylingDefinition(Set.of(classNames), null);
+ }
+
+ public static StylingDefinition style(final String style)
+ {
+ return new StylingDefinition(Set.of(), style);
+ }
+}
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java
new file mode 100644
index 0000000..400db1c
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java
@@ -0,0 +1,25 @@
+package software.xdev.spring.security.web.authentication.ui.advanced.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import software.xdev.spring.security.web.authentication.ui.advanced.AdditionalRegistrationData;
+
+
+@ConfigurationProperties(prefix = "spring.security.oauth2.client")
+public class AdditionalOAuth2ClientProperties
+{
+ private Map registration = new HashMap<>();
+
+ public Map getRegistration()
+ {
+ return this.registration;
+ }
+
+ public void setRegistration(final Map registration)
+ {
+ this.registration = registration;
+ }
+}
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java
new file mode 100644
index 0000000..be998b2
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java
@@ -0,0 +1,25 @@
+package software.xdev.spring.security.web.authentication.ui.advanced.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import software.xdev.spring.security.web.authentication.ui.advanced.AdditionalRegistrationData;
+
+
+@ConfigurationProperties(prefix = "spring.security.saml2.client")
+public class AdditionalSaml2ClientProperties
+{
+ private Map registration = new HashMap<>();
+
+ public Map getRegistration()
+ {
+ return this.registration;
+ }
+
+ public void setRegistration(final Map registration)
+ {
+ this.registration = registration;
+ }
+}
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java
new file mode 100644
index 0000000..82e9b88
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java
@@ -0,0 +1,333 @@
+package software.xdev.spring.security.web.authentication.ui.advanced.filters;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+import org.springframework.web.util.HtmlUtils;
+
+import software.xdev.spring.security.web.authentication.ui.advanced.AdditionalRegistrationData;
+import software.xdev.spring.security.web.authentication.ui.advanced.StylingDefinition;
+import software.xdev.spring.security.web.authentication.ui.extendable.filters.ExtendableDefaultLoginPageGeneratingFilter;
+
+
+public class AdvancedLoginPageGeneratingFilter
+ extends ExtendableDefaultLoginPageGeneratingFilter
+ implements AdvancedSharedPageGeneratingFilter
+{
+ protected List headerElements = new ArrayList<>(DEFAULT_HEADER_ELEMENTS);
+
+ protected String pageTitle = "Please sign in";
+
+ protected Map headerMetas = new LinkedHashMap<>(DEFAULT_HEADER_METAS);
+
+ protected Map additionalOAuth2RegistrationProperties;
+
+ protected Map additionalSaml2RegistrationProperties;
+
+ protected AdditionalStylingData additionalStylingData;
+
+ protected String header = "";
+
+ protected String footer = "";
+
+ @Override
+ public AdvancedLoginPageGeneratingFilter setHeaderElements(final List headerElements)
+ {
+ this.headerElements = headerElements;
+ return this;
+ }
+
+ @Override
+ public AdvancedLoginPageGeneratingFilter addHeaderElement(final String element)
+ {
+ this.headerElements.add(element);
+ return this;
+ }
+
+ @Override
+ public AdvancedLoginPageGeneratingFilter pageTitle(final String pageTitle)
+ {
+ this.pageTitle = pageTitle;
+ return this;
+ }
+
+ @Override
+ public AdvancedLoginPageGeneratingFilter setHeaderMetas(final Map headerMetas)
+ {
+ this.headerMetas = headerMetas;
+ return this;
+ }
+
+ @Override
+ public AdvancedLoginPageGeneratingFilter addHeaderMeta(final String name, final String content)
+ {
+ this.headerMetas.put(name, content);
+ return this;
+ }
+
+ public AdvancedLoginPageGeneratingFilter additionalOAuth2RegistrationProperties(
+ final Map additionalOAuth2RegistrationProperties)
+ {
+ this.additionalOAuth2RegistrationProperties = additionalOAuth2RegistrationProperties;
+ return this;
+ }
+
+ public AdvancedLoginPageGeneratingFilter additionalSaml2RegistrationProperties(
+ final Map additionalSaml2RegistrationProperties)
+ {
+ this.additionalSaml2RegistrationProperties = additionalSaml2RegistrationProperties;
+ return this;
+ }
+
+ public AdvancedLoginPageGeneratingFilter additionalStylingData(final AdditionalStylingData additionalStylingData)
+ {
+ this.additionalStylingData = additionalStylingData;
+ return this;
+ }
+
+ public AdvancedLoginPageGeneratingFilter header(final String header)
+ {
+ this.header = Objects.requireNonNullElse(header, "");
+ return this;
+ }
+
+ public AdvancedLoginPageGeneratingFilter footer(final String footer)
+ {
+ this.footer = Objects.requireNonNullElse(footer, "");
+ return this;
+ }
+
+ protected Optional additionalRegistrationData(
+ final Map additionalRegistrationProperties,
+ final String url)
+ {
+ return Optional.ofNullable(additionalRegistrationProperties)
+ .map(registrations -> {
+ final int index = url.lastIndexOf('/');
+ return registrations.get(index != -1 ? url.substring(index + 1) : url);
+ });
+ }
+
+ @Override
+ protected String generateLoginPageHtml(
+ final HttpServletRequest request,
+ final boolean loginError,
+ final boolean logoutSuccess)
+ {
+ return ""
+ + ""
+ + this.generateHeader()
+ + this.generateBody(request, loginError, logoutSuccess)
+ + "";
+ }
+
+ protected String generateHeader()
+ {
+ return " "
+ + " "
+ + this.headerMetas.entrySet()
+ .stream()
+ .map(e -> " ")
+ .collect(Collectors.joining())
+ + Optional.ofNullable(this.pageTitle).map(t -> " " + t + " ").orElse("")
+ + this.headerElements.stream().map(s -> " " + s).collect(Collectors.joining())
+ + " ";
+ }
+
+ protected String generateBody(
+ final HttpServletRequest request,
+ final boolean loginError,
+ final boolean logoutSuccess)
+ {
+ final String errorMsg = loginError ? this.getLoginErrorMessage(request) : "Invalid credentials";
+ final String contextPath = request.getContextPath();
+
+ return " " + this.createBodyElement()
+ + " " + this.createContainerElement()
+ + " " + this.createMainElement()
+ + this.createError(loginError, errorMsg)
+ + this.createLogoutSuccess(logoutSuccess)
+ + this.header
+ + this.createFormLogin(request, contextPath)
+ + (this.hasSSOLogin() ? this.createHeaderLoginWith() : "")
+ + this.createOAuth2LoginPagePart(contextPath)
+ + this.createSaml2LoginPagePart(contextPath)
+ + this.footer
+ + " "
+ + " "
+ + "
" style='" + s + "'").orElse("")
+ + ">";
+ }
+
+ protected String createContainerElement()
+ {
+ return " " style='" + s + "'")
+ .orElse("")
+ + ">";
+ }
+
+ protected String createMainElement()
+ {
+ return "
";
+ }
+
+ protected String createFormLogin(final HttpServletRequest request, final String contextPath)
+ {
+ if(!this.formLoginEnabled)
+ {
+ return "";
+ }
+
+ return "";
+ }
+
+ @Override
+ protected String createRememberMe(final String paramName)
+ {
+ return ""
+ + "Remember me on this computer "
+ + " "
+ + "
";
+ }
+
+ protected boolean hasSSOLogin()
+ {
+ return this.oauth2LoginEnabled || this.saml2LoginEnabled;
+ }
+
+ protected String createHeaderLoginWith()
+ {
+ return "Login with ";
+ }
+
+ protected String createOAuth2LoginPagePart(final String contextPath)
+ {
+ if(!this.oauth2LoginEnabled)
+ {
+ return "";
+ }
+ return this.createSSOLoginPagePart(
+ contextPath,
+ "login-oauth2",
+ this.oauth2AuthenticationUrlToClientName,
+ this.additionalOAuth2RegistrationProperties);
+ }
+
+ protected String createSaml2LoginPagePart(final String contextPath)
+ {
+ if(!this.saml2LoginEnabled)
+ {
+ return "";
+ }
+ return this.createSSOLoginPagePart(
+ contextPath,
+ "login-saml2",
+ this.saml2AuthenticationUrlToProviderName,
+ this.additionalSaml2RegistrationProperties);
+ }
+
+ protected String createSSOLoginPagePart(
+ final String contextPath,
+ final String className,
+ final Map authenticationUrlToClientName,
+ final Map additionalRegistrationData)
+ {
+ return "";
+ }
+
+ protected record ButtonBuildingData(
+ String url,
+ String name,
+ Optional registrationData)
+ {
+ }
+
+
+ public record AdditionalStylingData(
+ StylingDefinition body,
+ StylingDefinition container,
+ StylingDefinition main
+ )
+ {
+ public static AdditionalStylingData empty()
+ {
+ return new AdditionalStylingData(
+ null,
+ null,
+ null);
+ }
+
+ public Optional classNames(final Function accessor)
+ {
+ return Optional.ofNullable(accessor.apply(this))
+ .map(StylingDefinition::classNameString);
+ }
+
+ public Optional style(final Function accessor)
+ {
+ return Optional.ofNullable(accessor.apply(this))
+ .map(StylingDefinition::style);
+ }
+ }
+}
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java
new file mode 100644
index 0000000..ebbf114
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java
@@ -0,0 +1,104 @@
+package software.xdev.spring.security.web.authentication.ui.advanced.filters;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import software.xdev.spring.security.web.authentication.ui.extendable.filters.ExtendableDefaultLogoutPageGeneratingFilter;
+
+
+public class AdvancedLogoutPageGeneratingFilter
+ extends ExtendableDefaultLogoutPageGeneratingFilter
+ implements AdvancedSharedPageGeneratingFilter
+{
+ protected List headerElements = new ArrayList<>(DEFAULT_HEADER_ELEMENTS);
+
+ protected String pageTitle = "Confirm Log Out?";
+
+ protected Map headerMetas = new LinkedHashMap<>(DEFAULT_HEADER_METAS);
+
+ @Override
+ public AdvancedLogoutPageGeneratingFilter setHeaderElements(final List headerElements)
+ {
+ this.headerElements = headerElements;
+ return this;
+ }
+
+ @Override
+ public AdvancedLogoutPageGeneratingFilter addHeaderElement(final String element)
+ {
+ this.headerElements.add(element);
+ return this;
+ }
+
+ @Override
+ public AdvancedLogoutPageGeneratingFilter pageTitle(final String pageTitle)
+ {
+ this.pageTitle = pageTitle;
+ return this;
+ }
+
+ @Override
+ public AdvancedLogoutPageGeneratingFilter setHeaderMetas(final Map headerMetas)
+ {
+ this.headerMetas = headerMetas;
+ return this;
+ }
+
+ @Override
+ public AdvancedLogoutPageGeneratingFilter addHeaderMeta(final String name, final String content)
+ {
+ this.headerMetas.put(name, content);
+ return this;
+ }
+
+ @Override
+ protected void renderLogout(final HttpServletRequest request, final HttpServletResponse response) throws IOException
+ {
+ response.setContentType("text/html;charset=UTF-8");
+ response.getWriter().write(this.generateLogoutPageHtml(request, response));
+ }
+
+ protected String generateLogoutPageHtml(final HttpServletRequest request, final HttpServletResponse response)
+ {
+ return ""
+ + ""
+ + this.generateHeader()
+ + this.generateBody(request)
+ + "";
+ }
+
+ protected String generateHeader()
+ {
+ return " "
+ + " "
+ + this.headerMetas.entrySet()
+ .stream()
+ .map(e -> " ")
+ .collect(Collectors.joining())
+ + Optional.ofNullable(this.pageTitle).map(t -> " " + t + " ").orElse("")
+ + this.headerElements.stream().map(s -> " " + s).collect(Collectors.joining())
+ + " ";
+ }
+
+ protected String generateBody(final HttpServletRequest request)
+ {
+ return " "
+ + " "
+ + " "
+ + "
"
+ + " ";
+ }
+}
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java
new file mode 100644
index 0000000..6ce7829
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java
@@ -0,0 +1,31 @@
+package software.xdev.spring.security.web.authentication.ui.advanced.filters;
+
+import java.util.List;
+import java.util.Map;
+
+import software.xdev.spring.security.web.authentication.ui.extendable.filters.ExtendableDefaultPageGeneratingFilter;
+
+
+public interface AdvancedSharedPageGeneratingFilter>
+ extends ExtendableDefaultPageGeneratingFilter
+{
+ List DEFAULT_HEADER_ELEMENTS = List.of(
+ " ");
+
+ S setHeaderElements(List headerElements);
+
+ S addHeaderElement(String element);
+
+ S pageTitle(String pageTitle);
+
+ Map DEFAULT_HEADER_METAS = Map.of(
+ "viewport", "width=device-width, initial-scale=1, shrink-to-fit=no");
+
+ S setHeaderMetas(Map headerMetas);
+
+ S addHeaderMeta(String name, String content);
+}
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java
new file mode 100644
index 0000000..be13a1e
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java
@@ -0,0 +1,288 @@
+package software.xdev.spring.security.web.authentication.ui.extendable;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
+import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
+import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
+
+import software.xdev.spring.security.web.authentication.ui.extendable.filters.ExtendableDefaultLoginPageGeneratingFilter;
+import software.xdev.spring.security.web.authentication.ui.extendable.filters.ExtendableDefaultLogoutPageGeneratingFilter;
+import software.xdev.spring.security.web.authentication.ui.extendable.filters.ExtendableDefaultPageGeneratingFilter;
+import software.xdev.spring.security.web.authentication.ui.extendable.filters.GeneratingFilterFillDataFrom;
+
+
+@SuppressWarnings("java:S119")
+public abstract class ExtendableLoginPageAdapter<
+ SELF extends ExtendableLoginPageAdapter,
+ SHARED_FILTER extends ExtendableDefaultPageGeneratingFilter,
+ LOGIN_FILTER extends ExtendableDefaultLoginPageGeneratingFilter,
+ LOGOUT_FILTER extends ExtendableDefaultLogoutPageGeneratingFilter,
+ H extends HttpSecurityBuilder>
+ extends AbstractHttpConfigurer
+{
+ protected final LOGIN_FILTER loginPageGeneratingFilter;
+ protected final LOGOUT_FILTER logoutPageGeneratingFilter;
+ protected final boolean copyDataFromExistingFilters;
+ protected final boolean installUsingInitMethod;
+
+ private Consumer customizePages;
+ protected Consumer customizeLoginPage;
+ protected Consumer customizeLogoutPage;
+
+
+ public static class InitConfiguration<
+ LOGIN_FILTER extends ExtendableDefaultLoginPageGeneratingFilter,
+ LOGOUT_FILTER extends ExtendableDefaultLogoutPageGeneratingFilter>
+ {
+ /**
+ * Used for constructor initialization
+ */
+ protected HttpSecurity httpSecurity;
+ protected Supplier loginPageGeneratingFilterSupplier;
+ protected Supplier logoutPageGeneratingFilterSupplier;
+ /**
+ * Copy data over from existing PageGeneratingFilters (required when configuration using
+ * {@link DefaultLoginPageConfigurer} was already done)
+ */
+ protected boolean copyDataFromExistingFilters = true;
+ protected InstallWith installWith;
+
+ public InitConfiguration(
+ final Supplier loginPageGeneratingFilterSupplier,
+ final Supplier logoutPageGeneratingFilterSupplier)
+ {
+ this.loginPageGeneratingFilterSupplier = loginPageGeneratingFilterSupplier;
+ this.logoutPageGeneratingFilterSupplier = logoutPageGeneratingFilterSupplier;
+ }
+
+ public InitConfiguration httpSecurity(final HttpSecurity httpSecurity)
+ {
+ this.httpSecurity = httpSecurity;
+ return this;
+ }
+
+ public InitConfiguration loginPageGeneratingFilterSupplier(
+ final Supplier loginPageGeneratingFilterSupplier)
+ {
+ this.loginPageGeneratingFilterSupplier = loginPageGeneratingFilterSupplier;
+ return this;
+ }
+
+ public InitConfiguration logoutPageGeneratingFilterSupplier(
+ final Supplier logoutPageGeneratingFilterSupplier)
+ {
+ this.logoutPageGeneratingFilterSupplier = logoutPageGeneratingFilterSupplier;
+ return this;
+ }
+
+ public InitConfiguration copyDataFromExistingFilters(
+ final boolean copyDataFromExistingFilters)
+ {
+ this.copyDataFromExistingFilters = copyDataFromExistingFilters;
+ return this;
+ }
+
+ public InitConfiguration installWith(final InstallWith installWith)
+ {
+ this.installWith = installWith;
+ return this;
+ }
+
+ public HttpSecurity getHttpSecurity()
+ {
+ return this.httpSecurity;
+ }
+
+ public Supplier getLoginPageGeneratingFilterSupplier()
+ {
+ return this.loginPageGeneratingFilterSupplier;
+ }
+
+ public Supplier getLogoutPageGeneratingFilterSupplier()
+ {
+ return this.logoutPageGeneratingFilterSupplier;
+ }
+
+ public boolean isCopyDataFromExistingFilters()
+ {
+ return this.copyDataFromExistingFilters;
+ }
+
+ public InstallWith getInstallWith()
+ {
+ return this.installWith;
+ }
+ }
+
+
+ public enum InstallWith
+ {
+ /**
+ * Install inside the constructor.
+ *
+ * This is the recommended way as it occurs before {@link DefaultLoginPageConfigurer#init(HttpSecurityBuilder)}
+ * is executed and therefore init is only done once. Note however that you need to ensure that this is
+ * executed BEFORE {@link DefaultLoginPageConfigurer#init(HttpSecurityBuilder)}, which is usually the case.
+ *
+ *
+ * @implNote This method is automatically chosen when {@link InitConfiguration#httpSecurity} is NOT
+ * null
+ */
+ CONSTRUCTOR,
+ /**
+ * Install inside the {@link ExtendableLoginPageAdapter#init(HttpSecurityBuilder)} method.
+ *
+ * Not recommended as {@link DefaultLoginPageConfigurer#init(HttpSecurityBuilder)} AND
+ * {@link ExtendableLoginPageAdapter#init(HttpSecurityBuilder)} are executed
+ *
+ */
+ INIT_METHOD
+ }
+
+ protected ExtendableLoginPageAdapter(
+ final Supplier> s,
+ final Consumer> c)
+ {
+ this(crateAndApplyConsumer(s, c));
+ }
+
+ protected static T crateAndApplyConsumer(
+ final Supplier supplier,
+ final Consumer consumer)
+ {
+ final T obj = supplier.get();
+ if(consumer != null)
+ {
+ consumer.accept(obj);
+ }
+ return obj;
+ }
+
+ protected ExtendableLoginPageAdapter(final InitConfiguration initConfig)
+ {
+ this(
+ initConfig.getHttpSecurity(),
+ initConfig.getLoginPageGeneratingFilterSupplier(),
+ initConfig.getLogoutPageGeneratingFilterSupplier(),
+ initConfig.isCopyDataFromExistingFilters(),
+ initConfig.getInstallWith());
+ }
+
+ @SuppressWarnings("unchecked")
+ protected ExtendableLoginPageAdapter(
+ final HttpSecurity httpSecurity,
+ final Supplier loginPageGeneratingFilterSupplier,
+ final Supplier logoutPageGeneratingFilterSupplier,
+ final boolean copyDataFromExistingFilters,
+ final InstallWith installWith)
+ {
+ this.loginPageGeneratingFilter = loginPageGeneratingFilterSupplier.get();
+ this.logoutPageGeneratingFilter = logoutPageGeneratingFilterSupplier.get();
+ this.copyDataFromExistingFilters = copyDataFromExistingFilters;
+
+ this.installUsingInitMethod = installWith == InstallWith.INIT_METHOD
+ || (installWith == null && httpSecurity == null);
+
+ if(!this.installUsingInitMethod)
+ {
+ this.install(httpSecurity.getConfigurer(DefaultLoginPageConfigurer.class));
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void init(final H http)
+ {
+ if(!this.installUsingInitMethod)
+ {
+ return;
+ }
+
+ this.install(http.getConfigurer(DefaultLoginPageConfigurer.class));
+ // Override DefaultLoginPageConfigurer behavior
+ http.setSharedObject(DefaultLoginPageGeneratingFilter.class, this.loginPageGeneratingFilter);
+ }
+
+ @SuppressWarnings("rawtypes")
+ protected void install(final DefaultLoginPageConfigurer loginPageConfigurer)
+ {
+ Optional.ofNullable(loginPageConfigurer)
+ .ifPresent(c -> {
+ final FieldAccessor fieldAccessor = new FieldAccessor();
+
+ Stream.of(
+ new ProcessInfo<>(
+ "loginPageGeneratingFilter",
+ DefaultLoginPageGeneratingFilter.class,
+ this.loginPageGeneratingFilter),
+ new ProcessInfo<>(
+ "logoutPageGeneratingFilter",
+ DefaultLogoutPageGeneratingFilter.class,
+ this.logoutPageGeneratingFilter))
+ .forEach(p -> {
+ final Class clazz = DefaultLoginPageConfigurer.class;
+ if(this.copyDataFromExistingFilters)
+ {
+ p.filterFillDataFrom(fieldAccessor.getValue(clazz, c, p.fieldName()));
+ }
+ fieldAccessor.setValue(clazz, c, p.fieldName(), p.filter());
+ });
+ });
+ }
+
+ protected record ProcessInfo>(
+ String fieldName,
+ Class baseFilterClass,
+ F filter)
+ {
+ public void filterFillDataFrom(final Object source)
+ {
+ Optional.ofNullable(source)
+ .filter(this.baseFilterClass()::isInstance)
+ .map(this.baseFilterClass()::cast)
+ .ifPresent(this.filter()::fillDataFrom);
+ }
+ }
+
+ @Override
+ public void configure(final H builder)
+ {
+ Optional.ofNullable(this.customizePages).ifPresent(c ->
+ Stream.of(this.loginPageGeneratingFilter, this.logoutPageGeneratingFilter)
+ // Cast to SHARED_FILTER is required as it can't be specified in Generics
+ .forEach(f -> c.accept((SHARED_FILTER)f)));
+ Optional.ofNullable(this.customizeLoginPage).ifPresent(c -> c.accept(this.loginPageGeneratingFilter));
+ Optional.ofNullable(this.customizeLogoutPage).ifPresent(c -> c.accept(this.logoutPageGeneratingFilter));
+ }
+
+ public SELF customizePages(final Consumer customizePages)
+ {
+ this.customizePages = customizePages;
+ return this.self();
+ }
+
+ public SELF customizeLoginPage(final Consumer customizeLoginPage)
+ {
+ this.customizeLoginPage = customizeLoginPage;
+ return this.self();
+ }
+
+ public SELF customizeLogoutPage(final Consumer customizeLogoutPage)
+ {
+ this.customizeLogoutPage = customizeLogoutPage;
+ return this.self();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected SELF self()
+ {
+ return (SELF)this;
+ }
+}
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java
new file mode 100644
index 0000000..63b3efe
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java
@@ -0,0 +1,45 @@
+package software.xdev.spring.security.web.authentication.ui.extendable;
+
+import java.lang.reflect.Field;
+
+
+public class FieldAccessor
+{
+ @SuppressWarnings({"java:S3011"})
+ public void setValue(
+ final Class> fieldContainer,
+ final Object object,
+ final String fieldName,
+ final Object value)
+ {
+ try
+ {
+ final Field field = fieldContainer.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(object, value);
+ }
+ catch(final NoSuchFieldException | IllegalAccessException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @SuppressWarnings({"java:S3011", "unchecked"})
+ public T getValue(
+ final Class> fieldContainer,
+ final Object object,
+ final String fieldName)
+ {
+ try
+ {
+ final Field field = fieldContainer.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ final Object value = field.get(object);
+ return (T)value;
+ }
+ catch(final NoSuchFieldException | IllegalAccessException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java
new file mode 100644
index 0000000..ff6f36d
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java
@@ -0,0 +1,388 @@
+package software.xdev.spring.security.web.authentication.ui.extendable.filters;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.WebAttributes;
+import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+import org.springframework.web.util.HtmlUtils;
+
+
+/**
+ * Same as {@link DefaultLoginPageGeneratingFilter} but all fields and methods can be overridden
+ */
+@SuppressWarnings({"java:S2177", "java:S1192", "checkstyle:LineLength"})
+public class ExtendableDefaultLoginPageGeneratingFilter
+ extends DefaultLoginPageGeneratingFilter
+ implements GeneratingFilterFillDataFrom, ExtendableDefaultPageGeneratingFilter
+{
+ protected String loginPageUrl;
+
+ protected String logoutSuccessUrl;
+
+ protected String failureUrl;
+
+ protected boolean formLoginEnabled;
+
+ protected boolean oauth2LoginEnabled;
+
+ protected boolean saml2LoginEnabled;
+
+ protected String authenticationUrl;
+
+ protected String usernameParameter;
+
+ protected String passwordParameter;
+
+ protected String rememberMeParameter;
+
+ protected Map oauth2AuthenticationUrlToClientName;
+
+ protected Map saml2AuthenticationUrlToProviderName;
+
+ protected Function> resolveHiddenInputs =
+ request -> Collections.emptyMap();
+
+ @Override
+ public void fillDataFrom(final DefaultLoginPageGeneratingFilter source)
+ {
+ this.fillDataFrom(DefaultLoginPageGeneratingFilter.class, source, Set.of(
+ new CopyInfo<>("loginPageUrl", this::setLoginPageUrl),
+ new CopyInfo<>("logoutSuccessUrl", this::setLogoutSuccessUrl),
+ new CopyInfo<>("failureUrl", this::setFailureUrl),
+ new CopyInfo<>("formLoginEnabled", this::setFormLoginEnabled),
+ new CopyInfo<>("oauth2LoginEnabled", this::setOauth2LoginEnabled),
+ new CopyInfo<>("saml2LoginEnabled", this::setSaml2LoginEnabled),
+ new CopyInfo<>("authenticationUrl", this::setAuthenticationUrl),
+ new CopyInfo<>("usernameParameter", this::setUsernameParameter),
+ new CopyInfo<>("passwordParameter", this::setPasswordParameter),
+ new CopyInfo<>("rememberMeParameter", this::setRememberMeParameter),
+ new CopyInfo<>("oauth2AuthenticationUrlToClientName", this::setOauth2AuthenticationUrlToClientName),
+ new CopyInfo<>("saml2AuthenticationUrlToProviderName", this::setSaml2AuthenticationUrlToProviderName),
+ new CopyInfo<>("resolveHiddenInputs", this::setResolveHiddenInputs)
+ ));
+ }
+
+ /**
+ * Sets a Function used to resolve a Map of the hidden inputs where the key is the name of the input and the value
+ * is the value of the input. Typically this is used to resolve the CSRF token.
+ *
+ * @param resolveHiddenInputs the function to resolve the inputs
+ */
+ @Override
+ public void setResolveHiddenInputs(final Function> resolveHiddenInputs)
+ {
+ Assert.notNull(resolveHiddenInputs, "resolveHiddenInputs cannot be null");
+ this.resolveHiddenInputs = resolveHiddenInputs;
+ }
+
+ @Override
+ public boolean isEnabled()
+ {
+ return this.formLoginEnabled || this.oauth2LoginEnabled || this.saml2LoginEnabled;
+ }
+
+ @Override
+ public void setLogoutSuccessUrl(final String logoutSuccessUrl)
+ {
+ this.logoutSuccessUrl = logoutSuccessUrl;
+ }
+
+ @Override
+ public String getLoginPageUrl()
+ {
+ return this.loginPageUrl;
+ }
+
+ @Override
+ public void setLoginPageUrl(final String loginPageUrl)
+ {
+ this.loginPageUrl = loginPageUrl;
+ }
+
+ @Override
+ public void setFailureUrl(final String failureUrl)
+ {
+ this.failureUrl = failureUrl;
+ }
+
+ @Override
+ public void setFormLoginEnabled(final boolean formLoginEnabled)
+ {
+ this.formLoginEnabled = formLoginEnabled;
+ }
+
+ @Override
+ public void setOauth2LoginEnabled(final boolean oauth2LoginEnabled)
+ {
+ this.oauth2LoginEnabled = oauth2LoginEnabled;
+ }
+
+ @Override
+ public void setSaml2LoginEnabled(final boolean saml2LoginEnabled)
+ {
+ this.saml2LoginEnabled = saml2LoginEnabled;
+ }
+
+ @Override
+ public void setAuthenticationUrl(final String authenticationUrl)
+ {
+ this.authenticationUrl = authenticationUrl;
+ }
+
+ @Override
+ public void setUsernameParameter(final String usernameParameter)
+ {
+ this.usernameParameter = usernameParameter;
+ }
+
+ @Override
+ public void setPasswordParameter(final String passwordParameter)
+ {
+ this.passwordParameter = passwordParameter;
+ }
+
+ @Override
+ public void setRememberMeParameter(final String rememberMeParameter)
+ {
+ this.rememberMeParameter = rememberMeParameter;
+ }
+
+ @Override
+ public void setOauth2AuthenticationUrlToClientName(final Map oauth2AuthenticationUrlToClientName)
+ {
+ this.oauth2AuthenticationUrlToClientName = oauth2AuthenticationUrlToClientName;
+ }
+
+ @Override
+ public void setSaml2AuthenticationUrlToProviderName(final Map saml2AuthenticationUrlToProviderName)
+ {
+ this.saml2AuthenticationUrlToProviderName = saml2AuthenticationUrlToProviderName;
+ }
+
+ @Override
+ public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
+ throws IOException, ServletException
+ {
+ this.doFilter((HttpServletRequest)request, (HttpServletResponse)response, chain);
+ }
+
+ protected void doFilter(
+ final HttpServletRequest request, final HttpServletResponse response,
+ final FilterChain chain)
+ throws IOException, ServletException
+ {
+ final boolean loginError = this.isErrorPage(request);
+ final boolean logoutSuccess = this.isLogoutSuccess(request);
+ if(this.isLoginUrlRequest(request) || loginError || logoutSuccess)
+ {
+ final String loginPageHtml = this.generateLoginPageHtml(request, loginError, logoutSuccess);
+ response.setContentType("text/html;charset=UTF-8");
+ response.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length);
+ response.getWriter().write(loginPageHtml);
+ return;
+ }
+ chain.doFilter(request, response);
+ }
+
+ protected String generateLoginPageHtml(
+ final HttpServletRequest request,
+ final boolean loginError,
+ final boolean logoutSuccess)
+ {
+ // @formatter:off
+ final String errorMsg = loginError ? this.getLoginErrorMessage(request) : "Invalid credentials";
+ final String contextPath = request.getContextPath();
+ final StringBuilder sb = new StringBuilder();
+ sb.append("\n");
+ sb.append("\n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" Please sign in \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ if (this.formLoginEnabled)
+ {
+ sb.append("
\n");
+ }
+ if (this.oauth2LoginEnabled)
+ {
+ sb.append("
");
+ sb.append(this.createError(loginError, errorMsg));
+ sb.append(this.createLogoutSuccess(logoutSuccess));
+ sb.append("
\n");
+ }
+ if (this.saml2LoginEnabled)
+ {
+ sb.append("
");
+ sb.append(this.createError(loginError, errorMsg));
+ sb.append(this.createLogoutSuccess(logoutSuccess));
+ sb.append("
\n");
+ }
+ sb.append("
\n");
+ sb.append("");
+ return sb.toString();
+ // @formatter:on
+ }
+
+ protected String getLoginErrorMessage(final HttpServletRequest request)
+ {
+ final HttpSession session = request.getSession(false);
+ if(session == null)
+ {
+ return "Invalid credentials";
+ }
+ if(!(session
+ .getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) instanceof final AuthenticationException exception))
+ {
+ return "Invalid credentials";
+ }
+ if(!StringUtils.hasText(exception.getMessage()))
+ {
+ return "Invalid credentials";
+ }
+ return exception.getMessage();
+ }
+
+ protected String renderHiddenInputs(final HttpServletRequest request)
+ {
+ final StringBuilder sb = new StringBuilder();
+ for(final Map.Entry input : this.resolveHiddenInputs.apply(request).entrySet())
+ {
+ sb.append(" \n");
+ }
+ return sb.toString();
+ }
+
+ protected String createRememberMe(final String paramName)
+ {
+ if(paramName == null)
+ {
+ return "";
+ }
+ return " Remember me on this computer.
\n";
+ }
+
+ protected boolean isLogoutSuccess(final HttpServletRequest request)
+ {
+ return this.logoutSuccessUrl != null && this.matches(request, this.logoutSuccessUrl);
+ }
+
+ protected boolean isLoginUrlRequest(final HttpServletRequest request)
+ {
+ return this.matches(request, this.loginPageUrl);
+ }
+
+ protected boolean isErrorPage(final HttpServletRequest request)
+ {
+ return this.matches(request, this.failureUrl);
+ }
+
+ protected String createError(final boolean isError, final String message)
+ {
+ if(!isError)
+ {
+ return "";
+ }
+ return "" + HtmlUtils.htmlEscape(message) + "
";
+ }
+
+ protected String createLogoutSuccess(final boolean isLogoutSuccess)
+ {
+ if(!isLogoutSuccess)
+ {
+ return "";
+ }
+ return "You have been signed out
";
+ }
+
+ protected boolean matches(final HttpServletRequest request, final String url)
+ {
+ if(!"GET".equals(request.getMethod()) || url == null)
+ {
+ return false;
+ }
+ String uri = request.getRequestURI();
+ final int pathParamIndex = uri.indexOf(';');
+ if(pathParamIndex > 0)
+ {
+ // strip everything after the first semi-colon
+ uri = uri.substring(0, pathParamIndex);
+ }
+ if(request.getQueryString() != null)
+ {
+ uri += "?" + request.getQueryString();
+ }
+ if("".equals(request.getContextPath()))
+ {
+ return uri.equals(url);
+ }
+ return uri.equals(request.getContextPath() + url);
+ }
+}
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java
new file mode 100644
index 0000000..7456194
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java
@@ -0,0 +1,132 @@
+package software.xdev.spring.security.web.authentication.ui.extendable.filters;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.springframework.core.log.LogMessage;
+import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.util.Assert;
+
+
+/**
+ * Same as {@link DefaultLogoutPageGeneratingFilter} but all fields and methods can be overridden
+ */
+@SuppressWarnings({"java:S2177", "checkstyle:LineLength"})
+public class ExtendableDefaultLogoutPageGeneratingFilter
+ extends DefaultLogoutPageGeneratingFilter
+ implements GeneratingFilterFillDataFrom, ExtendableDefaultPageGeneratingFilter
+{
+ protected RequestMatcher matcher = new AntPathRequestMatcher("/logout", "GET");
+
+ protected Function> resolveHiddenInputs =
+ request -> Collections.emptyMap();
+
+ @Override
+ public void fillDataFrom(final DefaultLogoutPageGeneratingFilter source)
+ {
+ this.fillDataFrom(DefaultLogoutPageGeneratingFilter.class, source, Set.of(
+ new CopyInfo<>("matcher", this::setMatcher),
+ new CopyInfo<>("resolveHiddenInputs", this::setResolveHiddenInputs)
+ ));
+ }
+
+ @Override
+ protected void doFilterInternal(
+ final HttpServletRequest request,
+ final HttpServletResponse response,
+ final FilterChain filterChain)
+ throws ServletException, IOException
+ {
+ if(this.matcher.matches(request))
+ {
+ this.renderLogout(request, response);
+ }
+ else
+ {
+ if(this.logger.isTraceEnabled())
+ {
+ this.logger.trace(LogMessage.format(
+ "Did not render default logout page since request did not match [%s]",
+ this.matcher));
+ }
+ filterChain.doFilter(request, response);
+ }
+ }
+
+ protected void renderLogout(final HttpServletRequest request, final HttpServletResponse response) throws IOException
+ {
+ // @formatter:off
+ final StringBuilder sb = new StringBuilder();
+ sb.append("\n");
+ sb.append("\n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" Confirm Log Out? \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append("
\n");
+ sb.append(" \n");
+ sb.append("");
+ response.setContentType("text/html;charset=UTF-8");
+ response.getWriter().write(sb.toString());
+ // @formatter:on
+ }
+
+ /**
+ * Sets a Function used to resolve a Map of the hidden inputs where the key is the name of the input and the value
+ * is the value of the input. Typically this is used to resolve the CSRF token.
+ *
+ * @param resolveHiddenInputs the function to resolve the inputs
+ */
+ @Override
+ public void setResolveHiddenInputs(final Function> resolveHiddenInputs)
+ {
+ Assert.notNull(resolveHiddenInputs, "resolveHiddenInputs cannot be null");
+ this.resolveHiddenInputs = resolveHiddenInputs;
+ }
+
+ protected String renderHiddenInputs(final HttpServletRequest request)
+ {
+ final StringBuilder sb = new StringBuilder();
+ for(final Map.Entry input : this.resolveHiddenInputs.apply(request).entrySet())
+ {
+ sb.append(" \n");
+ }
+ return sb.toString();
+ }
+
+ // Method is missing on upstream
+ public void setMatcher(final RequestMatcher matcher)
+ {
+ this.matcher = matcher;
+ }
+}
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java
new file mode 100644
index 0000000..cec60ec
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java
@@ -0,0 +1,5 @@
+package software.xdev.spring.security.web.authentication.ui.extendable.filters;
+
+public interface ExtendableDefaultPageGeneratingFilter
+{
+}
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java
new file mode 100644
index 0000000..96c3a2c
--- /dev/null
+++ b/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java
@@ -0,0 +1,27 @@
+package software.xdev.spring.security.web.authentication.ui.extendable.filters;
+
+import java.util.Collection;
+import java.util.function.Consumer;
+
+import software.xdev.spring.security.web.authentication.ui.extendable.FieldAccessor;
+
+
+public interface GeneratingFilterFillDataFrom
+{
+ void fillDataFrom(F source);
+
+ default void fillDataFrom(
+ final Class fieldContainer,
+ final F source,
+ final Collection> copyInfos)
+ {
+ final FieldAccessor fieldAccessor = new FieldAccessor();
+ copyInfos.forEach(c -> c.setter().accept(fieldAccessor.getValue(fieldContainer, source, c.fieldName())));
+ }
+
+ record CopyInfo(
+ String fieldName,
+ Consumer setter)
+ {
+ }
+}
From 5c7dde650dd694cb448845c37c7fff098e606947 Mon Sep 17 00:00:00 2001
From: AB
Date: Tue, 2 Apr 2024 13:34:07 +0200
Subject: [PATCH 03/47] Rename
---
.run/Run Demo.run.xml | 2 +-
CONTRIBUTING.md | 4 ++--
README.md | 12 ++++++------
SECURITY.md | 2 +-
pom.xml | 6 +++---
.../pom.xml | 4 ++--
.../pom.xml | 12 ++++++------
.../ui/advanced/AdditionalRegistrationData.java | 0
.../ui/advanced/AdvancedLoginPageAdapter.java | 0
.../ui/advanced/StylingDefinition.java | 0
.../config/AdditionalOAuth2ClientProperties.java | 0
.../config/AdditionalSaml2ClientProperties.java | 0
.../filters/AdvancedLoginPageGeneratingFilter.java | 0
.../filters/AdvancedLogoutPageGeneratingFilter.java | 0
.../filters/AdvancedSharedPageGeneratingFilter.java | 0
.../ui/extendable/ExtendableLoginPageAdapter.java | 0
.../authentication/ui/extendable/FieldAccessor.java | 0
.../ExtendableDefaultLoginPageGeneratingFilter.java | 0
.../ExtendableDefaultLogoutPageGeneratingFilter.java | 0
.../ExtendableDefaultPageGeneratingFilter.java | 0
.../filters/GeneratingFilterFillDataFrom.java | 0
21 files changed, 21 insertions(+), 21 deletions(-)
rename {spring-security-advanced-login-demo => spring-security-advanced-authentication-ui-demo}/pom.xml (95%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/pom.xml (96%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java (100%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java (100%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java (100%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java (100%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java (100%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java (100%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java (100%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java (100%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java (100%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java (100%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java (100%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java (100%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java (100%)
rename {spring-security-advanced-login => spring-security-advanced-authentication-ui}/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java (100%)
diff --git a/.run/Run Demo.run.xml b/.run/Run Demo.run.xml
index 58378e0..b84785d 100644
--- a/.run/Run Demo.run.xml
+++ b/.run/Run Demo.run.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index dd3f598..51bc601 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -33,10 +33,10 @@ You should have the following things installed:
* Ensure that the JDK/Java-Version is correct
-## Releasing [![Build](https://img.shields.io/github/actions/workflow/status/xdev-software/spring-security-advanced-login/release.yml?branch=master)](https://github.com/xdev-software/spring-security-advanced-login/actions/workflows/release.yml)
+## Releasing [![Build](https://img.shields.io/github/actions/workflow/status/xdev-software/spring-security-advanced-authentication-ui/release.yml?branch=master)](https://github.com/xdev-software/spring-security-advanced-authentication-ui/actions/workflows/release.yml)
Before releasing:
-* Consider doing a [test-deployment](https://github.com/xdev-software/spring-security-advanced-login/actions/workflows/test-deploy.yml?query=branch%3Adevelop) before actually releasing.
+* Consider doing a [test-deployment](https://github.com/xdev-software/spring-security-advanced-authentication-ui/actions/workflows/test-deploy.yml?query=branch%3Adevelop) before actually releasing.
* Check the [changelog](CHANGELOG.md)
If the ``develop`` is ready for release, create a pull request to the ``master``-Branch and merge the changes
diff --git a/README.md b/README.md
index 305b34c..92cba1e 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,12 @@
-[![Latest version](https://img.shields.io/maven-central/v/software.xdev/spring-security-advanced-login?logo=apache%20maven)](https://mvnrepository.com/artifact/software.xdev/spring-security-advanced-login)
-[![Build](https://img.shields.io/github/actions/workflow/status/xdev-software/spring-security-advanced-login/checkBuild.yml?branch=develop)](https://github.com/xdev-software/spring-security-advanced-login/actions/workflows/checkBuild.yml?query=branch%3Adevelop)
-[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=xdev-software_spring-security-advanced-login&metric=alert_status)](https://sonarcloud.io/dashboard?id=xdev-software_spring-security-advanced-login)
+[![Latest version](https://img.shields.io/maven-central/v/software.xdev/spring-security-advanced-authentication-ui?logo=apache%20maven)](https://mvnrepository.com/artifact/software.xdev/spring-security-advanced-authentication-ui)
+[![Build](https://img.shields.io/github/actions/workflow/status/xdev-software/spring-security-advanced-authentication-ui/checkBuild.yml?branch=develop)](https://github.com/xdev-software/spring-security-advanced-authentication-ui/actions/workflows/checkBuild.yml?query=branch%3Adevelop)
+[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=xdev-software_spring-security-advanced-authentication-ui&metric=alert_status)](https://sonarcloud.io/dashboard?id=xdev-software_spring-security-advanced-authentication-ui)
-# spring-security-advanced-login
+# spring-security-advanced-authentication-ui
## Installation
-[Installation guide for the latest release](https://github.com/xdev-software/spring-security-advanced-login/releases/latest#Installation)
+[Installation guide for the latest release](https://github.com/xdev-software/spring-security-advanced-authentication-ui/releases/latest#Installation)
## Support
@@ -16,4 +16,4 @@ If you need support as soon as possible and you can't wait for any pull request,
See the [contributing guide](./CONTRIBUTING.md) for detailed instructions on how to get started with our project.
## Dependencies and Licenses
-View the [license of the current project](LICENSE) or the [summary including all dependencies](https://xdev-software.github.io/spring-security-advanced-login/dependencies)
+View the [license of the current project](LICENSE) or the [summary including all dependencies](https://xdev-software.github.io/spring-security-advanced-authentication-ui/dependencies)
diff --git a/SECURITY.md b/SECURITY.md
index 96b03b1..9b41fc8 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,4 +2,4 @@
## Reporting a Vulnerability
-Please report a security vulnerability [on GitHub Security Advisories](https://github.com/xdev-software/spring-security-advanced-login/security/advisories/new).
+Please report a security vulnerability [on GitHub Security Advisories](https://github.com/xdev-software/spring-security-advanced-authentication-ui/security/advisories/new).
diff --git a/pom.xml b/pom.xml
index d7b1a20..2c6448f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
4.0.0
software.xdev
- spring-security-advanced-login-root
+ spring-security-advanced-authentication-ui-root
1.0.0-SNAPSHOT
pom
@@ -15,8 +15,8 @@
- spring-security-advanced-login
- spring-security-advanced-login-demo
+ spring-security-advanced-authentication-ui
+ spring-security-advanced-authentication-ui-demo
diff --git a/spring-security-advanced-login-demo/pom.xml b/spring-security-advanced-authentication-ui-demo/pom.xml
similarity index 95%
rename from spring-security-advanced-login-demo/pom.xml
rename to spring-security-advanced-authentication-ui-demo/pom.xml
index 6711f25..2f5125f 100644
--- a/spring-security-advanced-login-demo/pom.xml
+++ b/spring-security-advanced-authentication-ui-demo/pom.xml
@@ -5,7 +5,7 @@
4.0.0
software.xdev
- spring-security-advanced-login-demo
+ spring-security-advanced-authentication-ui-demo
1.0.0-SNAPSHOT
jar
@@ -29,7 +29,7 @@
software.xdev
- spring-security-advanced-login
+ spring-security-advanced-authentication-ui
${project.version}
diff --git a/spring-security-advanced-login/pom.xml b/spring-security-advanced-authentication-ui/pom.xml
similarity index 96%
rename from spring-security-advanced-login/pom.xml
rename to spring-security-advanced-authentication-ui/pom.xml
index 85ea478..eef3ad9 100644
--- a/spring-security-advanced-login/pom.xml
+++ b/spring-security-advanced-authentication-ui/pom.xml
@@ -5,17 +5,17 @@
4.0.0
software.xdev
- spring-security-advanced-login
+ spring-security-advanced-authentication-ui
1.0.0-SNAPSHOT
jar
- spring-security-advanced-login
- spring-security-advanced-login
- https://github.com/xdev-software/spring-security-advanced-login
+ spring-security-advanced-authentication-ui
+ spring-security-advanced-authentication-ui
+ https://github.com/xdev-software/spring-security-advanced-authentication-ui
- https://github.com/xdev-software/spring-security-advanced-login
- scm:git:https://github.com/xdev-software/spring-security-advanced-login.git
+ https://github.com/xdev-software/spring-security-advanced-authentication-ui
+ scm:git:https://github.com/xdev-software/spring-security-advanced-authentication-ui.git
2023
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java
diff --git a/spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java
similarity index 100%
rename from spring-security-advanced-login/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java
rename to spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java
From 3f84106fa991d12917b6cf3c88ac28c853ee5752 Mon Sep 17 00:00:00 2001
From: AB
Date: Tue, 2 Apr 2024 13:34:48 +0200
Subject: [PATCH 04/47] Add license header
---
.../ui/advanced/AdditionalRegistrationData.java | 15 +++++++++++++++
.../ui/advanced/AdvancedLoginPageAdapter.java | 15 +++++++++++++++
.../ui/advanced/StylingDefinition.java | 15 +++++++++++++++
.../config/AdditionalOAuth2ClientProperties.java | 15 +++++++++++++++
.../config/AdditionalSaml2ClientProperties.java | 15 +++++++++++++++
.../AdvancedLoginPageGeneratingFilter.java | 15 +++++++++++++++
.../AdvancedLogoutPageGeneratingFilter.java | 15 +++++++++++++++
.../AdvancedSharedPageGeneratingFilter.java | 15 +++++++++++++++
.../ui/extendable/ExtendableLoginPageAdapter.java | 15 +++++++++++++++
.../ui/extendable/FieldAccessor.java | 15 +++++++++++++++
...xtendableDefaultLoginPageGeneratingFilter.java | 15 +++++++++++++++
...tendableDefaultLogoutPageGeneratingFilter.java | 15 +++++++++++++++
.../ExtendableDefaultPageGeneratingFilter.java | 15 +++++++++++++++
.../filters/GeneratingFilterFillDataFrom.java | 15 +++++++++++++++
14 files changed, 210 insertions(+)
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java
index 49c6f75..9795275 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.advanced;
public class AdditionalRegistrationData
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java
index 219b7e8..be22258 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.advanced;
import java.util.function.Consumer;
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java
index 0c069b9..fd3508e 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.advanced;
import java.util.Set;
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java
index 400db1c..eb9b660 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.advanced.config;
import java.util.HashMap;
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java
index be998b2..32827e2 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.advanced.config;
import java.util.HashMap;
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java
index 82e9b88..a69b618 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.advanced.filters;
import java.util.ArrayList;
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java
index ebbf114..d72bb51 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.advanced.filters;
import java.io.IOException;
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java
index 6ce7829..72d9840 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.advanced.filters;
import java.util.List;
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java
index be13a1e..6033833 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.extendable;
import java.util.Optional;
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java
index 63b3efe..6f27ca3 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.extendable;
import java.lang.reflect.Field;
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java
index ff6f36d..6f3e93d 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.extendable.filters;
import java.io.IOException;
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java
index 7456194..74d6e61 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.extendable.filters;
import java.io.IOException;
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java
index cec60ec..52f1a61 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.extendable.filters;
public interface ExtendableDefaultPageGeneratingFilter
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java
index 96c3a2c..08adf35 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright © 2023 XDEV Software (https://xdev.software)
+ *
+ * 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
+ *
+ * http://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 software.xdev.spring.security.web.authentication.ui.extendable.filters;
import java.util.Collection;
From 7c03fe3e7d314f24fc69a36ad7e20bb491ba51ed Mon Sep 17 00:00:00 2001
From: AB
Date: Tue, 2 Apr 2024 13:37:18 +0200
Subject: [PATCH 05/47] The year is 2024
---
spring-security-advanced-authentication-ui/pom.xml | 2 +-
.../authentication/ui/advanced/AdditionalRegistrationData.java | 2 +-
.../authentication/ui/advanced/AdvancedLoginPageAdapter.java | 2 +-
.../web/authentication/ui/advanced/StylingDefinition.java | 2 +-
.../ui/advanced/config/AdditionalOAuth2ClientProperties.java | 2 +-
.../ui/advanced/config/AdditionalSaml2ClientProperties.java | 2 +-
.../ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java | 2 +-
.../ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java | 2 +-
.../ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java | 2 +-
.../ui/extendable/ExtendableLoginPageAdapter.java | 2 +-
.../web/authentication/ui/extendable/FieldAccessor.java | 2 +-
.../filters/ExtendableDefaultLoginPageGeneratingFilter.java | 2 +-
.../filters/ExtendableDefaultLogoutPageGeneratingFilter.java | 2 +-
.../filters/ExtendableDefaultPageGeneratingFilter.java | 2 +-
.../ui/extendable/filters/GeneratingFilterFillDataFrom.java | 2 +-
15 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/spring-security-advanced-authentication-ui/pom.xml b/spring-security-advanced-authentication-ui/pom.xml
index eef3ad9..50b96a8 100644
--- a/spring-security-advanced-authentication-ui/pom.xml
+++ b/spring-security-advanced-authentication-ui/pom.xml
@@ -18,7 +18,7 @@
scm:git:https://github.com/xdev-software/spring-security-advanced-authentication-ui.git
- 2023
+ 2024
XDEV Software
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java
index 9795275..0186dbd 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdditionalRegistrationData.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java
index be22258..2516ba9 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/AdvancedLoginPageAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java
index fd3508e..a845e3b 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/StylingDefinition.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java
index eb9b660..f99c9b6 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalOAuth2ClientProperties.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java
index 32827e2..0b9439f 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/config/AdditionalSaml2ClientProperties.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java
index a69b618..dbf574c 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLoginPageGeneratingFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java
index d72bb51..bdf3c74 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedLogoutPageGeneratingFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java
index 72d9840..cd97fa0 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/advanced/filters/AdvancedSharedPageGeneratingFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java
index 6033833..8fa3903 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/ExtendableLoginPageAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java
index 6f27ca3..a6d59f7 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/FieldAccessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java
index 6f3e93d..0ff8073 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLoginPageGeneratingFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java
index 74d6e61..3e5c023 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultLogoutPageGeneratingFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java
index 52f1a61..07bbd4f 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/ExtendableDefaultPageGeneratingFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java
index 08adf35..bf9c94c 100644
--- a/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java
+++ b/spring-security-advanced-authentication-ui/src/main/java/software/xdev/spring/security/web/authentication/ui/extendable/filters/GeneratingFilterFillDataFrom.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2023 XDEV Software (https://xdev.software)
+ * Copyright © 2024 XDEV Software (https://xdev.software)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
From 8ffc9157c1525e6653ab7df4751300ec5e71dc6d Mon Sep 17 00:00:00 2001
From: AB
Date: Tue, 2 Apr 2024 13:42:25 +0200
Subject: [PATCH 06/47] Add dev infra
---
dev_infra/.gitignore | 1 +
dev_infra/README.md | 33 ++++++++++++
dev_infra/docker-compose.yml | 69 ++++++++++++++++++++++++
dev_infra/oidc-user-config.json.template | 19 +++++++
4 files changed, 122 insertions(+)
create mode 100644 dev_infra/.gitignore
create mode 100644 dev_infra/README.md
create mode 100644 dev_infra/docker-compose.yml
create mode 100644 dev_infra/oidc-user-config.json.template
diff --git a/dev_infra/.gitignore b/dev_infra/.gitignore
new file mode 100644
index 0000000..226f37c
--- /dev/null
+++ b/dev_infra/.gitignore
@@ -0,0 +1 @@
+oidc-user-config.json
diff --git a/dev_infra/README.md b/dev_infra/README.md
new file mode 100644
index 0000000..f78212d
--- /dev/null
+++ b/dev_infra/README.md
@@ -0,0 +1,33 @@
+## Development Infrastructure
+
+The infrastructure contains the following:
+* [Open ID Connect Mock Server](https://github.com/Soluto/oidc-server-mock) - for login in
+ * Available at http://localhost:4011
+
+### Setup
+* Requires Docker
+
+#### OIDC
+* Create ``oidc-user-config.json`` from [``oidc-user-config.json.template``](./oidc-user-config.json.template)
+ * File should not be tracked in Git
+ * Fill in your login details like mail, name, password
+
+
+### Usage
+Note: Commands are all executed inside a shell/CMD in the current folder. ([Tip for windows users](https://stackoverflow.com/a/40146208))
+
+| Use case | What to do? |
+| --- | --- |
+| Starting the infrastructure | ``docker compose up`` |
+| Stopping (and removing) the infrastructure | ``docker compose down`` |
+| (Re)Building the infrastructure e.g. after changes to the Dockerfiles | ``docker compose build --pull`` |
+
+See also ``docker compose --help``
+
+### Additional notes
+âš The containers don't automatically restart after a PC restart!
+
+âš After a PC restart the infrastructure is still present but it's stopped.
+In this case you have 2 options:
+* start the existing infrastructure again (``docker compose up``) or
+* do a clean start by first removing (``docker compose down``) and then starting the infrastructure
diff --git a/dev_infra/docker-compose.yml b/dev_infra/docker-compose.yml
new file mode 100644
index 0000000..4f2e9cb
--- /dev/null
+++ b/dev_infra/docker-compose.yml
@@ -0,0 +1,69 @@
+version: "3"
+
+services:
+ # Docs: https://docs.duendesoftware.com
+ oidc-server-mock:
+ container_name: oidc-server-mock
+ image: ghcr.io/soluto/oidc-server-mock:0.9.0
+ environment:
+ ASPNETCORE_ENVIRONMENT: Development
+ SERVER_OPTIONS_INLINE: |
+ {
+ "AccessTokenJwtType": "JWT",
+ "Discovery": {
+ "ShowKeySet": true
+ },
+ "Authentication": {
+ "CookieSameSiteMode": "Lax",
+ "CheckSessionCookieSameSiteMode": "Lax"
+ }
+ }
+ LOGIN_OPTIONS_INLINE: |
+ {
+ "AllowRememberLogin": false
+ }
+ LOGOUT_OPTIONS_INLINE: |
+ {
+ "AutomaticRedirectAfterSignOut": true
+ }
+ USERS_CONFIGURATION_PATH: /tmp/config/oidc-user-config.json
+ CLIENTS_CONFIGURATION_INLINE: |
+ [
+ {
+ "ClientId": "client-id",
+ "ClientSecrets": [
+ "client-secret"
+ ],
+ "Description": "TestClient",
+ "AllowedGrantTypes": [
+ "authorization_code",
+ "refresh_token"
+ ],
+ "RedirectUris": [
+ "*"
+ ],
+ "AllowedScopes": [
+ "openid",
+ "profile",
+ "email",
+ "offline_access"
+ ],
+ "AlwaysIncludeUserClaimsInIdToken": true,
+ "AllowOfflineAccess": true,
+ "RequirePkce": false
+ }
+ ]
+ ASPNET_SERVICES_OPTIONS_INLINE: |
+ {
+ "ForwardedHeadersOptions": {
+ "ForwardedHeaders" : "All"
+ }
+ }
+ volumes:
+ - ./oidc-user-config.json:/tmp/config/oidc-user-config.json:ro
+ ports:
+ - '4011:80'
+ deploy:
+ resources:
+ limits:
+ memory: 512m
diff --git a/dev_infra/oidc-user-config.json.template b/dev_infra/oidc-user-config.json.template
new file mode 100644
index 0000000..ac1a68a
--- /dev/null
+++ b/dev_infra/oidc-user-config.json.template
@@ -0,0 +1,19 @@
+[
+ {
+ "SubjectId":"1",
+ "Username":"your.name@example.org",
+ "Password":"pwd",
+ "Claims": [
+ {
+ "Type": "name",
+ "Value": "Your name",
+ "ValueType": "string"
+ },
+ {
+ "Type": "email",
+ "Value": "your.name@example.org",
+ "ValueType": "string"
+ }
+ ]
+ }
+]
From c18671340dd152dab6027d2a4cab3442ddd130af Mon Sep 17 00:00:00 2001
From: AB
Date: Tue, 2 Apr 2024 13:42:28 +0200
Subject: [PATCH 07/47] Update pom.xml
---
spring-security-advanced-authentication-ui/pom.xml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/spring-security-advanced-authentication-ui/pom.xml b/spring-security-advanced-authentication-ui/pom.xml
index 50b96a8..5315b11 100644
--- a/spring-security-advanced-authentication-ui/pom.xml
+++ b/spring-security-advanced-authentication-ui/pom.xml
@@ -194,6 +194,12 @@
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.5
+
From 22c9f2a1825d3fba7af4033342a1dba1c67741e1 Mon Sep 17 00:00:00 2001
From: AB
Date: Tue, 2 Apr 2024 17:07:08 +0200
Subject: [PATCH 08/47] Fix port
---
dev_infra/docker-compose.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dev_infra/docker-compose.yml b/dev_infra/docker-compose.yml
index 4f2e9cb..7973566 100644
--- a/dev_infra/docker-compose.yml
+++ b/dev_infra/docker-compose.yml
@@ -62,7 +62,7 @@ services:
volumes:
- ./oidc-user-config.json:/tmp/config/oidc-user-config.json:ro
ports:
- - '4011:80'
+ - '4011:8080'
deploy:
resources:
limits:
From f8c1b7b3c750ec4428c2371aabc7a64c09b7392b Mon Sep 17 00:00:00 2001
From: AB
Date: Tue, 2 Apr 2024 17:07:29 +0200
Subject: [PATCH 09/47] Create demo
---
.../README.md | 5 +
.../pom.xml | 54 ++++---
.../main/java/software/xdev/Application.java | 15 ++
.../xdev/controllers/RootController.java | 34 +++++
.../main/java/software/xdev/security/CSP.java | 27 ++++
.../xdev/security/MainWebSecurity.java | 119 +++++++++++++++
.../security/PublicStaticFilesSecurity.java | 34 +++++
.../META-INF/resources/assets/XDEV_LOGO.svg | 9 ++
.../META-INF/resources/assets/sky.avif | Bin 0 -> 94465 bytes
.../META-INF/resources/assets/sources.txt | 1 +
.../resources/META-INF/resources/favicon.ico | Bin 0 -> 1150 bytes
.../lib/bootstrap-5.3.3.bundle.min.js | 7 +
.../resources/lib/bootstrap-5.3.3.min.css | 6 +
.../META-INF/resources/lib/theme.css | 29 ++++
.../resources/META-INF/resources/lib/theme.js | 135 ++++++++++++++++++
.../src/main/resources/application.yml | 37 +++++
16 files changed, 494 insertions(+), 18 deletions(-)
create mode 100644 spring-security-advanced-authentication-ui-demo/README.md
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/Application.java
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/controllers/RootController.java
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/security/CSP.java
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/security/MainWebSecurity.java
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/security/PublicStaticFilesSecurity.java
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/resources/META-INF/resources/assets/XDEV_LOGO.svg
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/resources/META-INF/resources/assets/sky.avif
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/resources/META-INF/resources/assets/sources.txt
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/resources/META-INF/resources/favicon.ico
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/resources/META-INF/resources/lib/bootstrap-5.3.3.bundle.min.js
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/resources/META-INF/resources/lib/bootstrap-5.3.3.min.css
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/resources/META-INF/resources/lib/theme.css
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/resources/META-INF/resources/lib/theme.js
create mode 100644 spring-security-advanced-authentication-ui-demo/src/main/resources/application.yml
diff --git a/spring-security-advanced-authentication-ui-demo/README.md b/spring-security-advanced-authentication-ui-demo/README.md
new file mode 100644
index 0000000..534fc52
--- /dev/null
+++ b/spring-security-advanced-authentication-ui-demo/README.md
@@ -0,0 +1,5 @@
+# Demo
+
+* Start the [development infrastructure](../dev_infra/)
+* Run the application
+* Open ``http://localhost:8080``
diff --git a/spring-security-advanced-authentication-ui-demo/pom.xml b/spring-security-advanced-authentication-ui-demo/pom.xml
index 2f5125f..810daaa 100644
--- a/spring-security-advanced-authentication-ui-demo/pom.xml
+++ b/spring-security-advanced-authentication-ui-demo/pom.xml
@@ -24,14 +24,42 @@
UTF-8
software.xdev.Application
+
+ 3.2.4
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${org.springframework.boot.version}
+ pom
+ import
+
+
+
+
+
software.xdev
spring-security-advanced-authentication-ui
${project.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-client
+
@@ -49,31 +77,21 @@
+
- org.apache.maven.plugins
- maven-assembly-plugin
- 3.7.1
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${org.springframework.boot.version}
-
-
- ${mainClass}
-
-
- true
-
-
-
- jar-with-dependencies
-
- false
+ ${mainClass}
- make-assembly
- package
+ repackage
- single
+ repackage
+ package
diff --git a/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/Application.java b/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/Application.java
new file mode 100644
index 0000000..83fe4b1
--- /dev/null
+++ b/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/Application.java
@@ -0,0 +1,15 @@
+package software.xdev;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+
+@SuppressWarnings("checkstyle:HideUtilityClassConstructor")
+@SpringBootApplication
+public class Application
+{
+ public static void main(final String[] args)
+ {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/controllers/RootController.java b/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/controllers/RootController.java
new file mode 100644
index 0000000..b9eb822
--- /dev/null
+++ b/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/controllers/RootController.java
@@ -0,0 +1,34 @@
+package software.xdev.controllers;
+
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+@RestController
+@RequestMapping("/")
+public class RootController
+{
+ @GetMapping
+ public Result respond()
+ {
+ if(SecurityContextHolder.getContext()
+ .getAuthentication()
+ .getPrincipal() instanceof final DefaultOidcUser oidcUser)
+ {
+ return new Result(oidcUser.getFullName(), oidcUser.getEmail(), "/logout");
+ }
+ return null;
+ }
+
+ public record Result(
+ String name,
+ String email,
+ String logoutUrl
+ )
+ {
+
+ }
+}
diff --git a/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/security/CSP.java b/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/security/CSP.java
new file mode 100644
index 0000000..b3f16a8
--- /dev/null
+++ b/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/security/CSP.java
@@ -0,0 +1,27 @@
+package software.xdev.security;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+public final class CSP
+{
+ public static final String POLICY_SELF = "'self'";
+ public static final String POLICY_NONE = "'none'";
+
+ public static String build(final Map attr)
+ {
+ return attr.entrySet().stream().map(e ->
+ {
+ if(e.getValue() == null)
+ {
+ return e.getKey();
+ }
+ return e.getKey() + " " + e.getValue();
+ }).collect(Collectors.joining("; "));
+ }
+
+ private CSP()
+ {
+ }
+}
diff --git a/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/security/MainWebSecurity.java b/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/security/MainWebSecurity.java
new file mode 100644
index 0000000..1980a20
--- /dev/null
+++ b/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/security/MainWebSecurity.java
@@ -0,0 +1,119 @@
+package software.xdev.security;
+
+import static java.util.Map.entry;
+import static software.xdev.security.CSP.POLICY_NONE;
+import static software.xdev.security.CSP.POLICY_SELF;
+
+import java.time.Year;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+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.web.SecurityFilterChain;
+import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
+import org.springframework.security.web.savedrequest.NullRequestCache;
+
+import software.xdev.spring.security.web.authentication.ui.advanced.AdvancedLoginPageAdapter;
+import software.xdev.spring.security.web.authentication.ui.advanced.config.AdditionalOAuth2ClientProperties;
+
+
+@EnableWebSecurity
+@Configuration
+// Load additional OAuth2ClientProperties
+@EnableConfigurationProperties(AdditionalOAuth2ClientProperties.class)
+public class MainWebSecurity
+{
+ @Bean(name = "mainSecurityFilterChainBean")
+ public SecurityFilterChain configure(
+ final HttpSecurity http,
+ final AdditionalOAuth2ClientProperties additionalOAuth2ClientProperties) throws Exception
+ {
+ http.with(new AdvancedLoginPageAdapter<>(http), c -> c
+ .customizePages(p -> p
+ // No remote communication -> Use local resources
+ .setHeaderElements(List.of(
+ " ",
+ " ",
+ "",
+ ""
+ )))
+ .customizeLoginPage(p -> p
+ .additionalOAuth2RegistrationProperties(additionalOAuth2ClientProperties.getRegistration())
+ .additionalStylingData(sd -> sd
+ .body(s -> s.styles("background-image:url(\"/assets/sky.avif\")", "background-size:cover"))
+ .container(s -> s.classNames("d-flex", "h-100"))
+ .main(s -> s.classNames("align-middle").styles("background:var(--bs-body-bg)")))
+ .header(""
+ + "Advanced Auth UI Demo ")
+ .footer("© " + Year.now().getValue()
+ + " "
+ + " XDEV Software"
+ + " "
+ + "
")))
+ .headers(h -> h
+ .referrerPolicy(r -> r.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN))
+ .contentSecurityPolicy(csp -> csp.policyDirectives(this.getCSP())))
+ .formLogin(Customizer.withDefaults())
+ .oauth2Login(c -> c.defaultSuccessUrl("/"))
+ .authorizeHttpRequests(urlRegistry -> urlRegistry.anyRequest().authenticated())
+ .requestCache(c -> c.requestCache(new NullRequestCache()));
+
+ return http.build();
+ }
+
+ // Example CSP
+ protected String getCSP()
+ {
+ return CSP.build(Map.ofEntries(
+ entry(
+ "default-src",
+ POLICY_SELF
+ + (this.isDevMode()
+ // Allow ws://localhost:* in Demo mode for SpringbootDevTools
+ ? " ws://localhost:*"
+ : "")),
+ entry("script-src", POLICY_SELF + " 'unsafe-inline'"),
+ entry("style-src", POLICY_SELF + " 'unsafe-inline'"),
+ entry("font-src", POLICY_SELF),
+ entry("img-src", POLICY_SELF + " data:"),
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/object-src
+ // https://csp.withgoogle.com/docs/strict-csp.html
+ entry("object-src", POLICY_NONE),
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/base-uri
+ entry("base-uri", POLICY_SELF),
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/form-action
+ // When using 'self':
+ // * Webkit based Browsers have problems here: https://github.com/w3c/webappsec-csp/issues/8
+ // * Firefox is
+ // As of 2024-03 CSP3 added 'unsafe-allow-redirects' however it's not implemented by any browser yet
+ // Fallback for now '*'
+ entry("form-action", "*"),
+ // Replaces X-Frame-Options
+ entry("frame-src", POLICY_SELF),
+ entry("frame-ancestors", POLICY_SELF)));
+ }
+
+ protected boolean isDevMode()
+ {
+ try
+ {
+ Class.forName("org.springframework.boot.devtools.settings.DevToolsSettings");
+ return true;
+ }
+ catch(final ClassNotFoundException nf)
+ {
+ return false;
+ }
+ }
+}
diff --git a/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/security/PublicStaticFilesSecurity.java b/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/security/PublicStaticFilesSecurity.java
new file mode 100644
index 0000000..1b2792e
--- /dev/null
+++ b/spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/security/PublicStaticFilesSecurity.java
@@ -0,0 +1,34 @@
+package software.xdev.security;
+
+import java.util.Set;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.SecurityFilterChain;
+
+
+@EnableWebSecurity
+@Configuration
+public class PublicStaticFilesSecurity
+{
+ static final Set PUBLIC_STATIC_FILES = Set.of(
+ "/favicon.ico",
+ "/assets/**",
+ "/lib/**");
+
+ @Bean
+ @Order(2)
+ public SecurityFilterChain configureStaticResources(final HttpSecurity http) throws Exception
+ {
+ // Static resources that require no authentication
+ return http
+ .securityMatcher(PUBLIC_STATIC_FILES.toArray(String[]::new))
+ .authorizeHttpRequests(a -> a.anyRequest().permitAll())
+ .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .build();
+ }
+}
diff --git a/spring-security-advanced-authentication-ui-demo/src/main/resources/META-INF/resources/assets/XDEV_LOGO.svg b/spring-security-advanced-authentication-ui-demo/src/main/resources/META-INF/resources/assets/XDEV_LOGO.svg
new file mode 100644
index 0000000..62912a8
--- /dev/null
+++ b/spring-security-advanced-authentication-ui-demo/src/main/resources/META-INF/resources/assets/XDEV_LOGO.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/spring-security-advanced-authentication-ui-demo/src/main/resources/META-INF/resources/assets/sky.avif b/spring-security-advanced-authentication-ui-demo/src/main/resources/META-INF/resources/assets/sky.avif
new file mode 100644
index 0000000000000000000000000000000000000000..5f73a386927508f385f340555a1bd26fd2ce9a82
GIT binary patch
literal 94465
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