1
0
mirror of synced 2026-07-13 14:45:10 +00:00

Compare commits

..

26 Commits

Author SHA1 Message Date
Steve Riesenberg e38bf6e554 Release 5.6.1 2021-12-20 14:14:55 -06:00
Steve Riesenberg 624e0da669 Update spring-ldap-core to 2.3.5.RELEASE
Closes gh-10659
2021-12-20 12:28:54 -06:00
Steve Riesenberg b28aa6c647 Update org.springframework to 5.3.14
Closes gh-10658
2021-12-20 12:28:52 -06:00
Steve Riesenberg 9e83b4be25 Update junit-bom to 5.8.2
Closes gh-10656
2021-12-20 12:28:47 -06:00
Steve Riesenberg e9854c91ef Update org.jetbrains.kotlin to 1.5.32
Closes gh-10655
2021-12-20 12:28:45 -06:00
Steve Riesenberg 0345e29afb Update cas-client-core to 3.6.4
Closes gh-10654
2021-12-20 12:28:43 -06:00
Steve Riesenberg 8bd5795f8e Update hibernate-entitymanager to 5.6.3.Final
Closes gh-10653
2021-12-20 12:28:41 -06:00
Steve Riesenberg 4fbc98dcd2 Update io.projectreactor to 2020.0.14
Closes gh-10651
2021-12-20 12:28:36 -06:00
Steve Riesenberg 44cdbd6cf3 Update mockk to 1.12.1
Closes gh-10650
2021-12-20 12:28:34 -06:00
Steve Riesenberg a9af8c4128 Update jackson-bom to 2.13.1
Closes gh-10647
2021-12-20 12:28:28 -06:00
Steve Riesenberg 28c81af788 Update logback-classic to 1.2.9
Closes gh-10646
2021-12-20 12:28:26 -06:00
Steve Riesenberg 3a956daf0c Exclude minor version bump 2021-12-20 12:16:04 -06:00
Marcus Da Coregio 9c245865d7 Point to samples on 5.6.x branch 2021-12-10 14:38:24 -03:00
Guirong Hu 86ed937a47 Fix the bug that the custom GrantedAuthority comparison fails
Closes gh-10566
2021-12-08 08:51:54 -03:00
Steve Riesenberg 32ec8c3ae4 Fix Reactive OAuth2 Kotlin DSL examples
Closes gh-10580
2021-12-07 13:58:24 +01:00
Steve Riesenberg c7ffd2513a Update copyright year
Issue gh-10557
2021-12-01 17:36:19 -06:00
Steve Riesenberg b3e0f167ff Fix case sensitive headers comparison
Closes gh-10557
2021-12-01 15:01:06 -06:00
Jonas Erbe 8c063f8ccb Fix JwtClaimValidator wrong error code
Previously JwtClaimValidator returned the invalid_request error on claim validation failure.
But validators have to return invalid_token errors on failure according to:
https://datatracker.ietf.org/doc/html/rfc6750#section-3.1.
Also see gh-10337

Closes gh-10337
2021-11-29 12:13:24 -07:00
Henning Poettker f1ca42e501 Fix return type for NoOpPasswordEncoder bean in documentation 2021-11-23 14:14:05 -03:00
Lars Grefer 719149f01d Remove usages of Gradle's jcenter() repository
Closes gh-10253
2021-11-22 09:38:49 -03:00
Lars Grefer ccd30607a9 Fix Gradle Deprecation Warnings 2021-11-22 09:38:44 -03:00
Dávid Kováč aa1ef46d84 Update clockSkew javadoc according to implementation
Closes gh-10174
2021-11-19 13:33:05 +01:00
Josh Cummings 16a21264d0 Polish AuthRequestConverter Sample Doc
Issue gh-10364
2021-11-18 13:36:31 -07:00
Norbert Nowak 1c0f092176 Fix AuthnRequestConverter Sample Typos
Closes gh-10364
2021-11-18 13:26:53 -07:00
Jeff Maxwell 32d79f3f4e Fix setJWTClaimSetJWSKeySelector Typo
Closes gh-10504
2021-11-16 15:33:42 -07:00
Jeff Maxwell 088a24cf59 Fix jwtDecoder Documentation Usage
Closes gh-10505
2021-11-16 15:18:42 -07:00
577 changed files with 2366 additions and 29525 deletions
-2
View File
@@ -1,11 +1,9 @@
name: Generate Antora Files and Request Build
on:
workflow_dispatch:
push:
branches-ignore:
- 'gh-pages'
tags: '**'
env:
GH_ACTIONS_REPO_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
-26
View File
@@ -1,26 +0,0 @@
name: Backport Bot
on:
issues:
types: [labeled]
pull_request:
types: [labeled]
push:
branches:
- '*.x'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Download BackportBot
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
- name: Backport
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_EVENT: ${{ toJSON(github.event) }}
run: java -jar backport-bot-0.0.1-SNAPSHOT.jar --github.accessToken="$GITHUB_TOKEN" --github.event_name "$GITHUB_EVENT_NAME" --github.event "$GITHUB_EVENT"
@@ -95,15 +95,11 @@ jobs:
mkdir -p ~/.gradle
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
- name: Check samples project
env:
LOCAL_REPOSITORY_PATH: ${{ github.workspace }}/build/publications/repos
SAMPLES_INIT_SCRIPT: ${{ github.workspace }}/build/includeRepo/spring-security-ci.gradle
run: |
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
./gradlew publishMavenJavaPublicationToLocalRepository
./gradlew checkSamples -PsamplesInitScript="$SAMPLES_INIT_SCRIPT" -PlocalRepositoryPath="$LOCAL_REPOSITORY_PATH" --stacktrace
./gradlew checkSamples --stacktrace
check_tangles:
name: Check for Package Tangles
needs: [ prerequisites ]
-6
View File
@@ -1,6 +0,0 @@
# Use sdkman to run "sdk env" to initialize with correct JDK version
# Enable auto-env through the sdkman_auto_env config
# See https://sdkman.io/usage#config
# A summary is to add the following to ~/.sdkman/etc/config
# sdkman_auto_env=true
java=11.0.14-tem
+1 -11
View File
@@ -78,16 +78,6 @@ Alternatively, you can manually check using https://github.com/spring-projects/s
Update the version number in `gradle.properties` for the release, for example `5.5.0-M1`, `5.5.0-RC1`, `5.5.0`
= Update Antora Version
You will need to update the antora.yml version.
If you are unsure of what the values should be, the following task will instruct you what the expected values are:
[source,bash]
----
./gradlew :spring-security-docs:antoraCheckVersion
----
= Build Locally
Run the build using
@@ -129,7 +119,7 @@ git push origin 5.4.0-RC1
== 7. Update to Next Development Version
* Update `gradle.properties` version to next `+SNAPSHOT+` version, update antora.yml, and then push
* Update `gradle.properties` version to next `+SNAPSHOT+` version and then push
== 8. Update version on project page
@@ -42,7 +42,7 @@ import org.springframework.security.acls.domain.AuditLogger;
import org.springframework.security.acls.domain.DefaultPermissionFactory;
import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PermissionFactory;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.AccessControlEntry;
@@ -51,7 +51,6 @@ import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityGenerator;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.acls.model.Sid;
@@ -74,8 +73,8 @@ import org.springframework.util.Assert;
* one in <tt>lookupObjectIdentities</tt>. These are built from the same select and "order
* by" clause, using a different where clause in each case. In order to use custom schema
* or column names, each of these SQL clauses can be customized, but they must be
* consistent with each other and with the expected result set generated by the default
* values.
* consistent with each other and with the expected result set generated by the the
* default values.
*
* @author Ben Alex
*/
@@ -110,8 +109,6 @@ public class BasicLookupStrategy implements LookupStrategy {
private final AclAuthorizationStrategy aclAuthorizationStrategy;
private ObjectIdentityGenerator objectIdentityGenerator;
private PermissionFactory permissionFactory = new DefaultPermissionFactory();
private final AclCache aclCache;
@@ -165,7 +162,6 @@ public class BasicLookupStrategy implements LookupStrategy {
this.aclCache = aclCache;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.grantingStrategy = grantingStrategy;
this.objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();
this.aclClassIdUtils = new AclClassIdUtils();
this.fieldAces.setAccessible(true);
this.fieldAcl.setAccessible(true);
@@ -492,11 +488,6 @@ public class BasicLookupStrategy implements LookupStrategy {
}
}
public final void setObjectIdentityGenerator(ObjectIdentityGenerator objectIdentityGenerator) {
Assert.notNull(objectIdentityGenerator, "objectIdentityGenerator cannot be null");
this.objectIdentityGenerator = objectIdentityGenerator;
}
public final void setConversionService(ConversionService conversionService) {
this.aclClassIdUtils = new AclClassIdUtils(conversionService);
}
@@ -578,8 +569,7 @@ public class BasicLookupStrategy implements LookupStrategy {
// target id type, e.g. UUID.
Serializable identifier = (Serializable) rs.getObject("object_id_identity");
identifier = BasicLookupStrategy.this.aclClassIdUtils.identifierFrom(identifier, rs);
ObjectIdentity objectIdentity = BasicLookupStrategy.this.objectIdentityGenerator
.createObjectIdentity(identifier, rs.getString("class"));
ObjectIdentity objectIdentity = new ObjectIdentityImpl(rs.getString("class"), identifier);
Acl parentAcl = null;
long parentAclId = rs.getLong("parent_object");
@@ -31,12 +31,11 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityGenerator;
import org.springframework.security.acls.model.Sid;
import org.springframework.util.Assert;
@@ -82,8 +81,6 @@ public class JdbcAclService implements AclService {
private AclClassIdUtils aclClassIdUtils;
private ObjectIdentityGenerator objectIdentityGenerator;
public JdbcAclService(DataSource dataSource, LookupStrategy lookupStrategy) {
this(new JdbcTemplate(dataSource), lookupStrategy);
}
@@ -94,7 +91,6 @@ public class JdbcAclService implements AclService {
this.jdbcOperations = jdbcOperations;
this.lookupStrategy = lookupStrategy;
this.aclClassIdUtils = new AclClassIdUtils();
this.objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();
}
@Override
@@ -109,7 +105,7 @@ public class JdbcAclService implements AclService {
String javaType = rs.getString("class");
Serializable identifier = (Serializable) rs.getObject("obj_id");
identifier = this.aclClassIdUtils.identifierFrom(identifier, rs);
return this.objectIdentityGenerator.createObjectIdentity(identifier, javaType);
return new ObjectIdentityImpl(javaType, identifier);
}
@Override
@@ -169,11 +165,6 @@ public class JdbcAclService implements AclService {
this.aclClassIdUtils = new AclClassIdUtils(conversionService);
}
public void setObjectIdentityGenerator(ObjectIdentityGenerator objectIdentityGenerator) {
Assert.notNull(objectIdentityGenerator, "objectIdentityGenerator cannot be null");
this.objectIdentityGenerator = objectIdentityGenerator;
}
protected boolean isAclClassIdSupported() {
return this.aclClassIdSupported;
}
@@ -318,13 +318,4 @@ public abstract class AbstractBasicLookupStrategyTests {
assertThat(((GrantedAuthoritySid) result).getGrantedAuthority()).isEqualTo("sid");
}
@Test
public void setObjectIdentityGeneratorWhenNullThenThrowsIllegalArgumentException() {
// @formatter:off
assertThatIllegalArgumentException()
.isThrownBy(() -> this.strategy.setObjectIdentityGenerator(null))
.withMessage("objectIdentityGenerator cannot be null");
// @formatter:on
}
}
@@ -45,7 +45,6 @@ import org.springframework.security.acls.model.Sid;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
@@ -171,26 +170,6 @@ public class JdbcAclServiceTests {
.isEqualTo(UUID.fromString("25d93b3f-c3aa-4814-9d5e-c7c96ced7762"));
}
@Test
public void setObjectIdentityGeneratorWhenNullThenThrowsIllegalArgumentException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> this.aclServiceIntegration.setObjectIdentityGenerator(null))
.withMessage("objectIdentityGenerator cannot be null");
}
@Test
public void findChildrenWhenObjectIdentityGeneratorSetThenUsed() {
this.aclServiceIntegration
.setObjectIdentityGenerator((id, type) -> new ObjectIdentityImpl(type, "prefix:" + id));
ObjectIdentity objectIdentity = new ObjectIdentityImpl("location", "US");
this.aclServiceIntegration.setAclClassIdSupported(true);
List<ObjectIdentity> objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity);
assertThat(objectIdentities.size()).isEqualTo(1);
assertThat(objectIdentities.get(0).getType()).isEqualTo("location");
assertThat(objectIdentities.get(0).getIdentifier()).isEqualTo("prefix:US-PAL");
}
class MockLongIdDomainObject {
private Object id;
+4
View File
@@ -27,3 +27,7 @@ sourceSets.test.aspectj.srcDir "src/test/java"
sourceSets.test.java.srcDirs = files()
compileAspectj.ajcOptions.outxmlfile = "META-INF/aop.xml"
aspectj {
version = aspectjVersion
}
+5 -29
View File
@@ -2,12 +2,13 @@ buildscript {
dependencies {
classpath "io.spring.javaformat:spring-javaformat-gradle-plugin:$springJavaformatVersion"
classpath 'io.spring.nohttp:nohttp-gradle:0.0.10'
classpath "io.freefair.gradle:aspectj-plugin:6.4.3.1"
classpath "io.freefair.gradle:aspectj-plugin:6.2.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath "com.netflix.nebula:nebula-project-plugin:8.2.0"
}
repositories {
gradlePluginPortal()
maven { url 'https://repo.spring.io/plugins-snapshot' }
maven { url 'https://plugins.gradle.org/m2/' }
}
}
@@ -21,7 +22,6 @@ apply plugin: 'org.springframework.security.update-dependencies'
apply plugin: 'org.springframework.security.sagan'
apply plugin: 'org.springframework.github.milestone'
apply plugin: 'org.springframework.github.changelog'
apply plugin: 'org.springframework.github.release'
group = 'org.springframework.security'
description = 'Spring Security'
@@ -35,7 +35,7 @@ repositories {
}
tasks.named("saganCreateRelease") {
referenceDocUrl = "https://docs.spring.io/spring-security/reference/{version}/index.html"
referenceDocUrl = "https://docs.spring.io/spring-security/site/docs/{version}/reference/html5/"
apiDocUrl = "https://docs.spring.io/spring-security/site/docs/{version}/api/"
}
@@ -46,13 +46,6 @@ tasks.named("gitHubCheckMilestoneHasNoOpenIssues") {
}
}
tasks.named("createGitHubRelease") {
repository {
owner = "spring-projects"
name = "spring-security"
}
}
tasks.named("updateDependencies") {
// we aren't Gradle 7 compatible yet
checkForGradleUpdate = false
@@ -71,9 +64,9 @@ updateDependenciesSettings {
dependencyExcludes {
majorVersionBump()
minorVersionBump()
alphaBetaVersions()
releaseCandidatesVersions()
milestoneVersions()
alphaBetaVersions()
snapshotVersions()
addRule { components ->
components.withModule("commons-codec:commons-codec") { selection ->
@@ -102,18 +95,6 @@ updateDependenciesSettings {
selection.reject("org.opensaml maintains two different versions, so it must be updated manually");
}
}
components.withModule("io.spring.javaformat:spring-javaformat-gradle-plugin") { selection ->
ModuleComponentIdentifier candidate = selection.getCandidate();
if (!candidate.getVersion().equals(selection.getCurrentVersion())) {
selection.reject("spring-javaformat-gradle-plugin updates break checkstyle");
}
}
components.withModule("io.spring.javaformat:spring-javaformat-checkstyle") { selection ->
ModuleComponentIdentifier candidate = selection.getCandidate();
if (!candidate.getVersion().equals(selection.getCurrentVersion())) {
selection.reject("spring-javaformat-checkstyle updates break checkstyle");
}
}
}
}
}
@@ -125,7 +106,6 @@ subprojects {
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
options.compilerArgs.add("-parameters")
options.release = 8
}
}
@@ -173,10 +153,6 @@ tasks.register('checkSamples') {
includeCheckRemote {
repository = 'spring-projects/spring-security-samples'
ref = samplesBranch
if (project.hasProperty("samplesInitScript")) {
initScripts = [samplesInitScript]
projectProperties = ["localRepositoryPath": localRepositoryPath, "springSecurityVersion": project.version]
}
}
dependsOn checkRemote
}
+2 -15
View File
@@ -27,10 +27,6 @@ sourceSets {
gradlePlugin {
plugins {
checkAntoraVersion {
id = "org.springframework.antora.check-version"
implementationClass = "org.springframework.gradle.antora.CheckAntoraVersionPlugin"
}
trang {
id = "trang"
implementationClass = "trang.TrangPlugin"
@@ -59,10 +55,6 @@ gradlePlugin {
id = "org.springframework.github.changelog"
implementationClass = "org.springframework.gradle.github.changelog.GitHubChangelogPlugin"
}
githubRelease {
id = "org.springframework.github.release"
implementationClass = "org.springframework.gradle.github.release.GitHubReleasePlugin"
}
s101 {
id = "s101"
implementationClass = "s101.S101Plugin"
@@ -80,11 +72,10 @@ dependencies {
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.thaiopensource:trang:20091111'
implementation 'net.sourceforge.saxon:saxon:9.1.0.8'
implementation 'org.yaml:snakeyaml:1.30'
implementation localGroovy()
implementation 'io.github.gradle-nexus:publish-plugin:1.1.0'
implementation 'io.projectreactor:reactor-core:3.5.0-M1'
implementation 'io.projectreactor:reactor-core:3.4.13'
implementation 'gradle.plugin.org.gretty:gretty:3.0.1'
implementation 'com.apollographql.apollo:apollo-runtime:2.4.5'
implementation 'com.github.ben-manes:gradle-versions-plugin:0.38.0'
@@ -108,11 +99,7 @@ dependencies {
}
tasks.named('test', Test).configure {
test {
onlyIf { !project.hasProperty("buildSrc.skipTests") }
useJUnitPlatform()
jvmArgs(
'--add-opens', 'java.base/java.lang=ALL-UNNAMED',
'--add-opens', 'java.base/java.util=ALL-UNNAMED'
)
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -19,6 +19,7 @@ package io.spring.gradle.convention
import io.spring.gradle.IncludeRepoTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.provider.Property
import org.gradle.api.tasks.GradleBuild
import org.gradle.api.tasks.TaskProvider
@@ -39,12 +40,6 @@ class IncludeCheckRemotePlugin implements Plugin<Project> {
it.dependsOn 'includeRepo'
it.dir = includeRepoTask.get().outputDirectory
it.tasks = extension.getTasks()
extension.getInitScripts().forEach {script ->
it.startParameter.addInitScript(new File(script))
}
extension.getProjectProperties().entrySet().forEach { entry ->
it.startParameter.projectProperties.put(entry.getKey(), entry.getValue())
}
}
}
@@ -65,16 +60,6 @@ class IncludeCheckRemotePlugin implements Plugin<Project> {
*/
List<String> tasks = ['check']
/**
* Init scripts for the build
*/
List<String> initScripts = []
/**
* Map of properties for the build
*/
Map<String, String> projectProperties = [:]
}
}
@@ -59,22 +59,14 @@ public class IntegrationTestPlugin implements Plugin<Project> {
integrationTestRuntime {
extendsFrom integrationTestCompile, testRuntime, testRuntimeOnly
}
integrationTestCompileClasspath {
extendsFrom integrationTestCompile
canBeResolved = true
}
integrationTestRuntimeClasspath {
extendsFrom integrationTestRuntime
canBeResolved = true
}
}
project.sourceSets {
integrationTest {
java.srcDir project.file('src/integration-test/java')
resources.srcDir project.file('src/integration-test/resources')
compileClasspath = project.sourceSets.main.output + project.sourceSets.test.output + project.configurations.integrationTestCompileClasspath
runtimeClasspath = output + compileClasspath + project.configurations.integrationTestRuntimeClasspath
compileClasspath = project.sourceSets.main.output + project.sourceSets.test.output + project.configurations.integrationTestCompile
runtimeClasspath = output + compileClasspath + project.configurations.integrationTestRuntime
}
}
@@ -93,7 +85,7 @@ public class IntegrationTestPlugin implements Plugin<Project> {
project.idea {
module {
testSourceDirs += project.file('src/integration-test/java')
scopes.TEST.plus += [ project.configurations.integrationTestCompileClasspath ]
scopes.TEST.plus += [ project.configurations.integrationTestCompile ]
}
}
}
@@ -123,7 +115,7 @@ public class IntegrationTestPlugin implements Plugin<Project> {
project.plugins.withType(EclipsePlugin) {
project.eclipse.classpath {
plusConfigurations += [ project.configurations.integrationTestCompileClasspath ]
plusConfigurations += [ project.configurations.integrationTestCompile ]
}
}
}
@@ -21,7 +21,6 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.plugins.PluginManager
import org.springframework.gradle.classpath.CheckProhibitedDependenciesLifecyclePlugin
import org.springframework.gradle.maven.SpringNexusPublishPlugin
class RootProjectPlugin implements Plugin<Project> {
@@ -33,7 +32,6 @@ class RootProjectPlugin implements Plugin<Project> {
pluginManager.apply(SchemaPlugin)
pluginManager.apply(NoHttpPlugin)
pluginManager.apply(SpringNexusPublishPlugin)
pluginManager.apply(CheckProhibitedDependenciesLifecyclePlugin)
pluginManager.apply("org.sonarqube")
project.repositories.mavenCentral()
@@ -20,7 +20,6 @@ import org.gradle.api.Project
import org.gradle.api.plugins.JavaLibraryPlugin;
import org.gradle.api.plugins.MavenPlugin;
import org.gradle.api.plugins.PluginManager
import org.springframework.gradle.classpath.CheckClasspathForProhibitedDependenciesPlugin;
import org.springframework.gradle.maven.SpringMavenPlugin;
/**
@@ -33,7 +32,6 @@ class SpringModulePlugin extends AbstractSpringJavaPlugin {
PluginManager pluginManager = project.getPluginManager();
pluginManager.apply(JavaLibraryPlugin.class)
pluginManager.apply(SpringMavenPlugin.class);
pluginManager.apply(CheckClasspathForProhibitedDependenciesPlugin.class);
pluginManager.apply("io.spring.convention.jacoco");
def deployArtifacts = project.task("deployArtifacts")
@@ -1,85 +0,0 @@
package org.springframework.gradle.antora;
import org.gradle.api.Action;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.language.base.plugins.LifecycleBasePlugin;
public class CheckAntoraVersionPlugin implements Plugin<Project> {
public static final String ANTORA_CHECK_VERSION_TASK_NAME = "antoraCheckVersion";
@Override
public void apply(Project project) {
TaskProvider<CheckAntoraVersionTask> antoraCheckVersion = project.getTasks().register(ANTORA_CHECK_VERSION_TASK_NAME, CheckAntoraVersionTask.class, new Action<CheckAntoraVersionTask>() {
@Override
public void execute(CheckAntoraVersionTask antoraCheckVersion) {
antoraCheckVersion.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
antoraCheckVersion.setDescription("Checks the antora.yml version properties match the Gradle version");
antoraCheckVersion.getAntoraVersion().convention(project.provider(() -> getDefaultAntoraVersion(project)));
antoraCheckVersion.getAntoraPrerelease().convention(project.provider(() -> getDefaultAntoraPrerelease(project)));
antoraCheckVersion.getAntoraDisplayVersion().convention(project.provider(() -> getDefaultAntoraDisplayVersion(project)));
antoraCheckVersion.getAntoraYmlFile().fileProvider(project.provider(() -> project.file("antora.yml")));
}
});
project.getPlugins().withType(LifecycleBasePlugin.class, new Action<LifecycleBasePlugin>() {
@Override
public void execute(LifecycleBasePlugin lifecycleBasePlugin) {
project.getTasks().named(LifecycleBasePlugin.CHECK_TASK_NAME).configure(new Action<Task>() {
@Override
public void execute(Task check) {
check.dependsOn(antoraCheckVersion);
}
});
}
});
}
private static String getDefaultAntoraVersion(Project project) {
String projectVersion = getProjectVersion(project);
int preReleaseIndex = getSnapshotIndex(projectVersion);
return isSnapshot(projectVersion) ? projectVersion.substring(0, preReleaseIndex) : projectVersion;
}
private static String getDefaultAntoraPrerelease(Project project) {
String projectVersion = getProjectVersion(project);
if (isSnapshot(projectVersion)) {
int preReleaseIndex = getSnapshotIndex(projectVersion);
return projectVersion.substring(preReleaseIndex);
}
if (isPreRelease(projectVersion)) {
return Boolean.TRUE.toString();
}
return null;
}
private static String getDefaultAntoraDisplayVersion(Project project) {
String projectVersion = getProjectVersion(project);
if (!isSnapshot(projectVersion) && isPreRelease(projectVersion)) {
return getDefaultAntoraVersion(project);
}
return null;
}
private static String getProjectVersion(Project project) {
Object projectVersion = project.getVersion();
if (projectVersion == null) {
throw new GradleException("Please define antoraVersion and antoraPrerelease on " + ANTORA_CHECK_VERSION_TASK_NAME + " or provide a Project version so they can be defaulted");
}
return String.valueOf(projectVersion);
}
private static boolean isSnapshot(String projectVersion) {
return getSnapshotIndex(projectVersion) >= 0;
}
private static int getSnapshotIndex(String projectVersion) {
return projectVersion.lastIndexOf("-SNAPSHOT");
}
private static boolean isPreRelease(String projectVersion) {
return projectVersion.lastIndexOf("-") >= 0;
}
}
@@ -1,96 +0,0 @@
package org.springframework.gradle.antora;
import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.TaskAction;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.representer.Representer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public abstract class CheckAntoraVersionTask extends DefaultTask {
@TaskAction
public void check() throws FileNotFoundException {
File antoraYmlFile = getAntoraYmlFile().getAsFile().get();
String expectedAntoraVersion = getAntoraVersion().get();
String expectedAntoraPrerelease = getAntoraPrerelease().getOrElse(null);
String expectedAntoraDisplayVersion = getAntoraDisplayVersion().getOrElse(null);
Representer representer = new Representer();
representer.getPropertyUtils().setSkipMissingProperties(true);
Yaml yaml = new Yaml(new Constructor(AntoraYml.class), representer);
AntoraYml antoraYml = yaml.load(new FileInputStream(antoraYmlFile));
String actualAntoraPrerelease = antoraYml.getPrerelease();
boolean preReleaseMatches = antoraYml.getPrerelease() == null && expectedAntoraPrerelease == null ||
(actualAntoraPrerelease != null && actualAntoraPrerelease.equals(expectedAntoraPrerelease));
String actualAntoraDisplayVersion = antoraYml.getDisplay_version();
boolean displayVersionMatches = antoraYml.getDisplay_version() == null && expectedAntoraDisplayVersion == null ||
(actualAntoraDisplayVersion != null && actualAntoraDisplayVersion.equals(expectedAntoraDisplayVersion));
String actualAntoraVersion = antoraYml.getVersion();
if (!preReleaseMatches ||
!displayVersionMatches ||
!expectedAntoraVersion.equals(actualAntoraVersion)) {
throw new GradleException("The Gradle version of '" + getProject().getVersion() + "' should have version: '"
+ expectedAntoraVersion + "' prerelease: '" + expectedAntoraPrerelease + "' display_version: '"
+ expectedAntoraDisplayVersion + "' defined in " + antoraYmlFile + " but got version: '"
+ actualAntoraVersion + "' prerelease: '" + actualAntoraPrerelease + "' display_version: '" + actualAntoraDisplayVersion + "'");
}
}
@InputFile
public abstract RegularFileProperty getAntoraYmlFile();
@Input
public abstract Property<String> getAntoraVersion();
@Input
@Optional
public abstract Property<String> getAntoraPrerelease();
@Input
@Optional
public abstract Property<String> getAntoraDisplayVersion();
public static class AntoraYml {
private String version;
private String prerelease;
private String display_version;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getPrerelease() {
return prerelease;
}
public void setPrerelease(String prerelease) {
this.prerelease = prerelease;
}
public String getDisplay_version() {
return display_version;
}
public void setDisplay_version(String display_version) {
this.display_version = display_version;
}
}
}
@@ -1,99 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.gradle.classpath;
import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ModuleVersionIdentifier;
import org.gradle.api.artifacts.ResolvedConfiguration;
import org.gradle.api.file.FileCollection;
import org.gradle.api.tasks.Classpath;
import org.gradle.api.tasks.TaskAction;
import java.io.IOException;
import java.util.TreeSet;
import java.util.stream.Collectors;
/**
* A {@link Task} for checking the classpath for prohibited dependencies.
*
* @author Andy Wilkinson
*/
public class CheckClasspathForProhibitedDependencies extends DefaultTask {
private Configuration classpath;
public CheckClasspathForProhibitedDependencies() {
getOutputs().upToDateWhen((task) -> true);
}
public void setClasspath(Configuration classpath) {
this.classpath = classpath;
}
@Classpath
public FileCollection getClasspath() {
return this.classpath;
}
@TaskAction
public void checkForProhibitedDependencies() throws IOException {
ResolvedConfiguration resolvedConfiguration = this.classpath.getResolvedConfiguration();
TreeSet<String> prohibited = resolvedConfiguration.getResolvedArtifacts().stream()
.map((artifact) -> artifact.getModuleVersion().getId()).filter(this::prohibited)
.map((id) -> id.getGroup() + ":" + id.getName()).collect(Collectors.toCollection(TreeSet::new));
if (!prohibited.isEmpty()) {
StringBuilder message = new StringBuilder(String.format("Found prohibited dependencies in '%s':%n", this.classpath.getName()));
for (String dependency : prohibited) {
message.append(String.format(" %s%n", dependency));
}
throw new GradleException(message.toString());
}
}
private boolean prohibited(ModuleVersionIdentifier id) {
String group = id.getGroup();
if (group.equals("javax.batch")) {
return false;
}
if (group.equals("javax.cache")) {
return false;
}
if (group.equals("javax.money")) {
return false;
}
if (group.startsWith("javax")) {
return true;
}
if (group.equals("commons-logging")) {
return true;
}
if (group.equals("org.slf4j") && id.getName().equals("jcl-over-slf4j")) {
return true;
}
if (group.startsWith("org.jboss.spec")) {
return true;
}
if (group.equals("org.apache.geronimo.specs")) {
return true;
}
return false;
}
}
@@ -1,67 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.gradle.classpath;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.language.base.plugins.LifecycleBasePlugin;
import org.springframework.util.StringUtils;
/**
* @author Andy Wilkinson
* @author Rob Winch
*/
public class CheckClasspathForProhibitedDependenciesPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getPlugins().apply(CheckProhibitedDependenciesLifecyclePlugin.class);
project.getPlugins().withType(JavaBasePlugin.class, javaBasePlugin -> {
configureProhibitedDependencyChecks(project);
});
}
private void configureProhibitedDependencyChecks(Project project) {
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
sourceSets.all((sourceSet) -> createProhibitedDependenciesChecks(project,
sourceSet.getCompileClasspathConfigurationName(), sourceSet.getRuntimeClasspathConfigurationName()));
}
private void createProhibitedDependenciesChecks(Project project, String... configurationNames) {
ConfigurationContainer configurations = project.getConfigurations();
for (String configurationName : configurationNames) {
Configuration configuration = configurations.getByName(configurationName);
createProhibitedDependenciesCheck(configuration, project);
}
}
private void createProhibitedDependenciesCheck(Configuration classpath, Project project) {
String taskName = "check" + StringUtils.capitalize(classpath.getName() + "ForProhibitedDependencies");
TaskProvider<CheckClasspathForProhibitedDependencies> checkClasspathTask = project.getTasks().register(taskName,
CheckClasspathForProhibitedDependencies.class, checkClasspath -> {
checkClasspath.setGroup(LifecycleBasePlugin.CHECK_TASK_NAME);
checkClasspath.setDescription("Checks " + classpath.getName() + " for prohibited dependencies");
checkClasspath.setClasspath(classpath);
});
project.getTasks().named(CheckProhibitedDependenciesLifecyclePlugin.CHECK_PROHIBITED_DEPENDENCIES_TASK_NAME, checkProhibitedTask -> checkProhibitedTask.dependsOn(checkClasspathTask));
}
}
@@ -1,41 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.gradle.classpath;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.tasks.TaskProvider;
/**
* @author Rob Winch
*/
public class CheckProhibitedDependenciesLifecyclePlugin implements Plugin<Project> {
public static final String CHECK_PROHIBITED_DEPENDENCIES_TASK_NAME = "checkForProhibitedDependencies";
@Override
public void apply(Project project) {
TaskProvider<Task> checkProhibitedDependencies = project.getTasks().register(CheckProhibitedDependenciesLifecyclePlugin.CHECK_PROHIBITED_DEPENDENCIES_TASK_NAME, task -> {
task.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
task.setDescription("Checks both the compile/runtime classpath of every SourceSet for prohibited dependencies");
});
project.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME, checkTask -> {
checkTask.dependsOn(checkProhibitedDependencies);
});
}
}
@@ -16,9 +16,6 @@
package org.springframework.gradle.github.changelog;
import java.io.File;
import java.nio.file.Paths;
import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
@@ -31,10 +28,12 @@ import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
import org.gradle.api.artifacts.repositories.IvyPatternRepositoryLayout;
import org.gradle.api.tasks.JavaExec;
import java.io.File;
import java.nio.file.Paths;
public class GitHubChangelogPlugin implements Plugin<Project> {
public static final String CHANGELOG_GENERATOR_CONFIGURATION_NAME = "changelogGenerator";
public static final String RELEASE_NOTES_PATH = "changelog/release-notes.md";
@Override
public void apply(Project project) {
@@ -43,7 +42,7 @@ public class GitHubChangelogPlugin implements Plugin<Project> {
project.getTasks().register("generateChangelog", JavaExec.class, new Action<JavaExec>() {
@Override
public void execute(JavaExec generateChangelog) {
File outputFile = project.file(Paths.get(project.getBuildDir().getPath(), RELEASE_NOTES_PATH));
File outputFile = project.file(Paths.get(project.getBuildDir().getPath(), "changelog/release-notes.md"));
outputFile.getParentFile().mkdirs();
generateChangelog.setGroup("Release");
generateChangelog.setDescription("Generates the changelog");
@@ -16,9 +16,6 @@
package org.springframework.gradle.github.milestones;
import java.io.IOException;
import java.util.List;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import okhttp3.Interceptor;
@@ -26,7 +23,8 @@ import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.gradle.github.RepositoryRef;
import java.io.IOException;
import java.util.List;
public class GitHubMilestoneApi {
private String baseUrl = "https://api.github.com";
@@ -21,8 +21,6 @@ import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.TaskAction;
import org.springframework.gradle.github.RepositoryRef;
public class GitHubMilestoneHasNoOpenIssuesTask extends DefaultTask {
@Input
private RepositoryRef repository = new RepositoryRef();
@@ -29,7 +29,7 @@ public class GitHubMilestonePlugin implements Plugin<Project> {
githubCheckMilestoneHasNoOpenIssues.setGroup("Release");
githubCheckMilestoneHasNoOpenIssues.setDescription("Checks if there are any open issues for the specified repository and milestone");
githubCheckMilestoneHasNoOpenIssues.setMilestoneTitle((String) project.findProperty("nextVersion"));
if (project.hasProperty("gitHubAccessToken")) {
if (project.hasProperty("githubAccessToken")) {
githubCheckMilestoneHasNoOpenIssues.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken"));
}
}
@@ -1,11 +1,10 @@
package org.springframework.gradle.github;
package org.springframework.gradle.github.milestones;
public class RepositoryRef {
private String owner;
private String name;
public RepositoryRef() {
RepositoryRef() {
}
public RepositoryRef(String owner, String name) {
@@ -63,3 +62,4 @@ public class RepositoryRef {
}
}
}
@@ -1,130 +0,0 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.gradle.github.release;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
import org.gradle.api.Project;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.TaskAction;
import org.springframework.gradle.github.RepositoryRef;
import org.springframework.gradle.github.changelog.GitHubChangelogPlugin;
/**
* @author Steve Riesenberg
*/
public class CreateGitHubReleaseTask extends DefaultTask {
@Input
private RepositoryRef repository = new RepositoryRef();
@Input @Optional
private String gitHubAccessToken;
@Input
private String version;
@Input @Optional
private String branch = "main";
@Input
private boolean createRelease = false;
@TaskAction
public void createGitHubRelease() {
String body = readReleaseNotes();
Release release = Release.tag(this.version)
.commit(this.branch)
.name(this.version)
.body(body)
.preRelease(this.version.contains("-"))
.build();
System.out.printf("%sCreating GitHub release for %s/%s@%s\n",
this.createRelease ? "" : "[DRY RUN] ",
this.repository.getOwner(),
this.repository.getName(),
this.version
);
System.out.printf(" Release Notes:\n\n----\n%s\n----\n\n", body.trim());
if (this.createRelease) {
GitHubReleaseApi github = new GitHubReleaseApi(this.gitHubAccessToken);
github.publishRelease(this.repository, release);
}
}
private String readReleaseNotes() {
Project project = getProject();
File inputFile = project.file(Paths.get(project.getBuildDir().getPath(), GitHubChangelogPlugin.RELEASE_NOTES_PATH));
try {
return Files.readString(inputFile.toPath());
} catch (IOException ex) {
throw new RuntimeException("Unable to read release notes from " + inputFile, ex);
}
}
public RepositoryRef getRepository() {
return repository;
}
public void repository(Action<RepositoryRef> repository) {
repository.execute(this.repository);
}
public void setRepository(RepositoryRef repository) {
this.repository = repository;
}
public String getGitHubAccessToken() {
return gitHubAccessToken;
}
public void setGitHubAccessToken(String gitHubAccessToken) {
this.gitHubAccessToken = gitHubAccessToken;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getBranch() {
return branch;
}
public void setBranch(String branch) {
this.branch = branch;
}
public boolean isCreateRelease() {
return createRelease;
}
public void setCreateRelease(boolean createRelease) {
this.createRelease = createRelease;
}
}
@@ -1,91 +0,0 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.gradle.github.release;
import java.io.IOException;
import com.google.gson.Gson;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.springframework.gradle.github.RepositoryRef;
/**
* Manage GitHub releases.
*
* @author Steve Riesenberg
*/
public class GitHubReleaseApi {
private String baseUrl = "https://api.github.com";
private final OkHttpClient httpClient;
private Gson gson = new Gson();
public GitHubReleaseApi(String gitHubAccessToken) {
this.httpClient = new OkHttpClient.Builder()
.addInterceptor(new AuthorizationInterceptor(gitHubAccessToken))
.build();
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
/**
* Publish a release with no binary attachments.
*
* @param repository The repository owner/name
* @param release The contents of the release
*/
public void publishRelease(RepositoryRef repository, Release release) {
String url = this.baseUrl + "/repos/" + repository.getOwner() + "/" + repository.getName() + "/releases";
String json = this.gson.toJson(release);
RequestBody body = RequestBody.create(MediaType.parse("application/json"), json);
Request request = new Request.Builder().url(url).post(body).build();
try {
Response response = this.httpClient.newCall(request).execute();
if (!response.isSuccessful()) {
throw new RuntimeException(String.format("Could not create release %s for repository %s/%s. Got response %s",
release.getName(), repository.getOwner(), repository.getName(), response));
}
} catch (IOException ex) {
throw new RuntimeException(String.format("Could not create release %s for repository %s/%s",
release.getName(), repository.getOwner(), repository.getName()), ex);
}
}
private static class AuthorizationInterceptor implements Interceptor {
private final String token;
public AuthorizationInterceptor(String token) {
this.token = token;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer " + this.token)
.build();
return chain.proceed(request);
}
}
}
@@ -1,49 +0,0 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.gradle.github.release;
import groovy.lang.MissingPropertyException;
import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
/**
* @author Steve Riesenberg
*/
public class GitHubReleasePlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getTasks().register("createGitHubRelease", CreateGitHubReleaseTask.class, new Action<CreateGitHubReleaseTask>() {
@Override
public void execute(CreateGitHubReleaseTask createGitHubRelease) {
createGitHubRelease.setGroup("Release");
createGitHubRelease.setDescription("Create a github release");
createGitHubRelease.dependsOn("generateChangelog");
createGitHubRelease.setCreateRelease("true".equals(project.findProperty("createRelease")));
createGitHubRelease.setVersion((String) project.findProperty("nextVersion"));
if (project.hasProperty("branch")) {
createGitHubRelease.setBranch((String) project.findProperty("branch"));
}
createGitHubRelease.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken"));
if (createGitHubRelease.isCreateRelease() && createGitHubRelease.getGitHubAccessToken() == null) {
throw new MissingPropertyException("Please provide an access token with -PgitHubAccessToken=...");
}
}
});
}
}
@@ -1,156 +0,0 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.gradle.github.release;
import com.google.gson.annotations.SerializedName;
/**
* @author Steve Riesenberg
*/
public class Release {
@SerializedName("tag_name")
private final String tag;
@SerializedName("target_commitish")
private final String commit;
@SerializedName("name")
private final String name;
@SerializedName("body")
private final String body;
@SerializedName("draft")
private final boolean draft;
@SerializedName("prerelease")
private final boolean preRelease;
@SerializedName("generate_release_notes")
private final boolean generateReleaseNotes;
private Release(String tag, String commit, String name, String body, boolean draft, boolean preRelease, boolean generateReleaseNotes) {
this.tag = tag;
this.commit = commit;
this.name = name;
this.body = body;
this.draft = draft;
this.preRelease = preRelease;
this.generateReleaseNotes = generateReleaseNotes;
}
public String getTag() {
return tag;
}
public String getCommit() {
return commit;
}
public String getName() {
return name;
}
public String getBody() {
return body;
}
public boolean isDraft() {
return draft;
}
public boolean isPreRelease() {
return preRelease;
}
public boolean isGenerateReleaseNotes() {
return generateReleaseNotes;
}
@Override
public String toString() {
return "Release{" +
"tag='" + tag + '\'' +
", commit='" + commit + '\'' +
", name='" + name + '\'' +
", body='" + body + '\'' +
", draft=" + draft +
", preRelease=" + preRelease +
", generateReleaseNotes=" + generateReleaseNotes +
'}';
}
public static Builder tag(String tag) {
return new Builder().tag(tag);
}
public static Builder commit(String commit) {
return new Builder().commit(commit);
}
public static final class Builder {
private String tag;
private String commit;
private String name;
private String body;
private boolean draft;
private boolean preRelease;
private boolean generateReleaseNotes;
private Builder() {
}
public Builder tag(String tag) {
this.tag = tag;
return this;
}
public Builder commit(String commit) {
this.commit = commit;
return this;
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder body(String body) {
this.body = body;
return this;
}
public Builder draft(boolean draft) {
this.draft = draft;
return this;
}
public Builder preRelease(boolean preRelease) {
this.preRelease = preRelease;
return this;
}
public Builder generateReleaseNotes(boolean generateReleaseNotes) {
this.generateReleaseNotes = generateReleaseNotes;
return this;
}
public Release build() {
return new Release(tag, commit, name, body, draft, preRelease, generateReleaseNotes);
}
}
}
@@ -164,18 +164,14 @@ public class S101Configurer {
String source = "https://structure101.com/binaries/v6";
try (final WebClient webClient = new WebClient()) {
HtmlPage page = webClient.getPage(source);
Matcher matcher = null;
for (HtmlAnchor anchor : page.getAnchors()) {
Matcher candidate = Pattern.compile("(structure101-build-java-all-)(.*).zip").matcher(anchor.getHrefAttribute());
if (candidate.find()) {
matcher = candidate;
Matcher matcher = Pattern.compile("(structure101-build-java-all-)(.*).zip").matcher(anchor.getHrefAttribute());
if (matcher.find()) {
copyZipToFilesystem(source, installationDirectory, matcher.group(1) + matcher.group(2));
return matcher.group(2);
}
}
if (matcher == null) {
return null;
}
copyZipToFilesystem(source, installationDirectory, matcher.group(1) + matcher.group(2));
return matcher.group(2);
return null;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -16,11 +16,6 @@
package io.spring.gradle.convention;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import io.spring.gradle.IncludeRepoTask;
import org.apache.commons.io.FileUtils;
import org.gradle.api.Project;
@@ -29,6 +24,8 @@ import org.gradle.testfixtures.ProjectBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import static org.assertj.core.api.Assertions.assertThat;
class IncludeCheckRemotePluginTest {
@@ -71,40 +68,6 @@ class IncludeCheckRemotePluginTest {
assertThat(checkRemote.getTasks()).containsExactly("clean", "build", "test");
}
@Test
void applyWhenExtensionPropertiesInitScriptsThenCreateCheckRemoteWithProvidedTasks() {
this.rootProject = ProjectBuilder.builder().build();
this.rootProject.getPluginManager().apply(IncludeCheckRemotePlugin.class);
this.rootProject.getExtensions().configure(IncludeCheckRemotePlugin.IncludeCheckRemoteExtension.class,
(includeCheckRemoteExtension) -> {
includeCheckRemoteExtension.setProperty("repository", "my-project/my-repository");
includeCheckRemoteExtension.setProperty("ref", "main");
includeCheckRemoteExtension.setProperty("initScripts", Arrays.asList("spring-security-ci.gradle"));
});
GradleBuild checkRemote = (GradleBuild) this.rootProject.getTasks().named("checkRemote").get();
assertThat(checkRemote.getStartParameter().getAllInitScripts()).extracting(File::getName).containsExactly("spring-security-ci.gradle");
}
@Test
void applyWhenExtensionPropertiesBuildPropertiesThenCreateCheckRemoteWithProvidedTasks() {
Map<String, String> projectProperties = new HashMap<>();
projectProperties.put("localRepositoryPath", "~/local/repository");
projectProperties.put("anotherProperty", "some_value");
this.rootProject = ProjectBuilder.builder().build();
this.rootProject.getPluginManager().apply(IncludeCheckRemotePlugin.class);
this.rootProject.getExtensions().configure(IncludeCheckRemotePlugin.IncludeCheckRemoteExtension.class,
(includeCheckRemoteExtension) -> {
includeCheckRemoteExtension.setProperty("repository", "my-project/my-repository");
includeCheckRemoteExtension.setProperty("ref", "main");
includeCheckRemoteExtension.setProperty("projectProperties", projectProperties);
});
GradleBuild checkRemote = (GradleBuild) this.rootProject.getTasks().named("checkRemote").get();
assertThat(checkRemote.getStartParameter().getProjectProperties()).containsEntry("localRepositoryPath", "~/local/repository")
.containsEntry("anotherProperty", "some_value");
}
@Test
void applyWhenExtensionPropertiesThenRegisterIncludeRepoTaskWithExtensionProperties() {
this.rootProject = ProjectBuilder.builder().build();
@@ -58,7 +58,7 @@ public class SaganApiTests {
Release release = new Release();
release.setVersion("5.6.0-SNAPSHOT");
release.setApiDocUrl("https://docs.spring.io/spring-security/site/docs/{version}/api/");
release.setReferenceDocUrl("https://docs.spring.io/spring-security/reference/{version}/index.html");
release.setReferenceDocUrl("https://docs.spring.io/spring-security/site/docs/{version}/reference/html5/");
this.sagan.createReleaseForProject(release, "spring-security");
RecordedRequest request = this.server.takeRequest(1, TimeUnit.SECONDS);
assertThat(request.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/projects/spring-security/releases");
@@ -67,7 +67,7 @@ public class SaganApiTests {
assertThat(request.getBody().readString(Charset.defaultCharset())).isEqualToIgnoringWhitespace("{\n" +
" \"version\":\"5.6.0-SNAPSHOT\",\n" +
" \"current\":false,\n" +
" \"referenceDocUrl\":\"https://docs.spring.io/spring-security/reference/{version}/index.html\",\n" +
" \"referenceDocUrl\":\"https://docs.spring.io/spring-security/site/docs/{version}/reference/html5/\",\n" +
" \"apiDocUrl\":\"https://docs.spring.io/spring-security/site/docs/{version}/api/\"\n" +
"}");
}
@@ -1,16 +1,15 @@
package io.spring.gradle.github.milestones;
import java.util.concurrent.TimeUnit;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.gradle.github.RepositoryRef;
import org.springframework.gradle.github.milestones.GitHubMilestoneApi;
import org.springframework.gradle.github.milestones.RepositoryRef;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -1,270 +0,0 @@
package org.springframework.gradle.antora;
import org.apache.commons.io.IOUtils;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.testfixtures.ProjectBuilder;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIOException;
class CheckAntoraVersionPluginTests {
@Test
void defaultsPropertiesWhenSnapshot() {
String expectedVersion = "1.0.0-SNAPSHOT";
Project project = ProjectBuilder.builder().build();
project.setVersion(expectedVersion);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
assertThat(checkAntoraVersionTask.getAntoraVersion().get()).isEqualTo("1.0.0");
assertThat(checkAntoraVersionTask.getAntoraPrerelease().get()).isEqualTo("-SNAPSHOT");
assertThat(checkAntoraVersionTask.getAntoraDisplayVersion().isPresent()).isFalse();
assertThat(checkAntoraVersionTask.getAntoraYmlFile().getAsFile().get()).isEqualTo(project.file("antora.yml"));
}
@Test
void defaultsPropertiesWhenMilestone() {
String expectedVersion = "1.0.0-M1";
Project project = ProjectBuilder.builder().build();
project.setVersion(expectedVersion);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
assertThat(checkAntoraVersionTask.getAntoraVersion().get()).isEqualTo("1.0.0-M1");
assertThat(checkAntoraVersionTask.getAntoraPrerelease().get()).isEqualTo("true");
assertThat(checkAntoraVersionTask.getAntoraDisplayVersion().get()).isEqualTo(checkAntoraVersionTask.getAntoraVersion().get());
assertThat(checkAntoraVersionTask.getAntoraYmlFile().getAsFile().get()).isEqualTo(project.file("antora.yml"));
}
@Test
void defaultsPropertiesWhenRc() {
String expectedVersion = "1.0.0-RC1";
Project project = ProjectBuilder.builder().build();
project.setVersion(expectedVersion);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
assertThat(checkAntoraVersionTask.getAntoraVersion().get()).isEqualTo("1.0.0-RC1");
assertThat(checkAntoraVersionTask.getAntoraPrerelease().get()).isEqualTo("true");
assertThat(checkAntoraVersionTask.getAntoraDisplayVersion().get()).isEqualTo(checkAntoraVersionTask.getAntoraVersion().get());
assertThat(checkAntoraVersionTask.getAntoraYmlFile().getAsFile().get()).isEqualTo(project.file("antora.yml"));
}
@Test
void defaultsPropertiesWhenRelease() {
String expectedVersion = "1.0.0";
Project project = ProjectBuilder.builder().build();
project.setVersion(expectedVersion);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
assertThat(checkAntoraVersionTask.getAntoraVersion().get()).isEqualTo("1.0.0");
assertThat(checkAntoraVersionTask.getAntoraPrerelease().isPresent()).isFalse();
assertThat(checkAntoraVersionTask.getAntoraDisplayVersion().isPresent()).isFalse();
assertThat(checkAntoraVersionTask.getAntoraYmlFile().getAsFile().get()).isEqualTo(project.file("antora.yml"));
}
@Test
void explicitProperties() {
Project project = ProjectBuilder.builder().build();
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
checkAntoraVersionTask.getAntoraVersion().set("1.0.0");
checkAntoraVersionTask.getAntoraPrerelease().set("-SNAPSHOT");
assertThat(checkAntoraVersionTask.getAntoraVersion().get()).isEqualTo("1.0.0");
assertThat(checkAntoraVersionTask.getAntoraPrerelease().get()).isEqualTo("-SNAPSHOT");
assertThat(checkAntoraVersionTask.getAntoraDisplayVersion().isPresent()).isFalse();
assertThat(checkAntoraVersionTask.getAntoraYmlFile().getAsFile().get()).isEqualTo(project.file("antora.yml"));
}
@Test
void versionNotDefined() throws Exception {
Project project = ProjectBuilder.builder().build();
File rootDir = project.getRootDir();
IOUtils.write("version: '1.0.0'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
assertThatExceptionOfType(GradleException.class).isThrownBy(() -> checkAntoraVersionTask.check());
}
@Test
void antoraFileNotFound() throws Exception {
String expectedVersion = "1.0.0-SNAPSHOT";
Project project = ProjectBuilder.builder().build();
project.setVersion(expectedVersion);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
assertThatIOException().isThrownBy(() -> checkAntoraVersionTask.check());
}
@Test
void actualAntoraPrereleaseNull() throws Exception {
String expectedVersion = "1.0.0-SNAPSHOT";
Project project = ProjectBuilder.builder().build();
File rootDir = project.getRootDir();
IOUtils.write("version: '1.0.0'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
project.setVersion(expectedVersion);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
assertThatExceptionOfType(GradleException.class).isThrownBy(() -> checkAntoraVersionTask.check());
}
@Test
void matchesWhenSnapshot() throws Exception {
String expectedVersion = "1.0.0-SNAPSHOT";
Project project = ProjectBuilder.builder().build();
File rootDir = project.getRootDir();
IOUtils.write("version: '1.0.0'\nprerelease: '-SNAPSHOT'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
project.setVersion(expectedVersion);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
checkAntoraVersionTask.check();
}
@Test
void matchesWhenMilestone() throws Exception {
String expectedVersion = "1.0.0-M1";
Project project = ProjectBuilder.builder().build();
File rootDir = project.getRootDir();
IOUtils.write("version: '1.0.0-M1'\nprerelease: 'true'\ndisplay_version: '1.0.0-M1'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
project.setVersion(expectedVersion);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
checkAntoraVersionTask.check();
}
@Test
void matchesWhenRc() throws Exception {
String expectedVersion = "1.0.0-RC1";
Project project = ProjectBuilder.builder().build();
File rootDir = project.getRootDir();
IOUtils.write("version: '1.0.0-RC1'\nprerelease: 'true'\ndisplay_version: '1.0.0-RC1'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
project.setVersion(expectedVersion);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
checkAntoraVersionTask.check();
}
@Test
void matchesWhenReleaseAndPrereleaseUndefined() throws Exception {
String expectedVersion = "1.0.0";
Project project = ProjectBuilder.builder().build();
File rootDir = project.getRootDir();
IOUtils.write("version: '1.0.0'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
project.setVersion(expectedVersion);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
checkAntoraVersionTask.check();
}
@Test
void matchesWhenExplicitRelease() throws Exception {
Project project = ProjectBuilder.builder().build();
File rootDir = project.getRootDir();
IOUtils.write("version: '1.0.0'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
((CheckAntoraVersionTask) task).getAntoraVersion().set("1.0.0");
checkAntoraVersionTask.check();
}
@Test
void matchesWhenExplicitPrerelease() throws Exception {
Project project = ProjectBuilder.builder().build();
File rootDir = project.getRootDir();
IOUtils.write("version: '1.0.0'\nprerelease: '-SNAPSHOT'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
((CheckAntoraVersionTask) task).getAntoraVersion().set("1.0.0");
((CheckAntoraVersionTask) task).getAntoraPrerelease().set("-SNAPSHOT");
checkAntoraVersionTask.check();
}
@Test
void matchesWhenMissingPropertyDefined() throws Exception {
Project project = ProjectBuilder.builder().build();
File rootDir = project.getRootDir();
IOUtils.write("name: 'ROOT'\nversion: '1.0.0'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
((CheckAntoraVersionTask) task).getAntoraVersion().set("1.0.0");
checkAntoraVersionTask.check();
}
}
@@ -1,7 +1,5 @@
package org.springframework.gradle.github.milestones;
import java.util.concurrent.TimeUnit;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
@@ -9,7 +7,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.gradle.github.RepositoryRef;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -1,151 +0,0 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.gradle.github.release;
import java.util.concurrent.TimeUnit;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.Test;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.gradle.github.RepositoryRef;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Steve Riesenberg
*/
public class GitHubReleaseApiTests {
private GitHubReleaseApi github;
private RepositoryRef repository = new RepositoryRef("spring-projects", "spring-security");
private MockWebServer server;
private String baseUrl;
@BeforeEach
public void setup() throws Exception {
this.server = new MockWebServer();
this.server.start();
this.github = new GitHubReleaseApi("mock-oauth-token");
this.baseUrl = this.server.url("/api").toString();
this.github.setBaseUrl(this.baseUrl);
}
@AfterEach
public void cleanup() throws Exception {
this.server.shutdown();
}
@Test
public void publishReleaseWhenValidParametersThenSuccess() throws Exception {
String responseJson = "{\n" +
" \"url\": \"https://api.github.com/spring-projects/spring-security/releases/1\",\n" +
" \"html_url\": \"https://github.com/spring-projects/spring-security/releases/tags/v1.0.0\",\n" +
" \"assets_url\": \"https://api.github.com/spring-projects/spring-security/releases/1/assets\",\n" +
" \"upload_url\": \"https://uploads.github.com/spring-projects/spring-security/releases/1/assets{?name,label}\",\n" +
" \"tarball_url\": \"https://api.github.com/spring-projects/spring-security/tarball/v1.0.0\",\n" +
" \"zipball_url\": \"https://api.github.com/spring-projects/spring-security/zipball/v1.0.0\",\n" +
" \"discussion_url\": \"https://github.com/spring-projects/spring-security/discussions/90\",\n" +
" \"id\": 1,\n" +
" \"node_id\": \"MDc6UmVsZWFzZTE=\",\n" +
" \"tag_name\": \"v1.0.0\",\n" +
" \"target_commitish\": \"main\",\n" +
" \"name\": \"v1.0.0\",\n" +
" \"body\": \"Description of the release\",\n" +
" \"draft\": false,\n" +
" \"prerelease\": false,\n" +
" \"created_at\": \"2013-02-27T19:35:32Z\",\n" +
" \"published_at\": \"2013-02-27T19:35:32Z\",\n" +
" \"author\": {\n" +
" \"login\": \"sjohnr\",\n" +
" \"id\": 1,\n" +
" \"node_id\": \"MDQ6VXNlcjE=\",\n" +
" \"avatar_url\": \"https://github.com/images/avatar.gif\",\n" +
" \"gravatar_id\": \"\",\n" +
" \"url\": \"https://api.github.com/users/sjohnr\",\n" +
" \"html_url\": \"https://github.com/sjohnr\",\n" +
" \"followers_url\": \"https://api.github.com/users/sjohnr/followers\",\n" +
" \"following_url\": \"https://api.github.com/users/sjohnr/following{/other_user}\",\n" +
" \"gists_url\": \"https://api.github.com/users/sjohnr/gists{/gist_id}\",\n" +
" \"starred_url\": \"https://api.github.com/users/sjohnr/starred{/owner}{/repo}\",\n" +
" \"subscriptions_url\": \"https://api.github.com/users/sjohnr/subscriptions\",\n" +
" \"organizations_url\": \"https://api.github.com/users/sjohnr/orgs\",\n" +
" \"repos_url\": \"https://api.github.com/users/sjohnr/repos\",\n" +
" \"events_url\": \"https://api.github.com/users/sjohnr/events{/privacy}\",\n" +
" \"received_events_url\": \"https://api.github.com/users/sjohnr/received_events\",\n" +
" \"type\": \"User\",\n" +
" \"site_admin\": false\n" +
" },\n" +
" \"assets\": [\n" +
" {\n" +
" \"url\": \"https://api.github.com/spring-projects/spring-security/releases/assets/1\",\n" +
" \"browser_download_url\": \"https://github.com/spring-projects/spring-security/releases/download/v1.0.0/example.zip\",\n" +
" \"id\": 1,\n" +
" \"node_id\": \"MDEyOlJlbGVhc2VBc3NldDE=\",\n" +
" \"name\": \"example.zip\",\n" +
" \"label\": \"short description\",\n" +
" \"state\": \"uploaded\",\n" +
" \"content_type\": \"application/zip\",\n" +
" \"size\": 1024,\n" +
" \"download_count\": 42,\n" +
" \"created_at\": \"2013-02-27T19:35:32Z\",\n" +
" \"updated_at\": \"2013-02-27T19:35:32Z\",\n" +
" \"uploader\": {\n" +
" \"login\": \"sjohnr\",\n" +
" \"id\": 1,\n" +
" \"node_id\": \"MDQ6VXNlcjE=\",\n" +
" \"avatar_url\": \"https://github.com/images/avatar.gif\",\n" +
" \"gravatar_id\": \"\",\n" +
" \"url\": \"https://api.github.com/users/sjohnr\",\n" +
" \"html_url\": \"https://github.com/sjohnr\",\n" +
" \"followers_url\": \"https://api.github.com/users/sjohnr/followers\",\n" +
" \"following_url\": \"https://api.github.com/users/sjohnr/following{/other_user}\",\n" +
" \"gists_url\": \"https://api.github.com/users/sjohnr/gists{/gist_id}\",\n" +
" \"starred_url\": \"https://api.github.com/users/sjohnr/starred{/owner}{/repo}\",\n" +
" \"subscriptions_url\": \"https://api.github.com/users/sjohnr/subscriptions\",\n" +
" \"organizations_url\": \"https://api.github.com/users/sjohnr/orgs\",\n" +
" \"repos_url\": \"https://api.github.com/users/sjohnr/repos\",\n" +
" \"events_url\": \"https://api.github.com/users/sjohnr/events{/privacy}\",\n" +
" \"received_events_url\": \"https://api.github.com/users/sjohnr/received_events\",\n" +
" \"type\": \"User\",\n" +
" \"site_admin\": false\n" +
" }\n" +
" }\n" +
" ]\n" +
"}";
this.server.enqueue(new MockResponse().setBody(responseJson));
this.github.publishRelease(this.repository, Release.tag("1.0.0").build());
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("post");
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/releases");
assertThat(recordedRequest.getBody().toString()).isEqualTo("{\"tag_name\":\"1.0.0\"}");
}
@Test
public void publishReleaseWhenErrorResponseThenException() throws Exception {
this.server.enqueue(new MockResponse().setResponseCode(400));
assertThatExceptionOfType(RuntimeException.class)
.isThrownBy(() -> this.github.publishRelease(this.repository, Release.tag("1.0.0").build()));
}
}
@@ -9,6 +9,6 @@ repositories {
}
dependencies {
optional 'jakarta.servlet:jakarta.servlet-api:3.1.0'
optional 'javax.servlet:javax.servlet-api:3.1.0'
testCompile 'junit:junit:4.12'
}
+2 -5
View File
@@ -4,10 +4,7 @@ dependencies {
management platform(project(":spring-security-dependencies"))
api project(':spring-security-core')
api project(':spring-security-web')
api('org.jasig.cas.client:cas-client-core') {
exclude group: 'org.glassfish.jaxb', module: 'jaxb-core'
exclude group: 'javax.xml.bind', module: 'jaxb-api'
}
api 'org.jasig.cas.client:cas-client-core'
api 'org.springframework:spring-beans'
api 'org.springframework:spring-context'
api 'org.springframework:spring-core'
@@ -16,7 +13,7 @@ dependencies {
optional 'com.fasterxml.jackson.core:jackson-databind'
optional 'net.sf.ehcache:ehcache'
provided 'jakarta.servlet:jakarta.servlet-api'
provided 'javax.servlet:javax.servlet-api'
testImplementation "org.assertj:assertj-core"
testImplementation "org.junit.jupiter:junit-jupiter-api"
@@ -42,8 +42,6 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.context.NullSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
@@ -207,8 +205,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
private AuthenticationFailureHandler proxyFailureHandler = new SimpleUrlAuthenticationFailureHandler();
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
public CasAuthenticationFilter() {
super("/login/cas");
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
@@ -227,7 +223,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authResult);
SecurityContextHolder.setContext(context);
this.securityContextRepository.saveContext(context, request, response);
if (this.eventPublisher != null) {
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
@@ -251,8 +246,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
this.logger.debug("Failed to obtain an artifact (cas ticket)");
password = "";
}
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
password);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
return this.getAuthenticationManager().authenticate(authRequest);
}
@@ -280,18 +274,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
return result;
}
/**
* Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on
* authentication success. The default action is not to save the
* {@link SecurityContext}.
* @param securityContextRepository the {@link SecurityContextRepository} to use.
* Cannot be null.
*/
public void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
this.securityContextRepository = securityContextRepository;
}
/**
* Sets the {@link AuthenticationFailureHandler} for proxy requests.
* @param proxyFailureHandler
@@ -87,8 +87,8 @@ public class CasAuthenticationProviderTests {
cap.setServiceProperties(makeServiceProperties());
cap.setTicketValidator(new MockTicketValidator(true));
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
.unauthenticated(CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "ST-123");
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "ST-123");
token.setDetails("details");
Authentication result = cap.authenticate(token);
// Confirm ST-123 was NOT added to the cache
@@ -120,8 +120,8 @@ public class CasAuthenticationProviderTests {
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
.unauthenticated(CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, "ST-456");
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, "ST-456");
token.setDetails("details");
Authentication result = cap.authenticate(token);
// Confirm ST-456 was added to the cache
@@ -157,8 +157,8 @@ public class CasAuthenticationProviderTests {
cap.setServiceProperties(serviceProperties);
cap.afterPropertiesSet();
String ticket = "ST-456";
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
.unauthenticated(CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
Authentication result = cap.authenticate(token);
}
@@ -178,8 +178,8 @@ public class CasAuthenticationProviderTests {
cap.setServiceProperties(serviceProperties);
cap.afterPropertiesSet();
String ticket = "ST-456";
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
.unauthenticated(CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
Authentication result = cap.authenticate(token);
verify(validator).validate(ticket, serviceProperties.getService());
serviceProperties.setAuthenticateAllArtifacts(true);
@@ -211,8 +211,8 @@ public class CasAuthenticationProviderTests {
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
.unauthenticated(CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "");
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "");
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> cap.authenticate(token));
}
@@ -314,8 +314,8 @@ public class CasAuthenticationProviderTests {
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
.authenticated("some_normal_user", "password", AuthorityUtils.createAuthorityList("ROLE_A"));
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("some_normal_user",
"password", AuthorityUtils.createAuthorityList("ROLE_A"));
assertThat(cap.authenticate(token)).isNull();
}
@@ -121,8 +121,8 @@ public class CasAuthenticationTokenTests {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion);
UsernamePasswordAuthenticationToken token2 = UsernamePasswordAuthenticationToken.authenticated("Test",
"Password", this.ROLES);
UsernamePasswordAuthenticationToken token2 = new UsernamePasswordAuthenticationToken("Test", "Password",
this.ROLES);
assertThat(!token1.equals(token2)).isTrue();
}
@@ -21,7 +21,6 @@ import javax.servlet.FilterChain;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
@@ -33,15 +32,12 @@ import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.context.SecurityContextRepository;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -186,38 +182,6 @@ public class CasAuthenticationFilterTests {
verify(successHandler).onAuthenticationSuccess(request, response, authentication);
}
@Test
public void testSecurityContextHolder() throws Exception {
SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);
AuthenticationManager manager = mock(AuthenticationManager.class);
Authentication authentication = new TestingAuthenticationToken("un", "pwd", "ROLE_USER");
given(manager.authenticate(any(Authentication.class))).willReturn(authentication);
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setAuthenticateAllArtifacts(true);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("ticket", "ST-1-123");
request.setServletPath("/authenticate");
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = mock(FilterChain.class);
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setServiceProperties(serviceProperties);
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
filter.setAuthenticationManager(manager);
filter.setSecurityContextRepository(securityContextRepository);
filter.afterPropertiesSet();
filter.doFilter(request, response, chain);
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull()
.withFailMessage("Authentication should not be null");
verify(chain).doFilter(request, response);
// validate for when the filterProcessUrl matches
filter.setFilterProcessesUrl(request.getServletPath());
SecurityContextHolder.clearContext();
filter.doFilter(request, response, chain);
ArgumentCaptor<SecurityContext> contextArg = ArgumentCaptor.forClass(SecurityContext.class);
verify(securityContextRepository).saveContext(contextArg.capture(), eq(request), eq(response));
assertThat(contextArg.getValue().getAuthentication().getPrincipal()).isEqualTo(authentication.getName());
}
// SEC-1592
@Test
public void testChainNotInvokedForProxyReceptor() throws Exception {
+6 -14
View File
@@ -37,9 +37,9 @@ dependencies {
optional'org.springframework:spring-websocket'
optional 'org.jetbrains.kotlin:kotlin-reflect'
optional 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
optional 'jakarta.annotation:jakarta.annotation-api'
optional 'javax.annotation:jsr250-api'
provided 'jakarta.servlet:jakarta.servlet-api'
provided 'javax.servlet:javax.servlet-api'
testImplementation project(':spring-security-aspects')
testImplementation project(':spring-security-cas')
@@ -62,10 +62,8 @@ dependencies {
testImplementation 'ch.qos.logback:logback-classic'
testImplementation 'io.projectreactor.netty:reactor-netty'
testImplementation 'io.rsocket:rsocket-transport-netty'
testImplementation 'jakarta.annotation:jakarta.annotation-api:1.0'
testImplementation "jakarta.inject:jakarta.inject-api"
testImplementation "jakarta.transaction:jakarta.transaction-api"
testImplementation 'jakarta.xml.bind:jakarta.xml.bind-api'
testImplementation 'javax.annotation:jsr250-api:1.0'
testImplementation 'javax.xml.bind:jaxb-api'
testImplementation 'ldapsdk:ldapsdk:4.1'
testImplementation('net.sourceforge.htmlunit:htmlunit') {
exclude group: 'commons-logging', module: 'commons-logging'
@@ -76,20 +74,13 @@ dependencies {
testImplementation "org.apache.directory.server:apacheds-protocol-ldap"
testImplementation "org.apache.directory.server:apacheds-server-jndi"
testImplementation 'org.apache.directory.shared:shared-ldap'
testImplementation "com.unboundid:unboundid-ldapsdk"
testImplementation 'org.eclipse.persistence:javax.persistence'
testImplementation('org.hibernate:hibernate-entitymanager') {
exclude group: 'javax.activation', module: 'javax.activation-api'
exclude group: 'javax.persistence', module: 'javax.persistence-api'
exclude group: 'javax.xml.bind', module: 'jaxb-api'
exclude group: 'org.jboss.spec.javax.transaction', module: 'jboss-transaction-api_1.2_spec'
}
testImplementation 'org.hibernate:hibernate-entitymanager'
testImplementation 'org.hsqldb:hsqldb'
testImplementation 'org.mockito:mockito-core'
testImplementation "org.mockito:mockito-inline"
testImplementation ('org.openid4java:openid4java-nodeps') {
exclude group: 'com.google.code.guice', module: 'guice'
exclude group: 'commons-logging', module: 'commons-logging'
}
testImplementation('org.seleniumhq.selenium:htmlunit-driver') {
exclude group: 'commons-logging', module: 'commons-logging'
@@ -98,6 +89,7 @@ dependencies {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: 'io.netty', module: 'netty'
}
testImplementation 'org.slf4j:jcl-over-slf4j'
testImplementation 'org.springframework.ldap:spring-ldap-core'
testImplementation 'org.springframework:spring-expression'
testImplementation 'org.springframework:spring-jdbc'
@@ -1,185 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.ldap;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.test.web.servlet.MockMvc;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
@ExtendWith(SpringTestContextExtension.class)
public class EmbeddedLdapServerContextSourceFactoryBeanITests {
public final SpringTestContext spring = new SpringTestContext(this);
@Autowired
private MockMvc mockMvc;
@Test
public void contextSourceFactoryBeanWhenEmbeddedServerThenAuthenticates() throws Exception {
this.spring.register(FromEmbeddedLdapServerConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("bob"));
}
@Test
public void contextSourceFactoryBeanWhenPortZeroThenAuthenticates() throws Exception {
this.spring.register(PortZeroConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("bob"));
}
@Test
public void contextSourceFactoryBeanWhenCustomLdifAndRootThenAuthenticates() throws Exception {
this.spring.register(CustomLdifAndRootConfig.class).autowire();
this.mockMvc.perform(formLogin().user("pg").password("password")).andExpect(authenticated().withUsername("pg"));
}
@Test
public void contextSourceFactoryBeanWhenCustomManagerDnThenAuthenticates() throws Exception {
this.spring.register(CustomManagerDnConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("bob"));
}
@Test
public void contextSourceFactoryBeanWhenManagerDnAndNoPasswordThenException() {
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(() -> this.spring.register(CustomManagerDnNoPasswordConfig.class).autowire())
.withRootCauseInstanceOf(IllegalStateException.class)
.withMessageContaining("managerPassword is required if managerDn is supplied");
}
@EnableWebSecurity
static class FromEmbeddedLdapServerConfig {
@Bean
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
return EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();
}
@Bean
AuthenticationManager authenticationManager(LdapContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=people");
return factory.createAuthenticationManager();
}
}
@EnableWebSecurity
static class PortZeroConfig {
@Bean
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
EmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean
.fromEmbeddedLdapServer();
factoryBean.setPort(0);
return factoryBean;
}
@Bean
AuthenticationManager authenticationManager(LdapContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=people");
return factory.createAuthenticationManager();
}
}
@EnableWebSecurity
static class CustomLdifAndRootConfig {
@Bean
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
EmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean
.fromEmbeddedLdapServer();
factoryBean.setLdif("classpath*:test-server2.xldif");
factoryBean.setRoot("dc=monkeymachine,dc=co,dc=uk");
return factoryBean;
}
@Bean
AuthenticationManager authenticationManager(LdapContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=gorillas");
return factory.createAuthenticationManager();
}
}
@EnableWebSecurity
static class CustomManagerDnConfig {
@Bean
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
EmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean
.fromEmbeddedLdapServer();
factoryBean.setManagerDn("uid=admin,ou=system");
factoryBean.setManagerPassword("secret");
return factoryBean;
}
@Bean
AuthenticationManager authenticationManager(LdapContextSource contextSource) {
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(
contextSource, NoOpPasswordEncoder.getInstance());
factory.setUserDnPatterns("uid={0},ou=people");
return factory.createAuthenticationManager();
}
}
@EnableWebSecurity
static class CustomManagerDnNoPasswordConfig {
@Bean
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
EmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean
.fromEmbeddedLdapServer();
factoryBean.setManagerDn("uid=admin,ou=system");
return factoryBean;
}
@Bean
AuthenticationManager authenticationManager(LdapContextSource contextSource) {
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(
contextSource, NoOpPasswordEncoder.getInstance());
factory.setUserDnPatterns("uid={0},ou=people");
return factory.createAuthenticationManager();
}
}
}
@@ -1,241 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.ldap;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Mockito.mock;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
@ExtendWith(SpringTestContextExtension.class)
public class LdapBindAuthenticationManagerFactoryITests {
public final SpringTestContext spring = new SpringTestContext(this);
@Autowired
private MockMvc mockMvc;
@Test
public void authenticationManagerFactoryWhenFromContextSourceThenAuthenticates() throws Exception {
this.spring.register(FromContextSourceConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("bob"));
}
@Test
public void ldapAuthenticationProviderCustomLdapAuthoritiesPopulator() throws Exception {
CustomAuthoritiesPopulatorConfig.LAP = new DefaultLdapAuthoritiesPopulator(mock(LdapContextSource.class),
null) {
@Override
protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {
return new HashSet<>(AuthorityUtils.createAuthorityList("ROLE_EXTRA"));
}
};
this.spring.register(CustomAuthoritiesPopulatorConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword")).andExpect(
authenticated().withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_EXTRA"))));
}
@Test
public void authenticationManagerFactoryWhenCustomAuthoritiesMapperThenUsed() throws Exception {
CustomAuthoritiesMapperConfig.AUTHORITIES_MAPPER = ((authorities) -> AuthorityUtils
.createAuthorityList("ROLE_CUSTOM"));
this.spring.register(CustomAuthoritiesMapperConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword")).andExpect(
authenticated().withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_CUSTOM"))));
}
@Test
public void authenticationManagerFactoryWhenCustomUserDetailsContextMapperThenUsed() throws Exception {
CustomUserDetailsContextMapperConfig.CONTEXT_MAPPER = new UserDetailsContextMapper() {
@Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
Collection<? extends GrantedAuthority> authorities) {
return User.withUsername("other").password("password").roles("USER").build();
}
@Override
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
}
};
this.spring.register(CustomUserDetailsContextMapperConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("other"));
}
@Test
public void authenticationManagerFactoryWhenCustomUserDnPatternsThenUsed() throws Exception {
this.spring.register(CustomUserDnPatternsConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("bob"));
}
@Test
public void authenticationManagerFactoryWhenCustomUserSearchThenUsed() throws Exception {
this.spring.register(CustomUserSearchConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("bob"));
}
@EnableWebSecurity
static class FromContextSourceConfig extends BaseLdapServerConfig {
@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=people");
return factory.createAuthenticationManager();
}
}
@EnableWebSecurity
static class CustomAuthoritiesMapperConfig extends BaseLdapServerConfig {
static GrantedAuthoritiesMapper AUTHORITIES_MAPPER;
@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=people");
factory.setAuthoritiesMapper(AUTHORITIES_MAPPER);
return factory.createAuthenticationManager();
}
}
@EnableWebSecurity
static class CustomAuthoritiesPopulatorConfig extends BaseLdapServerConfig {
static LdapAuthoritiesPopulator LAP;
@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=people");
factory.setLdapAuthoritiesPopulator(LAP);
return factory.createAuthenticationManager();
}
}
@EnableWebSecurity
static class CustomUserDetailsContextMapperConfig extends BaseLdapServerConfig {
static UserDetailsContextMapper CONTEXT_MAPPER;
@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=people");
factory.setUserDetailsContextMapper(CONTEXT_MAPPER);
return factory.createAuthenticationManager();
}
}
@EnableWebSecurity
static class CustomUserDnPatternsConfig extends BaseLdapServerConfig {
@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=people");
return factory.createAuthenticationManager();
}
}
@EnableWebSecurity
static class CustomUserSearchConfig extends BaseLdapServerConfig {
@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserSearchFilter("uid={0}");
factory.setUserSearchBase("ou=people");
return factory.createAuthenticationManager();
}
}
@EnableWebSecurity
abstract static class BaseLdapServerConfig implements DisposableBean {
private ApacheDSContainer container;
@Bean
ApacheDSContainer ldapServer() throws Exception {
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
this.container.setPort(0);
return this.container;
}
@Bean
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
int port = container.getLocalPort();
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
}
@Override
public void destroy() {
this.container.stop();
}
}
}
@@ -1,114 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.ldap;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
@ExtendWith(SpringTestContextExtension.class)
public class LdapPasswordComparisonAuthenticationManagerFactoryITests {
public final SpringTestContext spring = new SpringTestContext(this);
@Autowired
private MockMvc mockMvc;
@Test
public void authenticationManagerFactoryWhenCustomPasswordEncoderThenUsed() throws Exception {
this.spring.register(CustomPasswordEncoderConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bcrypt").password("password"))
.andExpect(authenticated().withUsername("bcrypt"));
}
@Test
public void authenticationManagerFactoryWhenCustomPasswordAttributeThenUsed() throws Exception {
this.spring.register(CustomPasswordAttributeConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bob")).andExpect(authenticated().withUsername("bob"));
}
@EnableWebSecurity
static class CustomPasswordEncoderConfig extends BaseLdapServerConfig {
@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(
contextSource, new BCryptPasswordEncoder());
factory.setUserDnPatterns("uid={0},ou=people");
return factory.createAuthenticationManager();
}
}
@EnableWebSecurity
static class CustomPasswordAttributeConfig extends BaseLdapServerConfig {
@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(
contextSource, NoOpPasswordEncoder.getInstance());
factory.setPasswordAttribute("uid");
factory.setUserDnPatterns("uid={0},ou=people");
return factory.createAuthenticationManager();
}
}
@EnableWebSecurity
abstract static class BaseLdapServerConfig implements DisposableBean {
private ApacheDSContainer container;
@Bean
ApacheDSContainer ldapServer() throws Exception {
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
this.container.setPort(0);
return this.container;
}
@Bean
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
int port = container.getLocalPort();
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
}
@Override
public void destroy() {
this.container.stop();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -56,7 +56,7 @@ public class LdapProviderBeanDefinitionParserTests {
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
UserDetails ben = (UserDetails) auth.getPrincipal();
assertThat(ben.getAuthorities()).hasSize(3);
}
@@ -89,7 +89,7 @@ public class LdapProviderBeanDefinitionParserTests {
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
assertThat(auth).isNotNull();
}
@@ -104,8 +104,7 @@ public class LdapProviderBeanDefinitionParserTests {
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "ben"));
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("ben", "ben"));
assertThat(auth).isNotNull();
}
@@ -122,7 +121,7 @@ public class LdapProviderBeanDefinitionParserTests {
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("bcrypt", "password"));
.authenticate(new UsernamePasswordAuthenticationToken("bcrypt", "password"));
assertThat(auth).isNotNull();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -138,10 +138,4 @@ public abstract class Elements {
public static final String PASSWORD_MANAGEMENT = "password-management";
public static final String RELYING_PARTY_REGISTRATIONS = "relying-party-registrations";
public static final String SAML2_LOGIN = "saml2-login";
public static final String SAML2_LOGOUT = "saml2-logout";
}
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2022 the original author or authors.
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,7 +47,6 @@ import org.springframework.security.config.method.InterceptMethodsBeanDefinition
import org.springframework.security.config.method.MethodSecurityBeanDefinitionParser;
import org.springframework.security.config.method.MethodSecurityMetadataSourceBeanDefinitionParser;
import org.springframework.security.config.oauth2.client.ClientRegistrationsBeanDefinitionParser;
import org.springframework.security.config.saml2.RelyingPartyRegistrationsBeanDefinitionParser;
import org.springframework.security.config.websocket.WebSocketMessageBrokerSecurityBeanDefinitionParser;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.util.ClassUtils;
@@ -95,7 +94,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
if (!namespaceMatchesVersion(element)) {
pc.getReaderContext().fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or "
+ "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
+ "with Spring Security 5.7. Please update your schema declarations to the 5.7 schema.", element);
+ "with Spring Security 5.6. Please update your schema declarations to the 5.6 schema.", element);
}
String name = pc.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(name);
@@ -191,7 +190,6 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
this.parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser());
this.filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
this.parsers.put(Elements.CLIENT_REGISTRATIONS, new ClientRegistrationsBeanDefinitionParser());
this.parsers.put(Elements.RELYING_PARTY_REGISTRATIONS, new RelyingPartyRegistrationsBeanDefinitionParser());
}
private void loadWebSocketParsers() {
@@ -217,7 +215,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
private boolean matchesVersionInternal(Element element) {
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
return schemaLocation.matches("(?m).*spring-security-5\\.7.*.xsd.*")
return schemaLocation.matches("(?m).*spring-security-5\\.6.*.xsd.*")
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|| !schemaLocation.matches("(?m).*spring-security.*");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,16 +17,16 @@
package org.springframework.security.config.annotation.method.configuration;
import org.springframework.aop.Advisor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
@@ -45,64 +45,59 @@ import org.springframework.security.config.core.GrantedAuthorityDefaults;
*/
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
final class PrePostMethodSecurityConfiguration {
final class PrePostMethodSecurityConfiguration implements ApplicationContextAware {
private final PreFilterAuthorizationMethodInterceptor preFilterAuthorizationMethodInterceptor = new PreFilterAuthorizationMethodInterceptor();
private final AuthorizationManagerBeforeMethodInterceptor preAuthorizeAuthorizationMethodInterceptor;
private final PreAuthorizeAuthorizationManager preAuthorizeAuthorizationManager = new PreAuthorizeAuthorizationManager();
private final AuthorizationManagerAfterMethodInterceptor postAuthorizeAuthorizaitonMethodInterceptor;
private final PostAuthorizeAuthorizationManager postAuthorizeAuthorizationManager = new PostAuthorizeAuthorizationManager();
private final PostFilterAuthorizationMethodInterceptor postFilterAuthorizationMethodInterceptor = new PostFilterAuthorizationMethodInterceptor();
private final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
@Autowired
PrePostMethodSecurityConfiguration(ApplicationContext context) {
this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
this.preAuthorizeAuthorizationMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor
.preAuthorize(this.preAuthorizeAuthorizationManager);
this.postAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
this.postAuthorizeAuthorizaitonMethodInterceptor = AuthorizationManagerAfterMethodInterceptor
.postAuthorize(this.postAuthorizeAuthorizationManager);
this.preFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
this.postFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
this.expressionHandler.setApplicationContext(context);
AuthorizationEventPublisher publisher = new SpringAuthorizationEventPublisher(context);
this.preAuthorizeAuthorizationMethodInterceptor.setAuthorizationEventPublisher(publisher);
this.postAuthorizeAuthorizaitonMethodInterceptor.setAuthorizationEventPublisher(publisher);
}
private boolean customMethodSecurityExpressionHandler = false;
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor preFilterAuthorizationMethodInterceptor() {
if (!this.customMethodSecurityExpressionHandler) {
this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
}
return this.preFilterAuthorizationMethodInterceptor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor preAuthorizeAuthorizationMethodInterceptor() {
return this.preAuthorizeAuthorizationMethodInterceptor;
if (!this.customMethodSecurityExpressionHandler) {
this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
}
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(this.preAuthorizeAuthorizationManager);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor postAuthorizeAuthorizationMethodInterceptor() {
return this.postAuthorizeAuthorizaitonMethodInterceptor;
if (!this.customMethodSecurityExpressionHandler) {
this.postAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
}
return AuthorizationManagerAfterMethodInterceptor.postAuthorize(this.postAuthorizeAuthorizationManager);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor postFilterAuthorizationMethodInterceptor() {
if (!this.customMethodSecurityExpressionHandler) {
this.postFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
}
return this.postFilterAuthorizationMethodInterceptor;
}
@Autowired(required = false)
void setMethodSecurityExpressionHandler(MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
this.customMethodSecurityExpressionHandler = true;
this.preFilterAuthorizationMethodInterceptor.setExpressionHandler(methodSecurityExpressionHandler);
this.preAuthorizeAuthorizationManager.setExpressionHandler(methodSecurityExpressionHandler);
this.postAuthorizeAuthorizationManager.setExpressionHandler(methodSecurityExpressionHandler);
@@ -114,10 +109,9 @@ final class PrePostMethodSecurityConfiguration {
this.expressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
}
@Autowired(required = false)
void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {
this.preAuthorizeAuthorizationMethodInterceptor.setAuthorizationEventPublisher(eventPublisher);
this.postAuthorizeAuthorizaitonMethodInterceptor.setAuthorizationEventPublisher(eventPublisher);
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.expressionHandler.setApplicationContext(context);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,8 +42,6 @@ import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.DisableEncodeUrlFilter;
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
import org.springframework.security.web.session.SessionManagementFilter;
/**
@@ -126,8 +124,6 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
* The ordering of the Filters is:
*
* <ul>
* <li>{@link ForceEagerSessionCreationFilter}</li>
* <li>{@link DisableEncodeUrlFilter}</li>
* <li>{@link ChannelProcessingFilter}</li>
* <li>{@link SecurityContextPersistenceFilter}</li>
* <li>{@link LogoutFilter}</li>
@@ -37,7 +37,6 @@ import org.springframework.security.web.authentication.ui.DefaultLoginPageGenera
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextHolderFilter;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
import org.springframework.security.web.csrf.CsrfFilter;
@@ -46,8 +45,6 @@ import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.DisableEncodeUrlFilter;
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.web.filter.CorsFilter;
@@ -70,12 +67,9 @@ final class FilterOrderRegistration {
FilterOrderRegistration() {
Step order = new Step(INITIAL_ORDER, ORDER_STEP);
put(DisableEncodeUrlFilter.class, order.next());
put(ForceEagerSessionCreationFilter.class, order.next());
put(ChannelProcessingFilter.class, order.next());
order.next(); // gh-8105
put(WebAsyncManagerIntegrationFilter.class, order.next());
put(SecurityContextHolderFilter.class, order.next());
put(SecurityContextPersistenceFilter.class, order.next());
put(HeaderWriterFilter.class, order.next());
put(CorsFilter.class, order.next());
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -2889,15 +2889,8 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
}
}
@SuppressWarnings("unchecked")
@Override
protected DefaultSecurityFilterChain performBuild() {
ExpressionUrlAuthorizationConfigurer<?> expressionConfigurer = getConfigurer(
ExpressionUrlAuthorizationConfigurer.class);
AuthorizeHttpRequestsConfigurer<?> httpConfigurer = getConfigurer(AuthorizeHttpRequestsConfigurer.class);
boolean oneConfigurerPresent = expressionConfigurer == null ^ httpConfigurer == null;
Assert.state((expressionConfigurer == null && httpConfigurer == null) || oneConfigurerPresent,
"authorizeHttpRequests cannot be used in conjunction with authorizeRequests. Please select just one.");
this.filters.sort(OrderComparator.INSTANCE);
List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
for (Filter filter : this.filters) {
@@ -3290,26 +3283,20 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
*/
public final class MvcMatchersRequestMatcherConfigurer extends RequestMatcherConfigurer {
private final List<MvcRequestMatcher> mvcMatchers;
/**
* Creates a new instance
* @param context the {@link ApplicationContext} to use
* @param mvcMatchers the {@link MvcRequestMatcher} instances to set the servlet
* path on if {@link #servletPath(String)} is set.
* @param allMatchers the {@link RequestMatcher} instances to continue the
* configuration
* @param matchers the {@link MvcRequestMatcher} instances to set the servlet path
* on if {@link #servletPath(String)} is set.
*/
private MvcMatchersRequestMatcherConfigurer(ApplicationContext context, List<MvcRequestMatcher> mvcMatchers,
List<RequestMatcher> allMatchers) {
private MvcMatchersRequestMatcherConfigurer(ApplicationContext context, List<MvcRequestMatcher> matchers) {
super(context);
this.mvcMatchers = new ArrayList<>(mvcMatchers);
this.matchers = allMatchers;
this.matchers = new ArrayList<>(matchers);
}
public RequestMatcherConfigurer servletPath(String servletPath) {
for (MvcRequestMatcher matcher : this.mvcMatchers) {
matcher.setServletPath(servletPath);
for (RequestMatcher matcher : this.matchers) {
((MvcRequestMatcher) matcher).setServletPath(servletPath);
}
return this;
}
@@ -3334,7 +3321,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
public MvcMatchersRequestMatcherConfigurer mvcMatchers(HttpMethod method, String... mvcPatterns) {
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
setMatchers(mvcMatchers);
return new MvcMatchersRequestMatcherConfigurer(getContext(), mvcMatchers, this.matchers);
return new MvcMatchersRequestMatcherConfigurer(getContext(), mvcMatchers);
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
@@ -34,7 +33,6 @@ import org.springframework.http.HttpMethod;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder;
@@ -49,12 +47,9 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.debug.DebugFilter;
import org.springframework.security.web.firewall.HttpFirewall;
@@ -62,9 +57,7 @@ import org.springframework.security.web.firewall.RequestRejectedHandler;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
import org.springframework.util.Assert;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.filter.DelegatingFilterProxy;
/**
@@ -88,7 +81,7 @@ import org.springframework.web.filter.DelegatingFilterProxy;
* @see WebSecurityConfiguration
*/
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
implements SecurityBuilder<Filter>, ApplicationContextAware, ServletContextAware {
implements SecurityBuilder<Filter>, ApplicationContextAware {
private final Log logger = LogFactory.getLog(getClass());
@@ -115,8 +108,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
private Runnable postBuildAction = () -> {
};
private ServletContext servletContext;
/**
* Creates a new instance
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
@@ -261,8 +252,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
* {@link WebSecurityConfigurerAdapter}.
* @param securityInterceptor the {@link FilterSecurityInterceptor} to use
* @return the {@link WebSecurity} for further customizations
* @deprecated Use {@link #privilegeEvaluator(WebInvocationPrivilegeEvaluator)}
* instead
*/
public WebSecurity securityInterceptor(FilterSecurityInterceptor securityInterceptor) {
this.filterSecurityInterceptor = securityInterceptor;
@@ -279,19 +268,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
return this;
}
/**
* Sets the handler to handle
* {@link org.springframework.security.web.firewall.RequestRejectedException}
* @param requestRejectedHandler
* @return the {@link WebSecurity} for further customizations
* @since 5.7
*/
public WebSecurity requestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {
Assert.notNull(requestRejectedHandler, "requestRejectedHandler cannot be null");
this.requestRejectedHandler = requestRejectedHandler;
return this;
}
@Override
protected Filter performBuild() throws Exception {
Assert.state(!this.securityFilterChainBuilders.isEmpty(),
@@ -302,24 +278,11 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
+ ".addSecurityFilterChainBuilder directly");
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest
+ ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
if (this.privilegeEvaluator == null) {
this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
requestMatcherPrivilegeEvaluatorsEntries);
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (this.httpFirewall != null) {
@@ -343,29 +306,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
return result;
}
private RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> getRequestMatcherPrivilegeEvaluatorsEntry(
SecurityFilterChain securityFilterChain) {
List<WebInvocationPrivilegeEvaluator> privilegeEvaluators = new ArrayList<>();
for (Filter filter : securityFilterChain.getFilters()) {
if (filter instanceof FilterSecurityInterceptor) {
DefaultWebInvocationPrivilegeEvaluator defaultWebInvocationPrivilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
(FilterSecurityInterceptor) filter);
defaultWebInvocationPrivilegeEvaluator.setServletContext(this.servletContext);
privilegeEvaluators.add(defaultWebInvocationPrivilegeEvaluator);
continue;
}
if (filter instanceof AuthorizationFilter) {
AuthorizationManager<HttpServletRequest> authorizationManager = ((AuthorizationFilter) filter)
.getAuthorizationManager();
AuthorizationManagerWebInvocationPrivilegeEvaluator evaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(
authorizationManager);
evaluator.setServletContext(this.servletContext);
privilegeEvaluators.add(evaluator);
}
}
return new RequestMatcherEntry<>(securityFilterChain::matches, privilegeEvaluators);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.defaultWebSecurityExpressionHandler.setApplicationContext(applicationContext);
@@ -393,11 +333,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
}
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
/**
* An {@link IgnoredRequestConfigurer} that allows optionally configuring the
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
package org.springframework.security.config.annotation.web.configuration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
@@ -25,13 +24,11 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.context.request.async.WebAsyncManagerIntegrationFilter;
@@ -100,7 +97,6 @@ class HttpSecurityConfiguration {
.apply(new DefaultLoginPageConfigurer<>());
http.logout(withDefaults());
// @formatter:on
applyDefaultConfigurers(http);
return http;
}
@@ -109,15 +105,6 @@ class HttpSecurityConfiguration {
: this.authenticationConfiguration.getAuthenticationManager();
}
private void applyDefaultConfigurers(HttpSecurity http) throws Exception {
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader
.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
private Map<Class<?>, Object> createSharedObjects() {
Map<Class<?>, Object> sharedObjects = new HashMap<>();
sharedObjects.put(ApplicationContext.class, this.context);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,14 +16,10 @@
package org.springframework.security.config.annotation.web.configuration;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -71,22 +67,17 @@ class SecurityReactorContextConfiguration {
private static final String SECURITY_REACTOR_CONTEXT_OPERATOR_KEY = "org.springframework.security.SECURITY_REACTOR_CONTEXT_OPERATOR";
private static final Map<Object, Supplier<Object>> CONTEXT_ATTRIBUTE_VALUE_LOADERS = new HashMap<>();
static {
CONTEXT_ATTRIBUTE_VALUE_LOADERS.put(HttpServletRequest.class,
SecurityReactorContextSubscriberRegistrar::getRequest);
CONTEXT_ATTRIBUTE_VALUE_LOADERS.put(HttpServletResponse.class,
SecurityReactorContextSubscriberRegistrar::getResponse);
CONTEXT_ATTRIBUTE_VALUE_LOADERS.put(Authentication.class,
SecurityReactorContextSubscriberRegistrar::getAuthentication);
}
@Override
public void afterPropertiesSet() throws Exception {
Function<? super Publisher<Object>, ? extends Publisher<Object>> lifter = Operators
.liftPublisher((pub, sub) -> createSubscriberIfNecessary(sub));
Hooks.onLastOperator(SECURITY_REACTOR_CONTEXT_OPERATOR_KEY, lifter::apply);
Hooks.onLastOperator(SECURITY_REACTOR_CONTEXT_OPERATOR_KEY, (pub) -> {
if (!contextAttributesAvailable()) {
// No need to decorate so return original Publisher
return pub;
}
return lifter.apply(pub);
});
}
@Override
@@ -102,30 +93,36 @@ class SecurityReactorContextConfiguration {
return new SecurityReactorContextSubscriber<>(delegate, getContextAttributes());
}
private static boolean contextAttributesAvailable() {
return SecurityContextHolder.getContext().getAuthentication() != null
|| RequestContextHolder.getRequestAttributes() instanceof ServletRequestAttributes;
}
private static Map<Object, Object> getContextAttributes() {
return new LoadingMap<>(CONTEXT_ATTRIBUTE_VALUE_LOADERS);
}
private static HttpServletRequest getRequest() {
HttpServletRequest servletRequest = null;
HttpServletResponse servletResponse = null;
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
return servletRequestAttributes.getRequest();
servletRequest = servletRequestAttributes.getRequest();
servletResponse = servletRequestAttributes.getResponse(); // possible null
}
return null;
}
private static HttpServletResponse getResponse() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
return servletRequestAttributes.getResponse(); // possible null
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null && servletRequest == null) {
return Collections.emptyMap();
}
Map<Object, Object> contextAttributes = new HashMap<>();
if (servletRequest != null) {
contextAttributes.put(HttpServletRequest.class, servletRequest);
}
if (servletResponse != null) {
contextAttributes.put(HttpServletResponse.class, servletResponse);
}
if (authentication != null) {
contextAttributes.put(Authentication.class, authentication);
}
return null;
}
private static Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
return contextAttributes;
}
}
@@ -178,112 +175,4 @@ class SecurityReactorContextConfiguration {
}
/**
* A map that computes each value when {@link #get} is invoked
*/
static class LoadingMap<K, V> implements Map<K, V> {
private final Map<K, V> loaded = new ConcurrentHashMap<>();
private final Map<K, Supplier<V>> loaders;
LoadingMap(Map<K, Supplier<V>> loaders) {
this.loaders = Collections.unmodifiableMap(new HashMap<>(loaders));
}
@Override
public int size() {
return this.loaders.size();
}
@Override
public boolean isEmpty() {
return this.loaders.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return this.loaders.containsKey(key);
}
@Override
public Set<K> keySet() {
return this.loaders.keySet();
}
@Override
public V get(Object key) {
if (!this.loaders.containsKey(key)) {
throw new IllegalArgumentException(
"This map only supports the following keys: " + this.loaders.keySet());
}
return this.loaded.computeIfAbsent((K) key, (k) -> this.loaders.get(k).get());
}
@Override
public V put(K key, V value) {
if (!this.loaders.containsKey(key)) {
throw new IllegalArgumentException(
"This map only supports the following keys: " + this.loaders.keySet());
}
return this.loaded.put(key, value);
}
@Override
public V remove(Object key) {
if (!this.loaders.containsKey(key)) {
throw new IllegalArgumentException(
"This map only supports the following keys: " + this.loaders.keySet());
}
return this.loaded.remove(key);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@Override
public void clear() {
this.loaded.clear();
}
@Override
public boolean containsValue(Object value) {
return this.loaded.containsValue(value);
}
@Override
public Collection<V> values() {
return this.loaded.values();
}
@Override
public Set<Entry<K, V>> entrySet() {
return this.loaded.entrySet();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LoadingMap<?, ?> that = (LoadingMap<?, ?>) o;
return this.loaded.equals(that.loaded);
}
@Override
public int hashCode() {
return this.loaded.hashCode();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import javax.servlet.Filter;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
@@ -127,8 +128,8 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
}
/**
* Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary to evaluate
* privileges for a given web URI
* Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary for the JSP
* tag support.
* @return the {@link WebInvocationPrivilegeEvaluator}
*/
@Bean
@@ -142,20 +143,19 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
* instances used to create the web configuration.
* @param objectPostProcessor the {@link ObjectPostProcessor} used to create a
* {@link WebSecurity} instance
* @param beanFactory the bean factory to use to retrieve the relevant
* @param webSecurityConfigurers the
* {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to
* create the web configuration
* @throws Exception
*/
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
ConfigurableListableBeanFactory beanFactory) throws Exception {
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
if (this.debugEnabled != null) {
this.webSecurity.debug(this.debugEnabled);
}
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new AutowiredWebSecurityConfigurersIgnoreParents(
beanFactory).getWebSecurityConfigurers();
webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
@@ -189,6 +189,12 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
return new RsaKeyConversionServicePostProcessor();
}
@Bean
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableWebSecurityAttrMap = importMetadata
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -89,12 +89,8 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
*
* @author Rob Winch
* @see EnableWebSecurity
* @deprecated Use a {@link org.springframework.security.web.SecurityFilterChain} Bean to
* configure {@link HttpSecurity} or a {@link WebSecurityCustomizer} Bean to configure
* {@link WebSecurity}
*/
@Order(100)
@Deprecated
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
private final Log logger = LogFactory.getLog(WebSecurityConfigurerAdapter.class);
@@ -38,7 +38,6 @@ import org.springframework.security.web.authentication.SavedRequestAwareAuthenti
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
@@ -147,11 +146,6 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
return getSelf();
}
public T securityContextRepository(SecurityContextRepository securityContextRepository) {
this.authFilter.setSecurityContextRepository(securityContextRepository);
return getSelf();
}
/**
* Create the {@link RequestMatcher} given a loginProcessingUrl
* @param loginProcessingUrl creates the {@link RequestMatcher} based upon the
@@ -293,12 +287,6 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
if (rememberMeServices != null) {
this.authFilter.setRememberMeServices(rememberMeServices);
}
SecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);
if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {
SecurityContextRepository securityContextRepository = securityContextConfigurer
.getSecurityContextRepository();
this.authFilter.setSecurityContextRepository(securityContextRepository);
}
F filter = postProcess(this.authFilter);
http.addFilter(filter);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,9 +25,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
import org.springframework.security.authorization.AuthorityAuthorizationManager;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
@@ -36,7 +34,6 @@ import org.springframework.security.web.access.intercept.RequestAuthorizationCon
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
import org.springframework.util.Assert;
/**
@@ -49,25 +46,14 @@ import org.springframework.util.Assert;
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {
static final AuthorizationManager<RequestAuthorizationContext> permitAllAuthorizationManager = (a,
o) -> new AuthorizationDecision(true);
private final AuthorizationManagerRequestMatcherRegistry registry;
private final AuthorizationEventPublisher publisher;
/**
* Creates an instance.
* @param context the {@link ApplicationContext} to use
*/
public AuthorizeHttpRequestsConfigurer(ApplicationContext context) {
this.registry = new AuthorizationManagerRequestMatcherRegistry(context);
if (context.getBeanNamesForType(AuthorizationEventPublisher.class).length > 0) {
this.publisher = context.getBean(AuthorizationEventPublisher.class);
}
else {
this.publisher = new SpringAuthorizationEventPublisher(context);
}
}
/**
@@ -84,8 +70,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
public void configure(H http) {
AuthorizationManager<HttpServletRequest> authorizationManager = this.registry.createAuthorizationManager();
AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
authorizationFilter.setAuthorizationEventPublisher(this.publisher);
authorizationFilter.setShouldFilterAllDispatcherTypes(this.registry.shouldFilterAllDispatcherTypes);
http.addFilter(postProcess(authorizationFilter));
}
@@ -97,12 +81,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
return this.registry;
}
AuthorizationManagerRequestMatcherRegistry addFirst(RequestMatcher matcher,
AuthorizationManager<RequestAuthorizationContext> manager) {
this.registry.addFirst(matcher, manager);
return this.registry;
}
/**
* Registry for mapping a {@link RequestMatcher} to an {@link AuthorizationManager}.
*
@@ -118,8 +96,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
private int mappingCount;
private boolean shouldFilterAllDispatcherTypes = false;
private AuthorizationManagerRequestMatcherRegistry(ApplicationContext context) {
setApplicationContext(context);
}
@@ -130,12 +106,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
this.mappingCount++;
}
private void addFirst(RequestMatcher matcher, AuthorizationManager<RequestAuthorizationContext> manager) {
this.unmappedMatchers = null;
this.managerBuilder.mappings((m) -> m.add(0, new RequestMatcherEntry<>(matcher, manager)));
this.mappingCount++;
}
private AuthorizationManager<HttpServletRequest> createAuthorizationManager() {
Assert.state(this.unmappedMatchers == null,
() -> "An incomplete mapping was found for " + this.unmappedMatchers
@@ -173,19 +143,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
return this;
}
/**
* Sets whether all dispatcher types should be filtered.
* @param shouldFilter should filter all dispatcher types. Default is
* {@code false}
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
* customizations
* @since 5.7
*/
public AuthorizationManagerRequestMatcherRegistry shouldFilterAllDispatcherTypes(boolean shouldFilter) {
this.shouldFilterAllDispatcherTypes = shouldFilter;
return this;
}
/**
* Return the {@link HttpSecurityBuilder} when done using the
* {@link AuthorizeHttpRequestsConfigurer}. This is useful for method chaining.
@@ -252,7 +209,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry permitAll() {
return access(permitAllAuthorizationManager);
return access((a, o) -> new AuthorizationDecision(true));
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,9 +30,7 @@ import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.access.channel.ChannelProcessor;
@@ -77,7 +75,6 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
*
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
* @author Rob Winch
* @author Onur Kagan Ozcan
* @since 3.2
*/
public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
@@ -89,8 +86,6 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
private List<ChannelProcessor> channelProcessors;
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
private final ChannelRequestMatcherRegistry REGISTRY;
/**
@@ -128,11 +123,9 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
if (portMapper != null) {
RetryWithHttpEntryPoint httpEntryPoint = new RetryWithHttpEntryPoint();
httpEntryPoint.setPortMapper(portMapper);
httpEntryPoint.setRedirectStrategy(this.redirectStrategy);
insecureChannelProcessor.setEntryPoint(httpEntryPoint);
RetryWithHttpsEntryPoint httpsEntryPoint = new RetryWithHttpsEntryPoint();
httpsEntryPoint.setPortMapper(portMapper);
httpsEntryPoint.setRedirectStrategy(this.redirectStrategy);
secureChannelProcessor.setEntryPoint(httpsEntryPoint);
}
insecureChannelProcessor = postProcess(insecureChannelProcessor);
@@ -192,17 +185,6 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Sets the {@link RedirectStrategy} instances to use in
* {@link RetryWithHttpEntryPoint} and {@link RetryWithHttpsEntryPoint}
* @param redirectStrategy
* @return the {@link ChannelSecurityConfigurer} for further customizations
*/
public ChannelRequestMatcherRegistry redirectStrategy(RedirectStrategy redirectStrategy) {
ChannelSecurityConfigurer.this.redirectStrategy = redirectStrategy;
return this;
}
/**
* Return the {@link SecurityBuilder} when done using the
* {@link SecurityConfigurer}. This is useful for method chaining.
@@ -237,8 +237,8 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Gets the default {@link AccessDeniedHandler} from the
* {@link ExceptionHandlingConfigurer#getAccessDeniedHandler(HttpSecurityBuilder)} or
* create a {@link AccessDeniedHandlerImpl} if not available.
* {@link ExceptionHandlingConfigurer#getAccessDeniedHandler()} or create a
* {@link AccessDeniedHandlerImpl} if not available.
* @param http the {@link HttpSecurityBuilder}
* @return the {@link AccessDeniedHandler}
*/
@@ -247,7 +247,7 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
ExceptionHandlingConfigurer<H> exceptionConfig = http.getConfigurer(ExceptionHandlingConfigurer.class);
AccessDeniedHandler handler = null;
if (exceptionConfig != null) {
handler = exceptionConfig.getAccessDeniedHandler(http);
handler = exceptionConfig.getAccessDeniedHandler();
}
if (handler == null) {
handler = new AccessDeniedHandlerImpl();
@@ -369,7 +369,9 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
}
/**
* Specify that URLs requires a specific IP Address or subnet.
* Specify that URLs requires a specific IP Address or <a href=
* "https://forum.spring.io/showthread.php?102783-How-to-use-hasIpAddress&p=343971#post343971"
* >subnet</a>.
* @param ipaddressExpression the ipaddress (i.e. 192.168.1.79) or local subnet
* (i.e. 192.168.0/24)
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,9 +31,6 @@ import org.springframework.security.web.header.HeaderWriter;
import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;
import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;
import org.springframework.security.web.header.writers.HpkpHeaderWriter;
import org.springframework.security.web.header.writers.HstsHeaderWriter;
@@ -100,12 +97,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
private final PermissionsPolicyConfig permissionsPolicy = new PermissionsPolicyConfig();
private final CrossOriginOpenerPolicyConfig crossOriginOpenerPolicy = new CrossOriginOpenerPolicyConfig();
private final CrossOriginEmbedderPolicyConfig crossOriginEmbedderPolicy = new CrossOriginEmbedderPolicyConfig();
private final CrossOriginResourcePolicyConfig crossOriginResourcePolicy = new CrossOriginResourcePolicyConfig();
/**
* Creates a new instance
*
@@ -401,9 +392,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
addIfNotNull(writers, this.referrerPolicy.writer);
addIfNotNull(writers, this.featurePolicy.writer);
addIfNotNull(writers, this.permissionsPolicy.writer);
addIfNotNull(writers, this.crossOriginOpenerPolicy.writer);
addIfNotNull(writers, this.crossOriginEmbedderPolicy.writer);
addIfNotNull(writers, this.crossOriginResourcePolicy.writer);
writers.addAll(this.headerWriters);
return writers;
}
@@ -556,129 +544,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this.permissionsPolicy;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a> header.
* <p>
* Configuration is provided to the {@link CrossOriginOpenerPolicyHeaderWriter} which
* responsible for writing the header.
* </p>
* @return the {@link CrossOriginOpenerPolicyConfig} for additional confniguration
* @since 5.7
* @see CrossOriginOpenerPolicyHeaderWriter
*/
public CrossOriginOpenerPolicyConfig crossOriginOpenerPolicy() {
this.crossOriginOpenerPolicy.writer = new CrossOriginOpenerPolicyHeaderWriter();
return this.crossOriginOpenerPolicy;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a> header.
* <p>
* Calling this method automatically enables (includes) the
* {@code Cross-Origin-Opener-Policy} header in the response using the supplied
* policy.
* <p>
* <p>
* Configuration is provided to the {@link CrossOriginOpenerPolicyHeaderWriter} which
* responsible for writing the header.
* </p>
* @return the {@link HeadersConfigurer} for additional customizations
* @since 5.7
* @see CrossOriginOpenerPolicyHeaderWriter
*/
public HeadersConfigurer<H> crossOriginOpenerPolicy(
Customizer<CrossOriginOpenerPolicyConfig> crossOriginOpenerPolicyCustomizer) {
this.crossOriginOpenerPolicy.writer = new CrossOriginOpenerPolicyHeaderWriter();
crossOriginOpenerPolicyCustomizer.customize(this.crossOriginOpenerPolicy);
return HeadersConfigurer.this;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a> header.
* <p>
* Configuration is provided to the {@link CrossOriginEmbedderPolicyHeaderWriter}
* which is responsible for writing the header.
* </p>
* @return the {@link CrossOriginEmbedderPolicyConfig} for additional customizations
* @since 5.7
* @see CrossOriginEmbedderPolicyHeaderWriter
*/
public CrossOriginEmbedderPolicyConfig crossOriginEmbedderPolicy() {
this.crossOriginEmbedderPolicy.writer = new CrossOriginEmbedderPolicyHeaderWriter();
return this.crossOriginEmbedderPolicy;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a> header.
* <p>
* Calling this method automatically enables (includes) the
* {@code Cross-Origin-Embedder-Policy} header in the response using the supplied
* policy.
* <p>
* <p>
* Configuration is provided to the {@link CrossOriginEmbedderPolicyHeaderWriter}
* which is responsible for writing the header.
* </p>
* @return the {@link HeadersConfigurer} for additional customizations
* @since 5.7
* @see CrossOriginEmbedderPolicyHeaderWriter
*/
public HeadersConfigurer<H> crossOriginEmbedderPolicy(
Customizer<CrossOriginEmbedderPolicyConfig> crossOriginEmbedderPolicyCustomizer) {
this.crossOriginEmbedderPolicy.writer = new CrossOriginEmbedderPolicyHeaderWriter();
crossOriginEmbedderPolicyCustomizer.customize(this.crossOriginEmbedderPolicy);
return HeadersConfigurer.this;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a> header.
* <p>
* Configuration is provided to the {@link CrossOriginResourcePolicyHeaderWriter}
* which is responsible for writing the header:
* </p>
* @return the {@link HeadersConfigurer} for additional customizations
* @since 5.7
* @see CrossOriginResourcePolicyHeaderWriter
*/
public CrossOriginResourcePolicyConfig crossOriginResourcePolicy() {
this.crossOriginResourcePolicy.writer = new CrossOriginResourcePolicyHeaderWriter();
return this.crossOriginResourcePolicy;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a> header.
* <p>
* Calling this method automatically enables (includes) the
* {@code Cross-Origin-Resource-Policy} header in the response using the supplied
* policy.
* <p>
* <p>
* Configuration is provided to the {@link CrossOriginResourcePolicyHeaderWriter}
* which is responsible for writing the header:
* </p>
* @return the {@link HeadersConfigurer} for additional customizations
* @since 5.7
* @see CrossOriginResourcePolicyHeaderWriter
*/
public HeadersConfigurer<H> crossOriginResourcePolicy(
Customizer<CrossOriginResourcePolicyConfig> crossOriginResourcePolicyCustomizer) {
this.crossOriginResourcePolicy.writer = new CrossOriginResourcePolicyHeaderWriter();
crossOriginResourcePolicyCustomizer.customize(this.crossOriginResourcePolicy);
return HeadersConfigurer.this;
}
public final class ContentTypeOptionsConfig {
private XContentTypeOptionsHeaderWriter writer;
@@ -1277,96 +1142,4 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
}
public final class CrossOriginOpenerPolicyConfig {
private CrossOriginOpenerPolicyHeaderWriter writer;
public CrossOriginOpenerPolicyConfig() {
}
/**
* Sets the policy to be used in the {@code Cross-Origin-Opener-Policy} header
* @param openerPolicy a {@code Cross-Origin-Opener-Policy}
* @return the {@link CrossOriginOpenerPolicyConfig} for additional configuration
* @throws IllegalArgumentException if openerPolicy is null
*/
public CrossOriginOpenerPolicyConfig policy(
CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy openerPolicy) {
this.writer.setPolicy(openerPolicy);
return this;
}
/**
* Allows completing configuration of Cross Origin Opener Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
public final class CrossOriginEmbedderPolicyConfig {
private CrossOriginEmbedderPolicyHeaderWriter writer;
public CrossOriginEmbedderPolicyConfig() {
}
/**
* Sets the policy to be used in the {@code Cross-Origin-Embedder-Policy} header
* @param embedderPolicy a {@code Cross-Origin-Embedder-Policy}
* @return the {@link CrossOriginEmbedderPolicyConfig} for additional
* configuration
* @throws IllegalArgumentException if embedderPolicy is null
*/
public CrossOriginEmbedderPolicyConfig policy(
CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy embedderPolicy) {
this.writer.setPolicy(embedderPolicy);
return this;
}
/**
* Allows completing configuration of Cross-Origin-Embedder-Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
public final class CrossOriginResourcePolicyConfig {
private CrossOriginResourcePolicyHeaderWriter writer;
public CrossOriginResourcePolicyConfig() {
}
/**
* Sets the policy to be used in the {@code Cross-Origin-Resource-Policy} header
* @param resourcePolicy a {@code Cross-Origin-Resource-Policy}
* @return the {@link CrossOriginResourcePolicyConfig} for additional
* configuration
* @throws IllegalArgumentException if resourcePolicy is null
*/
public CrossOriginResourcePolicyConfig policy(
CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy resourcePolicy) {
this.writer.setPolicy(resourcePolicy);
return this;
}
/**
* Allows completing configuration of Cross-Origin-Resource-Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,22 +48,11 @@ final class PermitAllSupport {
RequestMatcher... requestMatchers) {
ExpressionUrlAuthorizationConfigurer<?> configurer = http
.getConfigurer(ExpressionUrlAuthorizationConfigurer.class);
AuthorizeHttpRequestsConfigurer<?> httpConfigurer = http.getConfigurer(AuthorizeHttpRequestsConfigurer.class);
boolean oneConfigurerPresent = configurer == null ^ httpConfigurer == null;
Assert.state(oneConfigurerPresent,
"permitAll only works with either HttpSecurity.authorizeRequests() or HttpSecurity.authorizeHttpRequests(). "
+ "Please define one or the other but not both.");
Assert.state(configurer != null, "permitAll only works with HttpSecurity.authorizeRequests()");
for (RequestMatcher matcher : requestMatchers) {
if (matcher != null) {
if (configurer != null) {
configurer.getRegistry().addMapping(0, new UrlMapping(matcher,
SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
}
else {
httpConfigurer.addFirst(matcher, AuthorizeHttpRequestsConfigurer.permitAllAuthorizationManager);
}
configurer.getRegistry().addMapping(0, new UrlMapping(matcher,
SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,6 @@ package org.springframework.security.config.annotation.web.configurers;
import java.util.UUID;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -153,10 +151,8 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
* when a remember me token is valid. The default is to use the
* {@link UserDetailsService} found by invoking
* {@link HttpSecurity#getSharedObject(Class)} which is set when using
* {@link WebSecurityConfigurerAdapter#configure(AuthenticationManagerBuilder)}. When
* using a {@link org.springframework.security.web.SecurityFilterChain} bean, the
* default is to look for a {@link UserDetailsService} bean. Alternatively, one can
* populate {@link #rememberMeServices(RememberMeServices)}.
* {@link WebSecurityConfigurerAdapter#configure(AuthenticationManagerBuilder)}.
* Alternatively, one can populate {@link #rememberMeServices(RememberMeServices)}.
* @param userDetailsService the {@link UserDetailsService} to configure
* @return the {@link RememberMeConfigurer} for further customization
* @see AbstractRememberMeServices
@@ -399,16 +395,15 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
}
/**
* Gets the {@link UserDetailsService} to use. Either the explicitly configured
* {@link UserDetailsService} from {@link #userDetailsService(UserDetailsService)}, a
* shared object from {@link HttpSecurity#getSharedObject(Class)} or the
* {@link UserDetailsService} bean.
* Gets the {@link UserDetailsService} to use. Either the explicitly configure
* {@link UserDetailsService} from {@link #userDetailsService(UserDetailsService)} or
* a shared object from {@link HttpSecurity#getSharedObject(Class)}.
* @param http {@link HttpSecurity} to get the shared {@link UserDetailsService}
* @return the {@link UserDetailsService} to use
*/
private UserDetailsService getUserDetailsService(H http) {
if (this.userDetailsService == null) {
this.userDetailsService = getSharedOrBean(http, UserDetailsService.class);
this.userDetailsService = http.getSharedObject(UserDetailsService.class);
}
Assert.state(this.userDetailsService != null,
() -> "userDetailsService cannot be null. Invoke " + RememberMeConfigurer.class.getSimpleName()
@@ -436,25 +431,4 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
return this.key;
}
private <C> C getSharedOrBean(H http, Class<C> type) {
C shared = http.getSharedObject(type);
if (shared != null) {
return shared;
}
return getBeanOrNull(type);
}
private <T> T getBeanOrNull(Class<T> type) {
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
if (context == null) {
return null;
}
try {
return context.getBean(type);
}
catch (NoSuchBeanDefinitionException ex) {
return null;
}
}
}
@@ -22,10 +22,8 @@ import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextHolderFilter;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
/**
* Allows persisting and restoring of the {@link SecurityContext} found on the
@@ -64,8 +62,6 @@ import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<SecurityContextConfigurer<H>, H> {
private boolean requireExplicitSave;
/**
* Creates a new instance
* @see HttpSecurity#securityContext()
@@ -83,46 +79,23 @@ public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
public SecurityContextConfigurer<H> requireExplicitSave(boolean requireExplicitSave) {
this.requireExplicitSave = requireExplicitSave;
return this;
}
boolean isRequireExplicitSave() {
return this.requireExplicitSave;
}
SecurityContextRepository getSecurityContextRepository() {
SecurityContextRepository securityContextRepository = getBuilder()
.getSharedObject(SecurityContextRepository.class);
if (securityContextRepository == null) {
securityContextRepository = new HttpSessionSecurityContextRepository();
}
return securityContextRepository;
}
@Override
@SuppressWarnings("unchecked")
public void configure(H http) {
SecurityContextRepository securityContextRepository = getSecurityContextRepository();
if (this.requireExplicitSave) {
SecurityContextHolderFilter securityContextHolderFilter = postProcess(
new SecurityContextHolderFilter(securityContextRepository));
http.addFilter(securityContextHolderFilter);
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
if (securityContextRepository == null) {
securityContextRepository = new HttpSessionSecurityContextRepository();
}
else {
SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter(
securityContextRepository);
SessionManagementConfigurer<?> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
SessionCreationPolicy sessionCreationPolicy = (sessionManagement != null)
? sessionManagement.getSessionCreationPolicy() : null;
if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {
securityContextFilter.setForceEagerSessionCreation(true);
http.addFilter(postProcess(new ForceEagerSessionCreationFilter()));
}
securityContextFilter = postProcess(securityContextFilter);
http.addFilter(securityContextFilter);
SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter(
securityContextRepository);
SessionManagementConfigurer<?> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
SessionCreationPolicy sessionCreationPolicy = (sessionManagement != null)
? sessionManagement.getSessionCreationPolicy() : null;
if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {
securityContextFilter.setForceEagerSessionCreation(true);
}
securityContextFilter = postProcess(securityContextFilter);
http.addFilter(securityContextFilter);
}
}
@@ -52,8 +52,6 @@ import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.DisableEncodeUrlFilter;
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.security.web.session.SessionManagementFilter;
@@ -203,12 +201,6 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
* {@link HttpServletResponse#encodeRedirectURL(String)} or
* {@link HttpServletResponse#encodeURL(String)}, otherwise disallows HTTP sessions to
* be included in the URL. This prevents leaking information to external domains.
* <p>
* This is achieved by guarding {@link HttpServletResponse#encodeURL} and
* {@link HttpServletResponse#encodeRedirectURL} invocations. Any code that also
* overrides either of these two methods, like
* {@link org.springframework.web.servlet.resource.ResourceUrlEncodingFilter}, needs
* to come after the security filter chain or risk being skipped.
* @param enableSessionUrlRewriting true if should allow the JSESSIONID to be
* rewritten into the URLs, else false (default)
* @return the {@link SessionManagementConfigurer} for further customization
@@ -378,12 +370,6 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
concurrentSessionFilter = postProcess(concurrentSessionFilter);
http.addFilter(concurrentSessionFilter);
}
if (!this.enableSessionUrlRewriting) {
http.addFilter(new DisableEncodeUrlFilter());
}
if (this.sessionPolicy == SessionCreationPolicy.ALWAYS) {
http.addFilter(new ForceEagerSessionCreationFilter());
}
}
private ConcurrentSessionFilter createConcurrencyFilter(H http) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,19 +18,15 @@ package org.springframework.security.config.annotation.web.configurers;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
@@ -145,9 +141,7 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
/**
* Specifies the {@link AuthenticationUserDetailsService} to use. If not specified,
* the shared {@link UserDetailsService} will be used to create a
* {@link UserDetailsByNameServiceWrapper}. If a {@link SecurityFilterChain} bean is
* used instead of the {@link WebSecurityConfigurerAdapter}, then the
* {@link UserDetailsService} bean will be used by default.
* {@link UserDetailsByNameServiceWrapper}.
* @param authenticationUserDetailsService the
* {@link AuthenticationUserDetailsService} to use
* @return the {@link X509Configurer} for further customizations
@@ -206,30 +200,9 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> getAuthenticationUserDetailsService(
H http) {
if (this.authenticationUserDetailsService == null) {
userDetailsService(getSharedOrBean(http, UserDetailsService.class));
userDetailsService(http.getSharedObject(UserDetailsService.class));
}
return this.authenticationUserDetailsService;
}
private <C> C getSharedOrBean(H http, Class<C> type) {
C shared = http.getSharedObject(type);
if (shared != null) {
return shared;
}
return getBeanOrNull(type);
}
private <T> T getBeanOrNull(Class<T> type) {
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
if (context == null) {
return null;
}
try {
return context.getBean(type);
}
catch (NoSuchBeanDefinitionException ex) {
return null;
}
}
}
@@ -18,9 +18,6 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.se
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Supplier;
import javax.servlet.http.HttpServletRequest;
@@ -28,7 +25,6 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
@@ -55,9 +51,6 @@ import org.springframework.security.oauth2.server.resource.web.DefaultBearerToke
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
import org.springframework.security.web.csrf.CsrfException;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
@@ -160,19 +153,12 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
private OpaqueTokenConfigurer opaqueTokenConfigurer;
private AccessDeniedHandler accessDeniedHandler = new DelegatingAccessDeniedHandler(
new LinkedHashMap<>(createAccessDeniedHandlers()), new BearerTokenAccessDeniedHandler());
private AccessDeniedHandler accessDeniedHandler = new BearerTokenAccessDeniedHandler();
private AuthenticationEntryPoint authenticationEntryPoint = new BearerTokenAuthenticationEntryPoint();
private BearerTokenRequestMatcher requestMatcher = new BearerTokenRequestMatcher();
private static Map<Class<? extends AccessDeniedException>, AccessDeniedHandler> createAccessDeniedHandlers() {
Map<Class<? extends AccessDeniedException>, AccessDeniedHandler> handlers = new HashMap<>();
handlers.put(CsrfException.class, new AccessDeniedHandlerImpl());
return handlers;
}
public OAuth2ResourceServerConfigurer(ApplicationContext context) {
Assert.notNull(context, "context cannot be null");
this.context = context;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.security.config.annotation.web.configurers.saml2;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.opensaml.core.Version;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -48,7 +50,6 @@ import org.springframework.security.saml2.provider.service.web.RelyingPartyRegis
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestContextResolver;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
@@ -114,12 +115,10 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
private String loginPage;
private String authenticationRequestUri = "/saml2/authenticate/{registrationId}";
private Saml2AuthenticationRequestResolver authenticationRequestResolver;
private String loginProcessingUrl = Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
private AuthenticationRequestEndpointConfig authenticationRequestEndpoint = new AuthenticationRequestEndpointConfig();
private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;
private AuthenticationConverter authenticationConverter;
@@ -177,20 +176,6 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Use this {@link Saml2AuthenticationRequestResolver} for generating SAML 2.0
* Authentication Requests.
* @param authenticationRequestResolver
* @return the {@link Saml2LoginConfigurer} for further configuration
* @since 5.7
*/
public Saml2LoginConfigurer<B> authenticationRequestResolver(
Saml2AuthenticationRequestResolver authenticationRequestResolver) {
Assert.notNull(authenticationRequestResolver, "authenticationRequestResolver cannot be null");
this.authenticationRequestResolver = authenticationRequestResolver;
return this;
}
/**
* Specifies the URL to validate the credentials. If specified a custom URL, consider
* specifying a custom {@link AuthenticationConverter} via
@@ -215,7 +200,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
/**
* {@inheritDoc}
* <p>
*
* Initializes this filter chain for SAML 2 Login. The following actions are taken:
* <ul>
* <li>The WebSSO endpoint has CSRF disabled, typically {@code /login/saml2/sso}</li>
@@ -241,8 +226,8 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
super.init(http);
}
else {
Map<String, String> providerUrlMap = getIdentityProviderUrlMap(this.authenticationRequestUri,
this.relyingPartyRegistrationRepository);
Map<String, String> providerUrlMap = getIdentityProviderUrlMap(
this.authenticationRequestEndpoint.filterProcessingUrl, this.relyingPartyRegistrationRepository);
boolean singleProvider = providerUrlMap.size() == 1;
if (singleProvider) {
// Setup auto-redirect to provider login page
@@ -262,16 +247,14 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
/**
* {@inheritDoc}
* <p>
*
* During the {@code configure} phase, a
* {@link Saml2WebSsoAuthenticationRequestFilter} is added to handle SAML 2.0
* AuthNRequest redirects
*/
@Override
public void configure(B http) throws Exception {
Saml2WebSsoAuthenticationRequestFilter filter = getAuthenticationRequestFilter(http);
filter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));
http.addFilter(postProcess(filter));
http.addFilter(this.authenticationRequestEndpoint.build(http));
super.configure(http);
if (this.authenticationManager == null) {
registerDefaultAuthenticationProvider(http);
@@ -281,11 +264,6 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
}
}
private RelyingPartyRegistrationResolver relyingPartyRegistrationResolver(B http) {
RelyingPartyRegistrationRepository registrations = relyingPartyRegistrationRepository(http);
return new DefaultRelyingPartyRegistrationResolver(registrations);
}
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository(B http) {
if (this.relyingPartyRegistrationRepository == null) {
this.relyingPartyRegistrationRepository = getSharedOrBean(http, RelyingPartyRegistrationRepository.class);
@@ -298,46 +276,6 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
saml2WebSsoAuthenticationFilter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));
}
private Saml2WebSsoAuthenticationRequestFilter getAuthenticationRequestFilter(B http) {
Saml2AuthenticationRequestResolver authenticationRequestResolver = getAuthenticationRequestResolver(http);
if (authenticationRequestResolver != null) {
return new Saml2WebSsoAuthenticationRequestFilter(authenticationRequestResolver);
}
return new Saml2WebSsoAuthenticationRequestFilter(getAuthenticationRequestContextResolver(http),
getAuthenticationRequestFactory(http));
}
private Saml2AuthenticationRequestResolver getAuthenticationRequestResolver(B http) {
if (this.authenticationRequestResolver != null) {
return this.authenticationRequestResolver;
}
return getBeanOrNull(http, Saml2AuthenticationRequestResolver.class);
}
private Saml2AuthenticationRequestFactory getAuthenticationRequestFactory(B http) {
Saml2AuthenticationRequestFactory resolver = getSharedOrBean(http, Saml2AuthenticationRequestFactory.class);
if (resolver != null) {
return resolver;
}
if (version().startsWith("4")) {
return new OpenSaml4AuthenticationRequestFactory();
}
else {
return new OpenSamlAuthenticationRequestFactory();
}
}
private Saml2AuthenticationRequestContextResolver getAuthenticationRequestContextResolver(B http) {
Saml2AuthenticationRequestContextResolver resolver = getBeanOrNull(http,
Saml2AuthenticationRequestContextResolver.class);
if (resolver != null) {
return resolver;
}
RelyingPartyRegistrationResolver registrationResolver = new DefaultRelyingPartyRegistrationResolver(
this.relyingPartyRegistrationRepository);
return new DefaultSaml2AuthenticationRequestContextResolver(registrationResolver);
}
private AuthenticationConverter getAuthenticationConverter(B http) {
if (this.authenticationConverter != null) {
return this.authenticationConverter;
@@ -359,7 +297,8 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
if (version != null) {
return version;
}
return Version.getVersion();
return Version.class.getModule().getDescriptor().version().map(Object::toString)
.orElseThrow(() -> new IllegalStateException("cannot determine OpenSAML version"));
}
private void registerDefaultAuthenticationProvider(B http) {
@@ -386,8 +325,8 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return;
}
loginPageGeneratingFilter.setSaml2LoginEnabled(true);
loginPageGeneratingFilter.setSaml2AuthenticationUrlToProviderName(
this.getIdentityProviderUrlMap(this.authenticationRequestUri, this.relyingPartyRegistrationRepository));
loginPageGeneratingFilter.setSaml2AuthenticationUrlToProviderName(this.getIdentityProviderUrlMap(
this.authenticationRequestEndpoint.filterProcessingUrl, this.relyingPartyRegistrationRepository));
loginPageGeneratingFilter.setLoginPageUrl(this.getLoginPage());
loginPageGeneratingFilter.setFailureUrl(this.getFailureUrl());
}
@@ -441,4 +380,46 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
}
}
private final class AuthenticationRequestEndpointConfig {
private String filterProcessingUrl = "/saml2/authenticate/{registrationId}";
private AuthenticationRequestEndpointConfig() {
}
private Filter build(B http) {
Saml2AuthenticationRequestFactory authenticationRequestResolver = getResolver(http);
Saml2AuthenticationRequestContextResolver contextResolver = getContextResolver(http);
Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> repository = getAuthenticationRequestRepository(
http);
Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(contextResolver,
authenticationRequestResolver);
filter.setAuthenticationRequestRepository(repository);
return postProcess(filter);
}
private Saml2AuthenticationRequestFactory getResolver(B http) {
Saml2AuthenticationRequestFactory resolver = getSharedOrBean(http, Saml2AuthenticationRequestFactory.class);
if (resolver == null) {
if (version().startsWith("4")) {
return new OpenSaml4AuthenticationRequestFactory();
}
return new OpenSamlAuthenticationRequestFactory();
}
return resolver;
}
private Saml2AuthenticationRequestContextResolver getContextResolver(B http) {
Saml2AuthenticationRequestContextResolver resolver = getBeanOrNull(http,
Saml2AuthenticationRequestContextResolver.class);
if (resolver == null) {
RelyingPartyRegistrationResolver relyingPartyRegistrationResolver = new DefaultRelyingPartyRegistrationResolver(
Saml2LoginConfigurer.this.relyingPartyRegistrationRepository);
return new DefaultSaml2AuthenticationRequestContextResolver(relyingPartyRegistrationResolver);
}
return resolver;
}
}
}
@@ -319,7 +319,8 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
if (version != null) {
return version;
}
return Version.getVersion();
return Version.class.getModule().getDescriptor().version().map(Object::toString)
.orElseThrow(() -> new IllegalStateException("cannot determine OpenSAML version"));
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
@@ -42,7 +41,6 @@ import org.springframework.web.reactive.result.method.annotation.ArgumentResolve
* This {@code Configuration} is imported by {@link EnableWebFluxSecurity}
*
* @author Rob Winch
* @author Alavudin Kuttikkattil
* @since 5.1
*/
final class ReactiveOAuth2ClientImportSelector implements ImportSelector {
@@ -66,12 +64,14 @@ final class ReactiveOAuth2ClientImportSelector implements ImportSelector {
private ReactiveOAuth2AuthorizedClientService authorizedClientService;
private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
@Override
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
ReactiveOAuth2AuthorizedClientManager authorizedClientManager = getAuthorizedClientManager();
if (authorizedClientManager != null) {
if (this.authorizedClientRepository != null && this.clientRegistrationRepository != null) {
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder
.builder().authorizationCode().refreshToken().clientCredentials().password().build();
DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(
this.clientRegistrationRepository, getAuthorizedClientRepository());
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
configurer.addCustomResolver(new OAuth2AuthorizedClientArgumentResolver(authorizedClientManager));
}
}
@@ -93,13 +93,6 @@ final class ReactiveOAuth2ClientImportSelector implements ImportSelector {
}
}
@Autowired(required = false)
void setAuthorizedClientManager(List<ReactiveOAuth2AuthorizedClientManager> authorizedClientManager) {
if (authorizedClientManager.size() == 1) {
this.authorizedClientManager = authorizedClientManager.get(0);
}
}
private ServerOAuth2AuthorizedClientRepository getAuthorizedClientRepository() {
if (this.authorizedClientRepository != null) {
return this.authorizedClientRepository;
@@ -110,23 +103,6 @@ final class ReactiveOAuth2ClientImportSelector implements ImportSelector {
return null;
}
private ReactiveOAuth2AuthorizedClientManager getAuthorizedClientManager() {
if (this.authorizedClientManager != null) {
return this.authorizedClientManager;
}
ReactiveOAuth2AuthorizedClientManager authorizedClientManager = null;
if (this.authorizedClientRepository != null && this.clientRegistrationRepository != null) {
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder
.builder().authorizationCode().refreshToken().clientCredentials().password().build();
DefaultReactiveOAuth2AuthorizedClientManager defaultReactiveOAuth2AuthorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(
this.clientRegistrationRepository, getAuthorizedClientRepository());
defaultReactiveOAuth2AuthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
authorizedClientManager = defaultReactiveOAuth2AuthorizedClientManager;
}
return authorizedClientManager;
}
}
}
@@ -102,9 +102,7 @@ public class AuthenticationManagerBeanDefinitionParser implements BeanDefinition
pc.getRegistry().registerAlias(id, alias);
pc.getReaderContext().fireAliasRegistered(id, alias, pc.extractSource(element));
}
if (!BeanIds.AUTHENTICATION_MANAGER.equals(id)
&& !pc.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER)
&& !pc.getRegistry().isAlias(BeanIds.AUTHENTICATION_MANAGER)) {
if (!BeanIds.AUTHENTICATION_MANAGER.equals(id)) {
pc.getRegistry().registerAlias(id, BeanIds.AUTHENTICATION_MANAGER);
pc.getReaderContext().fireAliasRegistered(id, BeanIds.AUTHENTICATION_MANAGER, pc.extractSource(element));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -164,8 +164,6 @@ final class AuthenticationConfigBuilder {
@SuppressWarnings("rawtypes")
private ManagedList logoutHandlers;
private BeanMetadataElement logoutSuccessHandler;
private BeanDefinition loginPageGenerationFilter;
private BeanDefinition logoutPageGenerationFilter;
@@ -204,20 +202,6 @@ final class AuthenticationConfigBuilder {
private BeanDefinition oauth2LoginLinks;
private BeanDefinition saml2AuthenticationUrlToProviderName;
private BeanDefinition saml2AuthorizationRequestFilter;
private String saml2AuthenticationFilterId;
private String saml2AuthenticationRequestFilterId;
private String saml2LogoutFilterId;
private String saml2LogoutRequestFilterId;
private String saml2LogoutResponseFilterId;
private boolean oauth2ClientEnabled;
private BeanDefinition authorizationRequestRedirectFilter;
@@ -236,8 +220,8 @@ final class AuthenticationConfigBuilder {
AuthenticationConfigBuilder(Element element, boolean forceAutoConfig, ParserContext pc,
SessionCreationPolicy sessionPolicy, BeanReference requestCache, BeanReference authenticationManager,
BeanReference authenticationFilterSecurityContextRepositoryRef, BeanReference sessionStrategy,
BeanReference portMapper, BeanReference portResolver, BeanMetadataElement csrfLogoutHandler) {
BeanReference sessionStrategy, BeanReference portMapper, BeanReference portResolver,
BeanMetadataElement csrfLogoutHandler) {
this.httpElt = element;
this.pc = pc;
this.requestCache = requestCache;
@@ -251,16 +235,12 @@ final class AuthenticationConfigBuilder {
createRememberMeFilter(authenticationManager);
createBasicFilter(authenticationManager);
createBearerTokenAuthenticationFilter(authenticationManager);
createFormLoginFilter(sessionStrategy, authenticationManager, authenticationFilterSecurityContextRepositoryRef);
createOAuth2ClientFilters(sessionStrategy, requestCache, authenticationManager,
authenticationFilterSecurityContextRepositoryRef);
createOpenIDLoginFilter(sessionStrategy, authenticationManager,
authenticationFilterSecurityContextRepositoryRef);
createSaml2LoginFilter(authenticationManager, authenticationFilterSecurityContextRepositoryRef);
createFormLoginFilter(sessionStrategy, authenticationManager);
createOAuth2ClientFilters(sessionStrategy, requestCache, authenticationManager);
createOpenIDLoginFilter(sessionStrategy, authenticationManager);
createX509Filter(authenticationManager);
createJeeFilter(authenticationManager);
createLogoutFilter();
createSaml2LogoutFilter();
createLoginPageFilterIfNeeded();
createUserDetailsServiceFactory();
createExceptionTranslationFilter();
@@ -292,8 +272,7 @@ final class AuthenticationConfigBuilder {
this.rememberMeProviderRef = new RuntimeBeanReference(id);
}
void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager,
BeanReference authenticationFilterSecurityContextRepositoryRef) {
void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
Element formLoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.FORM_LOGIN);
RootBeanDefinition formFilter = null;
if (formLoginElt != null || this.autoConfig) {
@@ -309,10 +288,6 @@ final class AuthenticationConfigBuilder {
if (formFilter != null) {
formFilter.getPropertyValues().addPropertyValue("allowSessionCreation", this.allowSessionCreation);
formFilter.getPropertyValues().addPropertyValue("authenticationManager", authManager);
if (authenticationFilterSecurityContextRepositoryRef != null) {
formFilter.getPropertyValues().addPropertyValue("securityContextRepository",
authenticationFilterSecurityContextRepositoryRef);
}
// Id is required by login page filter
this.formFilterId = this.pc.getReaderContext().generateBeanName(formFilter);
this.pc.registerBeanComponent(new BeanComponentDefinition(formFilter, this.formFilterId));
@@ -321,15 +296,13 @@ final class AuthenticationConfigBuilder {
}
void createOAuth2ClientFilters(BeanReference sessionStrategy, BeanReference requestCache,
BeanReference authenticationManager, BeanReference authenticationFilterSecurityContextRepositoryRef) {
createOAuth2LoginFilter(sessionStrategy, authenticationManager,
authenticationFilterSecurityContextRepositoryRef);
createOAuth2ClientFilter(requestCache, authenticationManager, authenticationFilterSecurityContextRepositoryRef);
BeanReference authenticationManager) {
createOAuth2LoginFilter(sessionStrategy, authenticationManager);
createOAuth2ClientFilter(requestCache, authenticationManager);
registerOAuth2ClientPostProcessors();
}
void createOAuth2LoginFilter(BeanReference sessionStrategy, BeanReference authManager,
BeanReference authenticationFilterSecurityContextRepositoryRef) {
void createOAuth2LoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
Element oauth2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_LOGIN);
if (oauth2LoginElt == null) {
return;
@@ -341,10 +314,6 @@ final class AuthenticationConfigBuilder {
BeanDefinition defaultAuthorizedClientRepository = parser.getDefaultAuthorizedClientRepository();
registerDefaultAuthorizedClientRepositoryIfNecessary(defaultAuthorizedClientRepository);
oauth2LoginFilterBean.getPropertyValues().addPropertyValue("authenticationManager", authManager);
if (authenticationFilterSecurityContextRepositoryRef != null) {
oauth2LoginFilterBean.getPropertyValues().addPropertyValue("securityContextRepository",
authenticationFilterSecurityContextRepositoryRef);
}
// retrieve the other bean result
BeanDefinition oauth2LoginAuthProvider = parser.getOAuth2LoginAuthenticationProvider();
@@ -374,15 +343,14 @@ final class AuthenticationConfigBuilder {
this.oauth2LoginOidcAuthenticationProviderRef = new RuntimeBeanReference(oauth2LoginOidcAuthProviderId);
}
void createOAuth2ClientFilter(BeanReference requestCache, BeanReference authenticationManager,
BeanReference authenticationFilterSecurityContextRepositoryRef) {
void createOAuth2ClientFilter(BeanReference requestCache, BeanReference authenticationManager) {
Element oauth2ClientElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_CLIENT);
if (oauth2ClientElt == null) {
return;
}
this.oauth2ClientEnabled = true;
OAuth2ClientBeanDefinitionParser parser = new OAuth2ClientBeanDefinitionParser(requestCache,
authenticationManager, authenticationFilterSecurityContextRepositoryRef);
authenticationManager);
parser.parse(oauth2ClientElt, this.pc);
BeanDefinition defaultAuthorizedClientRepository = parser.getDefaultAuthorizedClientRepository();
registerDefaultAuthorizedClientRepositoryIfNecessary(defaultAuthorizedClientRepository);
@@ -427,8 +395,7 @@ final class AuthenticationConfigBuilder {
}
}
void createOpenIDLoginFilter(BeanReference sessionStrategy, BeanReference authManager,
BeanReference authenticationFilterSecurityContextRepositoryRef) {
void createOpenIDLoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
Element openIDLoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OPENID_LOGIN);
RootBeanDefinition openIDFilter = null;
if (openIDLoginElt != null) {
@@ -437,10 +404,6 @@ final class AuthenticationConfigBuilder {
if (openIDFilter != null) {
openIDFilter.getPropertyValues().addPropertyValue("allowSessionCreation", this.allowSessionCreation);
openIDFilter.getPropertyValues().addPropertyValue("authenticationManager", authManager);
if (authenticationFilterSecurityContextRepositoryRef != null) {
openIDFilter.getPropertyValues().addPropertyValue("securityContextRepository",
authenticationFilterSecurityContextRepositoryRef);
}
// Required by login page filter
this.openIDFilterId = this.pc.getReaderContext().generateBeanName(openIDFilter);
this.pc.registerBeanComponent(new BeanComponentDefinition(openIDFilter, this.openIDFilterId));
@@ -449,31 +412,6 @@ final class AuthenticationConfigBuilder {
}
}
private void createSaml2LoginFilter(BeanReference authenticationManager,
BeanReference authenticationFilterSecurityContextRepositoryRef) {
Element saml2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.SAML2_LOGIN);
if (saml2LoginElt == null) {
return;
}
Saml2LoginBeanDefinitionParser parser = new Saml2LoginBeanDefinitionParser(this.csrfIgnoreRequestMatchers,
this.portMapper, this.portResolver, this.requestCache, this.allowSessionCreation, authenticationManager,
authenticationFilterSecurityContextRepositoryRef, this.authenticationProviders,
this.defaultEntryPointMappings);
BeanDefinition saml2WebSsoAuthenticationFilter = parser.parse(saml2LoginElt, this.pc);
this.saml2AuthorizationRequestFilter = parser.getSaml2WebSsoAuthenticationRequestFilter();
this.saml2AuthenticationFilterId = this.pc.getReaderContext().generateBeanName(saml2WebSsoAuthenticationFilter);
this.saml2AuthenticationRequestFilterId = this.pc.getReaderContext()
.generateBeanName(this.saml2AuthorizationRequestFilter);
this.saml2AuthenticationUrlToProviderName = parser.getSaml2AuthenticationUrlToProviderName();
// register the component
this.pc.registerBeanComponent(
new BeanComponentDefinition(saml2WebSsoAuthenticationFilter, this.saml2AuthenticationFilterId));
this.pc.registerBeanComponent(new BeanComponentDefinition(this.saml2AuthorizationRequestFilter,
this.saml2AuthenticationRequestFilterId));
}
/**
* Parses OpenID 1.0 and 2.0 - related parts of configuration xmls
* @param sessionStrategy sessionStrategy
@@ -728,12 +666,6 @@ final class AuthenticationConfigBuilder {
loginPageFilter.addPropertyValue("Oauth2LoginEnabled", true);
loginPageFilter.addPropertyValue("Oauth2AuthenticationUrlToClientName", this.oauth2LoginLinks);
}
if (this.saml2AuthenticationFilterId != null) {
loginPageFilter.addConstructorArgReference(this.saml2AuthenticationFilterId);
loginPageFilter.addPropertyValue("saml2LoginEnabled", true);
loginPageFilter.addPropertyValue("saml2AuthenticationUrlToProviderName",
this.saml2AuthenticationUrlToProviderName);
}
this.loginPageGenerationFilter = loginPageFilter.getBeanDefinition();
this.logoutPageGenerationFilter = logoutPageFilter.getBeanDefinition();
}
@@ -750,33 +682,9 @@ final class AuthenticationConfigBuilder {
this.rememberMeServicesId, this.csrfLogoutHandler);
this.logoutFilter = logoutParser.parse(logoutElt, this.pc);
this.logoutHandlers = logoutParser.getLogoutHandlers();
this.logoutSuccessHandler = logoutParser.getLogoutSuccessHandler();
}
}
private void createSaml2LogoutFilter() {
Element saml2LogoutElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.SAML2_LOGOUT);
if (saml2LogoutElt == null) {
return;
}
Saml2LogoutBeanDefinitionParser parser = new Saml2LogoutBeanDefinitionParser(this.logoutHandlers,
this.logoutSuccessHandler);
parser.parse(saml2LogoutElt, this.pc);
BeanDefinition saml2LogoutFilter = parser.getLogoutFilter();
BeanDefinition saml2LogoutRequestFilter = parser.getLogoutRequestFilter();
BeanDefinition saml2LogoutResponseFilter = parser.getLogoutResponseFilter();
this.saml2LogoutFilterId = this.pc.getReaderContext().generateBeanName(saml2LogoutFilter);
this.saml2LogoutRequestFilterId = this.pc.getReaderContext().generateBeanName(saml2LogoutRequestFilter);
this.saml2LogoutResponseFilterId = this.pc.getReaderContext().generateBeanName(saml2LogoutResponseFilter);
// register the component
this.pc.registerBeanComponent(new BeanComponentDefinition(saml2LogoutFilter, this.saml2LogoutFilterId));
this.pc.registerBeanComponent(
new BeanComponentDefinition(saml2LogoutRequestFilter, this.saml2LogoutRequestFilterId));
this.pc.registerBeanComponent(
new BeanComponentDefinition(saml2LogoutResponseFilter, this.saml2LogoutResponseFilterId));
}
@SuppressWarnings({ "rawtypes", "unchecked" })
ManagedList getLogoutHandlers() {
if (this.logoutHandlers == null && this.rememberMeProviderRef != null) {
@@ -932,8 +840,7 @@ final class AuthenticationConfigBuilder {
if (formLoginElt != null && this.oauth2LoginEntryPoint != null) {
return this.formEntryPoint;
}
// If form login was enabled through auto-config, and Oauth2 login & Saml2
// login was not
// If form login was enabled through auto-config, and Oauth2 login was not
// enabled then use form login
if (this.oauth2LoginEntryPoint == null) {
return this.formEntryPoint;
@@ -1016,20 +923,6 @@ final class AuthenticationConfigBuilder {
filters.add(new OrderDecorator(this.authorizationCodeGrantFilter,
SecurityFilters.OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER));
}
if (this.saml2AuthenticationFilterId != null) {
filters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2AuthenticationFilterId),
SecurityFilters.SAML2_AUTHENTICATION_FILTER));
filters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2AuthenticationRequestFilterId),
SecurityFilters.SAML2_AUTHENTICATION_REQUEST_FILTER));
}
if (this.saml2LogoutFilterId != null) {
filters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2LogoutFilterId),
SecurityFilters.SAML2_LOGOUT_FILTER));
filters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2LogoutRequestFilterId),
SecurityFilters.SAML2_LOGOUT_REQUEST_FILTER));
filters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2LogoutResponseFilterId),
SecurityFilters.SAML2_LOGOUT_RESPONSE_FILTER));
}
filters.add(new OrderDecorator(this.etf, SecurityFilters.EXCEPTION_TRANSLATION_FILTER));
return filters;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,9 +36,6 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;
import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;
import org.springframework.security.web.header.writers.HpkpHeaderWriter;
import org.springframework.security.web.header.writers.HstsHeaderWriter;
@@ -125,12 +122,6 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
private static final String PERMISSIONS_POLICY_ELEMENT = "permissions-policy";
private static final String CROSS_ORIGIN_OPENER_POLICY_ELEMENT = "cross-origin-opener-policy";
private static final String CROSS_ORIGIN_EMBEDDER_POLICY_ELEMENT = "cross-origin-embedder-policy";
private static final String CROSS_ORIGIN_RESOURCE_POLICY_ELEMENT = "cross-origin-resource-policy";
private static final String ALLOW_FROM = "ALLOW-FROM";
private ManagedList<BeanMetadataElement> headerWriters;
@@ -153,9 +144,6 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
parseReferrerPolicyElement(element, parserContext);
parseFeaturePolicyElement(element, parserContext);
parsePermissionsPolicyElement(element, parserContext);
parseCrossOriginOpenerPolicy(disabled, element);
parseCrossOriginEmbedderPolicy(disabled, element);
parseCrossOriginResourcePolicy(disabled, element);
parseHeaderElements(element);
boolean noWriters = this.headerWriters.isEmpty();
if (disabled && !noWriters) {
@@ -388,75 +376,6 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
this.headerWriters.add(headersWriter.getBeanDefinition());
}
private void parseCrossOriginOpenerPolicy(boolean elementDisabled, Element element) {
if (elementDisabled || element == null) {
return;
}
CrossOriginOpenerPolicyHeaderWriter writer = new CrossOriginOpenerPolicyHeaderWriter();
Element crossOriginOpenerPolicyElement = DomUtils.getChildElementByTagName(element,
CROSS_ORIGIN_OPENER_POLICY_ELEMENT);
if (crossOriginOpenerPolicyElement != null) {
addCrossOriginOpenerPolicy(crossOriginOpenerPolicyElement, writer);
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(CrossOriginOpenerPolicyHeaderWriter.class, () -> writer);
this.headerWriters.add(builder.getBeanDefinition());
}
private void parseCrossOriginEmbedderPolicy(boolean elementDisabled, Element element) {
if (elementDisabled || element == null) {
return;
}
CrossOriginEmbedderPolicyHeaderWriter writer = new CrossOriginEmbedderPolicyHeaderWriter();
Element crossOriginEmbedderPolicyElement = DomUtils.getChildElementByTagName(element,
CROSS_ORIGIN_EMBEDDER_POLICY_ELEMENT);
if (crossOriginEmbedderPolicyElement != null) {
addCrossOriginEmbedderPolicy(crossOriginEmbedderPolicyElement, writer);
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(CrossOriginEmbedderPolicyHeaderWriter.class, () -> writer);
this.headerWriters.add(builder.getBeanDefinition());
}
private void parseCrossOriginResourcePolicy(boolean elementDisabled, Element element) {
if (elementDisabled || element == null) {
return;
}
CrossOriginResourcePolicyHeaderWriter writer = new CrossOriginResourcePolicyHeaderWriter();
Element crossOriginResourcePolicyElement = DomUtils.getChildElementByTagName(element,
CROSS_ORIGIN_RESOURCE_POLICY_ELEMENT);
if (crossOriginResourcePolicyElement != null) {
addCrossOriginResourcePolicy(crossOriginResourcePolicyElement, writer);
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(CrossOriginResourcePolicyHeaderWriter.class, () -> writer);
this.headerWriters.add(builder.getBeanDefinition());
}
private void addCrossOriginResourcePolicy(Element crossOriginResourcePolicyElement,
CrossOriginResourcePolicyHeaderWriter writer) {
String policy = crossOriginResourcePolicyElement.getAttribute(ATT_POLICY);
if (StringUtils.hasText(policy)) {
writer.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.from(policy));
}
}
private void addCrossOriginEmbedderPolicy(Element crossOriginEmbedderPolicyElement,
CrossOriginEmbedderPolicyHeaderWriter writer) {
String policy = crossOriginEmbedderPolicyElement.getAttribute(ATT_POLICY);
if (StringUtils.hasText(policy)) {
writer.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.from(policy));
}
}
private void addCrossOriginOpenerPolicy(Element crossOriginOpenerPolicyElement,
CrossOriginOpenerPolicyHeaderWriter writer) {
String policy = crossOriginOpenerPolicyElement.getAttribute(ATT_POLICY);
if (StringUtils.hasText(policy)) {
writer.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.from(policy));
}
}
private void attrNotAllowed(ParserContext context, String attrName, String otherAttrName, Element element) {
context.getReaderContext().error("Only one of '" + attrName + "' or '" + otherAttrName + "' can be set.",
element);
@@ -59,7 +59,6 @@ import org.springframework.security.web.authentication.session.RegisterSessionAu
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.NullSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextHolderFilter;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
@@ -68,8 +67,6 @@ import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.DisableEncodeUrlFilter;
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
@@ -107,8 +104,6 @@ class HttpConfigurationBuilder {
private static final String ATT_SECURITY_CONTEXT_REPOSITORY = "security-context-repository-ref";
private static final String ATT_SECURITY_CONTEXT_EXPLICIT_SAVE = "security-context-explicit-save";
private static final String ATT_INVALID_SESSION_STRATEGY_REF = "invalid-session-strategy-ref";
private static final String ATT_DISABLE_URL_REWRITING = "disable-url-rewriting";
@@ -149,8 +144,6 @@ class HttpConfigurationBuilder {
private BeanDefinition securityContextPersistenceFilter;
private BeanDefinition forceEagerSessionCreationFilter;
private BeanReference contextRepoRef;
private BeanReference sessionRegistryRef;
@@ -183,8 +176,6 @@ class HttpConfigurationBuilder {
private BeanDefinition csrfFilter;
private BeanDefinition disableUrlRewriteFilter;
private BeanDefinition wellKnownChangePasswordRedirectFilter;
private BeanMetadataElement csrfLogoutHandler;
@@ -210,10 +201,8 @@ class HttpConfigurationBuilder {
String createSession = element.getAttribute(ATT_CREATE_SESSION);
this.sessionPolicy = !StringUtils.hasText(createSession) ? SessionCreationPolicy.IF_REQUIRED
: createPolicy(createSession);
createForceEagerSessionCreationFilter();
createDisableEncodeUrlFilter();
createCsrfFilter();
createSecurityPersistence();
createSecurityContextPersistenceFilter();
createSessionManagementFilters();
createWebAsyncManagerFilter();
createRequestCacheFilter();
@@ -289,51 +278,19 @@ class HttpConfigurationBuilder {
return lowerCase ? path.toLowerCase() : path;
}
BeanReference getSecurityContextRepositoryForAuthenticationFilters() {
return (isExplicitSave()) ? this.contextRepoRef : null;
}
private void createSecurityPersistence() {
createSecurityContextRepository();
if (isExplicitSave()) {
createSecurityContextHolderFilter();
}
else {
createSecurityContextPersistenceFilter();
}
}
private boolean isExplicitSave() {
String explicitSaveAttr = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_EXPLICIT_SAVE);
return Boolean.parseBoolean(explicitSaveAttr);
}
private void createForceEagerSessionCreationFilter() {
if (this.sessionPolicy == SessionCreationPolicy.ALWAYS) {
this.forceEagerSessionCreationFilter = new RootBeanDefinition(ForceEagerSessionCreationFilter.class);
}
}
private void createSecurityContextPersistenceFilter() {
BeanDefinitionBuilder scpf = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextPersistenceFilter.class);
switch (this.sessionPolicy) {
case ALWAYS:
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
break;
case NEVER:
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
break;
default:
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
}
scpf.addConstructorArgValue(this.contextRepoRef);
this.securityContextPersistenceFilter = scpf.getBeanDefinition();
}
private void createSecurityContextRepository() {
String repoRef = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_REPOSITORY);
if (!StringUtils.hasText(repoRef)) {
String disableUrlRewriting = this.httpElt.getAttribute(ATT_DISABLE_URL_REWRITING);
if (!StringUtils.hasText(disableUrlRewriting)) {
disableUrlRewriting = "true";
}
if (StringUtils.hasText(repoRef)) {
if (this.sessionPolicy == SessionCreationPolicy.ALWAYS) {
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
}
}
else {
BeanDefinitionBuilder contextRepo;
if (this.sessionPolicy == SessionCreationPolicy.STATELESS) {
contextRepo = BeanDefinitionBuilder.rootBeanDefinition(NullSecurityContextRepository.class);
@@ -343,14 +300,17 @@ class HttpConfigurationBuilder {
switch (this.sessionPolicy) {
case ALWAYS:
contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE);
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
break;
case NEVER:
contextRepo.addPropertyValue("allowSessionCreation", Boolean.FALSE);
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
break;
default:
contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE);
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
}
if (isDisableUrlRewriting()) {
if ("true".equals(disableUrlRewriting)) {
contextRepo.addPropertyValue("disableUrlRewriting", Boolean.TRUE);
}
}
@@ -360,17 +320,9 @@ class HttpConfigurationBuilder {
}
this.contextRepoRef = new RuntimeBeanReference(repoRef);
}
scpf.addConstructorArgValue(this.contextRepoRef);
private boolean isDisableUrlRewriting() {
String disableUrlRewriting = this.httpElt.getAttribute(ATT_DISABLE_URL_REWRITING);
return !"false".equals(disableUrlRewriting);
}
private void createSecurityContextHolderFilter() {
BeanDefinitionBuilder filter = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextHolderFilter.class);
filter.addConstructorArgValue(this.contextRepoRef);
this.securityContextPersistenceFilter = filter.getBeanDefinition();
this.securityContextPersistenceFilter = scpf.getBeanDefinition();
}
private void createSessionManagementFilters() {
@@ -733,12 +685,6 @@ class HttpConfigurationBuilder {
}
private void createDisableEncodeUrlFilter() {
if (isDisableUrlRewriting()) {
this.disableUrlRewriteFilter = new RootBeanDefinition(DisableEncodeUrlFilter.class);
}
}
private void createCsrfFilter() {
Element elmt = DomUtils.getChildElementByTagName(this.httpElt, Elements.CSRF);
this.csrfParser = new CsrfBeanDefinitionParser();
@@ -778,13 +724,6 @@ class HttpConfigurationBuilder {
List<OrderDecorator> getFilters() {
List<OrderDecorator> filters = new ArrayList<>();
if (this.forceEagerSessionCreationFilter != null) {
filters.add(new OrderDecorator(this.forceEagerSessionCreationFilter,
SecurityFilters.FORCE_EAGER_SESSION_FILTER));
}
if (this.disableUrlRewriteFilter != null) {
filters.add(new OrderDecorator(this.disableUrlRewriteFilter, SecurityFilters.DISABLE_ENCODE_URL_FILTER));
}
if (this.cpf != null) {
filters.add(new OrderDecorator(this.cpf, SecurityFilters.CHANNEL_FILTER));
}
@@ -144,11 +144,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
boolean forceAutoConfig = isDefaultHttpConfig(element);
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper,
portResolver, authenticationManager);
httpBldr.getSecurityContextRepositoryForAuthenticationFilters();
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc,
httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager,
httpBldr.getSecurityContextRepositoryForAuthenticationFilters(), httpBldr.getSessionStrategy(),
portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
httpBldr.setEntryPoint(authBldr.getEntryPointBean());
httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
@@ -59,8 +59,6 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
private boolean csrfEnabled;
private BeanMetadataElement logoutSuccessHandler;
LogoutBeanDefinitionParser(String loginPageUrl, String rememberMeServices, BeanMetadataElement csrfLogoutHandler) {
this.defaultLogoutUrl = loginPageUrl + "?logout";
this.rememberMeServices = rememberMeServices;
@@ -100,7 +98,6 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
pc.extractSource(element));
}
builder.addConstructorArgReference(successHandlerRef);
this.logoutSuccessHandler = new RuntimeBeanReference(successHandlerRef);
}
else {
// Use the logout URL if no handler set
@@ -140,8 +137,4 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
return this.logoutHandlers;
}
BeanMetadataElement getLogoutSuccessHandler() {
return this.logoutSuccessHandler;
}
}
@@ -50,8 +50,6 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
private final BeanReference authenticationManager;
private final BeanReference authenticationFilterSecurityContextRepositoryRef;
private BeanDefinition defaultAuthorizedClientRepository;
private BeanDefinition authorizationRequestRedirectFilter;
@@ -60,11 +58,9 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
private BeanDefinition authorizationCodeAuthenticationProvider;
OAuth2ClientBeanDefinitionParser(BeanReference requestCache, BeanReference authenticationManager,
BeanReference authenticationFilterSecurityContextRepositoryRef) {
OAuth2ClientBeanDefinitionParser(BeanReference requestCache, BeanReference authenticationManager) {
this.requestCache = requestCache;
this.authenticationManager = authenticationManager;
this.authenticationFilterSecurityContextRepositoryRef = authenticationFilterSecurityContextRepositoryRef;
}
@Override
@@ -96,16 +92,11 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
this.authorizationRequestRedirectFilter = authorizationRequestRedirectFilterBuilder
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository)
.addPropertyValue("requestCache", this.requestCache).getBeanDefinition();
BeanDefinitionBuilder authorizationCodeGrantFilterBldr = BeanDefinitionBuilder
this.authorizationCodeGrantFilter = BeanDefinitionBuilder
.rootBeanDefinition(OAuth2AuthorizationCodeGrantFilter.class)
.addConstructorArgValue(clientRegistrationRepository).addConstructorArgValue(authorizedClientRepository)
.addConstructorArgValue(this.authenticationManager)
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository);
if (this.authenticationFilterSecurityContextRepositoryRef != null) {
authorizationCodeGrantFilterBldr.addPropertyValue("securityContextRepository",
this.authenticationFilterSecurityContextRepositoryRef);
}
this.authorizationCodeGrantFilter = authorizationCodeGrantFilterBldr.getBeanDefinition();
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository).getBeanDefinition();
BeanMetadataElement accessTokenResponseClient = getAccessTokenResponseClient(authorizationCodeGrantElt);
this.authorizationCodeAuthenticationProvider = BeanDefinitionBuilder
@@ -1,323 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.http;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.ResolvableType;
import org.springframework.security.config.Elements;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
/**
* SAML 2.0 Login {@link BeanDefinitionParser}
*
* @author Marcus da Coregio
* @since 5.7
*/
final class Saml2LoginBeanDefinitionParser implements BeanDefinitionParser {
private static final String DEFAULT_LOGIN_URI = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL;
private static final String DEFAULT_AUTHENTICATION_REQUEST_PROCESSING_URL = "/saml2/authenticate/{registrationId}";
private static final String ATT_LOGIN_PROCESSING_URL = "login-processing-url";
private static final String ATT_LOGIN_PAGE = "login-page";
private static final String ELT_RELYING_PARTY_REGISTRATION = "relying-party-registration";
private static final String ELT_REGISTRATION_ID = "registration-id";
private static final String ATT_AUTHENTICATION_FAILURE_HANDLER_REF = "authentication-failure-handler-ref";
private static final String ATT_AUTHENTICATION_SUCCESS_HANDLER_REF = "authentication-success-handler-ref";
private static final String ATT_AUTHENTICATION_MANAGER_REF = "authentication-manager-ref";
private final List<BeanDefinition> csrfIgnoreRequestMatchers;
private final BeanReference portMapper;
private final BeanReference portResolver;
private final BeanReference requestCache;
private final boolean allowSessionCreation;
private final BeanReference authenticationManager;
private final BeanReference authenticationFilterSecurityContextRepositoryRef;
private final List<BeanReference> authenticationProviders;
private final Map<BeanDefinition, BeanMetadataElement> entryPoints;
private String loginProcessingUrl = Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
private BeanDefinition saml2WebSsoAuthenticationRequestFilter;
private BeanDefinition saml2AuthenticationUrlToProviderName;
Saml2LoginBeanDefinitionParser(List<BeanDefinition> csrfIgnoreRequestMatchers, BeanReference portMapper,
BeanReference portResolver, BeanReference requestCache, boolean allowSessionCreation,
BeanReference authenticationManager, BeanReference authenticationFilterSecurityContextRepositoryRef,
List<BeanReference> authenticationProviders, Map<BeanDefinition, BeanMetadataElement> entryPoints) {
this.csrfIgnoreRequestMatchers = csrfIgnoreRequestMatchers;
this.portMapper = portMapper;
this.portResolver = portResolver;
this.requestCache = requestCache;
this.allowSessionCreation = allowSessionCreation;
this.authenticationManager = authenticationManager;
this.authenticationFilterSecurityContextRepositoryRef = authenticationFilterSecurityContextRepositoryRef;
this.authenticationProviders = authenticationProviders;
this.entryPoints = entryPoints;
}
@Override
public BeanDefinition parse(Element element, ParserContext pc) {
String loginProcessingUrl = element.getAttribute(ATT_LOGIN_PROCESSING_URL);
if (StringUtils.hasText(loginProcessingUrl)) {
this.loginProcessingUrl = loginProcessingUrl;
}
BeanDefinition saml2LoginBeanConfig = BeanDefinitionBuilder.rootBeanDefinition(Saml2LoginBeanConfig.class)
.getBeanDefinition();
String saml2LoginBeanConfigId = pc.getReaderContext().generateBeanName(saml2LoginBeanConfig);
pc.registerBeanComponent(new BeanComponentDefinition(saml2LoginBeanConfig, saml2LoginBeanConfigId));
registerDefaultCsrfOverride();
BeanMetadataElement relyingPartyRegistrationRepository = Saml2LoginBeanDefinitionParserUtils
.getRelyingPartyRegistrationRepository(element);
BeanMetadataElement authenticationRequestRepository = Saml2LoginBeanDefinitionParserUtils
.getAuthenticationRequestRepository(element);
BeanMetadataElement authenticationRequestResolver = Saml2LoginBeanDefinitionParserUtils
.getAuthenticationRequestResolver(element);
if (authenticationRequestResolver == null) {
authenticationRequestResolver = Saml2LoginBeanDefinitionParserUtils
.createDefaultAuthenticationRequestResolver(relyingPartyRegistrationRepository);
}
BeanMetadataElement authenticationConverter = Saml2LoginBeanDefinitionParserUtils
.getAuthenticationConverter(element);
if (authenticationConverter == null) {
if (!this.loginProcessingUrl.contains("{registrationId}")) {
pc.getReaderContext().error("loginProcessingUrl must contain {registrationId} path variable", element);
}
authenticationConverter = Saml2LoginBeanDefinitionParserUtils
.createDefaultAuthenticationConverter(relyingPartyRegistrationRepository);
}
// Configure the Saml2WebSsoAuthenticationFilter
BeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder = BeanDefinitionBuilder
.rootBeanDefinition(Saml2WebSsoAuthenticationFilter.class)
.addConstructorArgValue(authenticationConverter).addConstructorArgValue(this.loginProcessingUrl)
.addPropertyValue("authenticationRequestRepository", authenticationRequestRepository);
resolveLoginPage(element, pc);
resolveAuthenticationSuccessHandler(element, saml2WebSsoAuthenticationFilterBuilder);
resolveAuthenticationFailureHandler(element, saml2WebSsoAuthenticationFilterBuilder);
resolveAuthenticationManager(element, saml2WebSsoAuthenticationFilterBuilder);
resolveSecurityContextRepository(element, saml2WebSsoAuthenticationFilterBuilder);
// Configure the Saml2WebSsoAuthenticationRequestFilter
this.saml2WebSsoAuthenticationRequestFilter = BeanDefinitionBuilder
.rootBeanDefinition(Saml2WebSsoAuthenticationRequestFilter.class)
.addConstructorArgValue(authenticationRequestResolver)
.addPropertyValue("authenticationRequestRepository", authenticationRequestRepository)
.getBeanDefinition();
BeanDefinition saml2AuthenticationProvider = Saml2LoginBeanDefinitionParserUtils.createAuthenticationProvider();
this.authenticationProviders.add(
new RuntimeBeanReference(pc.getReaderContext().registerWithGeneratedName(saml2AuthenticationProvider)));
this.saml2AuthenticationUrlToProviderName = BeanDefinitionBuilder.rootBeanDefinition(Map.class)
.setFactoryMethodOnBean("getAuthenticationUrlToProviderName", saml2LoginBeanConfigId)
.getBeanDefinition();
return saml2WebSsoAuthenticationFilterBuilder.getBeanDefinition();
}
private void resolveAuthenticationManager(Element element,
BeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder) {
String authenticationManagerRef = element.getAttribute(ATT_AUTHENTICATION_MANAGER_REF);
if (StringUtils.hasText(authenticationManagerRef)) {
saml2WebSsoAuthenticationFilterBuilder.addPropertyReference("authenticationManager",
authenticationManagerRef);
}
else {
saml2WebSsoAuthenticationFilterBuilder.addPropertyValue("authenticationManager",
this.authenticationManager);
}
}
private void resolveSecurityContextRepository(Element element,
BeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder) {
if (this.authenticationFilterSecurityContextRepositoryRef != null) {
saml2WebSsoAuthenticationFilterBuilder.addPropertyValue("securityContextRepository",
this.authenticationFilterSecurityContextRepositoryRef);
}
}
private void resolveLoginPage(Element element, ParserContext parserContext) {
String loginPage = element.getAttribute(ATT_LOGIN_PAGE);
Object source = parserContext.extractSource(element);
BeanDefinition saml2LoginAuthenticationEntryPoint = null;
if (StringUtils.hasText(loginPage)) {
WebConfigUtils.validateHttpRedirect(loginPage, parserContext, source);
saml2LoginAuthenticationEntryPoint = BeanDefinitionBuilder
.rootBeanDefinition(LoginUrlAuthenticationEntryPoint.class).addConstructorArgValue(loginPage)
.addPropertyValue("portMapper", this.portMapper).addPropertyValue("portResolver", this.portResolver)
.getBeanDefinition();
}
else {
Map<String, String> identityProviderUrlMap = getIdentityProviderUrlMap(element);
if (identityProviderUrlMap.size() == 1) {
String loginUrl = identityProviderUrlMap.entrySet().iterator().next().getKey();
saml2LoginAuthenticationEntryPoint = BeanDefinitionBuilder
.rootBeanDefinition(LoginUrlAuthenticationEntryPoint.class).addConstructorArgValue(loginUrl)
.addPropertyValue("portMapper", this.portMapper)
.addPropertyValue("portResolver", this.portResolver).getBeanDefinition();
}
}
if (saml2LoginAuthenticationEntryPoint != null) {
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(AntPathRequestMatcher.class);
requestMatcherBuilder.addConstructorArgValue(this.loginProcessingUrl);
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
this.entryPoints.put(requestMatcher, saml2LoginAuthenticationEntryPoint);
}
}
private void resolveAuthenticationFailureHandler(Element element,
BeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder) {
String authenticationFailureHandlerRef = element.getAttribute(ATT_AUTHENTICATION_FAILURE_HANDLER_REF);
if (StringUtils.hasText(authenticationFailureHandlerRef)) {
saml2WebSsoAuthenticationFilterBuilder.addPropertyReference("authenticationFailureHandler",
authenticationFailureHandlerRef);
}
else {
BeanDefinitionBuilder failureHandlerBuilder = BeanDefinitionBuilder.rootBeanDefinition(
"org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler");
failureHandlerBuilder.addConstructorArgValue(
DEFAULT_LOGIN_URI + "?" + DefaultLoginPageGeneratingFilter.ERROR_PARAMETER_NAME);
failureHandlerBuilder.addPropertyValue("allowSessionCreation", this.allowSessionCreation);
saml2WebSsoAuthenticationFilterBuilder.addPropertyValue("authenticationFailureHandler",
failureHandlerBuilder.getBeanDefinition());
}
}
private void resolveAuthenticationSuccessHandler(Element element,
BeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder) {
String authenticationSuccessHandlerRef = element.getAttribute(ATT_AUTHENTICATION_SUCCESS_HANDLER_REF);
if (StringUtils.hasText(authenticationSuccessHandlerRef)) {
saml2WebSsoAuthenticationFilterBuilder.addPropertyReference("authenticationSuccessHandler",
authenticationSuccessHandlerRef);
}
else {
BeanDefinitionBuilder successHandlerBuilder = BeanDefinitionBuilder.rootBeanDefinition(
"org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler")
.addPropertyValue("requestCache", this.requestCache);
saml2WebSsoAuthenticationFilterBuilder.addPropertyValue("authenticationSuccessHandler",
successHandlerBuilder.getBeanDefinition());
}
}
private void registerDefaultCsrfOverride() {
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(AntPathRequestMatcher.class);
requestMatcherBuilder.addConstructorArgValue(this.loginProcessingUrl);
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
this.csrfIgnoreRequestMatchers.add(requestMatcher);
}
private Map<String, String> getIdentityProviderUrlMap(Element element) {
Map<String, String> idps = new LinkedHashMap<>();
Element relyingPartyRegistrationsElt = DomUtils.getChildElementByTagName(
element.getOwnerDocument().getDocumentElement(), Elements.RELYING_PARTY_REGISTRATIONS);
String authenticationRequestProcessingUrl = DEFAULT_AUTHENTICATION_REQUEST_PROCESSING_URL;
if (relyingPartyRegistrationsElt != null) {
List<Element> relyingPartyRegList = DomUtils.getChildElementsByTagName(relyingPartyRegistrationsElt,
ELT_RELYING_PARTY_REGISTRATION);
for (Element relyingPartyReg : relyingPartyRegList) {
String registrationId = relyingPartyReg.getAttribute(ELT_REGISTRATION_ID);
idps.put(authenticationRequestProcessingUrl.replace("{registrationId}", registrationId),
registrationId);
}
}
return idps;
}
BeanDefinition getSaml2WebSsoAuthenticationRequestFilter() {
return this.saml2WebSsoAuthenticationRequestFilter;
}
BeanDefinition getSaml2AuthenticationUrlToProviderName() {
return this.saml2AuthenticationUrlToProviderName;
}
/**
* Wrapper bean class to provide configuration from applicationContext
*/
public static class Saml2LoginBeanConfig implements ApplicationContextAware {
private ApplicationContext context;
@SuppressWarnings({ "unchecked", "unused" })
Map<String, String> getAuthenticationUrlToProviderName() {
Iterable<RelyingPartyRegistration> relyingPartyRegistrations = null;
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository = this.context
.getBean(RelyingPartyRegistrationRepository.class);
ResolvableType type = ResolvableType.forInstance(relyingPartyRegistrationRepository).as(Iterable.class);
if (type != ResolvableType.NONE
&& RelyingPartyRegistration.class.isAssignableFrom(type.resolveGenerics()[0])) {
relyingPartyRegistrations = (Iterable<RelyingPartyRegistration>) relyingPartyRegistrationRepository;
}
if (relyingPartyRegistrations == null) {
return Collections.emptyMap();
}
String authenticationRequestProcessingUrl = DEFAULT_AUTHENTICATION_REQUEST_PROCESSING_URL;
Map<String, String> saml2AuthenticationUrlToProviderName = new HashMap<>();
relyingPartyRegistrations.forEach((registration) -> saml2AuthenticationUrlToProviderName.put(
authenticationRequestProcessingUrl.replace("{registrationId}", registration.getRegistrationId()),
registration.getRegistrationId()));
return saml2AuthenticationUrlToProviderName;
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
}
}
@@ -1,107 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.http;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
import org.springframework.util.StringUtils;
/**
* @author Marcus da Coregio
* @since 5.7
*/
final class Saml2LoginBeanDefinitionParserUtils {
private static final String ATT_RELYING_PARTY_REGISTRATION_REPOSITORY_REF = "relying-party-registration-repository-ref";
private static final String ATT_AUTHENTICATION_REQUEST_REPOSITORY_REF = "authentication-request-repository-ref";
private static final String ATT_AUTHENTICATION_REQUEST_RESOLVER_REF = "authentication-request-resolver-ref";
private static final String ATT_AUTHENTICATION_CONVERTER = "authentication-converter-ref";
private Saml2LoginBeanDefinitionParserUtils() {
}
static BeanMetadataElement getRelyingPartyRegistrationRepository(Element element) {
String relyingPartyRegistrationRepositoryRef = element
.getAttribute(ATT_RELYING_PARTY_REGISTRATION_REPOSITORY_REF);
if (StringUtils.hasText(relyingPartyRegistrationRepositoryRef)) {
return new RuntimeBeanReference(relyingPartyRegistrationRepositoryRef);
}
return new RuntimeBeanReference(RelyingPartyRegistrationRepository.class);
}
static BeanMetadataElement getAuthenticationRequestRepository(Element element) {
String authenticationRequestRepositoryRef = element.getAttribute(ATT_AUTHENTICATION_REQUEST_REPOSITORY_REF);
if (StringUtils.hasText(authenticationRequestRepositoryRef)) {
return new RuntimeBeanReference(authenticationRequestRepositoryRef);
}
return BeanDefinitionBuilder.rootBeanDefinition(HttpSessionSaml2AuthenticationRequestRepository.class)
.getBeanDefinition();
}
static BeanMetadataElement getAuthenticationRequestResolver(Element element) {
String authenticationRequestContextResolver = element.getAttribute(ATT_AUTHENTICATION_REQUEST_RESOLVER_REF);
if (StringUtils.hasText(authenticationRequestContextResolver)) {
return new RuntimeBeanReference(authenticationRequestContextResolver);
}
return null;
}
static BeanMetadataElement createDefaultAuthenticationRequestResolver(
BeanMetadataElement relyingPartyRegistrationRepository) {
BeanMetadataElement defaultRelyingPartyRegistrationResolver = BeanDefinitionBuilder
.rootBeanDefinition(DefaultRelyingPartyRegistrationResolver.class)
.addConstructorArgValue(relyingPartyRegistrationRepository).getBeanDefinition();
return BeanDefinitionBuilder.rootBeanDefinition(
"org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver")
.addConstructorArgValue(defaultRelyingPartyRegistrationResolver).getBeanDefinition();
}
static BeanDefinition createAuthenticationProvider() {
return BeanDefinitionBuilder.rootBeanDefinition(
"org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider")
.getBeanDefinition();
}
static BeanMetadataElement getAuthenticationConverter(Element element) {
String authenticationConverter = element.getAttribute(ATT_AUTHENTICATION_CONVERTER);
if (StringUtils.hasText(authenticationConverter)) {
return new RuntimeBeanReference(authenticationConverter);
}
return null;
}
static BeanDefinition createDefaultAuthenticationConverter(BeanMetadataElement relyingPartyRegistrationRepository) {
AbstractBeanDefinition resolver = BeanDefinitionBuilder
.rootBeanDefinition(DefaultRelyingPartyRegistrationResolver.class)
.addConstructorArgValue(relyingPartyRegistrationRepository).getBeanDefinition();
return BeanDefinitionBuilder.rootBeanDefinition(Saml2AuthenticationTokenConverter.class)
.addConstructorArgValue(resolver).getBeanDefinition();
}
}
@@ -1,236 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.http;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import javax.servlet.http.HttpServletRequest;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2RelyingPartyInitiatedLogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessEventPublishingLogoutHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* SAML 2.0 Single Logout {@link BeanDefinitionParser}
*
* @author Marcus da Coregio
* @since 5.7
*/
final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
private static final String ATT_LOGOUT_REQUEST_URL = "logout-request-url";
private static final String ATT_LOGOUT_RESPONSE_URL = "logout-response-url";
private static final String ATT_LOGOUT_URL = "logout-url";
private List<BeanMetadataElement> logoutHandlers;
private String logoutUrl = "/logout";
private String logoutRequestUrl = "/logout/saml2/slo";
private String logoutResponseUrl = "/logout/saml2/slo";
private BeanMetadataElement logoutSuccessHandler;
private BeanDefinition logoutRequestFilter;
private BeanDefinition logoutResponseFilter;
private BeanDefinition logoutFilter;
Saml2LogoutBeanDefinitionParser(ManagedList<BeanMetadataElement> logoutHandlers,
BeanMetadataElement logoutSuccessHandler) {
this.logoutHandlers = logoutHandlers;
this.logoutSuccessHandler = logoutSuccessHandler;
}
@Override
public BeanDefinition parse(Element element, ParserContext pc) {
String logoutUrl = element.getAttribute(ATT_LOGOUT_URL);
if (StringUtils.hasText(logoutUrl)) {
this.logoutUrl = logoutUrl;
}
String logoutRequestUrl = element.getAttribute(ATT_LOGOUT_REQUEST_URL);
if (StringUtils.hasText(logoutRequestUrl)) {
this.logoutRequestUrl = logoutRequestUrl;
}
String logoutResponseUrl = element.getAttribute(ATT_LOGOUT_RESPONSE_URL);
if (StringUtils.hasText(logoutResponseUrl)) {
this.logoutResponseUrl = logoutResponseUrl;
}
WebConfigUtils.validateHttpRedirect(this.logoutUrl, pc, element);
WebConfigUtils.validateHttpRedirect(this.logoutRequestUrl, pc, element);
WebConfigUtils.validateHttpRedirect(this.logoutResponseUrl, pc, element);
if (CollectionUtils.isEmpty(this.logoutHandlers)) {
this.logoutHandlers = createDefaultLogoutHandlers();
}
if (this.logoutSuccessHandler == null) {
this.logoutSuccessHandler = createDefaultLogoutSuccessHandler();
}
BeanMetadataElement relyingPartyRegistrationRepository = Saml2LogoutBeanDefinitionParserUtils
.getRelyingPartyRegistrationRepository(element);
BeanMetadataElement registrations = BeanDefinitionBuilder
.rootBeanDefinition(DefaultRelyingPartyRegistrationResolver.class)
.addConstructorArgValue(relyingPartyRegistrationRepository).getBeanDefinition();
BeanMetadataElement logoutResponseResolver = Saml2LogoutBeanDefinitionParserUtils
.getLogoutResponseResolver(element, registrations);
BeanMetadataElement logoutRequestValidator = Saml2LogoutBeanDefinitionParserUtils
.getLogoutRequestValidator(element);
BeanMetadataElement logoutRequestMatcher = createSaml2LogoutRequestMatcher();
this.logoutRequestFilter = BeanDefinitionBuilder.rootBeanDefinition(Saml2LogoutRequestFilter.class)
.addConstructorArgValue(registrations).addConstructorArgValue(logoutRequestValidator)
.addConstructorArgValue(logoutResponseResolver).addConstructorArgValue(this.logoutHandlers)
.addPropertyValue("logoutRequestMatcher", logoutRequestMatcher).getBeanDefinition();
BeanMetadataElement logoutResponseValidator = Saml2LogoutBeanDefinitionParserUtils
.getLogoutResponseValidator(element);
BeanMetadataElement logoutRequestRepository = Saml2LogoutBeanDefinitionParserUtils
.getLogoutRequestRepository(element);
BeanMetadataElement logoutResponseMatcher = createSaml2LogoutResponseMatcher();
this.logoutResponseFilter = BeanDefinitionBuilder.rootBeanDefinition(Saml2LogoutResponseFilter.class)
.addConstructorArgValue(registrations).addConstructorArgValue(logoutResponseValidator)
.addConstructorArgValue(this.logoutSuccessHandler)
.addPropertyValue("logoutRequestMatcher", logoutResponseMatcher)
.addPropertyValue("logoutRequestRepository", logoutRequestRepository).getBeanDefinition();
BeanMetadataElement logoutRequestResolver = Saml2LogoutBeanDefinitionParserUtils
.getLogoutRequestResolver(element, registrations);
BeanMetadataElement saml2LogoutRequestSuccessHandler = BeanDefinitionBuilder
.rootBeanDefinition(Saml2RelyingPartyInitiatedLogoutSuccessHandler.class)
.addConstructorArgValue(logoutRequestResolver).getBeanDefinition();
this.logoutFilter = BeanDefinitionBuilder.rootBeanDefinition(LogoutFilter.class)
.addConstructorArgValue(saml2LogoutRequestSuccessHandler).addConstructorArgValue(this.logoutHandlers)
.addPropertyValue("logoutRequestMatcher", createLogoutRequestMatcher()).getBeanDefinition();
return null;
}
private static List<BeanMetadataElement> createDefaultLogoutHandlers() {
List<BeanMetadataElement> handlers = new ManagedList<>();
handlers.add(BeanDefinitionBuilder.rootBeanDefinition(SecurityContextLogoutHandler.class).getBeanDefinition());
handlers.add(BeanDefinitionBuilder.rootBeanDefinition(LogoutSuccessEventPublishingLogoutHandler.class)
.getBeanDefinition());
return handlers;
}
private static BeanMetadataElement createDefaultLogoutSuccessHandler() {
return BeanDefinitionBuilder.rootBeanDefinition(SimpleUrlLogoutSuccessHandler.class)
.addPropertyValue("defaultTargetUrl", "/login?logout").getBeanDefinition();
}
private BeanMetadataElement createLogoutRequestMatcher() {
BeanMetadataElement logoutMatcher = BeanDefinitionBuilder.rootBeanDefinition(AntPathRequestMatcher.class)
.addConstructorArgValue(this.logoutUrl).addConstructorArgValue("POST").getBeanDefinition();
BeanMetadataElement saml2Matcher = BeanDefinitionBuilder.rootBeanDefinition(Saml2RequestMatcher.class)
.getBeanDefinition();
return BeanDefinitionBuilder.rootBeanDefinition(AndRequestMatcher.class)
.addConstructorArgValue(toManagedList(logoutMatcher, saml2Matcher)).getBeanDefinition();
}
private BeanMetadataElement createSaml2LogoutRequestMatcher() {
BeanMetadataElement logoutRequestMatcher = BeanDefinitionBuilder.rootBeanDefinition(AntPathRequestMatcher.class)
.addConstructorArgValue(this.logoutRequestUrl).getBeanDefinition();
BeanMetadataElement saml2RequestMatcher = BeanDefinitionBuilder
.rootBeanDefinition(ParameterRequestMatcher.class).addConstructorArgValue("SAMLRequest")
.getBeanDefinition();
return BeanDefinitionBuilder.rootBeanDefinition(AndRequestMatcher.class)
.addConstructorArgValue(toManagedList(logoutRequestMatcher, saml2RequestMatcher)).getBeanDefinition();
}
private BeanMetadataElement createSaml2LogoutResponseMatcher() {
BeanMetadataElement logoutResponseMatcher = BeanDefinitionBuilder
.rootBeanDefinition(AntPathRequestMatcher.class).addConstructorArgValue(this.logoutResponseUrl)
.getBeanDefinition();
BeanMetadataElement saml2ResponseMatcher = BeanDefinitionBuilder
.rootBeanDefinition(ParameterRequestMatcher.class).addConstructorArgValue("SAMLResponse")
.getBeanDefinition();
return BeanDefinitionBuilder.rootBeanDefinition(AndRequestMatcher.class)
.addConstructorArgValue(toManagedList(logoutResponseMatcher, saml2ResponseMatcher)).getBeanDefinition();
}
private static List<BeanMetadataElement> toManagedList(BeanMetadataElement... elements) {
List<BeanMetadataElement> managedList = new ManagedList<>();
managedList.addAll(Arrays.asList(elements));
return managedList;
}
BeanDefinition getLogoutRequestFilter() {
return this.logoutRequestFilter;
}
BeanDefinition getLogoutResponseFilter() {
return this.logoutResponseFilter;
}
BeanDefinition getLogoutFilter() {
return this.logoutFilter;
}
private static class ParameterRequestMatcher implements RequestMatcher {
Predicate<String> test = Objects::nonNull;
String name;
ParameterRequestMatcher(String name) {
this.name = name;
}
@Override
public boolean matches(HttpServletRequest request) {
return this.test.test(request.getParameter(this.name));
}
}
private static class Saml2RequestMatcher implements RequestMatcher {
@Override
public boolean matches(HttpServletRequest request) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return false;
}
return authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal;
}
}
}
@@ -1,104 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.http;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlLogoutRequestValidator;
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlLogoutResponseValidator;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.web.authentication.logout.HttpSessionLogoutRequestRepository;
import org.springframework.util.StringUtils;
/**
* @author Marcus da Coregio
* @since 5.7
*/
final class Saml2LogoutBeanDefinitionParserUtils {
private static final String ATT_RELYING_PARTY_REGISTRATION_REPOSITORY_REF = "relying-party-registration-repository-ref";
private static final String ATT_LOGOUT_REQUEST_VALIDATOR_REF = "logout-request-validator-ref";
private static final String ATT_LOGOUT_REQUEST_REPOSITORY_REF = "logout-request-repository-ref";
private static final String ATT_LOGOUT_REQUEST_RESOLVER_REF = "logout-request-resolver-ref";
private static final String ATT_LOGOUT_RESPONSE_RESOLVER_REF = "logout-response-resolver-ref";
private static final String ATT_LOGOUT_RESPONSE_VALIDATOR_REF = "logout-response-validator-ref";
private Saml2LogoutBeanDefinitionParserUtils() {
}
static BeanMetadataElement getRelyingPartyRegistrationRepository(Element element) {
String relyingPartyRegistrationRepositoryRef = element
.getAttribute(ATT_RELYING_PARTY_REGISTRATION_REPOSITORY_REF);
if (StringUtils.hasText(relyingPartyRegistrationRepositoryRef)) {
return new RuntimeBeanReference(relyingPartyRegistrationRepositoryRef);
}
return new RuntimeBeanReference(RelyingPartyRegistrationRepository.class);
}
static BeanMetadataElement getLogoutResponseResolver(Element element, BeanMetadataElement registrations) {
String logoutResponseResolver = element.getAttribute(ATT_LOGOUT_RESPONSE_RESOLVER_REF);
if (StringUtils.hasText(logoutResponseResolver)) {
return new RuntimeBeanReference(logoutResponseResolver);
}
return BeanDefinitionBuilder.rootBeanDefinition(
"org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutResponseResolver")
.addConstructorArgValue(registrations).getBeanDefinition();
}
static BeanMetadataElement getLogoutRequestValidator(Element element) {
String logoutRequestValidator = element.getAttribute(ATT_LOGOUT_REQUEST_VALIDATOR_REF);
if (StringUtils.hasText(logoutRequestValidator)) {
return new RuntimeBeanReference(logoutRequestValidator);
}
return BeanDefinitionBuilder.rootBeanDefinition(OpenSamlLogoutRequestValidator.class).getBeanDefinition();
}
static BeanMetadataElement getLogoutResponseValidator(Element element) {
String logoutResponseValidator = element.getAttribute(ATT_LOGOUT_RESPONSE_VALIDATOR_REF);
if (StringUtils.hasText(logoutResponseValidator)) {
return new RuntimeBeanReference(logoutResponseValidator);
}
return BeanDefinitionBuilder.rootBeanDefinition(OpenSamlLogoutResponseValidator.class).getBeanDefinition();
}
static BeanMetadataElement getLogoutRequestRepository(Element element) {
String logoutRequestRepository = element.getAttribute(ATT_LOGOUT_REQUEST_REPOSITORY_REF);
if (StringUtils.hasText(logoutRequestRepository)) {
return new RuntimeBeanReference(logoutRequestRepository);
}
return BeanDefinitionBuilder.rootBeanDefinition(HttpSessionLogoutRequestRepository.class).getBeanDefinition();
}
static BeanMetadataElement getLogoutRequestResolver(Element element, BeanMetadataElement registrations) {
String logoutRequestResolver = element.getAttribute(ATT_LOGOUT_REQUEST_RESOLVER_REF);
if (StringUtils.hasText(logoutRequestResolver)) {
return new RuntimeBeanReference(logoutRequestResolver);
}
return BeanDefinitionBuilder.rootBeanDefinition(
"org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutRequestResolver")
.addConstructorArgValue(registrations).getBeanDefinition();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,10 +29,6 @@ enum SecurityFilters {
FIRST(Integer.MIN_VALUE),
DISABLE_ENCODE_URL_FILTER,
FORCE_EAGER_SESSION_FILTER,
CHANNEL_FILTER,
SECURITY_CONTEXT_FILTER,
@@ -45,20 +41,12 @@ enum SecurityFilters {
CORS_FILTER,
SAML2_LOGOUT_REQUEST_FILTER,
SAML2_LOGOUT_RESPONSE_FILTER,
CSRF_FILTER,
SAML2_LOGOUT_FILTER,
LOGOUT_FILTER,
OAUTH2_AUTHORIZATION_REQUEST_FILTER,
SAML2_AUTHENTICATION_REQUEST_FILTER,
X509_FILTER,
PRE_AUTH_FILTER,
@@ -67,8 +55,6 @@ enum SecurityFilters {
OAUTH2_LOGIN_FILTER,
SAML2_AUTHENTICATION_FILTER,
FORM_LOGIN_FILTER,
OPENID_FILTER,
@@ -1,183 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.ldap;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticator;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
/**
* Creates an {@link AuthenticationManager} that can perform LDAP authentication.
*
* @author Eleftheria Stein
* @since 5.7
*/
public abstract class AbstractLdapAuthenticationManagerFactory<T extends AbstractLdapAuthenticator> {
AbstractLdapAuthenticationManagerFactory(BaseLdapPathContextSource contextSource) {
this.contextSource = contextSource;
}
private BaseLdapPathContextSource contextSource;
private String[] userDnPatterns;
private LdapAuthoritiesPopulator ldapAuthoritiesPopulator;
private GrantedAuthoritiesMapper authoritiesMapper;
private UserDetailsContextMapper userDetailsContextMapper;
private String userSearchFilter;
private String userSearchBase = "";
/**
* Sets the {@link BaseLdapPathContextSource} used to perform LDAP authentication.
* @param contextSource the {@link BaseLdapPathContextSource} used to perform LDAP
* authentication
*/
public void setContextSource(BaseLdapPathContextSource contextSource) {
this.contextSource = contextSource;
}
/**
* Gets the {@link BaseLdapPathContextSource} used to perform LDAP authentication.
* @return the {@link BaseLdapPathContextSource} used to perform LDAP authentication
*/
protected final BaseLdapPathContextSource getContextSource() {
return this.contextSource;
}
/**
* Sets the {@link LdapAuthoritiesPopulator} used to obtain a list of granted
* authorities for an LDAP user.
* @param ldapAuthoritiesPopulator the {@link LdapAuthoritiesPopulator} to use
*/
public void setLdapAuthoritiesPopulator(LdapAuthoritiesPopulator ldapAuthoritiesPopulator) {
this.ldapAuthoritiesPopulator = ldapAuthoritiesPopulator;
}
/**
* Sets the {@link GrantedAuthoritiesMapper} used for converting the authorities
* loaded from storage to a new set of authorities which will be associated to the
* {@link UsernamePasswordAuthenticationToken}.
* @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the
* user's authorities
*/
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
/**
* Sets a custom strategy to be used for creating the {@link UserDetails} which will
* be stored as the principal in the {@link Authentication}.
* @param userDetailsContextMapper the strategy instance
*/
public void setUserDetailsContextMapper(UserDetailsContextMapper userDetailsContextMapper) {
this.userDetailsContextMapper = userDetailsContextMapper;
}
/**
* If your users are at a fixed location in the directory (i.e. you can work out the
* DN directly from the username without doing a directory search), you can use this
* attribute to map directly to the DN. It maps directly to the userDnPatterns
* property of AbstractLdapAuthenticator. The value is a specific pattern used to
* build the user's DN, for example "uid={0},ou=people". The key "{0}" must be present
* and will be substituted with the username.
* @param userDnPatterns the LDAP patterns for finding the usernames
*/
public void setUserDnPatterns(String... userDnPatterns) {
this.userDnPatterns = userDnPatterns;
}
/**
* The LDAP filter used to search for users (optional). For example "(uid={0})". The
* substituted parameter is the user's login name.
* @param userSearchFilter the LDAP filter used to search for users
*/
public void setUserSearchFilter(String userSearchFilter) {
this.userSearchFilter = userSearchFilter;
}
/**
* Search base for user searches. Defaults to "". Only used with
* {@link #setUserSearchFilter(String)}.
* @param userSearchBase search base for user searches
*/
public void setUserSearchBase(String userSearchBase) {
this.userSearchBase = userSearchBase;
}
/**
* Returns the configured {@link AuthenticationManager} that can be used to perform
* LDAP authentication.
* @return the configured {@link AuthenticationManager}
*/
public final AuthenticationManager createAuthenticationManager() {
LdapAuthenticationProvider ldapAuthenticationProvider = getProvider();
return new ProviderManager(ldapAuthenticationProvider);
}
private LdapAuthenticationProvider getProvider() {
AbstractLdapAuthenticator authenticator = getAuthenticator();
LdapAuthenticationProvider provider;
if (this.ldapAuthoritiesPopulator != null) {
provider = new LdapAuthenticationProvider(authenticator, this.ldapAuthoritiesPopulator);
}
else {
provider = new LdapAuthenticationProvider(authenticator);
}
if (this.authoritiesMapper != null) {
provider.setAuthoritiesMapper(this.authoritiesMapper);
}
if (this.userDetailsContextMapper != null) {
provider.setUserDetailsContextMapper(this.userDetailsContextMapper);
}
return provider;
}
private AbstractLdapAuthenticator getAuthenticator() {
AbstractLdapAuthenticator authenticator = createDefaultLdapAuthenticator();
if (this.userSearchFilter != null) {
authenticator.setUserSearch(
new FilterBasedLdapUserSearch(this.userSearchBase, this.userSearchFilter, this.contextSource));
}
if (this.userDnPatterns != null && this.userDnPatterns.length > 0) {
authenticator.setUserDnPatterns(this.userDnPatterns);
}
authenticator.afterPropertiesSet();
return authenticator;
}
/**
* Allows subclasses to supply the default {@link AbstractLdapAuthenticator}.
* @return the {@link AbstractLdapAuthenticator} that will be configured for LDAP
* authentication
*/
protected abstract T createDefaultLdapAuthenticator();
}
@@ -1,185 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.ldap;
import java.io.IOException;
import java.net.ServerSocket;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.Lifecycle;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.EmbeddedLdapServerContainer;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.util.ClassUtils;
/**
* Creates a {@link DefaultSpringSecurityContextSource} used to perform LDAP
* authentication and starts and in-memory LDAP server.
*
* @author Eleftheria Stein
* @since 5.7
*/
public class EmbeddedLdapServerContextSourceFactoryBean
implements FactoryBean<DefaultSpringSecurityContextSource>, DisposableBean, ApplicationContextAware {
private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
private static final int DEFAULT_PORT = 33389;
private static final int RANDOM_PORT = 0;
private Integer port;
private String ldif = "classpath*:*.ldif";
private String root = "dc=springframework,dc=org";
private ApplicationContext context;
private String managerDn;
private String managerPassword;
private EmbeddedLdapServerContainer container;
/**
* Create an EmbeddedLdapServerContextSourceFactoryBean that will use an embedded LDAP
* server to perform LDAP authentication. This requires a dependency on
* `com.unboundid:unboundid-ldapsdk`.
* @return the EmbeddedLdapServerContextSourceFactoryBean
*/
public static EmbeddedLdapServerContextSourceFactoryBean fromEmbeddedLdapServer() {
return new EmbeddedLdapServerContextSourceFactoryBean();
}
/**
* Specifies an LDIF to load at startup for an embedded LDAP server. The default is
* "classpath*:*.ldif".
* @param ldif the ldif to load at startup for an embedded LDAP server.
*/
public void setLdif(String ldif) {
this.ldif = ldif;
}
/**
* The port to connect to LDAP to (the default is 33389 or random available port if
* unavailable). Supplying 0 as the port indicates that a random available port should
* be selected.
* @param port the port to connect to
*/
public void setPort(int port) {
this.port = port;
}
/**
* Optional root suffix for the embedded LDAP server. Default is
* "dc=springframework,dc=org".
* @param root root suffix for the embedded LDAP server
*/
public void setRoot(String root) {
this.root = root;
}
/**
* Username (DN) of the "manager" user identity (i.e. "uid=admin,ou=system") which
* will be used to authenticate to an LDAP server. If omitted, anonymous access will
* be used.
* @param managerDn the username (DN) of the "manager" user identity used to
* authenticate to a LDAP server.
*/
public void setManagerDn(String managerDn) {
this.managerDn = managerDn;
}
/**
* The password for the manager DN. This is required if the
* {@link #setManagerDn(String)} is specified.
* @param managerPassword password for the manager DN
*/
public void setManagerPassword(String managerPassword) {
this.managerPassword = managerPassword;
}
@Override
public DefaultSpringSecurityContextSource getObject() throws Exception {
if (!ClassUtils.isPresent(UNBOUNDID_CLASSNAME, getClass().getClassLoader())) {
throw new IllegalStateException("Embedded LDAP server is not provided");
}
this.container = getContainer();
this.port = this.container.getPort();
DefaultSpringSecurityContextSource contextSourceFromProviderUrl = new DefaultSpringSecurityContextSource(
"ldap://127.0.0.1:" + this.port + "/" + this.root);
if (this.managerDn != null) {
contextSourceFromProviderUrl.setUserDn(this.managerDn);
if (this.managerPassword == null) {
throw new IllegalStateException("managerPassword is required if managerDn is supplied");
}
contextSourceFromProviderUrl.setPassword(this.managerPassword);
}
contextSourceFromProviderUrl.afterPropertiesSet();
return contextSourceFromProviderUrl;
}
@Override
public Class<?> getObjectType() {
return DefaultSpringSecurityContextSource.class;
}
@Override
public void destroy() {
if (this.container instanceof Lifecycle) {
((Lifecycle) this.container).stop();
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
private EmbeddedLdapServerContainer getContainer() {
if (!ClassUtils.isPresent(UNBOUNDID_CLASSNAME, getClass().getClassLoader())) {
throw new IllegalStateException("Embedded LDAP server is not provided");
}
UnboundIdContainer unboundIdContainer = new UnboundIdContainer(this.root, this.ldif);
unboundIdContainer.setApplicationContext(this.context);
unboundIdContainer.setPort(getEmbeddedServerPort());
unboundIdContainer.afterPropertiesSet();
return unboundIdContainer;
}
private int getEmbeddedServerPort() {
if (this.port == null) {
this.port = getDefaultEmbeddedServerPort();
}
return this.port;
}
private int getDefaultEmbeddedServerPort() {
try (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT)) {
return serverSocket.getLocalPort();
}
catch (IOException ex) {
return RANDOM_PORT;
}
}
}
@@ -1,41 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.ldap;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.ldap.authentication.BindAuthenticator;
/**
* Creates an {@link AuthenticationManager} that can perform LDAP authentication using
* bind authentication.
*
* @author Eleftheria Stein
* @since 5.7
*/
public class LdapBindAuthenticationManagerFactory extends AbstractLdapAuthenticationManagerFactory<BindAuthenticator> {
public LdapBindAuthenticationManagerFactory(BaseLdapPathContextSource contextSource) {
super(contextSource);
}
@Override
protected BindAuthenticator createDefaultLdapAuthenticator() {
return new BindAuthenticator(getContextSource());
}
}
@@ -1,75 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.ldap;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
import org.springframework.util.Assert;
/**
* Creates an {@link AuthenticationManager} that can perform LDAP authentication using
* password comparison.
*
* @author Eleftheria Stein
* @since 5.7
*/
public class LdapPasswordComparisonAuthenticationManagerFactory
extends AbstractLdapAuthenticationManagerFactory<PasswordComparisonAuthenticator> {
private PasswordEncoder passwordEncoder;
private String passwordAttribute;
public LdapPasswordComparisonAuthenticationManagerFactory(BaseLdapPathContextSource contextSource,
PasswordEncoder passwordEncoder) {
super(contextSource);
setPasswordEncoder(passwordEncoder);
}
/**
* Specifies the {@link PasswordEncoder} to be used when authenticating with password
* comparison.
* @param passwordEncoder the {@link PasswordEncoder} to use
*/
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
Assert.notNull(passwordEncoder, "passwordEncoder must not be null.");
this.passwordEncoder = passwordEncoder;
}
/**
* The attribute in the directory which contains the user password. Only used when
* authenticating with password comparison. Defaults to "userPassword".
* @param passwordAttribute the attribute in the directory which contains the user
* password
*/
public void setPasswordAttribute(String passwordAttribute) {
this.passwordAttribute = passwordAttribute;
}
@Override
protected PasswordComparisonAuthenticator createDefaultLdapAuthenticator() {
PasswordComparisonAuthenticator ldapAuthenticator = new PasswordComparisonAuthenticator(getContextSource());
if (this.passwordAttribute != null) {
ldapAuthenticator.setPasswordAttributeName(this.passwordAttribute);
}
ldapAuthenticator.setPasswordEncoder(this.passwordEncoder);
return ldapAuthenticator;
}
}
@@ -1,376 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.saml2;
import java.io.InputStream;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.security.converter.RsaKeyConverters;
import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations;
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
/**
* @author Marcus da Coregio
* @since 5.7
*/
public final class RelyingPartyRegistrationsBeanDefinitionParser implements BeanDefinitionParser {
private static final String ELT_RELYING_PARTY_REGISTRATION = "relying-party-registration";
private static final String ELT_SIGNING_CREDENTIAL = "signing-credential";
private static final String ELT_DECRYPTION_CREDENTIAL = "decryption-credential";
private static final String ELT_ASSERTING_PARTY = "asserting-party";
private static final String ELT_VERIFICATION_CREDENTIAL = "verification-credential";
private static final String ELT_ENCRYPTION_CREDENTIAL = "encryption-credential";
private static final String ATT_REGISTRATION_ID = "registration-id";
private static final String ATT_ASSERTING_PARTY_ID = "asserting-party-id";
private static final String ATT_ENTITY_ID = "entity-id";
private static final String ATT_METADATA_LOCATION = "metadata-location";
private static final String ATT_ASSERTION_CONSUMER_SERVICE_LOCATION = "assertion-consumer-service-location";
private static final String ATT_ASSERTION_CONSUMER_SERVICE_BINDING = "assertion-consumer-service-binding";
private static final String ATT_PRIVATE_KEY_LOCATION = "private-key-location";
private static final String ATT_CERTIFICATE_LOCATION = "certificate-location";
private static final String ATT_WANT_AUTHN_REQUESTS_SIGNED = "want-authn-requests-signed";
private static final String ATT_SINGLE_SIGN_ON_SERVICE_LOCATION = "single-sign-on-service-location";
private static final String ATT_SINGLE_SIGN_ON_SERVICE_BINDING = "single-sign-on-service-binding";
private static final String ATT_SIGNING_ALGORITHMS = "signing-algorithms";
private static final String ATT_SINGLE_LOGOUT_SERVICE_LOCATION = "single-logout-service-location";
private static final String ATT_SINGLE_LOGOUT_SERVICE_RESPONSE_LOCATION = "single-logout-service-response-location";
private static final String ATT_SINGLE_LOGOUT_SERVICE_BINDING = "single-logout-service-binding";
private static final ResourceLoader resourceLoader = new DefaultResourceLoader();
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),
parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
Map<String, Map<String, Object>> assertingParties = getAssertingParties(element);
List<RelyingPartyRegistration> relyingPartyRegistrations = getRelyingPartyRegistrations(element,
assertingParties, parserContext);
BeanDefinition relyingPartyRegistrationRepositoryBean = BeanDefinitionBuilder
.rootBeanDefinition(InMemoryRelyingPartyRegistrationRepository.class)
.addConstructorArgValue(relyingPartyRegistrations).getBeanDefinition();
String relyingPartyRegistrationRepositoryId = parserContext.getReaderContext()
.generateBeanName(relyingPartyRegistrationRepositoryBean);
parserContext.registerBeanComponent(new BeanComponentDefinition(relyingPartyRegistrationRepositoryBean,
relyingPartyRegistrationRepositoryId));
parserContext.popAndRegisterContainingComponent();
return null;
}
private static Map<String, Map<String, Object>> getAssertingParties(Element element) {
List<Element> assertingPartyElts = DomUtils.getChildElementsByTagName(element, ELT_ASSERTING_PARTY);
Map<String, Map<String, Object>> providers = new HashMap<>();
for (Element assertingPartyElt : assertingPartyElts) {
Map<String, Object> assertingParty = new HashMap<>();
String assertingPartyId = assertingPartyElt.getAttribute(ATT_ASSERTING_PARTY_ID);
String entityId = assertingPartyElt.getAttribute(ATT_ENTITY_ID);
String wantAuthnRequestsSigned = assertingPartyElt.getAttribute(ATT_WANT_AUTHN_REQUESTS_SIGNED);
String singleSignOnServiceLocation = assertingPartyElt.getAttribute(ATT_SINGLE_SIGN_ON_SERVICE_LOCATION);
String singleSignOnServiceBinding = assertingPartyElt.getAttribute(ATT_SINGLE_SIGN_ON_SERVICE_BINDING);
String signingAlgorithms = assertingPartyElt.getAttribute(ATT_SIGNING_ALGORITHMS);
String singleLogoutServiceLocation = assertingPartyElt.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_LOCATION);
String singleLogoutServiceResponseLocation = assertingPartyElt
.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_RESPONSE_LOCATION);
String singleLogoutServiceBinding = assertingPartyElt.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_BINDING);
assertingParty.put(ATT_ASSERTING_PARTY_ID, assertingPartyId);
assertingParty.put(ATT_ENTITY_ID, entityId);
assertingParty.put(ATT_WANT_AUTHN_REQUESTS_SIGNED, wantAuthnRequestsSigned);
assertingParty.put(ATT_SINGLE_SIGN_ON_SERVICE_LOCATION, singleSignOnServiceLocation);
assertingParty.put(ATT_SINGLE_SIGN_ON_SERVICE_BINDING, singleSignOnServiceBinding);
assertingParty.put(ATT_SIGNING_ALGORITHMS, signingAlgorithms);
assertingParty.put(ATT_SINGLE_LOGOUT_SERVICE_LOCATION, singleLogoutServiceLocation);
assertingParty.put(ATT_SINGLE_LOGOUT_SERVICE_RESPONSE_LOCATION, singleLogoutServiceResponseLocation);
assertingParty.put(ATT_SINGLE_LOGOUT_SERVICE_BINDING, singleLogoutServiceBinding);
addVerificationCredentials(assertingPartyElt, assertingParty);
addEncryptionCredentials(assertingPartyElt, assertingParty);
providers.put(assertingPartyId, assertingParty);
}
return providers;
}
private static void addVerificationCredentials(Map<String, Object> assertingParty,
RelyingPartyRegistration.AssertingPartyDetails.Builder builder) {
List<String> verificationCertificateLocations = (List<String>) assertingParty.get(ELT_VERIFICATION_CREDENTIAL);
List<Saml2X509Credential> verificationCredentials = new ArrayList<>();
for (String certificateLocation : verificationCertificateLocations) {
verificationCredentials.add(getSaml2VerificationCredential(certificateLocation));
}
builder.verificationX509Credentials((credentials) -> credentials.addAll(verificationCredentials));
}
private static void addEncryptionCredentials(Map<String, Object> assertingParty,
RelyingPartyRegistration.AssertingPartyDetails.Builder builder) {
List<String> encryptionCertificateLocations = (List<String>) assertingParty.get(ELT_ENCRYPTION_CREDENTIAL);
List<Saml2X509Credential> encryptionCredentials = new ArrayList<>();
for (String certificateLocation : encryptionCertificateLocations) {
encryptionCredentials.add(getSaml2EncryptionCredential(certificateLocation));
}
builder.encryptionX509Credentials((credentials) -> credentials.addAll(encryptionCredentials));
}
private static void addVerificationCredentials(Element assertingPartyElt, Map<String, Object> assertingParty) {
List<String> verificationCertificateLocations = new ArrayList<>();
List<Element> verificationCredentialElts = DomUtils.getChildElementsByTagName(assertingPartyElt,
ELT_VERIFICATION_CREDENTIAL);
for (Element verificationCredentialElt : verificationCredentialElts) {
String certificateLocation = verificationCredentialElt.getAttribute(ATT_CERTIFICATE_LOCATION);
verificationCertificateLocations.add(certificateLocation);
}
assertingParty.put(ELT_VERIFICATION_CREDENTIAL, verificationCertificateLocations);
}
private static void addEncryptionCredentials(Element assertingPartyElt, Map<String, Object> assertingParty) {
List<String> encryptionCertificateLocations = new ArrayList<>();
List<Element> encryptionCredentialElts = DomUtils.getChildElementsByTagName(assertingPartyElt,
ELT_VERIFICATION_CREDENTIAL);
for (Element encryptionCredentialElt : encryptionCredentialElts) {
String certificateLocation = encryptionCredentialElt.getAttribute(ATT_CERTIFICATE_LOCATION);
encryptionCertificateLocations.add(certificateLocation);
}
assertingParty.put(ELT_ENCRYPTION_CREDENTIAL, encryptionCertificateLocations);
}
private List<RelyingPartyRegistration> getRelyingPartyRegistrations(Element element,
Map<String, Map<String, Object>> assertingParties, ParserContext parserContext) {
List<Element> relyingPartyRegistrationElts = DomUtils.getChildElementsByTagName(element,
ELT_RELYING_PARTY_REGISTRATION);
List<RelyingPartyRegistration> relyingPartyRegistrations = new ArrayList<>();
for (Element relyingPartyRegistrationElt : relyingPartyRegistrationElts) {
RelyingPartyRegistration.Builder builder = getBuilderFromMetadataLocationIfPossible(
relyingPartyRegistrationElt, assertingParties, parserContext);
addSigningCredentials(relyingPartyRegistrationElt, builder);
addDecryptionCredentials(relyingPartyRegistrationElt, builder);
relyingPartyRegistrations.add(builder.build());
}
return relyingPartyRegistrations;
}
private static RelyingPartyRegistration.Builder getBuilderFromMetadataLocationIfPossible(
Element relyingPartyRegistrationElt, Map<String, Map<String, Object>> assertingParties,
ParserContext parserContext) {
String registrationId = relyingPartyRegistrationElt.getAttribute(ATT_REGISTRATION_ID);
String metadataLocation = relyingPartyRegistrationElt.getAttribute(ATT_METADATA_LOCATION);
String singleLogoutServiceLocation = relyingPartyRegistrationElt
.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_LOCATION);
String singleLogoutServiceResponseLocation = relyingPartyRegistrationElt
.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_RESPONSE_LOCATION);
Saml2MessageBinding singleLogoutServiceBinding = getSingleLogoutServiceBinding(relyingPartyRegistrationElt);
if (StringUtils.hasText(metadataLocation)) {
return RelyingPartyRegistrations.fromMetadataLocation(metadataLocation).registrationId(registrationId)
.singleLogoutServiceLocation(singleLogoutServiceLocation)
.singleLogoutServiceResponseLocation(singleLogoutServiceResponseLocation)
.singleLogoutServiceBinding(singleLogoutServiceBinding);
}
String entityId = relyingPartyRegistrationElt.getAttribute(ATT_ENTITY_ID);
String assertionConsumerServiceLocation = relyingPartyRegistrationElt
.getAttribute(ATT_ASSERTION_CONSUMER_SERVICE_LOCATION);
Saml2MessageBinding assertionConsumerServiceBinding = getAssertionConsumerServiceBinding(
relyingPartyRegistrationElt);
return RelyingPartyRegistration.withRegistrationId(registrationId).entityId(entityId)
.assertionConsumerServiceLocation(assertionConsumerServiceLocation)
.assertionConsumerServiceBinding(assertionConsumerServiceBinding)
.singleLogoutServiceLocation(singleLogoutServiceLocation)
.singleLogoutServiceResponseLocation(singleLogoutServiceResponseLocation)
.singleLogoutServiceBinding(singleLogoutServiceBinding)
.assertingPartyDetails((builder) -> buildAssertingParty(relyingPartyRegistrationElt, assertingParties,
builder, parserContext));
}
private static void buildAssertingParty(Element relyingPartyElt, Map<String, Map<String, Object>> assertingParties,
RelyingPartyRegistration.AssertingPartyDetails.Builder builder, ParserContext parserContext) {
String assertingPartyId = relyingPartyElt.getAttribute(ATT_ASSERTING_PARTY_ID);
if (!assertingParties.containsKey(assertingPartyId)) {
Object source = parserContext.extractSource(relyingPartyElt);
parserContext.getReaderContext()
.error(String.format("Could not find asserting party with id %s", assertingPartyId), source);
}
Map<String, Object> assertingParty = assertingParties.get(assertingPartyId);
String entityId = getAsString(assertingParty, ATT_ENTITY_ID);
String wantAuthnRequestsSigned = getAsString(assertingParty, ATT_WANT_AUTHN_REQUESTS_SIGNED);
String singleSignOnServiceLocation = getAsString(assertingParty, ATT_SINGLE_SIGN_ON_SERVICE_LOCATION);
String singleSignOnServiceBinding = getAsString(assertingParty, ATT_SINGLE_SIGN_ON_SERVICE_BINDING);
Saml2MessageBinding saml2MessageBinding = StringUtils.hasText(singleSignOnServiceBinding)
? Saml2MessageBinding.valueOf(singleSignOnServiceBinding) : Saml2MessageBinding.REDIRECT;
String singleLogoutServiceLocation = getAsString(assertingParty, ATT_SINGLE_LOGOUT_SERVICE_LOCATION);
String singleLogoutServiceResponseLocation = getAsString(assertingParty,
ATT_SINGLE_LOGOUT_SERVICE_RESPONSE_LOCATION);
String singleLogoutServiceBinding = getAsString(assertingParty, ATT_SINGLE_LOGOUT_SERVICE_BINDING);
Saml2MessageBinding saml2LogoutMessageBinding = StringUtils.hasText(singleLogoutServiceBinding)
? Saml2MessageBinding.valueOf(singleLogoutServiceBinding) : Saml2MessageBinding.REDIRECT;
builder.entityId(entityId).wantAuthnRequestsSigned(Boolean.parseBoolean(wantAuthnRequestsSigned))
.singleSignOnServiceLocation(singleSignOnServiceLocation)
.singleSignOnServiceBinding(saml2MessageBinding)
.singleLogoutServiceLocation(singleLogoutServiceLocation)
.singleLogoutServiceResponseLocation(singleLogoutServiceResponseLocation)
.singleLogoutServiceBinding(saml2LogoutMessageBinding);
addSigningAlgorithms(assertingParty, builder);
addVerificationCredentials(assertingParty, builder);
addEncryptionCredentials(assertingParty, builder);
}
private static void addSigningAlgorithms(Map<String, Object> assertingParty,
RelyingPartyRegistration.AssertingPartyDetails.Builder builder) {
String signingAlgorithmsAttr = getAsString(assertingParty, ATT_SIGNING_ALGORITHMS);
if (StringUtils.hasText(signingAlgorithmsAttr)) {
List<String> signingAlgorithms = Arrays.asList(signingAlgorithmsAttr.split(","));
builder.signingAlgorithms((s) -> s.addAll(signingAlgorithms));
}
}
private static void addSigningCredentials(Element relyingPartyRegistrationElt,
RelyingPartyRegistration.Builder builder) {
List<Element> credentialElts = DomUtils.getChildElementsByTagName(relyingPartyRegistrationElt,
ELT_SIGNING_CREDENTIAL);
for (Element credentialElt : credentialElts) {
String privateKeyLocation = credentialElt.getAttribute(ATT_PRIVATE_KEY_LOCATION);
String certificateLocation = credentialElt.getAttribute(ATT_CERTIFICATE_LOCATION);
builder.signingX509Credentials(
(c) -> c.add(getSaml2SigningCredential(privateKeyLocation, certificateLocation)));
}
}
private static void addDecryptionCredentials(Element relyingPartyRegistrationElt,
RelyingPartyRegistration.Builder builder) {
List<Element> credentialElts = DomUtils.getChildElementsByTagName(relyingPartyRegistrationElt,
ELT_DECRYPTION_CREDENTIAL);
for (Element credentialElt : credentialElts) {
String privateKeyLocation = credentialElt.getAttribute(ATT_PRIVATE_KEY_LOCATION);
String certificateLocation = credentialElt.getAttribute(ATT_CERTIFICATE_LOCATION);
Saml2X509Credential credential = getSaml2DecryptionCredential(privateKeyLocation, certificateLocation);
builder.decryptionX509Credentials((c) -> c.add(credential));
}
}
private static String getAsString(Map<String, Object> assertingParty, String key) {
return (String) assertingParty.get(key);
}
private static Saml2MessageBinding getAssertionConsumerServiceBinding(Element relyingPartyRegistrationElt) {
String assertionConsumerServiceBinding = relyingPartyRegistrationElt
.getAttribute(ATT_ASSERTION_CONSUMER_SERVICE_BINDING);
if (StringUtils.hasText(assertionConsumerServiceBinding)) {
return Saml2MessageBinding.valueOf(assertionConsumerServiceBinding);
}
return Saml2MessageBinding.REDIRECT;
}
private static Saml2MessageBinding getSingleLogoutServiceBinding(Element relyingPartyRegistrationElt) {
String singleLogoutServiceBinding = relyingPartyRegistrationElt.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_BINDING);
if (StringUtils.hasText(singleLogoutServiceBinding)) {
return Saml2MessageBinding.valueOf(singleLogoutServiceBinding);
}
return Saml2MessageBinding.POST;
}
private static Saml2X509Credential getSaml2VerificationCredential(String certificateLocation) {
return getSaml2Credential(certificateLocation, Saml2X509Credential.Saml2X509CredentialType.VERIFICATION);
}
private static Saml2X509Credential getSaml2EncryptionCredential(String certificateLocation) {
return getSaml2Credential(certificateLocation, Saml2X509Credential.Saml2X509CredentialType.ENCRYPTION);
}
private static Saml2X509Credential getSaml2SigningCredential(String privateKeyLocation,
String certificateLocation) {
return getSaml2Credential(privateKeyLocation, certificateLocation,
Saml2X509Credential.Saml2X509CredentialType.SIGNING);
}
private static Saml2X509Credential getSaml2DecryptionCredential(String privateKeyLocation,
String certificateLocation) {
return getSaml2Credential(privateKeyLocation, certificateLocation,
Saml2X509Credential.Saml2X509CredentialType.DECRYPTION);
}
private static Saml2X509Credential getSaml2Credential(String privateKeyLocation, String certificateLocation,
Saml2X509Credential.Saml2X509CredentialType credentialType) {
RSAPrivateKey privateKey = readPrivateKey(privateKeyLocation);
X509Certificate certificate = readCertificate(certificateLocation);
return new Saml2X509Credential(privateKey, certificate, credentialType);
}
private static Saml2X509Credential getSaml2Credential(String certificateLocation,
Saml2X509Credential.Saml2X509CredentialType credentialType) {
X509Certificate certificate = readCertificate(certificateLocation);
return new Saml2X509Credential(certificate, credentialType);
}
private static RSAPrivateKey readPrivateKey(String privateKeyLocation) {
Resource privateKey = resourceLoader.getResource(privateKeyLocation);
try (InputStream inputStream = privateKey.getInputStream()) {
return RsaKeyConverters.pkcs8().convert(inputStream);
}
catch (Exception ex) {
throw new IllegalArgumentException(ex);
}
}
private static X509Certificate readCertificate(String certificateLocation) {
Resource certificate = resourceLoader.getResource(certificateLocation);
try (InputStream inputStream = certificate.getInputStream()) {
return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(inputStream);
}
catch (Exception ex) {
throw new IllegalArgumentException(ex);
}
}
}
@@ -133,7 +133,6 @@ import org.springframework.security.web.server.authorization.AuthorizationContex
import org.springframework.security.web.server.authorization.AuthorizationWebFilter;
import org.springframework.security.web.server.authorization.DelegatingReactiveAuthorizationManager;
import org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter;
import org.springframework.security.web.server.authorization.IpAddressReactiveAuthorizationManager;
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
import org.springframework.security.web.server.authorization.ServerWebExchangeDelegatingServerAccessDeniedHandler;
import org.springframework.security.web.server.context.NoOpServerSecurityContextRepository;
@@ -149,12 +148,6 @@ import org.springframework.security.web.server.header.CacheControlServerHttpHead
import org.springframework.security.web.server.header.CompositeServerHttpHeadersWriter;
import org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy;
import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy;
import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy;
import org.springframework.security.web.server.header.FeaturePolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.HttpHeaderWriterWebFilter;
import org.springframework.security.web.server.header.PermissionsPolicyServerHttpHeadersWriter;
@@ -1689,18 +1682,6 @@ public class ServerHttpSecurity {
return access(AuthenticatedReactiveAuthorizationManager.authenticated());
}
/**
* Require a specific IP address or range using an IP/Netmask (e.g.
* 192.168.1.0/24).
* @param ipAddress the address or range of addresses from which the request
* must come.
* @return the {@link AuthorizeExchangeSpec} to configure
* @since 5.7
*/
public AuthorizeExchangeSpec hasIpAddress(String ipAddress) {
return access(IpAddressReactiveAuthorizationManager.hasIpAddress(ipAddress));
}
/**
* Allows plugging in a custom authorization strategy
* @param manager the authorization manager to use
@@ -2386,17 +2367,10 @@ public class ServerHttpSecurity {
private ReferrerPolicyServerHttpHeadersWriter referrerPolicy = new ReferrerPolicyServerHttpHeadersWriter();
private CrossOriginOpenerPolicyServerHttpHeadersWriter crossOriginOpenerPolicy = new CrossOriginOpenerPolicyServerHttpHeadersWriter();
private CrossOriginEmbedderPolicyServerHttpHeadersWriter crossOriginEmbedderPolicy = new CrossOriginEmbedderPolicyServerHttpHeadersWriter();
private CrossOriginResourcePolicyServerHttpHeadersWriter crossOriginResourcePolicy = new CrossOriginResourcePolicyServerHttpHeadersWriter();
private HeaderSpec() {
this.writers = new ArrayList<>(Arrays.asList(this.cacheControl, this.contentTypeOptions, this.hsts,
this.frameOptions, this.xss, this.featurePolicy, this.permissionsPolicy, this.contentSecurityPolicy,
this.referrerPolicy, this.crossOriginOpenerPolicy, this.crossOriginEmbedderPolicy,
this.crossOriginResourcePolicy));
this.referrerPolicy));
}
/**
@@ -2608,84 +2582,6 @@ public class ServerHttpSecurity {
return this;
}
/**
* Configures the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a> header.
* @return the {@link CrossOriginOpenerPolicySpec} to configure
* @since 5.7
* @see CrossOriginOpenerPolicyServerHttpHeadersWriter
*/
public CrossOriginOpenerPolicySpec crossOriginOpenerPolicy() {
return new CrossOriginOpenerPolicySpec();
}
/**
* Configures the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a> header.
* @return the {@link HeaderSpec} to customize
* @since 5.7
* @see CrossOriginOpenerPolicyServerHttpHeadersWriter
*/
public HeaderSpec crossOriginOpenerPolicy(
Customizer<CrossOriginOpenerPolicySpec> crossOriginOpenerPolicyCustomizer) {
crossOriginOpenerPolicyCustomizer.customize(new CrossOriginOpenerPolicySpec());
return this;
}
/**
* Configures the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a> header.
* @return the {@link CrossOriginEmbedderPolicySpec} to configure
* @since 5.7
* @see CrossOriginEmbedderPolicyServerHttpHeadersWriter
*/
public CrossOriginEmbedderPolicySpec crossOriginEmbedderPolicy() {
return new CrossOriginEmbedderPolicySpec();
}
/**
* Configures the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a> header.
* @return the {@link HeaderSpec} to customize
* @since 5.7
* @see CrossOriginEmbedderPolicyServerHttpHeadersWriter
*/
public HeaderSpec crossOriginEmbedderPolicy(
Customizer<CrossOriginEmbedderPolicySpec> crossOriginEmbedderPolicyCustomizer) {
crossOriginEmbedderPolicyCustomizer.customize(new CrossOriginEmbedderPolicySpec());
return this;
}
/**
* Configures the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a> header.
* @return the {@link CrossOriginResourcePolicySpec} to configure
* @since 5.7
* @see CrossOriginResourcePolicyServerHttpHeadersWriter
*/
public CrossOriginResourcePolicySpec crossOriginResourcePolicy() {
return new CrossOriginResourcePolicySpec();
}
/**
* Configures the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a> header.
* @return the {@link HeaderSpec} to customize
* @since 5.7
* @see CrossOriginResourcePolicyServerHttpHeadersWriter
*/
public HeaderSpec crossOriginResourcePolicy(
Customizer<CrossOriginResourcePolicySpec> crossOriginResourcePolicyCustomizer) {
crossOriginResourcePolicyCustomizer.customize(new CrossOriginResourcePolicySpec());
return this;
}
/**
* Configures cache control headers
*
@@ -3001,99 +2897,6 @@ public class ServerHttpSecurity {
}
/**
* Configures the Cross-Origin-Opener-Policy header
*
* @since 5.7
*/
public final class CrossOriginOpenerPolicySpec {
private CrossOriginOpenerPolicySpec() {
}
/**
* Sets the value to be used in the `Cross-Origin-Opener-Policy` header
* @param openerPolicy a opener policy
* @return the {@link CrossOriginOpenerPolicySpec} to continue configuring
*/
public CrossOriginOpenerPolicySpec policy(CrossOriginOpenerPolicy openerPolicy) {
HeaderSpec.this.crossOriginOpenerPolicy.setPolicy(openerPolicy);
return this;
}
/**
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
* @return the {@link HeaderSpec} to continue configuring
*/
public HeaderSpec and() {
return HeaderSpec.this;
}
}
/**
* Configures the Cross-Origin-Embedder-Policy header
*
* @since 5.7
*/
public final class CrossOriginEmbedderPolicySpec {
private CrossOriginEmbedderPolicySpec() {
}
/**
* Sets the value to be used in the `Cross-Origin-Embedder-Policy` header
* @param embedderPolicy a opener policy
* @return the {@link CrossOriginEmbedderPolicySpec} to continue configuring
*/
public CrossOriginEmbedderPolicySpec policy(CrossOriginEmbedderPolicy embedderPolicy) {
HeaderSpec.this.crossOriginEmbedderPolicy.setPolicy(embedderPolicy);
return this;
}
/**
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
* @return the {@link HeaderSpec} to continue configuring
*/
public HeaderSpec and() {
return HeaderSpec.this;
}
}
/**
* Configures the Cross-Origin-Resource-Policy header
*
* @since 5.7
*/
public final class CrossOriginResourcePolicySpec {
private CrossOriginResourcePolicySpec() {
}
/**
* Sets the value to be used in the `Cross-Origin-Resource-Policy` header
* @param resourcePolicy a opener policy
* @return the {@link CrossOriginResourcePolicySpec} to continue configuring
*/
public CrossOriginResourcePolicySpec policy(CrossOriginResourcePolicy resourcePolicy) {
HeaderSpec.this.crossOriginResourcePolicy.setPolicy(resourcePolicy);
return this;
}
/**
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
* @return the {@link HeaderSpec} to continue configuring
*/
public HeaderSpec and() {
return HeaderSpec.this;
}
}
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ import org.springframework.security.authorization.AuthorizationDecision
import org.springframework.security.authorization.ReactiveAuthorizationManager
import org.springframework.security.core.Authentication
import org.springframework.security.web.server.authorization.AuthorizationContext
import org.springframework.security.web.server.authorization.IpAddressReactiveAuthorizationManager
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers
import org.springframework.security.web.util.matcher.RequestMatcher
@@ -109,13 +108,6 @@ class AuthorizeExchangeDsl {
fun hasAnyAuthority(vararg authorities: String): ReactiveAuthorizationManager<AuthorizationContext> =
AuthorityReactiveAuthorizationManager.hasAnyAuthority<AuthorizationContext>(*authorities)
/**
* Require a specific IP or range of IP addresses.
* @since 5.7
*/
fun hasIpAddress(ipAddress: String): ReactiveAuthorizationManager<AuthorizationContext> =
IpAddressReactiveAuthorizationManager.hasIpAddress(ipAddress)
/**
* Require an authenticated user.
*/
@@ -1,42 +0,0 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.web.server
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter
/**
* A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Embedder-Policy header using
* idiomatic Kotlin code.
*
* @author Marcus Da Coregio
* @since 5.7
* @property policy the policy to be used in the response header.
*/
@ServerSecurityMarker
class ServerCrossOriginEmbedderPolicyDsl {
var policy: CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy? = null
internal fun get(): (ServerHttpSecurity.HeaderSpec.CrossOriginEmbedderPolicySpec) -> Unit {
return { crossOriginEmbedderPolicy ->
policy?.also {
crossOriginEmbedderPolicy.policy(policy)
}
}
}
}
@@ -1,42 +0,0 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.web.server
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter
/**
* A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Opener-Policy header using
* idiomatic Kotlin code.
*
* @author Marcus Da Coregio
* @since 5.7
* @property policy the policy to be used in the response header.
*/
@ServerSecurityMarker
class ServerCrossOriginOpenerPolicyDsl {
var policy: CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy? = null
internal fun get(): (ServerHttpSecurity.HeaderSpec.CrossOriginOpenerPolicySpec) -> Unit {
return { crossOriginOpenerPolicy ->
policy?.also {
crossOriginOpenerPolicy.policy(policy)
}
}
}
}
@@ -1,42 +0,0 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.web.server
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter
/**
* A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Resource-Policy header using
* idiomatic Kotlin code.
*
* @author Marcus Da Coregio
* @since 5.7
* @property policy the policy to be used in the response header.
*/
@ServerSecurityMarker
class ServerCrossOriginResourcePolicyDsl {
var policy: CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy? = null
internal fun get(): (ServerHttpSecurity.HeaderSpec.CrossOriginResourcePolicySpec) -> Unit {
return { crossOriginResourcePolicy ->
policy?.also {
crossOriginResourcePolicy.policy(policy)
}
}
}
}
@@ -16,12 +16,7 @@
package org.springframework.security.config.web.server
import org.springframework.security.web.server.header.CacheControlServerHttpHeadersWriter
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter
import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter
import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter
import org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter
import org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter
import org.springframework.security.web.server.header.*
/**
* A Kotlin DSL to configure [ServerHttpSecurity] headers using idiomatic Kotlin code.
@@ -40,9 +35,6 @@ class ServerHeadersDsl {
private var referrerPolicy: ((ServerHttpSecurity.HeaderSpec.ReferrerPolicySpec) -> Unit)? = null
private var featurePolicyDirectives: String? = null
private var permissionsPolicy: ((ServerHttpSecurity.HeaderSpec.PermissionsPolicySpec) -> Unit)? = null
private var crossOriginOpenerPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginOpenerPolicySpec) -> Unit)? = null
private var crossOriginEmbedderPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginEmbedderPolicySpec) -> Unit)? = null
private var crossOriginResourcePolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginResourcePolicySpec) -> Unit)? = null
private var disabled = false
@@ -165,39 +157,6 @@ class ServerHeadersDsl {
this.permissionsPolicy = ServerPermissionsPolicyDsl().apply(permissionsPolicyConfig).get()
}
/**
* Allows configuration for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a> header.
*
* @since 5.7
* @param crossOriginOpenerPolicyConfig the customization to apply to the header
*/
fun crossOriginOpenerPolicy(crossOriginOpenerPolicyConfig: ServerCrossOriginOpenerPolicyDsl.() -> Unit) {
this.crossOriginOpenerPolicy = ServerCrossOriginOpenerPolicyDsl().apply(crossOriginOpenerPolicyConfig).get()
}
/**
* Allows configuration for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a> header.
*
* @since 5.7
* @param crossOriginEmbedderPolicyConfig the customization to apply to the header
*/
fun crossOriginEmbedderPolicy(crossOriginEmbedderPolicyConfig: ServerCrossOriginEmbedderPolicyDsl.() -> Unit) {
this.crossOriginEmbedderPolicy = ServerCrossOriginEmbedderPolicyDsl().apply(crossOriginEmbedderPolicyConfig).get()
}
/**
* Allows configuration for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a> header.
*
* @since 5.7
* @param crossOriginResourcePolicyConfig the customization to apply to the header
*/
fun crossOriginResourcePolicy(crossOriginResourcePolicyConfig: ServerCrossOriginResourcePolicyDsl.() -> Unit) {
this.crossOriginResourcePolicy = ServerCrossOriginResourcePolicyDsl().apply(crossOriginResourcePolicyConfig).get()
}
/**
* Disables HTTP response headers.
*/
@@ -235,15 +194,6 @@ class ServerHeadersDsl {
referrerPolicy?.also {
headers.referrerPolicy(referrerPolicy)
}
crossOriginOpenerPolicy?.also {
headers.crossOriginOpenerPolicy(crossOriginOpenerPolicy)
}
crossOriginEmbedderPolicy?.also {
headers.crossOriginEmbedderPolicy(crossOriginEmbedderPolicy)
}
crossOriginResourcePolicy?.also {
headers.crossOriginResourcePolicy(crossOriginResourcePolicy)
}
if (disabled) {
headers.disable()
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@
package org.springframework.security.config.web.servlet
import org.springframework.http.HttpMethod
import org.springframework.security.authorization.AuthorizationManager
import org.springframework.security.web.access.intercept.RequestAuthorizationContext
import org.springframework.security.web.util.matcher.AnyRequestMatcher
import org.springframework.security.web.util.matcher.RequestMatcher
@@ -38,25 +36,14 @@ abstract class AbstractRequestMatcherDsl {
protected data class MatcherAuthorizationRule(val matcher: RequestMatcher,
override val rule: String) : AuthorizationRule(rule)
protected data class MatcherAuthorizationManagerRule(val matcher: RequestMatcher,
override val rule: AuthorizationManager<RequestAuthorizationContext>) : AuthorizationManagerRule(rule)
protected data class PatternAuthorizationRule(val pattern: String,
val patternType: PatternType,
val servletPath: String? = null,
val httpMethod: HttpMethod? = null,
override val rule: String) : AuthorizationRule(rule)
protected data class PatternAuthorizationManagerRule(val pattern: String,
val patternType: PatternType,
val servletPath: String? = null,
val httpMethod: HttpMethod? = null,
override val rule: AuthorizationManager<RequestAuthorizationContext>) : AuthorizationManagerRule(rule)
protected abstract class AuthorizationRule(open val rule: String)
protected abstract class AuthorizationManagerRule(open val rule: AuthorizationManager<RequestAuthorizationContext>)
protected enum class PatternType {
ANT, MVC
}
@@ -1,260 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.web.servlet
import org.springframework.http.HttpMethod
import org.springframework.security.authorization.AuthenticatedAuthorizationManager
import org.springframework.security.authorization.AuthorityAuthorizationManager
import org.springframework.security.authorization.AuthorizationDecision
import org.springframework.security.authorization.AuthorizationManager
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer
import org.springframework.security.core.Authentication
import org.springframework.security.web.access.intercept.AuthorizationFilter
import org.springframework.security.web.access.intercept.RequestAuthorizationContext
import org.springframework.security.web.util.matcher.AnyRequestMatcher
import org.springframework.security.web.util.matcher.RequestMatcher
import org.springframework.util.ClassUtils
import java.util.function.Supplier
/**
* A Kotlin DSL to configure [HttpSecurity] request authorization using idiomatic Kotlin code.
*
* @author Yuriy Savchenko
* @since 5.7
* @property shouldFilterAllDispatcherTypes whether the [AuthorizationFilter] should filter all dispatcher types
*/
class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl() {
var shouldFilterAllDispatcherTypes: Boolean? = null
private val authorizationRules = mutableListOf<AuthorizationManagerRule>()
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
private val MVC_PRESENT = ClassUtils.isPresent(
HANDLER_MAPPING_INTROSPECTOR,
AuthorizeHttpRequestsDsl::class.java.classLoader)
private val PATTERN_TYPE = if (MVC_PRESENT) PatternType.MVC else PatternType.ANT
/**
* Adds a request authorization rule.
*
* @param matches the [RequestMatcher] to match incoming requests against
* @param access the [AuthorizationManager] to secure the matching request
* (i.e. created via hasAuthority("ROLE_USER"))
*/
fun authorize(matches: RequestMatcher = AnyRequestMatcher.INSTANCE,
access: AuthorizationManager<RequestAuthorizationContext>) {
authorizationRules.add(MatcherAuthorizationManagerRule(matches, access))
}
/**
* Adds a request authorization rule for an endpoint matching the provided
* pattern.
* If Spring MVC is on the classpath, it will use an MVC matcher.
* If Spring MVC is not on the classpath, it will use an ant matcher.
* The MVC will use the same rules that Spring MVC uses for matching.
* For example, often times a mapping of the path "/path" will match on
* "/path", "/path/", "/path.html", etc.
* If the current request will not be processed by Spring MVC, a reasonable default
* using the pattern as an ant pattern will be used.
*
* @param pattern the pattern to match incoming requests against.
* @param access the [AuthorizationManager] to secure the matching request
* (i.e. created via hasAuthority("ROLE_USER"))
*/
fun authorize(pattern: String,
access: AuthorizationManager<RequestAuthorizationContext>) {
authorizationRules.add(
PatternAuthorizationManagerRule(
pattern = pattern,
patternType = PATTERN_TYPE,
rule = access
)
)
}
/**
* Adds a request authorization rule for an endpoint matching the provided
* pattern.
* If Spring MVC is on the classpath, it will use an MVC matcher.
* If Spring MVC is not on the classpath, it will use an ant matcher.
* The MVC will use the same rules that Spring MVC uses for matching.
* For example, often times a mapping of the path "/path" will match on
* "/path", "/path/", "/path.html", etc.
* If the current request will not be processed by Spring MVC, a reasonable default
* using the pattern as an ant pattern will be used.
*
* @param method the HTTP method to match the income requests against.
* @param pattern the pattern to match incoming requests against.
* @param access the [AuthorizationManager] to secure the matching request
* (i.e. created via hasAuthority("ROLE_USER"))
*/
fun authorize(method: HttpMethod,
pattern: String,
access: AuthorizationManager<RequestAuthorizationContext>) {
authorizationRules.add(
PatternAuthorizationManagerRule(
pattern = pattern,
patternType = PATTERN_TYPE,
httpMethod = method,
rule = access
)
)
}
/**
* Adds a request authorization rule for an endpoint matching the provided
* pattern.
* If Spring MVC is on the classpath, it will use an MVC matcher.
* If Spring MVC is not on the classpath, it will use an ant matcher.
* The MVC will use the same rules that Spring MVC uses for matching.
* For example, often times a mapping of the path "/path" will match on
* "/path", "/path/", "/path.html", etc.
* If the current request will not be processed by Spring MVC, a reasonable default
* using the pattern as an ant pattern will be used.
*
* @param pattern the pattern to match incoming requests against.
* @param servletPath the servlet path to match incoming requests against. This
* only applies when using an MVC pattern matcher.
* @param access the [AuthorizationManager] to secure the matching request
* (i.e. created via hasAuthority("ROLE_USER"))
*/
fun authorize(pattern: String,
servletPath: String,
access: AuthorizationManager<RequestAuthorizationContext>) {
authorizationRules.add(
PatternAuthorizationManagerRule(
pattern = pattern,
patternType = PATTERN_TYPE,
servletPath = servletPath,
rule = access
)
)
}
/**
* Adds a request authorization rule for an endpoint matching the provided
* pattern.
* If Spring MVC is on the classpath, it will use an MVC matcher.
* If Spring MVC is not on the classpath, it will use an ant matcher.
* The MVC will use the same rules that Spring MVC uses for matching.
* For example, often times a mapping of the path "/path" will match on
* "/path", "/path/", "/path.html", etc.
* If the current request will not be processed by Spring MVC, a reasonable default
* using the pattern as an ant pattern will be used.
*
* @param method the HTTP method to match the income requests against.
* @param pattern the pattern to match incoming requests against.
* @param servletPath the servlet path to match incoming requests against. This
* only applies when using an MVC pattern matcher.
* @param access the [AuthorizationManager] to secure the matching request
* (i.e. created via hasAuthority("ROLE_USER"))
*/
fun authorize(method: HttpMethod,
pattern: String,
servletPath: String,
access: AuthorizationManager<RequestAuthorizationContext>) {
authorizationRules.add(
PatternAuthorizationManagerRule(
pattern = pattern,
patternType = PATTERN_TYPE,
servletPath = servletPath,
httpMethod = method,
rule = access
)
)
}
/**
* Specify that URLs require a particular authority.
*
* @param authority the authority to require (i.e. ROLE_USER, ROLE_ADMIN, etc).
* @return the [AuthorizationManager] with the provided authority
*/
fun hasAuthority(authority: String): AuthorizationManager<RequestAuthorizationContext> {
return AuthorityAuthorizationManager.hasAuthority(authority)
}
/**
* Specify that URLs require any of the provided authorities.
*
* @param authorities the authorities to require (i.e. ROLE_USER, ROLE_ADMIN, etc).
* @return the [AuthorizationManager] with the provided authorities
*/
fun hasAnyAuthority(vararg authorities: String): AuthorizationManager<RequestAuthorizationContext> {
return AuthorityAuthorizationManager.hasAnyAuthority(*authorities)
}
/**
* Specify that URLs require a particular role.
*
* @param role the role to require (i.e. USER, ADMIN, etc).
* @return the [AuthorizationManager] with the provided role
*/
fun hasRole(role: String): AuthorizationManager<RequestAuthorizationContext> {
return AuthorityAuthorizationManager.hasRole(role)
}
/**
* Specify that URLs require any of the provided roles.
*
* @param roles the roles to require (i.e. USER, ADMIN, etc).
* @return the [AuthorizationManager] with the provided roles
*/
fun hasAnyRole(vararg roles: String): AuthorizationManager<RequestAuthorizationContext> {
return AuthorityAuthorizationManager.hasAnyRole(*roles)
}
/**
* Specify that URLs are allowed by anyone.
*/
val permitAll: AuthorizationManager<RequestAuthorizationContext> =
AuthorizationManager { _: Supplier<Authentication>, _: RequestAuthorizationContext -> AuthorizationDecision(true) }
/**
* Specify that URLs are not allowed by anyone.
*/
val denyAll: AuthorizationManager<RequestAuthorizationContext> =
AuthorizationManager { _: Supplier<Authentication>, _: RequestAuthorizationContext -> AuthorizationDecision(false) }
/**
* Specify that URLs are allowed by any authenticated user.
*/
val authenticated: AuthorizationManager<RequestAuthorizationContext> =
AuthenticatedAuthorizationManager.authenticated()
internal fun get(): (AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry) -> Unit {
return { requests ->
authorizationRules.forEach { rule ->
when (rule) {
is MatcherAuthorizationManagerRule -> requests.requestMatchers(rule.matcher).access(rule.rule)
is PatternAuthorizationManagerRule -> {
when (rule.patternType) {
PatternType.ANT -> requests.antMatchers(rule.httpMethod, rule.pattern).access(rule.rule)
PatternType.MVC -> requests.mvcMatchers(rule.httpMethod, rule.pattern)
.apply { if (rule.servletPath != null) servletPath(rule.servletPath) }
.access(rule.rule)
}
}
}
}
shouldFilterAllDispatcherTypes?.also { shouldFilter ->
requests.shouldFilterAllDispatcherTypes(shouldFilter)
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More