Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c474fdfe71 |
@@ -24,7 +24,6 @@ atlassian-ide-plugin.xml
|
||||
.checkstyle
|
||||
s101plugin.state
|
||||
.attach_pid*
|
||||
.~lock.*#
|
||||
|
||||
!.idea/checkstyle-idea.xml
|
||||
!.idea/externalDependencies.xml
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ Be sure to read the https://docs.spring.io/spring-security/site/docs/current/ref
|
||||
Extensive JavaDoc for the Spring Security code is also available in the https://docs.spring.io/spring-security/site/docs/current/api/[Spring Security API Documentation].
|
||||
|
||||
== Quick Start
|
||||
See https://docs.spring.io/spring-security/site/docs/5.3.x/reference/html5/#servlet-hello[Hello Spring Security] to get started with a "Hello, World" application.
|
||||
See https://docs.spring.io/spring-security/site/docs/5.2.x/reference/html5/#servlet-hello[Hello Spring Security] to get started with a "Hello, World" application.
|
||||
|
||||
== Building from Source
|
||||
Spring Security uses a https://gradle.org[Gradle]-based build system.
|
||||
|
||||
+18
-6
@@ -6,11 +6,10 @@ buildscript {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.38'
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.23.4.RELEASE'
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
|
||||
classpath 'io.spring.nohttp:nohttp-gradle:0.0.10'
|
||||
classpath "io.freefair.gradle:aspectj-plugin:4.1.6"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
classpath "io.freefair.gradle:aspectj-plugin:4.0.2"
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
@@ -25,11 +24,8 @@ buildscript {
|
||||
maven { url 'https://plugins.gradle.org/m2/' }
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.nohttp'
|
||||
apply plugin: 'locks'
|
||||
apply plugin: 'io.spring.convention.root'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
|
||||
group = 'org.springframework.security'
|
||||
description = 'Spring Security'
|
||||
@@ -53,3 +49,19 @@ subprojects {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
}
|
||||
|
||||
if (project.hasProperty('artifactoryUsername')) {
|
||||
allprojects { project ->
|
||||
project.repositories { repos ->
|
||||
all { repo ->
|
||||
if (!repo.url.toString().startsWith("https://repo.spring.io/")) {
|
||||
return;
|
||||
}
|
||||
repo.credentials {
|
||||
username = artifactoryUsername
|
||||
password = artifactoryPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@ gradlePlugin {
|
||||
id = "trang"
|
||||
implementationClass = "trang.TrangPlugin"
|
||||
}
|
||||
locks {
|
||||
id = "locks"
|
||||
implementationClass = "lock.GlobalLockPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package lock;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class GlobalLockPlugin implements Plugin<Project> {
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getTasks().register("writeLocks", GlobalLockTask.class, writeAll -> {
|
||||
writeAll.setDescription("Writes the locks for all projects");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package lock;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class GlobalLockTask extends DefaultTask {
|
||||
@TaskAction
|
||||
public void lock() {
|
||||
Project taskProject = getProject();
|
||||
if (!taskProject.getGradle().getStartParameter().isWriteDependencyLocks()) {
|
||||
throw new IllegalStateException("You just specify --write-locks argument");
|
||||
}
|
||||
writeLocksFor(taskProject);
|
||||
taskProject.getSubprojects().forEach(new Consumer<Project>() {
|
||||
@Override
|
||||
public void accept(Project subproject) {
|
||||
writeLocksFor(subproject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void writeLocksFor(Project project) {
|
||||
project.getConfigurations().configureEach(new Action<Configuration>() {
|
||||
@Override
|
||||
public void execute(Configuration configuration) {
|
||||
if (configuration.isCanBeResolved()) {
|
||||
configuration.resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
apply plugin: 'io.spring.convention.spring-module'
|
||||
apply plugin: 'trang'
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
// NB: Don't add other compile time dependencies to the config module as this breaks tooling
|
||||
@@ -28,8 +27,6 @@ dependencies {
|
||||
optional'org.springframework:spring-web'
|
||||
optional'org.springframework:spring-webflux'
|
||||
optional'org.springframework:spring-websocket'
|
||||
optional 'org.jetbrains.kotlin:kotlin-reflect'
|
||||
optional 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||
|
||||
provided 'javax.servlet:javax.servlet-api'
|
||||
|
||||
@@ -42,6 +39,7 @@ dependencies {
|
||||
testCompile project(path : ':spring-security-web', configuration : 'tests')
|
||||
testCompile apachedsDependencies
|
||||
testCompile powerMock2Dependencies
|
||||
testCompile spockDependencies
|
||||
testCompile 'com.squareup.okhttp3:mockwebserver'
|
||||
testCompile 'ch.qos.logback:logback-classic'
|
||||
testCompile 'io.projectreactor.netty:reactor-netty'
|
||||
@@ -52,6 +50,7 @@ dependencies {
|
||||
testCompile('net.sourceforge.htmlunit:htmlunit') {
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
}
|
||||
testCompile 'org.codehaus.groovy:groovy-all'
|
||||
testCompile 'org.eclipse.persistence:javax.persistence'
|
||||
testCompile 'org.hibernate:hibernate-entitymanager'
|
||||
testCompile 'org.hsqldb:hsqldb'
|
||||
@@ -75,6 +74,7 @@ dependencies {
|
||||
exclude group: 'org.aspectj', module: 'aspectjrt'
|
||||
}
|
||||
|
||||
testRuntime 'cglib:cglib-nodep'
|
||||
testRuntime 'org.hsqldb:hsqldb'
|
||||
}
|
||||
|
||||
@@ -85,11 +85,4 @@ rncToXsd {
|
||||
xslFile = new File(rncDir, 'spring-security.xsl')
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
freeCompilerArgs = ["-Xjsr305=strict"]
|
||||
}
|
||||
}
|
||||
|
||||
build.dependsOn rncToXsd
|
||||
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.annotation.authentication.ldap
|
||||
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory
|
||||
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication
|
||||
import org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessorTests
|
||||
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class LdapAuthenticationProviderBuilderSecurityBuilderTests extends BaseSpringSpec {
|
||||
def "default configuration"() {
|
||||
when:
|
||||
loadConfig(DefaultLdapConfig)
|
||||
LdapAuthenticationProvider provider = ldapProvider()
|
||||
then:
|
||||
provider.authoritiesPopulator.groupRoleAttribute == "cn"
|
||||
provider.authoritiesPopulator.groupSearchBase == ""
|
||||
provider.authoritiesPopulator.groupSearchFilter == "(uniqueMember={0})"
|
||||
ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "ROLE_"
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLdapConfig extends BaseLdapProviderConfig {
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
}
|
||||
}
|
||||
|
||||
def "group roles custom"() {
|
||||
when:
|
||||
loadConfig(GroupRolesConfig)
|
||||
LdapAuthenticationProvider provider = ldapProvider()
|
||||
then:
|
||||
provider.authoritiesPopulator.groupRoleAttribute == "group"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class GroupRolesConfig extends BaseLdapProviderConfig {
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.groupRoleAttribute("group")
|
||||
}
|
||||
}
|
||||
|
||||
def "group search custom"() {
|
||||
when:
|
||||
loadConfig(GroupSearchConfig)
|
||||
LdapAuthenticationProvider provider = ldapProvider()
|
||||
then:
|
||||
provider.authoritiesPopulator.groupSearchFilter == "ou=groupName"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class GroupSearchConfig extends BaseLdapProviderConfig {
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.groupSearchFilter("ou=groupName");
|
||||
}
|
||||
}
|
||||
|
||||
def "role prefix custom"() {
|
||||
when:
|
||||
loadConfig(RolePrefixConfig)
|
||||
LdapAuthenticationProvider provider = ldapProvider()
|
||||
then:
|
||||
ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "role_"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RolePrefixConfig extends BaseLdapProviderConfig {
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.rolePrefix("role_")
|
||||
}
|
||||
}
|
||||
|
||||
def "bind authentication"() {
|
||||
when:
|
||||
loadConfig(BindAuthenticationConfig)
|
||||
AuthenticationManager auth = context.getBean(AuthenticationManager)
|
||||
then:
|
||||
auth
|
||||
auth.authenticate(new UsernamePasswordAuthenticationToken("bob","bobspassword")).authorities.collect { it.authority }.sort() == ["ROLE_DEVELOPERS"]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class BindAuthenticationConfig extends BaseLdapServerConfig {
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.groupSearchBase("ou=groups")
|
||||
.groupSearchFilter("(member={0})")
|
||||
.userDnPatterns("uid={0},ou=people");
|
||||
}
|
||||
}
|
||||
|
||||
def "SEC-2472: Can use crypto PasswordEncoder"() {
|
||||
setup:
|
||||
loadConfig(PasswordEncoderConfig)
|
||||
when:
|
||||
AuthenticationManager auth = context.getBean(AuthenticationManager)
|
||||
then:
|
||||
auth.authenticate(new UsernamePasswordAuthenticationToken("bcrypt","password")).authorities.collect { it.authority }.sort() == ["ROLE_DEVELOPERS"]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class PasswordEncoderConfig extends BaseLdapServerConfig {
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.passwordEncoder(new BCryptPasswordEncoder())
|
||||
.groupSearchBase("ou=groups")
|
||||
.groupSearchFilter("(member={0})")
|
||||
.userDnPatterns("uid={0},ou=people");
|
||||
}
|
||||
}
|
||||
|
||||
def ldapProvider() {
|
||||
context.getBean(AuthenticationManager).providers[0]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static abstract class BaseLdapServerConfig extends BaseLdapProviderConfig {
|
||||
@Bean
|
||||
public ApacheDSContainer ldapServer() throws Exception {
|
||||
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
||||
apacheDSContainer.setPort(getPort());
|
||||
return apacheDSContainer;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalAuthentication
|
||||
@Import(ObjectPostProcessorConfiguration)
|
||||
static abstract class BaseLdapProviderConfig {
|
||||
|
||||
@Bean
|
||||
public BaseLdapPathContextSource contextSource() throws Exception {
|
||||
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
|
||||
"ldap://127.0.0.1:"+ getPort() + "/dc=springframework,dc=org")
|
||||
contextSource.userDn = "uid=admin,ou=system"
|
||||
contextSource.password = "secret"
|
||||
contextSource.afterPropertiesSet()
|
||||
return contextSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth) {
|
||||
configure(auth)
|
||||
auth.build()
|
||||
}
|
||||
|
||||
abstract protected void configure(AuthenticationManagerBuilder auth) throws Exception
|
||||
}
|
||||
|
||||
static Integer port;
|
||||
|
||||
static int getPort() {
|
||||
if(port == null) {
|
||||
ServerSocket socket = new ServerSocket(0)
|
||||
port = socket.localPort
|
||||
socket.close()
|
||||
}
|
||||
port
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.annotation.authentication.ldap
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Eddú Meléndez
|
||||
*
|
||||
*/
|
||||
class LdapAuthenticationProviderConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "authentication-manager support multiple default ldap contexts (ports dynamically allocated)"() {
|
||||
when:
|
||||
loadConfig(MultiLdapAuthenticationProvidersConfig)
|
||||
then:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob","bobspassword"))
|
||||
}
|
||||
|
||||
def "authentication-manager support multiple ldap context with default role prefix" () {
|
||||
when:
|
||||
loadConfig(MultiLdapAuthenticationProvidersConfig)
|
||||
then:
|
||||
def authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob", "bobspassword"))
|
||||
authenticate.authorities.contains(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))
|
||||
}
|
||||
|
||||
def "authentication-manager support multiple ldap context with custom role prefix"() {
|
||||
when:
|
||||
loadConfig(MultiLdapWithCustomRolePrefixAuthenticationProvidersConfig)
|
||||
then:
|
||||
def authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob", "bobspassword"))
|
||||
authenticate.authorities.contains(new SimpleGrantedAuthority("ROL_DEVELOPERS"))
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class MultiLdapAuthenticationProvidersConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.groupSearchBase("ou=groups")
|
||||
.groupSearchFilter("(member={0})")
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.and()
|
||||
.ldapAuthentication()
|
||||
.groupSearchBase("ou=groups")
|
||||
.groupSearchFilter("(member={0})")
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class MultiLdapWithCustomRolePrefixAuthenticationProvidersConfig extends
|
||||
WebSecurityConfigurerAdapter {
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.groupSearchBase("ou=groups")
|
||||
.groupSearchFilter("(member={0})")
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.rolePrefix("ROL_")
|
||||
.and()
|
||||
.ldapAuthentication()
|
||||
.groupSearchBase("ou=groups")
|
||||
.groupSearchFilter("(member={0})")
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.rolePrefix("RUOLO_")
|
||||
}
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.annotation.authentication.ldap
|
||||
|
||||
import static org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.*
|
||||
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.LdapAuthenticationProviderConfig;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
|
||||
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
|
||||
import org.springframework.security.ldap.userdetails.PersonContextMapper;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class NamespaceLdapAuthenticationProviderTests extends BaseSpringSpec {
|
||||
def "ldap-authentication-provider"() {
|
||||
when:
|
||||
loadConfig(LdapAuthenticationProviderConfig)
|
||||
then:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob","bobspassword"))
|
||||
}
|
||||
|
||||
def "ldap-authentication-provider custom"() {
|
||||
when:
|
||||
loadConfig(CustomLdapAuthenticationProviderConfig)
|
||||
LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider)
|
||||
then:
|
||||
provider.authoritiesPopulator.groupRoleAttribute == "cn"
|
||||
provider.authoritiesPopulator.groupSearchBase == "ou=groups"
|
||||
provider.authoritiesPopulator.groupSearchFilter == "(member={0})"
|
||||
ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "PREFIX_"
|
||||
provider.userDetailsContextMapper instanceof PersonContextMapper
|
||||
provider.authenticator.getUserDns("user") == ["uid=user,ou=people"]
|
||||
provider.authenticator.userSearch.searchBase == "ou=users"
|
||||
provider.authenticator.userSearch.searchFilter == "(uid={0})"
|
||||
}
|
||||
|
||||
def "SEC-2490: ldap-authentication-provider custom LdapAuthoritiesPopulator"() {
|
||||
setup:
|
||||
LdapAuthoritiesPopulator LAP = Mock()
|
||||
CustomAuthoritiesPopulatorConfig.LAP = LAP
|
||||
when:
|
||||
loadConfig(CustomAuthoritiesPopulatorConfig)
|
||||
LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider)
|
||||
then:
|
||||
provider.authoritiesPopulator == LAP
|
||||
}
|
||||
|
||||
def "ldap-authentication-provider password compare"() {
|
||||
when:
|
||||
loadConfig(PasswordCompareLdapConfig)
|
||||
LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider)
|
||||
then:
|
||||
provider.authenticator instanceof PasswordComparisonAuthenticator
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob","bobspassword"))
|
||||
}
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
package org.springframework.security.config.ldap
|
||||
|
||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder
|
||||
|
||||
import static org.mockito.Mockito.*
|
||||
|
||||
import java.text.MessageFormat
|
||||
|
||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder
|
||||
import org.springframework.security.config.AbstractXmlConfigTests
|
||||
import org.springframework.security.config.BeanIds
|
||||
import org.springframework.security.util.FieldUtils
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.context.ApplicationContextException
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
class LdapProviderBeanDefinitionParserTests extends AbstractXmlConfigTests {
|
||||
|
||||
// SEC-1182
|
||||
def multipleProvidersAreSupported() {
|
||||
xml.'ldap-server'(url: 'ldap://blah:389/dc=blah')
|
||||
xml.'authentication-manager'() {
|
||||
'ldap-authentication-provider'('group-search-filter': 'member={0}')
|
||||
'ldap-authentication-provider'('group-search-filter': 'uniqueMember={0}')
|
||||
}
|
||||
|
||||
createAppContext('')
|
||||
|
||||
def providers = appContext.getBean(BeanIds.AUTHENTICATION_MANAGER).providers
|
||||
|
||||
expect:
|
||||
|
||||
providers.size() == 2
|
||||
providers[0].authoritiesPopulator.groupSearchFilter == "member={0}"
|
||||
providers[1].authoritiesPopulator.groupSearchFilter == "uniqueMember={0}"
|
||||
}
|
||||
|
||||
|
||||
def simpleProviderAuthenticatesCorrectly() {
|
||||
xml.'ldap-server'(ldif:'test-server.ldif')
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'('group-search-filter':'member={0}')
|
||||
}
|
||||
|
||||
createAppContext('')
|
||||
|
||||
def am = appContext.getBean(BeanIds.AUTHENTICATION_MANAGER)
|
||||
|
||||
when:
|
||||
def auth = am.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"))
|
||||
def ben = auth.principal;
|
||||
|
||||
then:
|
||||
ben.authorities.size() == 3
|
||||
}
|
||||
|
||||
def missingServerEltCausesConfigException() {
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'()
|
||||
}
|
||||
|
||||
when:
|
||||
createAppContext('')
|
||||
|
||||
then:
|
||||
thrown(ApplicationContextException)
|
||||
}
|
||||
|
||||
def supportsPasswordComparisonAuthentication() {
|
||||
xml.'ldap-server'(ldif:'test-server.ldif')
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people')
|
||||
'password-compare'
|
||||
}
|
||||
createAppContext('')
|
||||
def am = appContext.getBean(BeanIds.AUTHENTICATION_MANAGER)
|
||||
|
||||
when:
|
||||
def auth = am.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"))
|
||||
|
||||
then:
|
||||
auth != null
|
||||
notThrown(AuthenticationException)
|
||||
}
|
||||
|
||||
def supportsPasswordComparisonAuthenticationWithPasswordEncoder() {
|
||||
xml.'ldap-server'(ldif:'test-server.ldif')
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people') {
|
||||
'password-compare'('password-attribute': 'uid') {
|
||||
'password-encoder'(ref: 'passwordEncoder')
|
||||
}
|
||||
}
|
||||
}
|
||||
xml.'b:bean'(id: 'passwordEncoder', 'class' : NoOpPasswordEncoder.name, 'factory-method': 'getInstance')
|
||||
|
||||
createAppContext('')
|
||||
def am = appContext.getBean(BeanIds.AUTHENTICATION_MANAGER)
|
||||
|
||||
when:
|
||||
def auth = am.authenticate(new UsernamePasswordAuthenticationToken("ben", "ben"))
|
||||
|
||||
then:
|
||||
auth != null
|
||||
notThrown(AuthenticationException)
|
||||
}
|
||||
|
||||
def 'SEC-2472: Supports Crypto PasswordEncoder'() {
|
||||
setup:
|
||||
xml.'ldap-server'(ldif:'test-server.ldif')
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'('user-dn-pattern': 'uid={0},ou=people') {
|
||||
'password-compare'() {
|
||||
'password-encoder'(ref: 'pe')
|
||||
}
|
||||
}
|
||||
}
|
||||
xml.'b:bean'(id:'pe','class':BCryptPasswordEncoder.class.name)
|
||||
|
||||
createAppContext('')
|
||||
def am = appContext.getBean(BeanIds.AUTHENTICATION_MANAGER)
|
||||
|
||||
when:
|
||||
def auth = am.authenticate(new UsernamePasswordAuthenticationToken("bcrypt", 'password'))
|
||||
|
||||
then:
|
||||
auth != null
|
||||
}
|
||||
|
||||
def inetOrgContextMapperIsSupported() {
|
||||
xml.'ldap-server'(url: 'ldap://127.0.0.1:343/dc=springframework,dc=org')
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'('user-details-class' :'inetOrgPerson')
|
||||
}
|
||||
createAppContext('')
|
||||
|
||||
expect:
|
||||
appContext.getBean(BeanIds.AUTHENTICATION_MANAGER).providers[0].userDetailsContextMapper instanceof InetOrgPersonContextMapper
|
||||
}
|
||||
|
||||
def ldapAuthenticationProviderWorksWithPlaceholders() {
|
||||
System.setProperty('udp','people')
|
||||
System.setProperty('gsf','member')
|
||||
|
||||
xml.'ldap-server'()
|
||||
xml.'authentication-manager'{
|
||||
'ldap-authentication-provider'('user-dn-pattern':'uid={0},ou=${udp}','group-search-filter':'${gsf}={0}')
|
||||
}
|
||||
bean(PropertyPlaceholderConfigurer.class.name, PropertyPlaceholderConfigurer.class)
|
||||
|
||||
createAppContext('')
|
||||
def provider = this.appContext.getBean(BeanIds.AUTHENTICATION_MANAGER).providers[0]
|
||||
|
||||
expect:
|
||||
[new MessageFormat("uid={0},ou=people")] == FieldUtils.getFieldValue(provider,"authenticator.userDnFormat")
|
||||
"member={0}" == FieldUtils.getFieldValue(provider, "authoritiesPopulator.groupSearchFilter")
|
||||
}
|
||||
}
|
||||
-250
@@ -1,250 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.annotation.authentication.ldap;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
|
||||
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
||||
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.singleton;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||
|
||||
public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Test
|
||||
public void defaultConfiguration() {
|
||||
this.spring.register(DefaultLdapConfig.class).autowire();
|
||||
LdapAuthenticationProvider provider = ldapProvider();
|
||||
|
||||
LdapAuthoritiesPopulator authoritiesPopulator = getAuthoritiesPopulator(provider);
|
||||
assertThat(authoritiesPopulator).hasFieldOrPropertyWithValue("groupRoleAttribute", "cn");
|
||||
assertThat(authoritiesPopulator).hasFieldOrPropertyWithValue("groupSearchBase", "");
|
||||
assertThat(authoritiesPopulator).hasFieldOrPropertyWithValue("groupSearchFilter", "(uniqueMember={0})");
|
||||
assertThat(ReflectionTestUtils.getField(getAuthoritiesMapper(provider), "prefix")).isEqualTo("ROLE_");
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class DefaultLdapConfig extends BaseLdapProviderConfig {
|
||||
// @formatter:off
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.userDnPatterns("uid={0},ou=people");
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupRolesCustom() {
|
||||
this.spring.register(GroupRolesConfig.class).autowire();
|
||||
LdapAuthenticationProvider provider = ldapProvider();
|
||||
|
||||
assertThat(ReflectionTestUtils.getField(getAuthoritiesPopulator(provider), "groupRoleAttribute")).isEqualTo("group");
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class GroupRolesConfig extends BaseLdapProviderConfig {
|
||||
// @formatter:off
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.groupRoleAttribute("group");
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupSearchCustom() {
|
||||
this.spring.register(GroupSearchConfig.class).autowire();
|
||||
LdapAuthenticationProvider provider = ldapProvider();
|
||||
|
||||
assertThat(ReflectionTestUtils.getField(getAuthoritiesPopulator(provider), "groupSearchFilter")).isEqualTo("ou=groupName");
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class GroupSearchConfig extends BaseLdapProviderConfig {
|
||||
// @formatter:off
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.groupSearchFilter("ou=groupName");
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rolePrefixCustom() {
|
||||
this.spring.register(RolePrefixConfig.class).autowire();
|
||||
LdapAuthenticationProvider provider = ldapProvider();
|
||||
|
||||
assertThat(ReflectionTestUtils.getField(getAuthoritiesMapper(provider), "prefix")).isEqualTo("role_");
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RolePrefixConfig extends BaseLdapProviderConfig {
|
||||
// @formatter:off
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.rolePrefix("role_");
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindAuthentication() throws Exception {
|
||||
this.spring.register(BindAuthenticationConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||
.andExpect(authenticated().withUsername("bob").withAuthorities(singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class BindAuthenticationConfig extends BaseLdapServerConfig {
|
||||
// @formatter:off
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.groupSearchBase("ou=groups")
|
||||
.groupSearchFilter("(member={0})")
|
||||
.userDnPatterns("uid={0},ou=people");
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
// SEC-2472
|
||||
@Test
|
||||
public void canUseCryptoPasswordEncoder() throws Exception {
|
||||
this.spring.register(PasswordEncoderConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bcrypt").password("password"))
|
||||
.andExpect(authenticated().withUsername("bcrypt").withAuthorities(singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class PasswordEncoderConfig extends BaseLdapServerConfig {
|
||||
// @formatter:off
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.passwordEncoder(new BCryptPasswordEncoder())
|
||||
.groupSearchBase("ou=groups")
|
||||
.groupSearchFilter("(member={0})")
|
||||
.userDnPatterns("uid={0},ou=people");
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private LdapAuthenticationProvider ldapProvider() {
|
||||
return ((List<LdapAuthenticationProvider>) ReflectionTestUtils.getField(authenticationManager, "providers")).get(0);
|
||||
}
|
||||
|
||||
private LdapAuthoritiesPopulator getAuthoritiesPopulator(LdapAuthenticationProvider provider) {
|
||||
return (LdapAuthoritiesPopulator) ReflectionTestUtils.getField(provider, "authoritiesPopulator");
|
||||
}
|
||||
|
||||
private GrantedAuthoritiesMapper getAuthoritiesMapper(LdapAuthenticationProvider provider) {
|
||||
return (GrantedAuthoritiesMapper) ReflectionTestUtils.getField(provider, "authoritiesMapper");
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static abstract class BaseLdapServerConfig extends BaseLdapProviderConfig {
|
||||
@Bean
|
||||
public ApacheDSContainer ldapServer() throws Exception {
|
||||
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
||||
apacheDSContainer.setPort(getPort());
|
||||
return apacheDSContainer;
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalAuthentication
|
||||
@Import(ObjectPostProcessorConfiguration.class)
|
||||
static abstract class BaseLdapProviderConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Bean
|
||||
public BaseLdapPathContextSource contextSource() throws Exception {
|
||||
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
|
||||
"ldap://127.0.0.1:" + getPort() + "/dc=springframework,dc=org");
|
||||
contextSource.setUserDn("uid=admin,ou=system");
|
||||
contextSource.setPassword("secret");
|
||||
contextSource.afterPropertiesSet();
|
||||
return contextSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth) throws Exception {
|
||||
configure(auth);
|
||||
return auth.build();
|
||||
}
|
||||
|
||||
abstract protected void configure(AuthenticationManagerBuilder auth) throws Exception;
|
||||
}
|
||||
|
||||
static Integer port;
|
||||
|
||||
static int getPort() throws IOException {
|
||||
if (port == null) {
|
||||
ServerSocket socket = new ServerSocket(0);
|
||||
port = socket.getLocalPort();
|
||||
socket.close();
|
||||
}
|
||||
return port;
|
||||
}
|
||||
}
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.annotation.authentication.ldap;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static java.util.Collections.singleton;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||
|
||||
public class LdapAuthenticationProviderConfigurerTests {
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void authenticationManagerSupportMultipleDefaultLdapContextsWithPortsDynamicallyAllocated() throws Exception {
|
||||
this.spring.register(MultiLdapAuthenticationProvidersConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||
.andExpect(authenticated().withUsername("bob"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticationManagerSupportMultipleLdapContextWithDefaultRolePrefix() throws Exception {
|
||||
this.spring.register(MultiLdapAuthenticationProvidersConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||
.andExpect(authenticated().withUsername("bob").withAuthorities(singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticationManagerSupportMultipleLdapContextWithCustomRolePrefix() throws Exception {
|
||||
this.spring.register(MultiLdapWithCustomRolePrefixAuthenticationProvidersConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||
.andExpect(authenticated().withUsername("bob").withAuthorities(singleton(new SimpleGrantedAuthority("ROL_DEVELOPERS"))));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class MultiLdapAuthenticationProvidersConfig extends WebSecurityConfigurerAdapter {
|
||||
// @formatter:off
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.groupSearchBase("ou=groups")
|
||||
.groupSearchFilter("(member={0})")
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.and()
|
||||
.ldapAuthentication()
|
||||
.groupSearchBase("ou=groups")
|
||||
.groupSearchFilter("(member={0})")
|
||||
.userDnPatterns("uid={0},ou=people");
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class MultiLdapWithCustomRolePrefixAuthenticationProvidersConfig extends WebSecurityConfigurerAdapter {
|
||||
// @formatter:off
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.groupSearchBase("ou=groups")
|
||||
.groupSearchFilter("(member={0})")
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.rolePrefix("ROL_")
|
||||
.and()
|
||||
.ldapAuthentication()
|
||||
.groupSearchBase("ou=groups")
|
||||
.groupSearchFilter("(member={0})")
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.rolePrefix("RUOLO_");
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.annotation.authentication.ldap;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.core.support.LdapContextSource;
|
||||
import org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.CustomAuthoritiesPopulatorConfig;
|
||||
import org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.CustomLdapAuthenticationProviderConfig;
|
||||
import org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.LdapAuthenticationProviderConfig;
|
||||
import org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.PasswordCompareLdapConfig;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||
|
||||
public class NamespaceLdapAuthenticationProviderTests {
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private FilterChainProxy filterChainProxy;
|
||||
|
||||
@Test
|
||||
public void ldapAuthenticationProvider() throws Exception {
|
||||
this.spring.register(LdapAuthenticationProviderConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||
.andExpect(authenticated().withUsername("bob"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ldapAuthenticationProviderCustom() throws Exception {
|
||||
this.spring.register(CustomLdapAuthenticationProviderConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||
.andExpect(authenticated().withAuthorities(Collections.singleton(new SimpleGrantedAuthority("PREFIX_DEVELOPERS"))));
|
||||
}
|
||||
|
||||
// SEC-2490
|
||||
@Test
|
||||
public void ldapAuthenticationProviderCustomLdapAuthoritiesPopulator() throws Exception {
|
||||
LdapContextSource contextSource = new DefaultSpringSecurityContextSource("ldap://blah.example.com:789/dc=springframework,dc=org");
|
||||
CustomAuthoritiesPopulatorConfig.LAP = new DefaultLdapAuthoritiesPopulator(contextSource, 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 ldapAuthenticationProviderPasswordCompare() throws Exception {
|
||||
this.spring.register(PasswordCompareLdapConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bcrypt").password("password"))
|
||||
.andExpect(authenticated().withUsername("bcrypt"));
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -18,7 +18,7 @@ package org.springframework.security.config.annotation.authentication.ldap;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
||||
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
|
||||
import org.springframework.security.ldap.userdetails.PersonContextMapper;
|
||||
|
||||
@@ -90,7 +90,7 @@ public class NamespaceLdapAuthenticationProviderTestsConfigs {
|
||||
.groupSearchBase("ou=groups")
|
||||
.userSearchFilter("(uid={0})")
|
||||
.passwordCompare()
|
||||
.passwordEncoder(new BCryptPasswordEncoder()) // ldap-authentication-provider/password-compare/password-encoder@ref
|
||||
.passwordEncoder(NoOpPasswordEncoder.getInstance()) // ldap-authentication-provider/password-compare/password-encoder@ref
|
||||
.passwordAttribute("userPassword"); // ldap-authentication-provider/password-compare@password-attribute
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
-177
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class LdapProviderBeanDefinitionParserTests {
|
||||
InMemoryXmlApplicationContext appCtx;
|
||||
|
||||
@After
|
||||
public void closeAppContext() {
|
||||
if (appCtx != null) {
|
||||
appCtx.close();
|
||||
appCtx = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleProviderAuthenticatesCorrectly() {
|
||||
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
|
||||
+ "<authentication-manager>"
|
||||
+ " <ldap-authentication-provider group-search-filter='member={0}' />"
|
||||
+ "</authentication-manager>"
|
||||
);
|
||||
|
||||
AuthenticationManager authenticationManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, AuthenticationManager.class);
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
|
||||
UserDetails ben = (UserDetails) auth.getPrincipal();
|
||||
assertThat(ben.getAuthorities()).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleProvidersAreSupported() {
|
||||
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
|
||||
+ "<authentication-manager>"
|
||||
+ " <ldap-authentication-provider group-search-filter='member={0}' />"
|
||||
+ " <ldap-authentication-provider group-search-filter='uniqueMember={0}' />"
|
||||
+ "</authentication-manager>"
|
||||
);
|
||||
|
||||
ProviderManager providerManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
|
||||
assertThat(providerManager.getProviders()).hasSize(2);
|
||||
assertThat(providerManager.getProviders())
|
||||
.extracting("authoritiesPopulator.groupSearchFilter")
|
||||
.containsExactly("member={0}", "uniqueMember={0}");
|
||||
}
|
||||
|
||||
@Test(expected = ApplicationContextException.class)
|
||||
public void missingServerEltCausesConfigException() {
|
||||
new InMemoryXmlApplicationContext("<authentication-manager>"
|
||||
+ " <ldap-authentication-provider />"
|
||||
+ "</authentication-manager>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsPasswordComparisonAuthentication() {
|
||||
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
|
||||
+ "<authentication-manager>"
|
||||
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>"
|
||||
+ " <password-compare />"
|
||||
+ " </ldap-authentication-provider>"
|
||||
+ "</authentication-manager>"
|
||||
);
|
||||
|
||||
AuthenticationManager authenticationManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, AuthenticationManager.class);
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
|
||||
|
||||
assertThat(auth).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsPasswordComparisonAuthenticationWithPasswordEncoder() {
|
||||
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
|
||||
+ "<authentication-manager>"
|
||||
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>"
|
||||
+ " <password-compare password-attribute='uid'>"
|
||||
+ " <password-encoder ref='passwordEncoder' />"
|
||||
+ " </password-compare>"
|
||||
+ " </ldap-authentication-provider>"
|
||||
+ "</authentication-manager>"
|
||||
+ "<b:bean id='passwordEncoder' class='org.springframework.security.crypto.password.NoOpPasswordEncoder' factory-method='getInstance' />"
|
||||
);
|
||||
|
||||
AuthenticationManager authenticationManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, AuthenticationManager.class);
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("ben", "ben"));
|
||||
|
||||
assertThat(auth).isNotNull();
|
||||
}
|
||||
|
||||
// SEC-2472
|
||||
@Test
|
||||
public void supportsCryptoPasswordEncoder() {
|
||||
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif'/>"
|
||||
+ "<authentication-manager>"
|
||||
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>"
|
||||
+ " <password-compare>"
|
||||
+ " <password-encoder ref='pe' />"
|
||||
+ " </password-compare>"
|
||||
+ " </ldap-authentication-provider>"
|
||||
+ "</authentication-manager>"
|
||||
+ "<b:bean id='pe' class='org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder' />"
|
||||
);
|
||||
|
||||
AuthenticationManager authenticationManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, AuthenticationManager.class);
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bcrypt", "password"));
|
||||
|
||||
assertThat(auth).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inetOrgContextMapperIsSupported() {
|
||||
appCtx = new InMemoryXmlApplicationContext("<ldap-server url='ldap://127.0.0.1:343/dc=springframework,dc=org'/>"
|
||||
+ "<authentication-manager>"
|
||||
+ " <ldap-authentication-provider user-details-class='inetOrgPerson' />"
|
||||
+ "</authentication-manager>"
|
||||
);
|
||||
|
||||
ProviderManager providerManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
|
||||
assertThat(providerManager.getProviders()).hasSize(1);
|
||||
assertThat(providerManager.getProviders())
|
||||
.extracting("userDetailsContextMapper")
|
||||
.allSatisfy(contextMapper -> assertThat(contextMapper).isInstanceOf(InetOrgPersonContextMapper.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ldapAuthenticationProviderWorksWithPlaceholders() {
|
||||
System.setProperty("udp", "people");
|
||||
System.setProperty("gsf", "member");
|
||||
appCtx = new InMemoryXmlApplicationContext("<ldap-server />"
|
||||
+ "<authentication-manager>"
|
||||
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=${udp}' group-search-filter='${gsf}={0}' />"
|
||||
+ "</authentication-manager>"
|
||||
+ "<b:bean id='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' />"
|
||||
);
|
||||
|
||||
ProviderManager providerManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
|
||||
assertThat(providerManager.getProviders()).hasSize(1);
|
||||
|
||||
AuthenticationProvider authenticationProvider = providerManager.getProviders().get(0);
|
||||
assertThat(authenticationProvider)
|
||||
.extracting("authenticator.userDnFormat")
|
||||
.satisfies(messageFormats -> assertThat(messageFormats).isEqualTo(new MessageFormat[]{new MessageFormat("uid={0},ou=people")}));
|
||||
assertThat(authenticationProvider)
|
||||
.extracting("authoritiesPopulator.groupSearchFilter")
|
||||
.satisfies(searchFilter -> assertThat(searchFilter).isEqualTo("member={0}"));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -70,14 +70,6 @@ public abstract class Elements {
|
||||
public static final String CORS = "cors";
|
||||
public static final String CSRF = "csrf";
|
||||
|
||||
public static final String OAUTH2_RESOURCE_SERVER = "oauth2-resource-server";
|
||||
public static final String JWT = "jwt";
|
||||
public static final String OPAQUE_TOKEN = "opaque-token";
|
||||
|
||||
public static final String WEBSOCKET_MESSAGE_BROKER = "websocket-message-broker";
|
||||
public static final String INTERCEPT_MESSAGE = "intercept-message";
|
||||
|
||||
public static final String OAUTH2_LOGIN = "oauth2-login";
|
||||
public static final String OAUTH2_CLIENT = "oauth2-client";
|
||||
public static final String CLIENT_REGISTRATIONS = "client-registrations";
|
||||
}
|
||||
|
||||
+3
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2009-2020 the original author or authors.
|
||||
* Copyright 2009-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.
|
||||
@@ -41,7 +41,6 @@ import org.springframework.security.config.ldap.LdapUserServiceBeanDefinitionPar
|
||||
import org.springframework.security.config.method.GlobalMethodSecurityBeanDefinitionParser;
|
||||
import org.springframework.security.config.method.InterceptMethodsBeanDefinitionDecorator;
|
||||
import org.springframework.security.config.method.MethodSecurityMetadataSourceBeanDefinitionParser;
|
||||
import org.springframework.security.config.oauth2.client.ClientRegistrationsBeanDefinitionParser;
|
||||
import org.springframework.security.config.websocket.WebSocketMessageBrokerSecurityBeanDefinitionParser;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -87,7 +86,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.3. Please update your schema declarations to the 5.3 schema.",
|
||||
+ "with Spring Security 5.2. Please update your schema declarations to the 5.2 schema.",
|
||||
element);
|
||||
}
|
||||
String name = pc.getDelegate().getLocalName(element);
|
||||
@@ -193,7 +192,6 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
new FilterInvocationSecurityMetadataSourceParser());
|
||||
parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser());
|
||||
filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
|
||||
parsers.put(Elements.CLIENT_REGISTRATIONS, new ClientRegistrationsBeanDefinitionParser());
|
||||
}
|
||||
|
||||
if (ClassUtils.isPresent(MESSAGE_CLASSNAME, getClass().getClassLoader())) {
|
||||
@@ -223,7 +221,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\\.3.*.xsd.*")
|
||||
return schemaLocation.matches("(?m).*spring-security-5\\.2.*.xsd.*")
|
||||
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|
||||
|| !schemaLocation.matches("(?m).*spring-security.*");
|
||||
}
|
||||
|
||||
+2
-2
@@ -286,10 +286,10 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
|
||||
* @return the {@link SecurityBuilder} for further customizations
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public B objectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
public O objectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
|
||||
this.objectPostProcessor = objectPostProcessor;
|
||||
return (B) this;
|
||||
return (O) this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+12
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -19,10 +19,13 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
/**
|
||||
* Lazily initializes the global authentication with an {@link AuthenticationProvider} if it is
|
||||
* not yet configured and there is only a single Bean of that type.
|
||||
* Lazily initializes the global authentication with a {@link UserDetailsService} if it is
|
||||
* not yet configured and there is only a single Bean of that type. Optionally, if a
|
||||
* {@link PasswordEncoder} is defined will wire this up too.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.1
|
||||
@@ -46,10 +49,10 @@ class InitializeAuthenticationProviderBeanManagerConfigurer
|
||||
|
||||
@Override
|
||||
public void init(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.apply(new InitializeAuthenticationProviderManagerConfigurer());
|
||||
auth.apply(new InitializeUserDetailsManagerConfigurer());
|
||||
}
|
||||
|
||||
class InitializeAuthenticationProviderManagerConfigurer
|
||||
class InitializeUserDetailsManagerConfigurer
|
||||
extends GlobalAuthenticationConfigurerAdapter {
|
||||
@Override
|
||||
public void configure(AuthenticationManagerBuilder auth) {
|
||||
@@ -67,17 +70,17 @@ class InitializeAuthenticationProviderBeanManagerConfigurer
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a bean of the requested class if there's just a single registered component, null otherwise.
|
||||
* @return
|
||||
*/
|
||||
private <T> T getBeanOrNull(Class<T> type) {
|
||||
String[] beanNames = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
|
||||
String[] userDetailsBeanNames = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
|
||||
.getBeanNamesForType(type);
|
||||
if (beanNames.length != 1) {
|
||||
if (userDetailsBeanNames.length != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return InitializeAuthenticationProviderBeanManagerConfigurer.this.context
|
||||
.getBean(beanNames[0], type);
|
||||
.getBean(userDetailsBeanNames[0], type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -85,14 +85,14 @@ class InitializeUserDetailsBeanManagerConfigurer
|
||||
* @return a bean of the requested class if there's just a single registered component, null otherwise.
|
||||
*/
|
||||
private <T> T getBeanOrNull(Class<T> type) {
|
||||
String[] beanNames = InitializeUserDetailsBeanManagerConfigurer.this.context
|
||||
String[] userDetailsBeanNames = InitializeUserDetailsBeanManagerConfigurer.this.context
|
||||
.getBeanNamesForType(type);
|
||||
if (beanNames.length != 1) {
|
||||
if (userDetailsBeanNames.length != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return InitializeUserDetailsBeanManagerConfigurer.this.context
|
||||
.getBean(beanNames[0], type);
|
||||
.getBean(userDetailsBeanNames[0], type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -384,7 +384,7 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
|
||||
*/
|
||||
public final class PasswordCompareConfigurer {
|
||||
|
||||
/**
|
||||
/**Us
|
||||
* Allows specifying the {@link PasswordEncoder} to use. The default is
|
||||
* {@link org.springframework.security.crypto.password.NoOpPasswordEncoder}.
|
||||
* @param passwordEncoder the {@link PasswordEncoder} to use
|
||||
|
||||
+6
-70
@@ -30,7 +30,6 @@ import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;
|
||||
import org.springframework.security.rsocket.api.PayloadInterceptor;
|
||||
import org.springframework.security.rsocket.authentication.AuthenticationPayloadExchangeConverter;
|
||||
import org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;
|
||||
import org.springframework.security.rsocket.authentication.AnonymousPayloadInterceptor;
|
||||
import org.springframework.security.rsocket.authentication.AuthenticationPayloadInterceptor;
|
||||
@@ -45,7 +44,6 @@ import org.springframework.security.rsocket.util.matcher.RoutePayloadExchangeMat
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -118,8 +116,6 @@ public class RSocketSecurity {
|
||||
|
||||
private BasicAuthenticationSpec basicAuthSpec;
|
||||
|
||||
private SimpleAuthenticationSpec simpleAuthSpec;
|
||||
|
||||
private JwtSpec jwtSpec;
|
||||
|
||||
private AuthorizePayloadsSpec authorizePayload;
|
||||
@@ -149,58 +145,6 @@ public class RSocketSecurity {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds support for validating a username and password using
|
||||
* <a href="https://github.com/rsocket/rsocket/blob/5920ed374d008abb712cb1fd7c9d91778b2f4a68/Extensions/Security/Simple.md">Simple Authentication</a>
|
||||
* @param simple a customizer
|
||||
* @return RSocketSecurity for additional configuration
|
||||
* @since 5.3
|
||||
*/
|
||||
public RSocketSecurity simpleAuthentication(Customizer<SimpleAuthenticationSpec> simple) {
|
||||
if (this.simpleAuthSpec == null) {
|
||||
this.simpleAuthSpec = new SimpleAuthenticationSpec();
|
||||
}
|
||||
simple.customize(this.simpleAuthSpec);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.3
|
||||
*/
|
||||
public class SimpleAuthenticationSpec {
|
||||
private ReactiveAuthenticationManager authenticationManager;
|
||||
|
||||
public SimpleAuthenticationSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
return this;
|
||||
}
|
||||
|
||||
private ReactiveAuthenticationManager getAuthenticationManager() {
|
||||
if (this.authenticationManager == null) {
|
||||
return RSocketSecurity.this.authenticationManager;
|
||||
}
|
||||
return this.authenticationManager;
|
||||
}
|
||||
|
||||
protected AuthenticationPayloadInterceptor build() {
|
||||
ReactiveAuthenticationManager manager = getAuthenticationManager();
|
||||
AuthenticationPayloadInterceptor result = new AuthenticationPayloadInterceptor(manager);
|
||||
result.setAuthenticationConverter(new AuthenticationPayloadExchangeConverter());
|
||||
result.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());
|
||||
return result;
|
||||
}
|
||||
|
||||
private SimpleAuthenticationSpec() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds authentication with BasicAuthenticationPayloadExchangeConverter.
|
||||
*
|
||||
* @param basic
|
||||
* @return
|
||||
* @deprecated Use {@link #simpleAuthentication(Customizer)}
|
||||
*/
|
||||
@Deprecated
|
||||
public RSocketSecurity basicAuthentication(Customizer<BasicAuthenticationSpec> basic) {
|
||||
if (this.basicAuthSpec == null) {
|
||||
this.basicAuthSpec = new BasicAuthenticationSpec();
|
||||
@@ -262,17 +206,12 @@ public class RSocketSecurity {
|
||||
return RSocketSecurity.this.authenticationManager;
|
||||
}
|
||||
|
||||
protected List<AuthenticationPayloadInterceptor> build() {
|
||||
protected AuthenticationPayloadInterceptor build() {
|
||||
ReactiveAuthenticationManager manager = getAuthenticationManager();
|
||||
AuthenticationPayloadInterceptor legacy = new AuthenticationPayloadInterceptor(manager);
|
||||
legacy.setAuthenticationConverter(new BearerPayloadExchangeConverter());
|
||||
legacy.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());
|
||||
|
||||
AuthenticationPayloadInterceptor standard = new AuthenticationPayloadInterceptor(manager);
|
||||
standard.setAuthenticationConverter(new AuthenticationPayloadExchangeConverter());
|
||||
standard.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());
|
||||
|
||||
return Arrays.asList(standard, legacy);
|
||||
AuthenticationPayloadInterceptor result = new AuthenticationPayloadInterceptor(manager);
|
||||
result.setAuthenticationConverter(new BearerPayloadExchangeConverter());
|
||||
result.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());
|
||||
return result;
|
||||
}
|
||||
|
||||
private JwtSpec() {}
|
||||
@@ -301,11 +240,8 @@ public class RSocketSecurity {
|
||||
if (this.basicAuthSpec != null) {
|
||||
result.add(this.basicAuthSpec.build());
|
||||
}
|
||||
if (this.simpleAuthSpec != null) {
|
||||
result.add(this.simpleAuthSpec.build());
|
||||
}
|
||||
if (this.jwtSpec != null) {
|
||||
result.addAll(this.jwtSpec.build());
|
||||
result.add(this.jwtSpec.build());
|
||||
}
|
||||
result.add(anonymous());
|
||||
|
||||
|
||||
-1
@@ -47,7 +47,6 @@ class SecuritySocketAcceptorInterceptorConfiguration {
|
||||
}
|
||||
rsocket
|
||||
.basicAuthentication(Customizer.withDefaults())
|
||||
.simpleAuthentication(Customizer.withDefaults())
|
||||
.authorizePayload(authz ->
|
||||
authz
|
||||
.setup().authenticated()
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
|
||||
|
||||
/**
|
||||
* Allows customization to the {@link WebSecurity}. In most instances users will use
|
||||
* {@link EnableWebSecurity} and create a {@link Configuration} that extends
|
||||
* {@link EnableWebSecurity} and a create {@link Configuration} that extends
|
||||
* {@link WebSecurityConfigurerAdapter} which will automatically be applied to the
|
||||
* {@link WebSecurity} by the {@link EnableWebSecurity} annotation.
|
||||
*
|
||||
|
||||
+2
-6
@@ -2331,10 +2331,6 @@ public final class HttpSecurity extends
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
*
|
||||
* @Value("${spring.security.oauth2.resourceserver.jwt.key-value}")
|
||||
* RSAPublicKey key;
|
||||
*
|
||||
* @Override
|
||||
* protected void configure(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
@@ -2346,14 +2342,14 @@ public final class HttpSecurity extends
|
||||
* oauth2ResourceServer
|
||||
* .jwt(jwt ->
|
||||
* jwt
|
||||
* .decoder(jwtDecoder())
|
||||
* .jwtAuthenticationConverter(jwtDecoder())
|
||||
* )
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public JwtDecoder jwtDecoder() {
|
||||
* return NimbusJwtDecoder.withPublicKey(this.key).build();
|
||||
* return JwtDecoders.fromOidcIssuerLocation(issuerUri);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
|
||||
+3
-15
@@ -26,7 +26,6 @@ import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.target.LazyInitTargetSource;
|
||||
@@ -37,7 +36,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||
@@ -196,11 +194,13 @@ public abstract class WebSecurityConfigurerAdapter implements
|
||||
return http;
|
||||
}
|
||||
|
||||
AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
|
||||
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
|
||||
.postProcess(new DefaultAuthenticationEventPublisher());
|
||||
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
|
||||
|
||||
AuthenticationManager authenticationManager = authenticationManager();
|
||||
authenticationBuilder.parentAuthenticationManager(authenticationManager);
|
||||
authenticationBuilder.authenticationEventPublisher(eventPublisher);
|
||||
Map<Class<?>, Object> sharedObjects = createSharedObjects();
|
||||
|
||||
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
|
||||
@@ -393,11 +393,6 @@ public abstract class WebSecurityConfigurerAdapter implements
|
||||
return super.eraseCredentials(eraseCredentials);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationManagerBuilder authenticationEventPublisher(AuthenticationEventPublisher eventPublisher) {
|
||||
authenticationBuilder.authenticationEventPublisher(eventPublisher);
|
||||
return super.authenticationEventPublisher(eventPublisher);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -423,13 +418,6 @@ public abstract class WebSecurityConfigurerAdapter implements
|
||||
this.authenticationConfiguration = authenticationConfiguration;
|
||||
}
|
||||
|
||||
private AuthenticationEventPublisher getAuthenticationEventPublisher() {
|
||||
if (this.context.getBeanNamesForType(AuthenticationEventPublisher.class).length > 0) {
|
||||
return this.context.getBean(AuthenticationEventPublisher.class);
|
||||
}
|
||||
return this.objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the shared objects
|
||||
*
|
||||
|
||||
+15
-7
@@ -557,13 +557,17 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
if (this.invalidSessionStrategy != null) {
|
||||
return this.invalidSessionStrategy;
|
||||
}
|
||||
|
||||
if (this.invalidSessionUrl != null) {
|
||||
this.invalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy(
|
||||
this.invalidSessionUrl);
|
||||
}
|
||||
if (this.invalidSessionUrl == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.invalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy(
|
||||
if (this.invalidSessionStrategy == null) {
|
||||
this.invalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy(
|
||||
this.invalidSessionUrl);
|
||||
}
|
||||
return this.invalidSessionStrategy;
|
||||
}
|
||||
|
||||
@@ -576,8 +580,10 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
return null;
|
||||
}
|
||||
|
||||
this.expiredSessionStrategy = new SimpleRedirectSessionInformationExpiredStrategy(
|
||||
this.expiredUrl);
|
||||
if (this.expiredSessionStrategy == null) {
|
||||
this.expiredSessionStrategy = new SimpleRedirectSessionInformationExpiredStrategy(
|
||||
this.expiredUrl);
|
||||
}
|
||||
return this.expiredSessionStrategy;
|
||||
}
|
||||
|
||||
@@ -590,8 +596,10 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
return null;
|
||||
}
|
||||
|
||||
this.sessionAuthenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler(
|
||||
this.sessionAuthenticationErrorUrl);
|
||||
if (this.sessionAuthenticationFailureHandler == null) {
|
||||
this.sessionAuthenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler(
|
||||
this.sessionAuthenticationErrorUrl);
|
||||
}
|
||||
return this.sessionAuthenticationFailureHandler;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -120,7 +120,7 @@ import org.springframework.util.ClassUtils;
|
||||
* <li>{@link OAuth2AuthorizedClientRepository}</li>
|
||||
* <li>{@link GrantedAuthoritiesMapper}</li>
|
||||
* <li>{@link DefaultLoginPageGeneratingFilter} - if {@link #loginPage(String)} is not configured
|
||||
* and {@code DefaultLoginPageGeneratingFilter} is available, then a default login page will be made available</li>
|
||||
* and {@code DefaultLoginPageGeneratingFilter} is available, than a default login page will be made available</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Joe Grandja
|
||||
|
||||
+10
-34
@@ -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.
|
||||
@@ -18,7 +18,7 @@ package org.springframework.security.config.annotation.web.configurers.saml2;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
|
||||
@@ -103,32 +103,13 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>> extend
|
||||
|
||||
private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;
|
||||
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
private Saml2WebSsoAuthenticationFilter saml2WebSsoAuthenticationFilter;
|
||||
|
||||
/**
|
||||
* Allows a configuration of a {@link AuthenticationManager} to be used during SAML 2 authentication.
|
||||
* If none is specified, the system will create one inject it into the {@link Saml2WebSsoAuthenticationFilter}
|
||||
* @param authenticationManager the authentication manager to be used
|
||||
* @return the {@link Saml2LoginConfigurer} for further configuration
|
||||
* @throws IllegalArgumentException if authenticationManager is null
|
||||
* configure the default manager
|
||||
* @since 5.3
|
||||
*/
|
||||
public Saml2LoginConfigurer<B> authenticationManager(AuthenticationManager authenticationManager) {
|
||||
Assert.notNull(authenticationManager, "authenticationManager cannot be null");
|
||||
this.authenticationManager = authenticationManager;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code RelyingPartyRegistrationRepository} of relying parties, each party representing a
|
||||
* service provider, SP and this host, and identity provider, IDP pair that communicate with each other.
|
||||
* @param repo the repository of relying parties
|
||||
* @return the {@link Saml2LoginConfigurer} for further configuration
|
||||
*/
|
||||
public Saml2LoginConfigurer<B> relyingPartyRegistrationRepository(RelyingPartyRegistrationRepository repo) {
|
||||
public Saml2LoginConfigurer relyingPartyRegistrationRepository(RelyingPartyRegistrationRepository repo) {
|
||||
this.relyingPartyRegistrationRepository = repo;
|
||||
return this;
|
||||
}
|
||||
@@ -183,11 +164,11 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>> extend
|
||||
this.relyingPartyRegistrationRepository = getSharedOrBean(http, RelyingPartyRegistrationRepository.class);
|
||||
}
|
||||
|
||||
saml2WebSsoAuthenticationFilter = new Saml2WebSsoAuthenticationFilter(
|
||||
Saml2WebSsoAuthenticationFilter webSsoFilter = new Saml2WebSsoAuthenticationFilter(
|
||||
this.relyingPartyRegistrationRepository,
|
||||
this.loginProcessingUrl
|
||||
);
|
||||
setAuthenticationFilter(saml2WebSsoAuthenticationFilter);
|
||||
setAuthenticationFilter(webSsoFilter);
|
||||
super.loginProcessingUrl(this.loginProcessingUrl);
|
||||
|
||||
if (hasText(this.loginPage)) {
|
||||
@@ -216,7 +197,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>> extend
|
||||
super.init(http);
|
||||
}
|
||||
}
|
||||
|
||||
http.authenticationProvider(getAuthenticationProvider());
|
||||
this.initDefaultLoginFilter(http);
|
||||
}
|
||||
|
||||
@@ -230,17 +211,11 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>> extend
|
||||
public void configure(B http) throws Exception {
|
||||
http.addFilter(this.authenticationRequestEndpoint.build(http));
|
||||
super.configure(http);
|
||||
if (this.authenticationManager == null) {
|
||||
registerDefaultAuthenticationProvider(http);
|
||||
}
|
||||
else {
|
||||
saml2WebSsoAuthenticationFilter.setAuthenticationManager(this.authenticationManager);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerDefaultAuthenticationProvider(B http) {
|
||||
OpenSamlAuthenticationProvider provider = postProcess(new OpenSamlAuthenticationProvider());
|
||||
http.authenticationProvider(provider);
|
||||
private AuthenticationProvider getAuthenticationProvider() {
|
||||
AuthenticationProvider provider = new OpenSamlAuthenticationProvider();
|
||||
return postProcess(provider);
|
||||
}
|
||||
|
||||
private void registerDefaultCsrfOverride(B http) {
|
||||
@@ -338,4 +313,5 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>> extend
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+11
-210
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -15,18 +15,10 @@
|
||||
*/
|
||||
package org.springframework.security.config.http;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import static org.springframework.security.config.http.SecurityFilters.*;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
@@ -46,9 +38,7 @@ import org.springframework.security.core.authority.mapping.SimpleAttributes2Gran
|
||||
import org.springframework.security.core.authority.mapping.SimpleMappableAttributesRetriever;
|
||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||
import org.springframework.security.web.access.RequestMatcherDelegatingAccessDeniedHandler;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
|
||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
|
||||
@@ -65,22 +55,12 @@ import org.springframework.security.web.csrf.CsrfToken;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import static org.springframework.security.config.http.SecurityFilters.ANONYMOUS_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.BASIC_AUTH_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.BEARER_TOKEN_AUTH_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.EXCEPTION_TRANSLATION_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.FORM_LOGIN_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.LOGIN_PAGE_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.LOGOUT_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.LOGOUT_PAGE_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.OAUTH2_AUTHORIZATION_REQUEST_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.OAUTH2_LOGIN_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.OPENID_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.PRE_AUTH_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.REMEMBER_ME_FILTER;
|
||||
import static org.springframework.security.config.http.SecurityFilters.X509_FILTER;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Handles creation of authentication mechanism filters and related beans for <http>
|
||||
@@ -140,8 +120,6 @@ final class AuthenticationConfigBuilder {
|
||||
private BeanMetadataElement mainEntryPoint;
|
||||
private BeanMetadataElement accessDeniedHandler;
|
||||
|
||||
private BeanDefinition bearerTokenAuthenticationFilter;
|
||||
|
||||
private BeanDefinition logoutFilter;
|
||||
@SuppressWarnings("rawtypes")
|
||||
private ManagedList logoutHandlers;
|
||||
@@ -160,21 +138,6 @@ final class AuthenticationConfigBuilder {
|
||||
|
||||
private String openIDLoginPage;
|
||||
|
||||
private String oauth2LoginFilterId;
|
||||
private BeanDefinition oauth2AuthorizationRequestRedirectFilter;
|
||||
private BeanDefinition oauth2LoginEntryPoint;
|
||||
private BeanReference oauth2LoginAuthenticationProviderRef;
|
||||
private BeanReference oauth2LoginOidcAuthenticationProviderRef;
|
||||
private BeanDefinition oauth2LoginLinks;
|
||||
private BeanDefinition authorizationRequestRedirectFilter;
|
||||
private BeanDefinition authorizationCodeGrantFilter;
|
||||
private BeanReference authorizationCodeAuthenticationProviderRef;
|
||||
|
||||
private final List<BeanReference> authenticationProviders = new ManagedList<>();
|
||||
private final Map<BeanDefinition, BeanMetadataElement> defaultDeniedHandlerMappings = new ManagedMap<>();
|
||||
private final Map<BeanDefinition, BeanMetadataElement> defaultEntryPointMappings = new ManagedMap<>();
|
||||
private final List<BeanDefinition> csrfIgnoreRequestMatchers = new ManagedList<>();
|
||||
|
||||
AuthenticationConfigBuilder(Element element, boolean forceAutoConfig,
|
||||
ParserContext pc, SessionCreationPolicy sessionPolicy,
|
||||
BeanReference requestCache, BeanReference authenticationManager,
|
||||
@@ -194,10 +157,7 @@ final class AuthenticationConfigBuilder {
|
||||
createAnonymousFilter();
|
||||
createRememberMeFilter(authenticationManager);
|
||||
createBasicFilter(authenticationManager);
|
||||
createBearerTokenAuthenticationFilter(authenticationManager);
|
||||
createFormLoginFilter(sessionStrategy, authenticationManager);
|
||||
createOAuth2LoginFilter(sessionStrategy, authenticationManager);
|
||||
createOAuth2ClientFilter(requestCache, authenticationManager);
|
||||
createOpenIDLoginFilter(sessionStrategy, authenticationManager);
|
||||
createX509Filter(authenticationManager);
|
||||
createJeeFilter(authenticationManager);
|
||||
@@ -205,6 +165,7 @@ final class AuthenticationConfigBuilder {
|
||||
createLoginPageFilterIfNeeded();
|
||||
createUserDetailsServiceFactory();
|
||||
createExceptionTranslationFilter();
|
||||
|
||||
}
|
||||
|
||||
void createRememberMeFilter(BeanReference authenticationManager) {
|
||||
@@ -274,76 +235,6 @@ final class AuthenticationConfigBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
void createOAuth2LoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
|
||||
Element oauth2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_LOGIN);
|
||||
if (oauth2LoginElt == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
OAuth2LoginBeanDefinitionParser parser = new OAuth2LoginBeanDefinitionParser(requestCache, portMapper,
|
||||
portResolver, sessionStrategy, allowSessionCreation);
|
||||
BeanDefinition oauth2LoginFilterBean = parser.parse(oauth2LoginElt, this.pc);
|
||||
oauth2LoginFilterBean.getPropertyValues().addPropertyValue("authenticationManager", authManager);
|
||||
|
||||
// retrieve the other bean result
|
||||
BeanDefinition oauth2LoginAuthProvider = parser.getOAuth2LoginAuthenticationProvider();
|
||||
oauth2AuthorizationRequestRedirectFilter = parser.getOAuth2AuthorizationRequestRedirectFilter();
|
||||
oauth2LoginEntryPoint = parser.getOAuth2LoginAuthenticationEntryPoint();
|
||||
|
||||
// generate bean name to be registered
|
||||
String oauth2LoginAuthProviderId = pc.getReaderContext()
|
||||
.generateBeanName(oauth2LoginAuthProvider);
|
||||
oauth2LoginFilterId = pc.getReaderContext().generateBeanName(oauth2LoginFilterBean);
|
||||
String oauth2AuthorizationRequestRedirectFilterId = pc.getReaderContext()
|
||||
.generateBeanName(oauth2AuthorizationRequestRedirectFilter);
|
||||
oauth2LoginLinks = parser.getOAuth2LoginLinks();
|
||||
|
||||
// register the component
|
||||
pc.registerBeanComponent(new BeanComponentDefinition(oauth2LoginFilterBean, oauth2LoginFilterId));
|
||||
pc.registerBeanComponent(new BeanComponentDefinition(
|
||||
oauth2AuthorizationRequestRedirectFilter, oauth2AuthorizationRequestRedirectFilterId));
|
||||
pc.registerBeanComponent(new BeanComponentDefinition(oauth2LoginAuthProvider, oauth2LoginAuthProviderId));
|
||||
|
||||
oauth2LoginAuthenticationProviderRef = new RuntimeBeanReference(oauth2LoginAuthProviderId);
|
||||
|
||||
// oidc provider
|
||||
BeanDefinition oauth2LoginOidcAuthProvider = parser.getOAuth2LoginOidcAuthenticationProvider();
|
||||
String oauth2LoginOidcAuthProviderId = pc.getReaderContext().generateBeanName(oauth2LoginOidcAuthProvider);
|
||||
pc.registerBeanComponent(new BeanComponentDefinition(
|
||||
oauth2LoginOidcAuthProvider, oauth2LoginOidcAuthProviderId));
|
||||
oauth2LoginOidcAuthenticationProviderRef = new RuntimeBeanReference(oauth2LoginOidcAuthProviderId);
|
||||
}
|
||||
|
||||
void createOAuth2ClientFilter(BeanReference requestCache, BeanReference authenticationManager) {
|
||||
Element oauth2ClientElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_CLIENT);
|
||||
if (oauth2ClientElt == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
OAuth2ClientBeanDefinitionParser parser = new OAuth2ClientBeanDefinitionParser(
|
||||
requestCache, authenticationManager);
|
||||
parser.parse(oauth2ClientElt, this.pc);
|
||||
|
||||
this.authorizationRequestRedirectFilter = parser.getAuthorizationRequestRedirectFilter();
|
||||
String authorizationRequestRedirectFilterId = pc.getReaderContext()
|
||||
.generateBeanName(this.authorizationRequestRedirectFilter);
|
||||
this.pc.registerBeanComponent(new BeanComponentDefinition(
|
||||
this.authorizationRequestRedirectFilter, authorizationRequestRedirectFilterId));
|
||||
|
||||
this.authorizationCodeGrantFilter = parser.getAuthorizationCodeGrantFilter();
|
||||
String authorizationCodeGrantFilterId = pc.getReaderContext()
|
||||
.generateBeanName(this.authorizationCodeGrantFilter);
|
||||
this.pc.registerBeanComponent(new BeanComponentDefinition(
|
||||
this.authorizationCodeGrantFilter, authorizationCodeGrantFilterId));
|
||||
|
||||
BeanDefinition authorizationCodeAuthenticationProvider = parser.getAuthorizationCodeAuthenticationProvider();
|
||||
String authorizationCodeAuthenticationProviderId = pc.getReaderContext()
|
||||
.generateBeanName(authorizationCodeAuthenticationProvider);
|
||||
this.pc.registerBeanComponent(new BeanComponentDefinition(
|
||||
authorizationCodeAuthenticationProvider, authorizationCodeAuthenticationProviderId));
|
||||
this.authorizationCodeAuthenticationProviderRef = new RuntimeBeanReference(authorizationCodeAuthenticationProviderId);
|
||||
}
|
||||
|
||||
void createOpenIDLoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
|
||||
Element openIDLoginElt = DomUtils.getChildElementByTagName(httpElt,
|
||||
Elements.OPENID_LOGIN);
|
||||
@@ -508,21 +399,6 @@ final class AuthenticationConfigBuilder {
|
||||
basicFilter = filterBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
void createBearerTokenAuthenticationFilter(BeanReference authManager) {
|
||||
Element resourceServerElt = DomUtils.getChildElementByTagName(httpElt,
|
||||
Elements.OAUTH2_RESOURCE_SERVER);
|
||||
|
||||
if (resourceServerElt == null) {
|
||||
// No resource server, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
OAuth2ResourceServerBeanDefinitionParser resourceServerBuilder =
|
||||
new OAuth2ResourceServerBeanDefinitionParser(authManager, authenticationProviders,
|
||||
defaultEntryPointMappings, defaultDeniedHandlerMappings, csrfIgnoreRequestMatchers);
|
||||
bearerTokenAuthenticationFilter = resourceServerBuilder.parse(resourceServerElt, pc);
|
||||
}
|
||||
|
||||
void createX509Filter(BeanReference authManager) {
|
||||
Element x509Elt = DomUtils.getChildElementByTagName(httpElt, Elements.X509);
|
||||
RootBeanDefinition filter = null;
|
||||
@@ -659,7 +535,7 @@ final class AuthenticationConfigBuilder {
|
||||
}
|
||||
|
||||
void createLoginPageFilterIfNeeded() {
|
||||
boolean needLoginPage = formFilterId != null || openIDFilterId != null || oauth2LoginFilterId != null;
|
||||
boolean needLoginPage = formFilterId != null || openIDFilterId != null;
|
||||
|
||||
// If no login page has been defined, add in the default page generator.
|
||||
if (needLoginPage && formLoginPage == null && openIDLoginPage == null) {
|
||||
@@ -685,12 +561,6 @@ final class AuthenticationConfigBuilder {
|
||||
openidLoginProcessingUrl);
|
||||
}
|
||||
|
||||
if (oauth2LoginFilterId != null) {
|
||||
loginPageFilter.addConstructorArgReference(oauth2LoginFilterId);
|
||||
loginPageFilter.addPropertyValue("Oauth2LoginEnabled", true);
|
||||
loginPageFilter.addPropertyValue("Oauth2AuthenticationUrlToClientName", oauth2LoginLinks);
|
||||
}
|
||||
|
||||
loginPageGenerationFilter = loginPageFilter.getBeanDefinition();
|
||||
this.logoutPageGenerationFilter = logoutPageFilter.getBeanDefinition();
|
||||
}
|
||||
@@ -733,10 +603,6 @@ final class AuthenticationConfigBuilder {
|
||||
return accessDeniedHandler;
|
||||
}
|
||||
|
||||
List<BeanDefinition> getCsrfIgnoreRequestMatchers() {
|
||||
return csrfIgnoreRequestMatchers;
|
||||
}
|
||||
|
||||
void createAnonymousFilter() {
|
||||
Element anonymousElt = DomUtils.getChildElementByTagName(httpElt,
|
||||
Elements.ANONYMOUS);
|
||||
@@ -830,7 +696,6 @@ final class AuthenticationConfigBuilder {
|
||||
|
||||
}
|
||||
accessDeniedHandler.addPropertyValue("errorPage", errorPage);
|
||||
return accessDeniedHandler.getBeanDefinition();
|
||||
}
|
||||
else if (StringUtils.hasText(ref)) {
|
||||
return new RuntimeBeanReference(ref);
|
||||
@@ -838,19 +703,6 @@ final class AuthenticationConfigBuilder {
|
||||
|
||||
}
|
||||
|
||||
if (this.defaultDeniedHandlerMappings.isEmpty()) {
|
||||
return accessDeniedHandler.getBeanDefinition();
|
||||
}
|
||||
if (this.defaultDeniedHandlerMappings.size() == 1) {
|
||||
return this.defaultDeniedHandlerMappings.values().iterator().next();
|
||||
}
|
||||
|
||||
accessDeniedHandler = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RequestMatcherDelegatingAccessDeniedHandler.class);
|
||||
accessDeniedHandler.addConstructorArgValue(this.defaultDeniedHandlerMappings);
|
||||
accessDeniedHandler.addConstructorArgValue
|
||||
(BeanDefinitionBuilder.rootBeanDefinition(AccessDeniedHandlerImpl.class));
|
||||
|
||||
return accessDeniedHandler.getBeanDefinition();
|
||||
}
|
||||
|
||||
@@ -863,16 +715,6 @@ final class AuthenticationConfigBuilder {
|
||||
return new RuntimeBeanReference(customEntryPoint);
|
||||
}
|
||||
|
||||
if (!defaultEntryPointMappings.isEmpty()) {
|
||||
if (defaultEntryPointMappings.size() == 1) {
|
||||
return defaultEntryPointMappings.values().iterator().next();
|
||||
}
|
||||
BeanDefinitionBuilder delegatingEntryPoint = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DelegatingAuthenticationEntryPoint.class);
|
||||
delegatingEntryPoint.addConstructorArgValue(defaultEntryPointMappings);
|
||||
return delegatingEntryPoint.getBeanDefinition();
|
||||
}
|
||||
|
||||
Element basicAuthElt = DomUtils.getChildElementByTagName(httpElt,
|
||||
Elements.BASIC_AUTH);
|
||||
Element formLoginElt = DomUtils.getChildElementByTagName(httpElt,
|
||||
@@ -880,7 +722,7 @@ final class AuthenticationConfigBuilder {
|
||||
Element openIDLoginElt = DomUtils.getChildElementByTagName(httpElt,
|
||||
Elements.OPENID_LOGIN);
|
||||
// Basic takes precedence if explicit element is used and no others are configured
|
||||
if (basicAuthElt != null && formLoginElt == null && openIDLoginElt == null && oauth2LoginEntryPoint == null) {
|
||||
if (basicAuthElt != null && formLoginElt == null && openIDLoginElt == null) {
|
||||
return basicEntryPoint;
|
||||
}
|
||||
|
||||
@@ -895,15 +737,7 @@ final class AuthenticationConfigBuilder {
|
||||
}
|
||||
|
||||
if (formFilterId != null && openIDLoginPage == null) {
|
||||
// gh-6802
|
||||
// If form login was enabled through element and Oauth2 login was enabled from element then use form login
|
||||
if (formLoginElt != null && oauth2LoginEntryPoint != null) {
|
||||
return formEntryPoint;
|
||||
}
|
||||
// If form login was enabled through auto-config, and Oauth2 login was not enabled then use form login
|
||||
if (oauth2LoginEntryPoint == null) {
|
||||
return formEntryPoint;
|
||||
}
|
||||
return formEntryPoint;
|
||||
}
|
||||
|
||||
// Otherwise use OpenID if enabled
|
||||
@@ -916,11 +750,6 @@ final class AuthenticationConfigBuilder {
|
||||
return preAuthEntryPoint;
|
||||
}
|
||||
|
||||
// OAuth2 entry point will not be null if only 1 client registration
|
||||
if (oauth2LoginEntryPoint != null) {
|
||||
return oauth2LoginEntryPoint;
|
||||
}
|
||||
|
||||
pc.getReaderContext()
|
||||
.error("No AuthenticationEntryPoint could be established. Please "
|
||||
+ "make sure you have a login mechanism configured through the namespace (such as form-login) or "
|
||||
@@ -969,11 +798,6 @@ final class AuthenticationConfigBuilder {
|
||||
FORM_LOGIN_FILTER));
|
||||
}
|
||||
|
||||
if (oauth2LoginFilterId != null) {
|
||||
filters.add(new OrderDecorator(new RuntimeBeanReference(oauth2LoginFilterId), OAUTH2_LOGIN_FILTER));
|
||||
filters.add(new OrderDecorator(oauth2AuthorizationRequestRedirectFilter, OAUTH2_AUTHORIZATION_REQUEST_FILTER));
|
||||
}
|
||||
|
||||
if (openIDFilterId != null) {
|
||||
filters.add(new OrderDecorator(new RuntimeBeanReference(openIDFilterId),
|
||||
OPENID_FILTER));
|
||||
@@ -988,15 +812,6 @@ final class AuthenticationConfigBuilder {
|
||||
filters.add(new OrderDecorator(basicFilter, BASIC_AUTH_FILTER));
|
||||
}
|
||||
|
||||
if (bearerTokenAuthenticationFilter != null) {
|
||||
filters.add(new OrderDecorator(bearerTokenAuthenticationFilter, BEARER_TOKEN_AUTH_FILTER));
|
||||
}
|
||||
|
||||
if (authorizationCodeGrantFilter != null) {
|
||||
filters.add(new OrderDecorator(authorizationRequestRedirectFilter, OAUTH2_AUTHORIZATION_REQUEST_FILTER.getOrder() + 1));
|
||||
filters.add(new OrderDecorator(authorizationCodeGrantFilter, OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER));
|
||||
}
|
||||
|
||||
filters.add(new OrderDecorator(etf, EXCEPTION_TRANSLATION_FILTER));
|
||||
|
||||
return filters;
|
||||
@@ -1025,20 +840,6 @@ final class AuthenticationConfigBuilder {
|
||||
providers.add(jeeProviderRef);
|
||||
}
|
||||
|
||||
if (oauth2LoginAuthenticationProviderRef != null) {
|
||||
providers.add(oauth2LoginAuthenticationProviderRef);
|
||||
}
|
||||
|
||||
if (oauth2LoginOidcAuthenticationProviderRef != null) {
|
||||
providers.add(oauth2LoginOidcAuthenticationProviderRef);
|
||||
}
|
||||
|
||||
if (authorizationCodeAuthenticationProviderRef != null) {
|
||||
providers.add(authorizationCodeAuthenticationProviderRef);
|
||||
}
|
||||
|
||||
providers.addAll(this.authenticationProviders);
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
|
||||
+5
-59
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -15,19 +15,12 @@
|
||||
*/
|
||||
package org.springframework.security.config.http;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
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.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
@@ -45,10 +38,6 @@ import org.springframework.security.web.csrf.MissingCsrfTokenException;
|
||||
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
|
||||
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
|
||||
import org.springframework.security.web.session.InvalidSessionStrategy;
|
||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -69,8 +58,6 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
|
||||
private String csrfRepositoryRef;
|
||||
private BeanDefinition csrfFilter;
|
||||
|
||||
private String requestMatcherRef;
|
||||
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext pc) {
|
||||
boolean disabled = element != null
|
||||
@@ -90,9 +77,10 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
}
|
||||
|
||||
String matcherRef = null;
|
||||
if (element != null) {
|
||||
this.csrfRepositoryRef = element.getAttribute(ATT_REPOSITORY);
|
||||
this.requestMatcherRef = element.getAttribute(ATT_MATCHER);
|
||||
matcherRef = element.getAttribute(ATT_MATCHER);
|
||||
}
|
||||
|
||||
if (!StringUtils.hasText(this.csrfRepositoryRef)) {
|
||||
@@ -112,8 +100,8 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
|
||||
.rootBeanDefinition(CsrfFilter.class);
|
||||
builder.addConstructorArgReference(this.csrfRepositoryRef);
|
||||
|
||||
if (StringUtils.hasText(this.requestMatcherRef)) {
|
||||
builder.addPropertyReference("requireCsrfProtectionMatcher", this.requestMatcherRef);
|
||||
if (StringUtils.hasText(matcherRef)) {
|
||||
builder.addPropertyReference("requireCsrfProtectionMatcher", matcherRef);
|
||||
}
|
||||
|
||||
this.csrfFilter = builder.getBeanDefinition();
|
||||
@@ -184,46 +172,4 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
|
||||
csrfAuthenticationStrategy.addConstructorArgReference(this.csrfRepositoryRef);
|
||||
return csrfAuthenticationStrategy.getBeanDefinition();
|
||||
}
|
||||
|
||||
void setIgnoreCsrfRequestMatchers(List<BeanDefinition> requestMatchers) {
|
||||
if (!requestMatchers.isEmpty()) {
|
||||
BeanMetadataElement requestMatcher;
|
||||
if (StringUtils.hasText(this.requestMatcherRef)) {
|
||||
requestMatcher = new RuntimeBeanReference(this.requestMatcherRef);
|
||||
} else {
|
||||
requestMatcher = new RootBeanDefinition(DefaultRequiresCsrfMatcher.class);
|
||||
}
|
||||
BeanDefinitionBuilder and = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(AndRequestMatcher.class);
|
||||
BeanDefinitionBuilder negated = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(NegatedRequestMatcher.class);
|
||||
BeanDefinitionBuilder or = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(OrRequestMatcher.class);
|
||||
or.addConstructorArgValue(requestMatchers);
|
||||
negated.addConstructorArgValue(or.getBeanDefinition());
|
||||
List<BeanMetadataElement> ands = new ManagedList<>();
|
||||
ands.add(requestMatcher);
|
||||
ands.add(negated.getBeanDefinition());
|
||||
and.addConstructorArgValue(ands);
|
||||
this.csrfFilter.getPropertyValues()
|
||||
.add("requireCsrfProtectionMatcher", and.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
|
||||
private final HashSet<String> allowedMethods = new HashSet<>(
|
||||
Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.security.web.util.matcher.RequestMatcher#matches(javax.
|
||||
* servlet.http.HttpServletRequest)
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
return !this.allowedMethods.contains(request.getMethod());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -238,12 +238,6 @@ class HttpConfigurationBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
void setCsrfIgnoreRequestMatchers(List<BeanDefinition> requestMatchers) {
|
||||
if (csrfParser != null) {
|
||||
csrfParser.setIgnoreCsrfRequestMatchers(requestMatchers);
|
||||
}
|
||||
}
|
||||
|
||||
// Needed to account for placeholders
|
||||
static String createPath(String path, boolean lowerCase) {
|
||||
return lowerCase ? path.toLowerCase() : path;
|
||||
|
||||
+4
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -15,14 +15,8 @@
|
||||
*/
|
||||
package org.springframework.security.config.http;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
@@ -39,7 +33,6 @@ import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
import org.springframework.security.config.Elements;
|
||||
@@ -50,6 +43,9 @@ import org.springframework.security.web.PortResolverImpl;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Sets up HTTP security: filter stack and protected URLs.
|
||||
@@ -159,7 +155,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
||||
httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
|
||||
httpBldr.setEntryPoint(authBldr.getEntryPointBean());
|
||||
httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
|
||||
httpBldr.setCsrfIgnoreRequestMatchers(authBldr.getCsrfIgnoreRequestMatchers());
|
||||
|
||||
authenticationProviders.addAll(authBldr.getProviders());
|
||||
|
||||
@@ -299,8 +294,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
||||
clearCredentials);
|
||||
}
|
||||
|
||||
// gh-6009
|
||||
authManager.addPropertyValue("authenticationEventPublisher", new RootBeanDefinition(DefaultAuthenticationEventPublisher.class));
|
||||
authManager.getRawBeanDefinition().setSource(pc.extractSource(element));
|
||||
BeanDefinition authMgrBean = authManager.getBeanDefinition();
|
||||
String id = pc.getReaderContext().generateBeanName(authMgrBean);
|
||||
|
||||
-172
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.springframework.beans.BeanMetadataElement;
|
||||
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.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* @author Joe Grandja
|
||||
* @since 5.3
|
||||
*/
|
||||
final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
||||
private static final String ELT_AUTHORIZATION_CODE_GRANT = "authorization-code-grant";
|
||||
private static final String ATT_CLIENT_REGISTRATION_REPOSITORY_REF = "client-registration-repository-ref";
|
||||
private static final String ATT_AUTHORIZED_CLIENT_REPOSITORY_REF = "authorized-client-repository-ref";
|
||||
private static final String ATT_AUTHORIZED_CLIENT_SERVICE_REF = "authorized-client-service-ref";
|
||||
private static final String ATT_AUTHORIZATION_REQUEST_REPOSITORY_REF = "authorization-request-repository-ref";
|
||||
private static final String ATT_AUTHORIZATION_REQUEST_RESOLVER_REF = "authorization-request-resolver-ref";
|
||||
private static final String ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF = "access-token-response-client-ref";
|
||||
private final BeanReference requestCache;
|
||||
private final BeanReference authenticationManager;
|
||||
private BeanDefinition authorizationRequestRedirectFilter;
|
||||
private BeanDefinition authorizationCodeGrantFilter;
|
||||
private BeanDefinition authorizationCodeAuthenticationProvider;
|
||||
|
||||
OAuth2ClientBeanDefinitionParser(BeanReference requestCache, BeanReference authenticationManager) {
|
||||
this.requestCache = requestCache;
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
Element authorizationCodeGrantElt = DomUtils.getChildElementByTagName(element, ELT_AUTHORIZATION_CODE_GRANT);
|
||||
|
||||
BeanMetadataElement clientRegistrationRepository = getClientRegistrationRepository(element);
|
||||
BeanMetadataElement authorizedClientRepository = getAuthorizedClientRepository(
|
||||
element, clientRegistrationRepository);
|
||||
BeanMetadataElement authorizationRequestRepository = getAuthorizationRequestRepository(
|
||||
authorizationCodeGrantElt);
|
||||
|
||||
BeanDefinitionBuilder authorizationRequestRedirectFilterBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(OAuth2AuthorizationRequestRedirectFilter.class);
|
||||
String authorizationRequestResolverRef = authorizationCodeGrantElt != null ?
|
||||
authorizationCodeGrantElt.getAttribute(ATT_AUTHORIZATION_REQUEST_RESOLVER_REF) : null;
|
||||
if (!StringUtils.isEmpty(authorizationRequestResolverRef)) {
|
||||
authorizationRequestRedirectFilterBuilder.addConstructorArgReference(authorizationRequestResolverRef);
|
||||
} else {
|
||||
authorizationRequestRedirectFilterBuilder.addConstructorArgValue(clientRegistrationRepository);
|
||||
}
|
||||
this.authorizationRequestRedirectFilter = authorizationRequestRedirectFilterBuilder
|
||||
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository)
|
||||
.addPropertyValue("requestCache", this.requestCache)
|
||||
.getBeanDefinition();
|
||||
|
||||
this.authorizationCodeGrantFilter = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(OAuth2AuthorizationCodeGrantFilter.class)
|
||||
.addConstructorArgValue(clientRegistrationRepository)
|
||||
.addConstructorArgValue(authorizedClientRepository)
|
||||
.addConstructorArgValue(this.authenticationManager)
|
||||
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository)
|
||||
.getBeanDefinition();
|
||||
|
||||
BeanMetadataElement accessTokenResponseClient = getAccessTokenResponseClient(authorizationCodeGrantElt);
|
||||
|
||||
this.authorizationCodeAuthenticationProvider = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(OAuth2AuthorizationCodeAuthenticationProvider.class)
|
||||
.addConstructorArgValue(accessTokenResponseClient)
|
||||
.getBeanDefinition();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private BeanMetadataElement getClientRegistrationRepository(Element element) {
|
||||
BeanMetadataElement clientRegistrationRepository;
|
||||
String clientRegistrationRepositoryRef = element.getAttribute(ATT_CLIENT_REGISTRATION_REPOSITORY_REF);
|
||||
if (!StringUtils.isEmpty(clientRegistrationRepositoryRef)) {
|
||||
clientRegistrationRepository = new RuntimeBeanReference(clientRegistrationRepositoryRef);
|
||||
} else {
|
||||
clientRegistrationRepository = new RuntimeBeanReference(ClientRegistrationRepository.class);
|
||||
}
|
||||
return clientRegistrationRepository;
|
||||
}
|
||||
|
||||
private BeanMetadataElement getAuthorizedClientRepository(Element element,
|
||||
BeanMetadataElement clientRegistrationRepository) {
|
||||
BeanMetadataElement authorizedClientRepository;
|
||||
String authorizedClientRepositoryRef = element.getAttribute(ATT_AUTHORIZED_CLIENT_REPOSITORY_REF);
|
||||
if (!StringUtils.isEmpty(authorizedClientRepositoryRef)) {
|
||||
authorizedClientRepository = new RuntimeBeanReference(authorizedClientRepositoryRef);
|
||||
} else {
|
||||
BeanMetadataElement authorizedClientService;
|
||||
String authorizedClientServiceRef = element.getAttribute(ATT_AUTHORIZED_CLIENT_SERVICE_REF);
|
||||
if (!StringUtils.isEmpty(authorizedClientServiceRef)) {
|
||||
authorizedClientService = new RuntimeBeanReference(authorizedClientServiceRef);
|
||||
} else {
|
||||
authorizedClientService = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(
|
||||
"org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService")
|
||||
.addConstructorArgValue(clientRegistrationRepository).getBeanDefinition();
|
||||
}
|
||||
authorizedClientRepository = BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository")
|
||||
.addConstructorArgValue(authorizedClientService).getBeanDefinition();
|
||||
}
|
||||
return authorizedClientRepository;
|
||||
}
|
||||
|
||||
private BeanMetadataElement getAuthorizationRequestRepository(Element element) {
|
||||
BeanMetadataElement authorizationRequestRepository;
|
||||
String authorizationRequestRepositoryRef = element != null ?
|
||||
element.getAttribute(ATT_AUTHORIZATION_REQUEST_REPOSITORY_REF) : null;
|
||||
if (!StringUtils.isEmpty(authorizationRequestRepositoryRef)) {
|
||||
authorizationRequestRepository = new RuntimeBeanReference(authorizationRequestRepositoryRef);
|
||||
} else {
|
||||
authorizationRequestRepository = BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository")
|
||||
.getBeanDefinition();
|
||||
}
|
||||
return authorizationRequestRepository;
|
||||
}
|
||||
|
||||
private BeanMetadataElement getAccessTokenResponseClient(Element element) {
|
||||
BeanMetadataElement accessTokenResponseClient;
|
||||
String accessTokenResponseClientRef = element != null ?
|
||||
element.getAttribute(ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF) : null;
|
||||
if (!StringUtils.isEmpty(accessTokenResponseClientRef)) {
|
||||
accessTokenResponseClient = new RuntimeBeanReference(accessTokenResponseClientRef);
|
||||
} else {
|
||||
accessTokenResponseClient = BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient")
|
||||
.getBeanDefinition();
|
||||
}
|
||||
return accessTokenResponseClient;
|
||||
}
|
||||
|
||||
BeanDefinition getAuthorizationRequestRedirectFilter() {
|
||||
return this.authorizationRequestRedirectFilter;
|
||||
}
|
||||
|
||||
BeanDefinition getAuthorizationCodeGrantFilter() {
|
||||
return this.authorizationCodeGrantFilter;
|
||||
}
|
||||
|
||||
BeanDefinition getAuthorizationCodeAuthenticationProvider() {
|
||||
return this.authorizationCodeAuthenticationProvider;
|
||||
}
|
||||
}
|
||||
-477
@@ -1,477 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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.http.MediaType;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.config.Elements;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcScopes;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.springframework.web.accept.ContentNegotiationStrategy;
|
||||
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* @author Ruby Hartono
|
||||
* @since 5.3
|
||||
*/
|
||||
final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization";
|
||||
private static final String DEFAULT_LOGIN_URI = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL;
|
||||
|
||||
private static final String ELT_CLIENT_REGISTRATION = "client-registration";
|
||||
private static final String ATT_REGISTRATION_ID = "registration-id";
|
||||
private static final String ATT_CLIENT_REGISTRATION_REPOSITORY_REF = "client-registration-repository-ref";
|
||||
private static final String ATT_AUTHORIZED_CLIENT_REPOSITORY_REF = "authorized-client-repository-ref";
|
||||
private static final String ATT_AUTHORIZED_CLIENT_SERVICE_REF = "authorized-client-service-ref";
|
||||
private static final String ATT_AUTHORIZATION_REQUEST_REPOSITORY_REF = "authorization-request-repository-ref";
|
||||
private static final String ATT_AUTHORIZATION_REQUEST_RESOLVER_REF = "authorization-request-resolver-ref";
|
||||
private static final String ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF = "access-token-response-client-ref";
|
||||
private static final String ATT_USER_AUTHORITIES_MAPPER_REF = "user-authorities-mapper-ref";
|
||||
private static final String ATT_USER_SERVICE_REF = "user-service-ref";
|
||||
private static final String ATT_OIDC_USER_SERVICE_REF = "oidc-user-service-ref";
|
||||
private static final String ATT_LOGIN_PROCESSING_URL = "login-processing-url";
|
||||
private static final String ATT_LOGIN_PAGE = "login-page";
|
||||
private static final String ATT_AUTHENTICATION_SUCCESS_HANDLER_REF = "authentication-success-handler-ref";
|
||||
private static final String ATT_AUTHENTICATION_FAILURE_HANDLER_REF = "authentication-failure-handler-ref";
|
||||
private static final String ATT_JWT_DECODER_FACTORY_REF = "jwt-decoder-factory-ref";
|
||||
|
||||
private final BeanReference requestCache;
|
||||
private final BeanReference portMapper;
|
||||
private final BeanReference portResolver;
|
||||
private final BeanReference sessionStrategy;
|
||||
private final boolean allowSessionCreation;
|
||||
|
||||
private BeanDefinition oauth2AuthorizationRequestRedirectFilter;
|
||||
|
||||
private BeanDefinition oauth2LoginAuthenticationEntryPoint;
|
||||
|
||||
private BeanDefinition oauth2LoginAuthenticationProvider;
|
||||
|
||||
private BeanDefinition oauth2LoginOidcAuthenticationProvider;
|
||||
|
||||
private BeanDefinition oauth2LoginLinks;
|
||||
|
||||
OAuth2LoginBeanDefinitionParser(BeanReference requestCache, BeanReference portMapper, BeanReference portResolver,
|
||||
BeanReference sessionStrategy, boolean allowSessionCreation) {
|
||||
this.requestCache = requestCache;
|
||||
this.portMapper = portMapper;
|
||||
this.portResolver = portResolver;
|
||||
this.sessionStrategy = sessionStrategy;
|
||||
this.allowSessionCreation = allowSessionCreation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
// register magic bean
|
||||
BeanDefinition oauth2LoginBeanConfig = BeanDefinitionBuilder.rootBeanDefinition(OAuth2LoginBeanConfig.class)
|
||||
.getBeanDefinition();
|
||||
String oauth2LoginBeanConfigId = parserContext.getReaderContext().generateBeanName(oauth2LoginBeanConfig);
|
||||
parserContext.registerBeanComponent(
|
||||
new BeanComponentDefinition(oauth2LoginBeanConfig, oauth2LoginBeanConfigId));
|
||||
|
||||
// configure filter
|
||||
BeanMetadataElement clientRegistrationRepository = getClientRegistrationRepository(element);
|
||||
BeanMetadataElement authorizedClientRepository = getAuthorizedClientRepository(element,
|
||||
clientRegistrationRepository);
|
||||
BeanMetadataElement accessTokenResponseClient = getAccessTokenResponseClient(element);
|
||||
BeanMetadataElement oauth2UserService = getOAuth2UserService(element);
|
||||
BeanMetadataElement authorizationRequestRepository = getAuthorizationRequestRepository(element);
|
||||
|
||||
BeanDefinitionBuilder oauth2LoginAuthenticationFilterBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(OAuth2LoginAuthenticationFilter.class)
|
||||
.addConstructorArgValue(clientRegistrationRepository)
|
||||
.addConstructorArgValue(authorizedClientRepository)
|
||||
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository);
|
||||
|
||||
if (sessionStrategy != null) {
|
||||
oauth2LoginAuthenticationFilterBuilder.addPropertyValue("sessionAuthenticationStrategy", sessionStrategy);
|
||||
}
|
||||
|
||||
Object source = parserContext.extractSource(element);
|
||||
String loginProcessingUrl = element.getAttribute(ATT_LOGIN_PROCESSING_URL);
|
||||
if (!StringUtils.isEmpty(loginProcessingUrl)) {
|
||||
WebConfigUtils.validateHttpRedirect(loginProcessingUrl, parserContext, source);
|
||||
oauth2LoginAuthenticationFilterBuilder.addConstructorArgValue(loginProcessingUrl);
|
||||
} else {
|
||||
oauth2LoginAuthenticationFilterBuilder
|
||||
.addConstructorArgValue(OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI);
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder oauth2LoginAuthenticationProviderBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(OAuth2LoginAuthenticationProvider.class)
|
||||
.addConstructorArgValue(accessTokenResponseClient)
|
||||
.addConstructorArgValue(oauth2UserService);
|
||||
|
||||
String userAuthoritiesMapperRef = element.getAttribute(ATT_USER_AUTHORITIES_MAPPER_REF);
|
||||
if (!StringUtils.isEmpty(userAuthoritiesMapperRef)) {
|
||||
oauth2LoginAuthenticationProviderBuilder.addPropertyReference("authoritiesMapper", userAuthoritiesMapperRef);
|
||||
}
|
||||
|
||||
oauth2LoginAuthenticationProvider = oauth2LoginAuthenticationProviderBuilder.getBeanDefinition();
|
||||
|
||||
oauth2LoginOidcAuthenticationProvider = getOidcAuthProvider(
|
||||
element, accessTokenResponseClient, userAuthoritiesMapperRef);
|
||||
|
||||
BeanDefinitionBuilder oauth2AuthorizationRequestRedirectFilterBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(OAuth2AuthorizationRequestRedirectFilter.class);
|
||||
|
||||
String authorizationRequestResolverRef = element.getAttribute(ATT_AUTHORIZATION_REQUEST_RESOLVER_REF);
|
||||
if (!StringUtils.isEmpty(authorizationRequestResolverRef)) {
|
||||
oauth2AuthorizationRequestRedirectFilterBuilder
|
||||
.addConstructorArgReference(authorizationRequestResolverRef);
|
||||
} else {
|
||||
oauth2AuthorizationRequestRedirectFilterBuilder.addConstructorArgValue(clientRegistrationRepository);
|
||||
}
|
||||
|
||||
oauth2AuthorizationRequestRedirectFilterBuilder
|
||||
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository)
|
||||
.addPropertyValue("requestCache", requestCache);
|
||||
oauth2AuthorizationRequestRedirectFilter = oauth2AuthorizationRequestRedirectFilterBuilder.getBeanDefinition();
|
||||
|
||||
String authenticationSuccessHandlerRef = element.getAttribute(ATT_AUTHENTICATION_SUCCESS_HANDLER_REF);
|
||||
if (!StringUtils.isEmpty(authenticationSuccessHandlerRef)) {
|
||||
oauth2LoginAuthenticationFilterBuilder.addPropertyReference("authenticationSuccessHandler",
|
||||
authenticationSuccessHandlerRef);
|
||||
} else {
|
||||
BeanDefinitionBuilder successHandlerBuilder = BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler")
|
||||
.addPropertyValue("requestCache", requestCache);
|
||||
oauth2LoginAuthenticationFilterBuilder.addPropertyValue("authenticationSuccessHandler",
|
||||
successHandlerBuilder.getBeanDefinition());
|
||||
}
|
||||
|
||||
String loginPage = element.getAttribute(ATT_LOGIN_PAGE);
|
||||
if (!StringUtils.isEmpty(loginPage)) {
|
||||
WebConfigUtils.validateHttpRedirect(loginPage, parserContext, source);
|
||||
oauth2LoginAuthenticationEntryPoint = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(LoginUrlAuthenticationEntryPoint.class)
|
||||
.addConstructorArgValue(loginPage)
|
||||
.addPropertyValue("portMapper", portMapper)
|
||||
.addPropertyValue("portResolver", portResolver)
|
||||
.getBeanDefinition();
|
||||
} else {
|
||||
Map<RequestMatcher, AuthenticationEntryPoint> entryPoint = getLoginEntryPoint(element);
|
||||
if (entryPoint != null) {
|
||||
oauth2LoginAuthenticationEntryPoint = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DelegatingAuthenticationEntryPoint.class)
|
||||
.addConstructorArgValue(entryPoint)
|
||||
.addPropertyValue("defaultEntryPoint", new LoginUrlAuthenticationEntryPoint(DEFAULT_LOGIN_URI))
|
||||
.getBeanDefinition();
|
||||
}
|
||||
}
|
||||
|
||||
String authenticationFailureHandlerRef = element.getAttribute(ATT_AUTHENTICATION_FAILURE_HANDLER_REF);
|
||||
if (!StringUtils.isEmpty(authenticationFailureHandlerRef)) {
|
||||
oauth2LoginAuthenticationFilterBuilder.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", allowSessionCreation);
|
||||
oauth2LoginAuthenticationFilterBuilder.addPropertyValue("authenticationFailureHandler",
|
||||
failureHandlerBuilder.getBeanDefinition());
|
||||
}
|
||||
|
||||
// prepare loginlinks
|
||||
oauth2LoginLinks = BeanDefinitionBuilder.rootBeanDefinition(Map.class)
|
||||
.setFactoryMethodOnBean("getLoginLinks", oauth2LoginBeanConfigId).getBeanDefinition();
|
||||
|
||||
return oauth2LoginAuthenticationFilterBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanMetadataElement getAuthorizationRequestRepository(Element element) {
|
||||
BeanMetadataElement authorizationRequestRepository;
|
||||
String authorizationRequestRepositoryRef = element.getAttribute(ATT_AUTHORIZATION_REQUEST_REPOSITORY_REF);
|
||||
if (!StringUtils.isEmpty(authorizationRequestRepositoryRef)) {
|
||||
authorizationRequestRepository = new RuntimeBeanReference(authorizationRequestRepositoryRef);
|
||||
} else {
|
||||
authorizationRequestRepository = BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository")
|
||||
.getBeanDefinition();
|
||||
}
|
||||
return authorizationRequestRepository;
|
||||
}
|
||||
|
||||
private BeanMetadataElement getAuthorizedClientRepository(Element element,
|
||||
BeanMetadataElement clientRegistrationRepository) {
|
||||
BeanMetadataElement authorizedClientRepository;
|
||||
String authorizedClientRepositoryRef = element.getAttribute(ATT_AUTHORIZED_CLIENT_REPOSITORY_REF);
|
||||
if (!StringUtils.isEmpty(authorizedClientRepositoryRef)) {
|
||||
authorizedClientRepository = new RuntimeBeanReference(authorizedClientRepositoryRef);
|
||||
} else {
|
||||
BeanMetadataElement authorizedClientService;
|
||||
String authorizedClientServiceRef = element.getAttribute(ATT_AUTHORIZED_CLIENT_SERVICE_REF);
|
||||
if (!StringUtils.isEmpty(authorizedClientServiceRef)) {
|
||||
authorizedClientService = new RuntimeBeanReference(authorizedClientServiceRef);
|
||||
} else {
|
||||
authorizedClientService = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(
|
||||
"org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService")
|
||||
.addConstructorArgValue(clientRegistrationRepository).getBeanDefinition();
|
||||
}
|
||||
authorizedClientRepository = BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository")
|
||||
.addConstructorArgValue(authorizedClientService).getBeanDefinition();
|
||||
}
|
||||
return authorizedClientRepository;
|
||||
}
|
||||
|
||||
private BeanMetadataElement getClientRegistrationRepository(Element element) {
|
||||
BeanMetadataElement clientRegistrationRepository;
|
||||
String clientRegistrationRepositoryRef = element.getAttribute(ATT_CLIENT_REGISTRATION_REPOSITORY_REF);
|
||||
if (!StringUtils.isEmpty(clientRegistrationRepositoryRef)) {
|
||||
clientRegistrationRepository = new RuntimeBeanReference(clientRegistrationRepositoryRef);
|
||||
} else {
|
||||
clientRegistrationRepository = new RuntimeBeanReference(ClientRegistrationRepository.class);
|
||||
}
|
||||
return clientRegistrationRepository;
|
||||
}
|
||||
|
||||
private BeanDefinition getOidcAuthProvider(Element element,
|
||||
BeanMetadataElement accessTokenResponseClient, String userAuthoritiesMapperRef) {
|
||||
|
||||
boolean oidcAuthenticationProviderEnabled = ClassUtils.isPresent(
|
||||
"org.springframework.security.oauth2.jwt.JwtDecoder", this.getClass().getClassLoader());
|
||||
if (!oidcAuthenticationProviderEnabled) {
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(OidcAuthenticationRequestChecker.class).getBeanDefinition();
|
||||
}
|
||||
|
||||
BeanMetadataElement oidcUserService = getOidcUserService(element);
|
||||
|
||||
BeanDefinitionBuilder oidcAuthProviderBuilder = BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider")
|
||||
.addConstructorArgValue(accessTokenResponseClient)
|
||||
.addConstructorArgValue(oidcUserService);
|
||||
|
||||
if (!StringUtils.isEmpty(userAuthoritiesMapperRef)) {
|
||||
oidcAuthProviderBuilder.addPropertyReference("authoritiesMapper", userAuthoritiesMapperRef);
|
||||
}
|
||||
|
||||
String jwtDecoderFactoryRef = element.getAttribute(ATT_JWT_DECODER_FACTORY_REF);
|
||||
if (!StringUtils.isEmpty(jwtDecoderFactoryRef)) {
|
||||
oidcAuthProviderBuilder.addPropertyReference("jwtDecoderFactory", jwtDecoderFactoryRef);
|
||||
}
|
||||
|
||||
return oidcAuthProviderBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanMetadataElement getOidcUserService(Element element) {
|
||||
BeanMetadataElement oidcUserService;
|
||||
String oidcUserServiceRef = element.getAttribute(ATT_OIDC_USER_SERVICE_REF);
|
||||
if (!StringUtils.isEmpty(oidcUserServiceRef)) {
|
||||
oidcUserService = new RuntimeBeanReference(oidcUserServiceRef);
|
||||
} else {
|
||||
oidcUserService = BeanDefinitionBuilder
|
||||
.rootBeanDefinition("org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService")
|
||||
.getBeanDefinition();
|
||||
}
|
||||
return oidcUserService;
|
||||
}
|
||||
|
||||
private BeanMetadataElement getOAuth2UserService(Element element) {
|
||||
BeanMetadataElement oauth2UserService;
|
||||
String oauth2UserServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);
|
||||
if (!StringUtils.isEmpty(oauth2UserServiceRef)) {
|
||||
oauth2UserService = new RuntimeBeanReference(oauth2UserServiceRef);
|
||||
} else {
|
||||
oauth2UserService = BeanDefinitionBuilder
|
||||
.rootBeanDefinition("org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService")
|
||||
.getBeanDefinition();
|
||||
}
|
||||
return oauth2UserService;
|
||||
}
|
||||
|
||||
private BeanMetadataElement getAccessTokenResponseClient(Element element) {
|
||||
BeanMetadataElement accessTokenResponseClient;
|
||||
String accessTokenResponseClientRef = element.getAttribute(ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF);
|
||||
if (!StringUtils.isEmpty(accessTokenResponseClientRef)) {
|
||||
accessTokenResponseClient = new RuntimeBeanReference(accessTokenResponseClientRef);
|
||||
} else {
|
||||
accessTokenResponseClient = BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient")
|
||||
.getBeanDefinition();
|
||||
}
|
||||
return accessTokenResponseClient;
|
||||
}
|
||||
|
||||
BeanDefinition getOAuth2AuthorizationRequestRedirectFilter() {
|
||||
return oauth2AuthorizationRequestRedirectFilter;
|
||||
}
|
||||
|
||||
BeanDefinition getOAuth2LoginAuthenticationEntryPoint() {
|
||||
return oauth2LoginAuthenticationEntryPoint;
|
||||
}
|
||||
|
||||
BeanDefinition getOAuth2LoginAuthenticationProvider() {
|
||||
return oauth2LoginAuthenticationProvider;
|
||||
}
|
||||
|
||||
BeanDefinition getOAuth2LoginOidcAuthenticationProvider() {
|
||||
return oauth2LoginOidcAuthenticationProvider;
|
||||
}
|
||||
|
||||
BeanDefinition getOAuth2LoginLinks() {
|
||||
return oauth2LoginLinks;
|
||||
}
|
||||
|
||||
private Map<RequestMatcher, AuthenticationEntryPoint> getLoginEntryPoint(Element element) {
|
||||
Map<RequestMatcher, AuthenticationEntryPoint> entryPoints = null;
|
||||
Element clientRegsElt = DomUtils.getChildElementByTagName(element.getOwnerDocument().getDocumentElement(),
|
||||
Elements.CLIENT_REGISTRATIONS);
|
||||
if (clientRegsElt != null) {
|
||||
List<Element> clientRegList = DomUtils.getChildElementsByTagName(clientRegsElt, ELT_CLIENT_REGISTRATION);
|
||||
if (clientRegList.size() == 1) {
|
||||
RequestMatcher loginPageMatcher = new AntPathRequestMatcher(DEFAULT_LOGIN_URI);
|
||||
RequestMatcher faviconMatcher = new AntPathRequestMatcher("/favicon.ico");
|
||||
RequestMatcher defaultEntryPointMatcher = this.getAuthenticationEntryPointMatcher();
|
||||
RequestMatcher defaultLoginPageMatcher = new AndRequestMatcher(
|
||||
new OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher);
|
||||
|
||||
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
|
||||
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
|
||||
|
||||
Element clientRegElt = clientRegList.get(0);
|
||||
entryPoints = new LinkedHashMap<>();
|
||||
entryPoints.put(
|
||||
new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher)),
|
||||
new LoginUrlAuthenticationEntryPoint(DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/"
|
||||
+ clientRegElt.getAttribute(ATT_REGISTRATION_ID)));
|
||||
}
|
||||
}
|
||||
return entryPoints;
|
||||
}
|
||||
|
||||
private RequestMatcher getAuthenticationEntryPointMatcher() {
|
||||
ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
|
||||
MediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
|
||||
MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), MediaType.TEXT_HTML,
|
||||
MediaType.TEXT_PLAIN);
|
||||
mediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
|
||||
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
|
||||
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
|
||||
return new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher));
|
||||
}
|
||||
|
||||
private static class OidcAuthenticationRequestChecker implements AuthenticationProvider {
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2LoginAuthenticationToken authorizationCodeAuthentication = (OAuth2LoginAuthenticationToken) authentication;
|
||||
|
||||
// Section 3.1.2.1 Authentication Request -
|
||||
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||
// scope
|
||||
// REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
|
||||
if (authorizationCodeAuthentication.getAuthorizationExchange().getAuthorizationRequest().getScopes()
|
||||
.contains(OidcScopes.OPENID)) {
|
||||
|
||||
OAuth2Error oauth2Error = new OAuth2Error("oidc_provider_not_configured",
|
||||
"An OpenID Connect Authentication Provider has not been configured. "
|
||||
+ "Check to ensure you include the dependency 'spring-security-oauth2-jose'.",
|
||||
null);
|
||||
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper bean class to provide configuration from applicationContext
|
||||
*/
|
||||
private static class OAuth2LoginBeanConfig implements ApplicationContextAware {
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "unused" })
|
||||
public Map<String, String> getLoginLinks() {
|
||||
Iterable<ClientRegistration> clientRegistrations = null;
|
||||
ClientRegistrationRepository clientRegistrationRepository = context
|
||||
.getBean(ClientRegistrationRepository.class);
|
||||
ResolvableType type = ResolvableType.forInstance(clientRegistrationRepository).as(Iterable.class);
|
||||
if (type != ResolvableType.NONE && ClientRegistration.class.isAssignableFrom(type.resolveGenerics()[0])) {
|
||||
clientRegistrations = (Iterable<ClientRegistration>) clientRegistrationRepository;
|
||||
}
|
||||
if (clientRegistrations == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
String authorizationRequestBaseUri = DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
|
||||
Map<String, String> loginUrlToClientName = new HashMap<>();
|
||||
clientRegistrations.forEach(registration -> loginUrlToClientName.put(
|
||||
authorizationRequestBaseUri + "/" + registration.getRegistrationId(),
|
||||
registration.getClientName()));
|
||||
|
||||
return loginUrlToClientName;
|
||||
}
|
||||
}
|
||||
}
|
||||
-358
@@ -1,358 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.List;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
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.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationManagerResolver;
|
||||
import org.springframework.security.config.Elements;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
||||
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* A {@link BeanDefinitionParser} for <http>'s <oauth2-resource-server> element.
|
||||
*
|
||||
* @since 5.3
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionParser {
|
||||
static final String AUTHENTICATION_MANAGER_RESOLVER_REF = "authentication-manager-resolver-ref";
|
||||
static final String BEARER_TOKEN_RESOLVER_REF = "bearer-token-resolver-ref";
|
||||
static final String ENTRY_POINT_REF = "entry-point-ref";
|
||||
|
||||
static final String BEARER_TOKEN_RESOLVER = "bearerTokenResolver";
|
||||
static final String AUTHENTICATION_ENTRY_POINT = "authenticationEntryPoint";
|
||||
|
||||
private final BeanReference authenticationManager;
|
||||
private final List<BeanReference> authenticationProviders;
|
||||
private final Map<BeanDefinition, BeanMetadataElement> entryPoints;
|
||||
private final Map<BeanDefinition, BeanMetadataElement> deniedHandlers;
|
||||
private final List<BeanDefinition> ignoreCsrfRequestMatchers;
|
||||
|
||||
private final BeanDefinition authenticationEntryPoint =
|
||||
new RootBeanDefinition(BearerTokenAuthenticationEntryPoint.class);
|
||||
private final BeanDefinition accessDeniedHandler =
|
||||
new RootBeanDefinition(BearerTokenAccessDeniedHandler.class);
|
||||
|
||||
OAuth2ResourceServerBeanDefinitionParser(BeanReference authenticationManager,
|
||||
List<BeanReference> authenticationProviders,
|
||||
Map<BeanDefinition, BeanMetadataElement> entryPoints,
|
||||
Map<BeanDefinition, BeanMetadataElement> deniedHandlers,
|
||||
List<BeanDefinition> ignoreCsrfRequestMatchers) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.authenticationProviders = authenticationProviders;
|
||||
this.entryPoints = entryPoints;
|
||||
this.deniedHandlers = deniedHandlers;
|
||||
this.ignoreCsrfRequestMatchers = ignoreCsrfRequestMatchers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a <oauth2-resource-server> element and return the corresponding
|
||||
* {@link BearerTokenAuthenticationFilter}
|
||||
*
|
||||
* @param oauth2ResourceServer the <oauth2-resource-server> element.
|
||||
* @param pc the {@link ParserContext}
|
||||
* @return a {@link BeanDefinition} representing a {@link BearerTokenAuthenticationFilter} definition
|
||||
*/
|
||||
@Override
|
||||
public BeanDefinition parse(Element oauth2ResourceServer, ParserContext pc) {
|
||||
Element jwt = DomUtils.getChildElementByTagName(oauth2ResourceServer, Elements.JWT);
|
||||
Element opaqueToken = DomUtils.getChildElementByTagName(oauth2ResourceServer, Elements.OPAQUE_TOKEN);
|
||||
|
||||
validateConfiguration(oauth2ResourceServer, jwt, opaqueToken, pc);
|
||||
|
||||
if (jwt != null) {
|
||||
BeanDefinition jwtAuthenticationProvider =
|
||||
new JwtBeanDefinitionParser().parse(jwt, pc);
|
||||
this.authenticationProviders.add(new RuntimeBeanReference
|
||||
(pc.getReaderContext().registerWithGeneratedName(jwtAuthenticationProvider)));
|
||||
}
|
||||
|
||||
if (opaqueToken != null) {
|
||||
BeanDefinition opaqueTokenAuthenticationProvider =
|
||||
new OpaqueTokenBeanDefinitionParser().parse(opaqueToken, pc);
|
||||
this.authenticationProviders.add(new RuntimeBeanReference
|
||||
(pc.getReaderContext().registerWithGeneratedName(opaqueTokenAuthenticationProvider)));
|
||||
}
|
||||
|
||||
BeanMetadataElement bearerTokenResolver = getBearerTokenResolver(oauth2ResourceServer);
|
||||
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(BearerTokenRequestMatcher.class);
|
||||
requestMatcherBuilder.addConstructorArgValue(bearerTokenResolver);
|
||||
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
|
||||
|
||||
BeanMetadataElement authenticationEntryPoint = getEntryPoint(oauth2ResourceServer);
|
||||
|
||||
this.entryPoints.put(requestMatcher, authenticationEntryPoint);
|
||||
this.deniedHandlers.put(requestMatcher, this.accessDeniedHandler);
|
||||
this.ignoreCsrfRequestMatchers.add(requestMatcher);
|
||||
|
||||
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(BearerTokenAuthenticationFilter.class);
|
||||
BeanMetadataElement authenticationManagerResolver = getAuthenticationManagerResolver(oauth2ResourceServer);
|
||||
filterBuilder.addConstructorArgValue(authenticationManagerResolver);
|
||||
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
|
||||
filterBuilder.addPropertyValue(AUTHENTICATION_ENTRY_POINT, authenticationEntryPoint);
|
||||
return filterBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
void validateConfiguration(Element oauth2ResourceServer, Element jwt, Element opaqueToken, ParserContext pc) {
|
||||
if (!oauth2ResourceServer.hasAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF)) {
|
||||
if (jwt == null && opaqueToken == null) {
|
||||
pc.getReaderContext().error
|
||||
("Didn't find authentication-manager-resolver-ref, <jwt>, or <opaque-token>. " +
|
||||
"Please select one.", oauth2ResourceServer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (jwt != null) {
|
||||
pc.getReaderContext().error
|
||||
("Found <jwt> as well as authentication-manager-resolver-ref. " +
|
||||
"Please select just one.", oauth2ResourceServer);
|
||||
}
|
||||
|
||||
if (opaqueToken != null) {
|
||||
pc.getReaderContext().error
|
||||
("Found <opaque-token> as well as authentication-manager-resolver-ref. " +
|
||||
"Please select just one.", oauth2ResourceServer);
|
||||
}
|
||||
}
|
||||
|
||||
BeanMetadataElement getAuthenticationManagerResolver(Element element) {
|
||||
String authenticationManagerResolverRef = element.getAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF);
|
||||
if (!StringUtils.isEmpty(authenticationManagerResolverRef)) {
|
||||
return new RuntimeBeanReference(authenticationManagerResolverRef);
|
||||
}
|
||||
BeanDefinitionBuilder authenticationManagerResolver = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(StaticAuthenticationManagerResolver.class);
|
||||
authenticationManagerResolver.addConstructorArgValue(this.authenticationManager);
|
||||
return authenticationManagerResolver.getBeanDefinition();
|
||||
}
|
||||
|
||||
BeanMetadataElement getBearerTokenResolver(Element element) {
|
||||
String bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF);
|
||||
if (StringUtils.isEmpty(bearerTokenResolverRef)) {
|
||||
return new RootBeanDefinition(DefaultBearerTokenResolver.class);
|
||||
} else {
|
||||
return new RuntimeBeanReference(bearerTokenResolverRef);
|
||||
}
|
||||
}
|
||||
|
||||
BeanMetadataElement getEntryPoint(Element element) {
|
||||
String entryPointRef = element.getAttribute(ENTRY_POINT_REF);
|
||||
if (StringUtils.isEmpty(entryPointRef)) {
|
||||
return this.authenticationEntryPoint;
|
||||
} else {
|
||||
return new RuntimeBeanReference(entryPointRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class JwtBeanDefinitionParser implements BeanDefinitionParser {
|
||||
static final String DECODER_REF = "decoder-ref";
|
||||
static final String JWK_SET_URI = "jwk-set-uri";
|
||||
static final String JWT_AUTHENTICATION_CONVERTER_REF = "jwt-authentication-converter-ref";
|
||||
static final String JWT_AUTHENTICATION_CONVERTER = "jwtAuthenticationConverter";
|
||||
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext pc) {
|
||||
validateConfiguration(element, pc);
|
||||
|
||||
BeanDefinitionBuilder jwtProviderBuilder =
|
||||
BeanDefinitionBuilder.rootBeanDefinition(JwtAuthenticationProvider.class);
|
||||
jwtProviderBuilder.addConstructorArgValue(getDecoder(element));
|
||||
jwtProviderBuilder.addPropertyValue(JWT_AUTHENTICATION_CONVERTER, getJwtAuthenticationConverter(element));
|
||||
|
||||
return jwtProviderBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
void validateConfiguration(Element element, ParserContext pc) {
|
||||
boolean usesDecoder = element.hasAttribute(DECODER_REF);
|
||||
boolean usesJwkSetUri = element.hasAttribute(JWK_SET_URI);
|
||||
|
||||
if (usesDecoder == usesJwkSetUri) {
|
||||
pc.getReaderContext().error
|
||||
("Please specify either decoder-ref or jwk-set-uri.", element);
|
||||
}
|
||||
}
|
||||
|
||||
Object getDecoder(Element element) {
|
||||
String decoderRef = element.getAttribute(DECODER_REF);
|
||||
if (!StringUtils.isEmpty(decoderRef)) {
|
||||
return new RuntimeBeanReference(decoderRef);
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(NimbusJwtDecoderJwkSetUriFactoryBean.class);
|
||||
builder.addConstructorArgValue(element.getAttribute(JWK_SET_URI));
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
Object getJwtAuthenticationConverter(Element element) {
|
||||
String jwtDecoderRef = element.getAttribute(JWT_AUTHENTICATION_CONVERTER_REF);
|
||||
if (!StringUtils.isEmpty(jwtDecoderRef)) {
|
||||
return new RuntimeBeanReference(jwtDecoderRef);
|
||||
}
|
||||
|
||||
return new JwtAuthenticationConverter();
|
||||
}
|
||||
|
||||
JwtBeanDefinitionParser() {}
|
||||
}
|
||||
|
||||
final class OpaqueTokenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
static final String INTROSPECTOR_REF = "introspector-ref";
|
||||
static final String INTROSPECTION_URI = "introspection-uri";
|
||||
static final String CLIENT_ID = "client-id";
|
||||
static final String CLIENT_SECRET = "client-secret";
|
||||
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext pc) {
|
||||
validateConfiguration(element, pc);
|
||||
|
||||
BeanMetadataElement introspector = getIntrospector(element);
|
||||
BeanDefinitionBuilder opaqueTokenProviderBuilder =
|
||||
BeanDefinitionBuilder.rootBeanDefinition(OpaqueTokenAuthenticationProvider.class);
|
||||
opaqueTokenProviderBuilder.addConstructorArgValue(introspector);
|
||||
|
||||
return opaqueTokenProviderBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
void validateConfiguration(Element element, ParserContext pc) {
|
||||
boolean usesIntrospector = element.hasAttribute(INTROSPECTOR_REF);
|
||||
boolean usesEndpoint = element.hasAttribute(INTROSPECTION_URI) ||
|
||||
element.hasAttribute(CLIENT_ID) ||
|
||||
element.hasAttribute(CLIENT_SECRET);
|
||||
|
||||
if (usesIntrospector == usesEndpoint) {
|
||||
pc.getReaderContext().error
|
||||
("Please specify either introspector-ref or all of " +
|
||||
"introspection-uri, client-id, and client-secret.", element);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usesEndpoint) {
|
||||
if (!(element.hasAttribute(INTROSPECTION_URI) &&
|
||||
element.hasAttribute(CLIENT_ID) &&
|
||||
element.hasAttribute(CLIENT_SECRET))) {
|
||||
pc.getReaderContext().error
|
||||
("Please specify introspection-uri, client-id, and client-secret together", element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BeanMetadataElement getIntrospector(Element element) {
|
||||
String introspectorRef = element.getAttribute(INTROSPECTOR_REF);
|
||||
if (!StringUtils.isEmpty(introspectorRef)) {
|
||||
return new RuntimeBeanReference(introspectorRef);
|
||||
}
|
||||
|
||||
String introspectionUri = element.getAttribute(INTROSPECTION_URI);
|
||||
String clientId = element.getAttribute(CLIENT_ID);
|
||||
String clientSecret = element.getAttribute(CLIENT_SECRET);
|
||||
|
||||
BeanDefinitionBuilder introspectorBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(NimbusOpaqueTokenIntrospector.class);
|
||||
introspectorBuilder.addConstructorArgValue(introspectionUri);
|
||||
introspectorBuilder.addConstructorArgValue(clientId);
|
||||
introspectorBuilder.addConstructorArgValue(clientSecret);
|
||||
|
||||
return introspectorBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
OpaqueTokenBeanDefinitionParser() {}
|
||||
}
|
||||
|
||||
final class StaticAuthenticationManagerResolver implements
|
||||
AuthenticationManagerResolver<HttpServletRequest> {
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
StaticAuthenticationManagerResolver(AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationManager resolve(HttpServletRequest context) {
|
||||
return this.authenticationManager;
|
||||
}
|
||||
}
|
||||
|
||||
final class NimbusJwtDecoderJwkSetUriFactoryBean implements FactoryBean<JwtDecoder> {
|
||||
private final String jwkSetUri;
|
||||
|
||||
NimbusJwtDecoderJwkSetUriFactoryBean(String jwkSetUri) {
|
||||
this.jwkSetUri = jwkSetUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JwtDecoder getObject() {
|
||||
return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return JwtDecoder.class;
|
||||
}
|
||||
}
|
||||
|
||||
final class BearerTokenRequestMatcher implements RequestMatcher {
|
||||
private final BearerTokenResolver bearerTokenResolver;
|
||||
|
||||
BearerTokenRequestMatcher(BearerTokenResolver bearerTokenResolver) {
|
||||
Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
|
||||
this.bearerTokenResolver = bearerTokenResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
try {
|
||||
return this.bearerTokenResolver.resolve(request) != null;
|
||||
} catch (OAuth2AuthenticationException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -34,11 +34,9 @@ enum SecurityFilters {
|
||||
HEADERS_FILTER, CORS_FILTER,
|
||||
CSRF_FILTER,
|
||||
LOGOUT_FILTER,
|
||||
OAUTH2_AUTHORIZATION_REQUEST_FILTER,
|
||||
X509_FILTER,
|
||||
PRE_AUTH_FILTER,
|
||||
CAS_FILTER,
|
||||
OAUTH2_LOGIN_FILTER,
|
||||
FORM_LOGIN_FILTER,
|
||||
OPENID_FILTER,
|
||||
LOGIN_PAGE_FILTER,
|
||||
@@ -51,7 +49,6 @@ enum SecurityFilters {
|
||||
JAAS_API_SUPPORT_FILTER,
|
||||
REMEMBER_ME_FILTER,
|
||||
ANONYMOUS_FILTER,
|
||||
OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER,
|
||||
SESSION_MANAGEMENT_FILTER,
|
||||
EXCEPTION_TRANSLATION_FILTER,
|
||||
FILTER_SECURITY_INTERCEPTOR,
|
||||
|
||||
-248
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.oauth2.client;
|
||||
|
||||
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.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrations;
|
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.core.AuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author Ruby Hartono
|
||||
* @since 5.3
|
||||
*/
|
||||
public final class ClientRegistrationsBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String ELT_CLIENT_REGISTRATION = "client-registration";
|
||||
private static final String ELT_PROVIDER = "provider";
|
||||
private static final String ATT_REGISTRATION_ID = "registration-id";
|
||||
private static final String ATT_CLIENT_ID = "client-id";
|
||||
private static final String ATT_CLIENT_SECRET = "client-secret";
|
||||
private static final String ATT_CLIENT_AUTHENTICATION_METHOD = "client-authentication-method";
|
||||
private static final String ATT_AUTHORIZATION_GRANT_TYPE = "authorization-grant-type";
|
||||
private static final String ATT_REDIRECT_URI = "redirect-uri";
|
||||
private static final String ATT_SCOPE = "scope";
|
||||
private static final String ATT_CLIENT_NAME = "client-name";
|
||||
private static final String ATT_PROVIDER_ID = "provider-id";
|
||||
private static final String ATT_AUTHORIZATION_URI = "authorization-uri";
|
||||
private static final String ATT_TOKEN_URI = "token-uri";
|
||||
private static final String ATT_USER_INFO_URI = "user-info-uri";
|
||||
private static final String ATT_USER_INFO_AUTHENTICATION_METHOD = "user-info-authentication-method";
|
||||
private static final String ATT_USER_INFO_USER_NAME_ATTRIBUTE = "user-info-user-name-attribute";
|
||||
private static final String ATT_JWK_SET_URI = "jwk-set-uri";
|
||||
private static final String ATT_ISSUER_URI = "issuer-uri";
|
||||
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),
|
||||
parserContext.extractSource(element));
|
||||
parserContext.pushContainingComponent(compositeDef);
|
||||
|
||||
Map<String, Map<String, String>> providers = getProviders(element);
|
||||
List<ClientRegistration> clientRegistrations = getClientRegistrations(element, parserContext, providers);
|
||||
|
||||
BeanDefinition clientRegistrationRepositoryBean = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(InMemoryClientRegistrationRepository.class)
|
||||
.addConstructorArgValue(clientRegistrations)
|
||||
.getBeanDefinition();
|
||||
String clientRegistrationRepositoryId = parserContext.getReaderContext().generateBeanName(
|
||||
clientRegistrationRepositoryBean);
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(
|
||||
clientRegistrationRepositoryBean, clientRegistrationRepositoryId));
|
||||
|
||||
parserContext.popAndRegisterContainingComponent();
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<ClientRegistration> getClientRegistrations(Element element, ParserContext parserContext,
|
||||
Map<String, Map<String, String>> providers) {
|
||||
List<Element> clientRegistrationElts = DomUtils.getChildElementsByTagName(element, ELT_CLIENT_REGISTRATION);
|
||||
List<ClientRegistration> clientRegistrations = new ArrayList<>();
|
||||
|
||||
for (Element clientRegistrationElt : clientRegistrationElts) {
|
||||
String registrationId = clientRegistrationElt.getAttribute(ATT_REGISTRATION_ID);
|
||||
String providerId = clientRegistrationElt.getAttribute(ATT_PROVIDER_ID);
|
||||
ClientRegistration.Builder builder = getBuilderFromIssuerIfPossible(registrationId, providerId, providers);
|
||||
if (builder == null) {
|
||||
builder = getBuilder(registrationId, providerId, providers);
|
||||
if (builder == null) {
|
||||
Object source = parserContext.extractSource(element);
|
||||
parserContext.getReaderContext().error(getErrorMessage(providerId, registrationId), source);
|
||||
// error on the config skip to next element
|
||||
continue;
|
||||
}
|
||||
}
|
||||
getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_CLIENT_ID))
|
||||
.ifPresent(builder::clientId);
|
||||
getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_CLIENT_SECRET))
|
||||
.ifPresent(builder::clientSecret);
|
||||
getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_CLIENT_AUTHENTICATION_METHOD))
|
||||
.map(ClientAuthenticationMethod::new)
|
||||
.ifPresent(builder::clientAuthenticationMethod);
|
||||
getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_AUTHORIZATION_GRANT_TYPE))
|
||||
.map(AuthorizationGrantType::new)
|
||||
.ifPresent(builder::authorizationGrantType);
|
||||
getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_REDIRECT_URI))
|
||||
.ifPresent(builder::redirectUriTemplate);
|
||||
getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_SCOPE))
|
||||
.map(StringUtils::commaDelimitedListToSet)
|
||||
.ifPresent(builder::scope);
|
||||
getOptionalIfNotEmpty(clientRegistrationElt.getAttribute(ATT_CLIENT_NAME))
|
||||
.ifPresent(builder::clientName);
|
||||
clientRegistrations.add(builder.build());
|
||||
}
|
||||
|
||||
return clientRegistrations;
|
||||
}
|
||||
|
||||
private Map<String, Map<String, String>> getProviders(Element element) {
|
||||
List<Element> providerElts = DomUtils.getChildElementsByTagName(element, ELT_PROVIDER);
|
||||
Map<String, Map<String, String>> providers = new HashMap<>();
|
||||
|
||||
for (Element providerElt : providerElts) {
|
||||
Map<String, String> provider = new HashMap<>();
|
||||
String providerId = providerElt.getAttribute(ATT_PROVIDER_ID);
|
||||
provider.put(ATT_PROVIDER_ID, providerId);
|
||||
getOptionalIfNotEmpty(providerElt.getAttribute(ATT_AUTHORIZATION_URI))
|
||||
.ifPresent(value -> provider.put(ATT_AUTHORIZATION_URI, value));
|
||||
getOptionalIfNotEmpty(providerElt.getAttribute(ATT_TOKEN_URI))
|
||||
.ifPresent(value -> provider.put(ATT_TOKEN_URI, value));
|
||||
getOptionalIfNotEmpty(providerElt.getAttribute(ATT_USER_INFO_URI))
|
||||
.ifPresent(value -> provider.put(ATT_USER_INFO_URI, value));
|
||||
getOptionalIfNotEmpty(providerElt.getAttribute(ATT_USER_INFO_AUTHENTICATION_METHOD))
|
||||
.ifPresent(value -> provider.put(ATT_USER_INFO_AUTHENTICATION_METHOD, value));
|
||||
getOptionalIfNotEmpty(providerElt.getAttribute(ATT_USER_INFO_USER_NAME_ATTRIBUTE))
|
||||
.ifPresent(value -> provider.put(ATT_USER_INFO_USER_NAME_ATTRIBUTE, value));
|
||||
getOptionalIfNotEmpty(providerElt.getAttribute(ATT_JWK_SET_URI))
|
||||
.ifPresent(value -> provider.put(ATT_JWK_SET_URI, value));
|
||||
getOptionalIfNotEmpty(providerElt.getAttribute(ATT_ISSUER_URI))
|
||||
.ifPresent(value -> provider.put(ATT_ISSUER_URI, value));
|
||||
providers.put(providerId, provider);
|
||||
}
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
private static ClientRegistration.Builder getBuilderFromIssuerIfPossible(String registrationId,
|
||||
String configuredProviderId, Map<String, Map<String, String>> providers) {
|
||||
String providerId = configuredProviderId != null ? configuredProviderId : registrationId;
|
||||
if (providers.containsKey(providerId)) {
|
||||
Map<String, String> provider = providers.get(providerId);
|
||||
String issuer = provider.get(ATT_ISSUER_URI);
|
||||
if (!StringUtils.isEmpty(issuer)) {
|
||||
ClientRegistration.Builder builder = ClientRegistrations.fromIssuerLocation(issuer)
|
||||
.registrationId(registrationId);
|
||||
return getBuilder(builder, provider);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ClientRegistration.Builder getBuilder(String registrationId, String configuredProviderId,
|
||||
Map<String, Map<String, String>> providers) {
|
||||
String providerId = (configuredProviderId != null) ? configuredProviderId : registrationId;
|
||||
CommonOAuth2Provider provider = getCommonProvider(providerId);
|
||||
if (provider == null && !providers.containsKey(providerId)) {
|
||||
return null;
|
||||
}
|
||||
ClientRegistration.Builder builder = provider != null ? provider.getBuilder(registrationId)
|
||||
: ClientRegistration.withRegistrationId(registrationId);
|
||||
if (providers.containsKey(providerId)) {
|
||||
return getBuilder(builder, providers.get(providerId));
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static ClientRegistration.Builder getBuilder(ClientRegistration.Builder builder,
|
||||
Map<String, String> provider) {
|
||||
getOptionalIfNotEmpty(provider.get(ATT_AUTHORIZATION_URI))
|
||||
.ifPresent(builder::authorizationUri);
|
||||
getOptionalIfNotEmpty(provider.get(ATT_TOKEN_URI))
|
||||
.ifPresent(builder::tokenUri);
|
||||
getOptionalIfNotEmpty(provider.get(ATT_USER_INFO_URI))
|
||||
.ifPresent(builder::userInfoUri);
|
||||
getOptionalIfNotEmpty(provider.get(ATT_USER_INFO_AUTHENTICATION_METHOD))
|
||||
.map(AuthenticationMethod::new)
|
||||
.ifPresent(builder::userInfoAuthenticationMethod);
|
||||
getOptionalIfNotEmpty(provider.get(ATT_JWK_SET_URI))
|
||||
.ifPresent(builder::jwkSetUri);
|
||||
getOptionalIfNotEmpty(provider.get(ATT_USER_INFO_USER_NAME_ATTRIBUTE))
|
||||
.ifPresent(builder::userNameAttributeName);
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static Optional<String> getOptionalIfNotEmpty(String str) {
|
||||
return Optional.ofNullable(str).filter(s -> !s.isEmpty());
|
||||
}
|
||||
|
||||
private static CommonOAuth2Provider getCommonProvider(String providerId) {
|
||||
try {
|
||||
String value = providerId.trim();
|
||||
if (value.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return CommonOAuth2Provider.valueOf(value);
|
||||
} catch (Exception ex) {
|
||||
return findEnum(value);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static CommonOAuth2Provider findEnum(String value) {
|
||||
String name = getCanonicalName(value);
|
||||
for (CommonOAuth2Provider candidate : EnumSet.allOf(CommonOAuth2Provider.class)) {
|
||||
String candidateName = getCanonicalName(candidate.name());
|
||||
if (name.equals(candidateName)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"No enum constant " + CommonOAuth2Provider.class.getCanonicalName() + "." + value);
|
||||
}
|
||||
|
||||
private static String getCanonicalName(String name) {
|
||||
StringBuilder canonicalName = new StringBuilder(name.length());
|
||||
name.chars().filter(Character::isLetterOrDigit).map(Character::toLowerCase)
|
||||
.forEach(c -> canonicalName.append((char) c));
|
||||
return canonicalName.toString();
|
||||
}
|
||||
|
||||
private static String getErrorMessage(String configuredProviderId, String registrationId) {
|
||||
return configuredProviderId != null ? "Unknown provider ID '" + configuredProviderId + "'"
|
||||
: "Provider ID must be specified for client registration '" + registrationId + "'";
|
||||
}
|
||||
}
|
||||
+7
-19
@@ -31,6 +31,8 @@ import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository;
|
||||
import org.springframework.security.oauth2.client.web.server.WebSessionOAuth2ServerAuthorizationRequestRepository;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
|
||||
@@ -45,6 +47,7 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;
|
||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||
@@ -1098,7 +1101,6 @@ public class ServerHttpSecurity {
|
||||
delegate.convert(exchange).onErrorMap(OAuth2AuthorizationException.class,
|
||||
e -> new OAuth2AuthenticationException(e.getError(), e.getError().toString()));
|
||||
this.authenticationConverter = authenticationConverter;
|
||||
return authenticationConverter;
|
||||
}
|
||||
return this.authenticationConverter;
|
||||
}
|
||||
@@ -1626,7 +1628,7 @@ public class ServerHttpSecurity {
|
||||
|
||||
private JwtSpec jwt;
|
||||
private OpaqueTokenSpec opaqueToken;
|
||||
private ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver;
|
||||
private ReactiveAuthenticationManagerResolver<ServerHttpRequest> authenticationManagerResolver;
|
||||
|
||||
/**
|
||||
* Configures the {@link ServerAccessDeniedHandler} to use for requests authenticating with
|
||||
@@ -1676,10 +1678,10 @@ public class ServerHttpSecurity {
|
||||
*
|
||||
* @param authenticationManagerResolver the {@link ReactiveAuthenticationManagerResolver}
|
||||
* @return the {@link OAuth2ResourceServerSpec} for additional configuration
|
||||
* @since 5.3
|
||||
* @since 5.2
|
||||
*/
|
||||
public OAuth2ResourceServerSpec authenticationManagerResolver(
|
||||
ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver) {
|
||||
ReactiveAuthenticationManagerResolver<ServerHttpRequest> authenticationManagerResolver) {
|
||||
Assert.notNull(authenticationManagerResolver, "authenticationManagerResolver cannot be null");
|
||||
this.authenticationManagerResolver = authenticationManagerResolver;
|
||||
return this;
|
||||
@@ -2660,7 +2662,7 @@ public class ServerHttpSecurity {
|
||||
|
||||
/**
|
||||
* Require a specific authority.
|
||||
* @param authority the authority to require (i.e. "USER" woudl require authority of "USER").
|
||||
* @param authority the authority to require (i.e. "USER" would require authority of "USER").
|
||||
* @return the {@link AuthorizeExchangeSpec} to configure
|
||||
*/
|
||||
public AuthorizeExchangeSpec hasAuthority(String authority) {
|
||||
@@ -3377,20 +3379,6 @@ public class ServerHttpSecurity {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures custom headers writer
|
||||
*
|
||||
* @param serverHttpHeadersWriter the {@link ServerHttpHeadersWriter} to provide custom headers writer
|
||||
* @return the {@link HeaderSpec} to customize
|
||||
* @since 5.3.0
|
||||
* @author Ankur Pathak
|
||||
*/
|
||||
public HeaderSpec writer(ServerHttpHeadersWriter serverHttpHeadersWriter) {
|
||||
Assert.notNull(serverHttpHeadersWriter, "serverHttpHeadersWriter cannot be null");
|
||||
this.writers.add(serverHttpHeadersWriter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the Strict Transport Security response headers
|
||||
* @return the {@link HstsSpec} to configure
|
||||
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.web.util.matcher.AnyRequestMatcher
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||
|
||||
/**
|
||||
* A base class that provides authorization rules for [RequestMatcher]s and patterns.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
*/
|
||||
@SecurityMarker
|
||||
abstract class AbstractRequestMatcherDsl {
|
||||
/**
|
||||
* Matches any request.
|
||||
*/
|
||||
val anyRequest: RequestMatcher = AnyRequestMatcher.INSTANCE
|
||||
|
||||
protected data class MatcherAuthorizationRule(val matcher: RequestMatcher,
|
||||
override val rule: String) : AuthorizationRule(rule)
|
||||
|
||||
protected data class PatternAuthorizationRule(val pattern: String,
|
||||
val patternType: PatternType,
|
||||
val servletPath: String?,
|
||||
override val rule: String) : AuthorizationRule(rule)
|
||||
|
||||
protected abstract class AuthorizationRule(open val rule: String)
|
||||
|
||||
protected enum class PatternType {
|
||||
ANT, MVC
|
||||
}
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.GrantedAuthority
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] anonymous authentication using idiomatic
|
||||
* Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property key the key to identify tokens created for anonymous authentication
|
||||
* @property principal the principal for [Authentication] objects of anonymous users
|
||||
* @property authorities the [Authentication.getAuthorities] for anonymous users
|
||||
* @property authenticationProvider the [AuthenticationProvider] used to validate an
|
||||
* anonymous user
|
||||
* @property authenticationFilter the [AnonymousAuthenticationFilter] used to populate
|
||||
* an anonymous user.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class AnonymousDsl {
|
||||
var key: String? = null
|
||||
var principal: Any? = null
|
||||
var authorities: List<GrantedAuthority>? = null
|
||||
var authenticationProvider: AuthenticationProvider? = null
|
||||
var authenticationFilter: AnonymousAuthenticationFilter? = null
|
||||
|
||||
private var disabled = false
|
||||
|
||||
/**
|
||||
* Disable anonymous authentication
|
||||
*/
|
||||
fun disable() {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
internal fun get(): (AnonymousConfigurer<HttpSecurity>) -> Unit {
|
||||
return { anonymous ->
|
||||
key?.also { anonymous.key(key) }
|
||||
principal?.also { anonymous.principal(principal) }
|
||||
authorities?.also { anonymous.authorities(authorities) }
|
||||
authenticationProvider?.also { anonymous.authenticationProvider(authenticationProvider) }
|
||||
authenticationFilter?.also { anonymous.authenticationFilter(authenticationFilter) }
|
||||
if (disabled) {
|
||||
anonymous.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-168
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||
import org.springframework.util.ClassUtils
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] request authorization using idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
*/
|
||||
class AuthorizeRequestsDsl : AbstractRequestMatcherDsl() {
|
||||
private val authorizationRules = mutableListOf<AuthorizationRule>()
|
||||
|
||||
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
|
||||
private val MVC_PRESENT = ClassUtils.isPresent(
|
||||
HANDLER_MAPPING_INTROSPECTOR,
|
||||
AuthorizeRequestsDsl::class.java.classLoader)
|
||||
|
||||
/**
|
||||
* Adds a request authorization rule.
|
||||
*
|
||||
* @param matches the [RequestMatcher] to match incoming requests against
|
||||
* @param access the SpEL expression to secure the matching request
|
||||
* (i.e. "hasAuthority('ROLE_USER') and hasAuthority('ROLE_SUPER')")
|
||||
*/
|
||||
fun authorize(matches: RequestMatcher = AnyRequestMatcher.INSTANCE,
|
||||
access: String = "authenticated") {
|
||||
authorizationRules.add(MatcherAuthorizationRule(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 an 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 SpEL expression to secure the matching request
|
||||
* (i.e. "hasAuthority('ROLE_USER') and hasAuthority('ROLE_SUPER')")
|
||||
*/
|
||||
fun authorize(pattern: String, access: String = "authenticated") {
|
||||
if (MVC_PRESENT) {
|
||||
authorizationRules.add(PatternAuthorizationRule(pattern, PatternType.MVC, null, access))
|
||||
} else {
|
||||
authorizationRules.add(PatternAuthorizationRule(pattern, PatternType.ANT, null, 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 an 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 SpEL expression to secure the matching request
|
||||
* (i.e. "hasAuthority('ROLE_USER') and hasAuthority('ROLE_SUPER')")
|
||||
*/
|
||||
fun authorize(pattern: String, servletPath: String, access: String = "authenticated") {
|
||||
if (MVC_PRESENT) {
|
||||
authorizationRules.add(PatternAuthorizationRule(pattern, PatternType.MVC, servletPath, access))
|
||||
} else {
|
||||
authorizationRules.add(PatternAuthorizationRule(pattern, PatternType.ANT, servletPath, access))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that URLs require a particular authority.
|
||||
*
|
||||
* @param authority the authority to require (i.e. ROLE_USER, ROLE_ADMIN, etc).
|
||||
* @return the SpEL expression "hasAuthority" with the given authority as a
|
||||
* parameter
|
||||
*/
|
||||
fun hasAuthority(authority: String) = "hasAuthority('$authority')"
|
||||
|
||||
/**
|
||||
* Specify that URLs require a particular role.
|
||||
*
|
||||
* @param role the role to require (i.e. USER, ADMIN, etc).
|
||||
* @return the SpEL expression "hasRole" with the given role as a
|
||||
* parameter
|
||||
*/
|
||||
fun hasRole(role: String) = "hasRole('$role')"
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by anyone.
|
||||
*/
|
||||
val permitAll = "permitAll"
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by anonymous users.
|
||||
*/
|
||||
val anonymous = "anonymous"
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by users that have been remembered.
|
||||
*/
|
||||
val rememberMe = "rememberMe"
|
||||
|
||||
/**
|
||||
* Specify that URLs are not allowed by anyone.
|
||||
*/
|
||||
val denyAll = "denyAll"
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by any authenticated user.
|
||||
*/
|
||||
val authenticated = "authenticated"
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by users who have authenticated and were not
|
||||
* "remembered".
|
||||
*/
|
||||
val fullyAuthenticated = "fullyAuthenticated"
|
||||
|
||||
internal fun get(): (ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry) -> Unit {
|
||||
return { requests ->
|
||||
authorizationRules.forEach { rule ->
|
||||
when (rule) {
|
||||
is MatcherAuthorizationRule -> requests.requestMatchers(rule.matcher).access(rule.rule)
|
||||
is PatternAuthorizationRule -> {
|
||||
when (rule.patternType) {
|
||||
PatternType.ANT -> requests.antMatchers(rule.pattern).access(rule.rule)
|
||||
PatternType.MVC -> {
|
||||
val mvcMatchersAuthorizeUrl = requests.mvcMatchers(rule.pattern)
|
||||
rule.servletPath?.also { mvcMatchersAuthorizeUrl.servletPath(rule.servletPath) }
|
||||
mvcMatchersAuthorizeUrl.access(rule.rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] CORS using idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
*/
|
||||
@SecurityMarker
|
||||
class CorsDsl {
|
||||
private var disabled = false
|
||||
|
||||
/**
|
||||
* Disable CORS.
|
||||
*/
|
||||
fun disable() {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
internal fun get(): (CorsConfigurer<HttpSecurity>) -> Unit {
|
||||
return { cors ->
|
||||
if (disabled) {
|
||||
cors.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy
|
||||
import org.springframework.security.web.csrf.CsrfTokenRepository
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] CSRF protection
|
||||
* using idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property csrfTokenRepository the [CsrfTokenRepository] to use.
|
||||
* @property requireCsrfProtectionMatcher specify the [RequestMatcher] to use for
|
||||
* determining when CSRF should be applied.
|
||||
* @property sessionAuthenticationStrategy the [SessionAuthenticationStrategy] to use.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class CsrfDsl {
|
||||
var csrfTokenRepository: CsrfTokenRepository? = null
|
||||
var requireCsrfProtectionMatcher: RequestMatcher? = null
|
||||
var sessionAuthenticationStrategy: SessionAuthenticationStrategy? = null
|
||||
|
||||
private var ignoringAntMatchers: Array<out String>? = null
|
||||
private var ignoringRequestMatchers: Array<out RequestMatcher>? = null
|
||||
private var disabled = false
|
||||
|
||||
/**
|
||||
* Allows specifying [HttpServletRequest]s that should not use CSRF Protection
|
||||
* even if they match the [requireCsrfProtectionMatcher].
|
||||
*
|
||||
* @param antMatchers the ANT pattern matchers that should not use CSRF
|
||||
* protection
|
||||
*/
|
||||
fun ignoringAntMatchers(vararg antMatchers: String) {
|
||||
ignoringAntMatchers = antMatchers
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying [HttpServletRequest]s that should not use CSRF Protection
|
||||
* even if they match the [requireCsrfProtectionMatcher].
|
||||
*
|
||||
* @param requestMatchers the request matchers that should not use CSRF
|
||||
* protection
|
||||
*/
|
||||
fun ignoringRequestMatchers(vararg requestMatchers: RequestMatcher) {
|
||||
ignoringRequestMatchers = requestMatchers
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable CSRF protection
|
||||
*/
|
||||
fun disable() {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
internal fun get(): (CsrfConfigurer<HttpSecurity>) -> Unit {
|
||||
return { csrf ->
|
||||
csrfTokenRepository?.also { csrf.csrfTokenRepository(csrfTokenRepository) }
|
||||
requireCsrfProtectionMatcher?.also { csrf.requireCsrfProtectionMatcher(requireCsrfProtectionMatcher) }
|
||||
sessionAuthenticationStrategy?.also { csrf.sessionAuthenticationStrategy(sessionAuthenticationStrategy) }
|
||||
ignoringAntMatchers?.also { csrf.ignoringAntMatchers(*ignoringAntMatchers!!) }
|
||||
ignoringRequestMatchers?.also { csrf.ignoringRequestMatchers(*ignoringRequestMatchers!!) }
|
||||
if (disabled) {
|
||||
csrf.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer
|
||||
import org.springframework.security.web.AuthenticationEntryPoint
|
||||
import org.springframework.security.web.access.AccessDeniedHandler
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] exception handling using idiomatic Kotlin
|
||||
* code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property accessDeniedPage the URL to the access denied page
|
||||
* @property accessDeniedHandler the [AccessDeniedHandler] to use
|
||||
* @property authenticationEntryPoint the [AuthenticationEntryPoint] to use
|
||||
*/
|
||||
@SecurityMarker
|
||||
class ExceptionHandlingDsl {
|
||||
var accessDeniedPage: String? = null
|
||||
var accessDeniedHandler: AccessDeniedHandler? = null
|
||||
var authenticationEntryPoint: AuthenticationEntryPoint? = null
|
||||
|
||||
private var defaultDeniedHandlerMappings: LinkedHashMap<RequestMatcher, AccessDeniedHandler> = linkedMapOf()
|
||||
private var defaultEntryPointMappings: LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> = linkedMapOf()
|
||||
private var disabled = false
|
||||
|
||||
/**
|
||||
* Sets a default [AccessDeniedHandler] to be used which prefers being
|
||||
* invoked for the provided [RequestMatcher].
|
||||
*
|
||||
* @param deniedHandler the [AccessDeniedHandler] to use
|
||||
* @param preferredMatcher the [RequestMatcher] for this default
|
||||
* [AccessDeniedHandler]
|
||||
*/
|
||||
fun defaultAccessDeniedHandlerFor(deniedHandler: AccessDeniedHandler, preferredMatcher: RequestMatcher) {
|
||||
defaultDeniedHandlerMappings[preferredMatcher] = deniedHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a default [AuthenticationEntryPoint] to be used which prefers being
|
||||
* invoked for the provided [RequestMatcher].
|
||||
*
|
||||
* @param entryPoint the [AuthenticationEntryPoint] to use
|
||||
* @param preferredMatcher the [RequestMatcher] for this default
|
||||
* [AccessDeniedHandler]
|
||||
*/
|
||||
fun defaultAuthenticationEntryPointFor(entryPoint: AuthenticationEntryPoint, preferredMatcher: RequestMatcher) {
|
||||
defaultEntryPointMappings[preferredMatcher] = entryPoint
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable exception handling.
|
||||
*/
|
||||
fun disable() {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
internal fun get(): (ExceptionHandlingConfigurer<HttpSecurity>) -> Unit {
|
||||
return { exceptionHandling ->
|
||||
accessDeniedPage?.also { exceptionHandling.accessDeniedPage(accessDeniedPage) }
|
||||
accessDeniedHandler?.also { exceptionHandling.accessDeniedHandler(accessDeniedHandler) }
|
||||
authenticationEntryPoint?.also { exceptionHandling.authenticationEntryPoint(authenticationEntryPoint) }
|
||||
defaultDeniedHandlerMappings.forEach { (matcher, handler) ->
|
||||
exceptionHandling.defaultAccessDeniedHandlerFor(handler, matcher)
|
||||
}
|
||||
defaultEntryPointMappings.forEach { (matcher, entryPoint) ->
|
||||
exceptionHandling.defaultAuthenticationEntryPointFor(entryPoint, matcher)
|
||||
}
|
||||
if (disabled) {
|
||||
exceptionHandling.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.HttpSecurityBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] form login using idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property loginPage the login page to redirect to if authentication is required (i.e.
|
||||
* "/login")
|
||||
* @property authenticationSuccessHandler the [AuthenticationSuccessHandler] used after
|
||||
* authentication success
|
||||
* @property authenticationFailureHandler the [AuthenticationFailureHandler] used after
|
||||
* authentication success
|
||||
* @property failureUrl the URL to send users if authentication fails
|
||||
* @property loginProcessingUrl the URL to validate the credentials
|
||||
* @property permitAll whether to grant access to the urls for [failureUrl] as well as
|
||||
* for the [HttpSecurityBuilder], the [loginPage] and [loginProcessingUrl] for every user
|
||||
*/
|
||||
@SecurityMarker
|
||||
class FormLoginDsl {
|
||||
var loginPage: String? = null
|
||||
var authenticationSuccessHandler: AuthenticationSuccessHandler? = null
|
||||
var authenticationFailureHandler: AuthenticationFailureHandler? = null
|
||||
var failureUrl: String? = null
|
||||
var loginProcessingUrl: String? = null
|
||||
var permitAll: Boolean? = null
|
||||
|
||||
private var defaultSuccessUrlOption: Pair<String, Boolean>? = null
|
||||
|
||||
/**
|
||||
* Grants access to the urls for [failureUrl] as well as for the [HttpSecurityBuilder], the
|
||||
* [loginPage] and [loginProcessingUrl] for every user.
|
||||
*/
|
||||
fun permitAll() {
|
||||
permitAll = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies where users will be redirected after authenticating successfully if
|
||||
* they have not visited a secured page prior to authenticating or [alwaysUse]
|
||||
* is true.
|
||||
*
|
||||
* @param defaultSuccessUrl the default success url
|
||||
* @param alwaysUse true if the [defaultSuccessUrl] should be used after
|
||||
* authentication despite if a protected page had been previously visited
|
||||
*/
|
||||
fun defaultSuccessUrl(defaultSuccessUrl: String, alwaysUse: Boolean) {
|
||||
defaultSuccessUrlOption = Pair(defaultSuccessUrl, alwaysUse)
|
||||
}
|
||||
|
||||
internal fun get(): (FormLoginConfigurer<HttpSecurity>) -> Unit {
|
||||
return { login ->
|
||||
loginPage?.also { login.loginPage(loginPage) }
|
||||
failureUrl?.also { login.failureUrl(failureUrl) }
|
||||
loginProcessingUrl?.also { login.loginProcessingUrl(loginProcessingUrl) }
|
||||
permitAll?.also { login.permitAll(permitAll!!) }
|
||||
defaultSuccessUrlOption?.also {
|
||||
login.defaultSuccessUrl(defaultSuccessUrlOption!!.first, defaultSuccessUrlOption!!.second)
|
||||
}
|
||||
authenticationSuccessHandler?.also { login.successHandler(authenticationSuccessHandler) }
|
||||
authenticationFailureHandler?.also { login.failureHandler(authenticationFailureHandler) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
|
||||
import org.springframework.security.config.web.servlet.headers.*
|
||||
import org.springframework.security.web.header.writers.*
|
||||
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] headers using idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property defaultsDisabled whether all of the default headers should be included in the response
|
||||
*/
|
||||
@SecurityMarker
|
||||
class HeadersDsl {
|
||||
private var contentTypeOptions: ((HeadersConfigurer<HttpSecurity>.ContentTypeOptionsConfig) -> Unit)? = null
|
||||
private var xssProtection: ((HeadersConfigurer<HttpSecurity>.XXssConfig) -> Unit)? = null
|
||||
private var cacheControl: ((HeadersConfigurer<HttpSecurity>.CacheControlConfig) -> Unit)? = null
|
||||
private var hsts: ((HeadersConfigurer<HttpSecurity>.HstsConfig) -> Unit)? = null
|
||||
private var frameOptions: ((HeadersConfigurer<HttpSecurity>.FrameOptionsConfig) -> Unit)? = null
|
||||
private var hpkp: ((HeadersConfigurer<HttpSecurity>.HpkpConfig) -> Unit)? = null
|
||||
private var contentSecurityPolicy: ((HeadersConfigurer<HttpSecurity>.ContentSecurityPolicyConfig) -> Unit)? = null
|
||||
private var referrerPolicy: ((HeadersConfigurer<HttpSecurity>.ReferrerPolicyConfig) -> Unit)? = null
|
||||
private var featurePolicyDirectives: String? = null
|
||||
|
||||
var defaultsDisabled: Boolean? = null
|
||||
|
||||
/**
|
||||
* Configures the [XContentTypeOptionsHeaderWriter] which inserts the <a href=
|
||||
* "https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx"
|
||||
* >X-Content-Type-Options header</a>
|
||||
*
|
||||
* @param contentTypeOptionsConfig the customization to apply to the header
|
||||
*/
|
||||
fun contentTypeOptions(contentTypeOptionsConfig: ContentTypeOptionsDsl.() -> Unit) {
|
||||
this.contentTypeOptions = ContentTypeOptionsDsl().apply(contentTypeOptionsConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* <strong>Note this is not comprehensive XSS protection!</strong>
|
||||
*
|
||||
* <p>
|
||||
* Allows customizing the [XXssProtectionHeaderWriter] which adds the <a href=
|
||||
* "https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx"
|
||||
* >X-XSS-Protection header</a>
|
||||
* </p>
|
||||
*
|
||||
* @param xssProtectionConfig the customization to apply to the header
|
||||
*/
|
||||
fun xssProtection(xssProtectionConfig: XssProtectionConfigDsl.() -> Unit) {
|
||||
this.xssProtection = XssProtectionConfigDsl().apply(xssProtectionConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows customizing the [CacheControlHeadersWriter]. Specifically it adds the
|
||||
* following headers:
|
||||
* <ul>
|
||||
* <li>Cache-Control: no-cache, no-store, max-age=0, must-revalidate</li>
|
||||
* <li>Pragma: no-cache</li>
|
||||
* <li>Expires: 0</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param cacheControlConfig the customization to apply to the header
|
||||
*/
|
||||
fun cacheControl(cacheControlConfig: CacheControlDsl.() -> Unit) {
|
||||
this.cacheControl = CacheControlDsl().apply(cacheControlConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows customizing the [HstsHeaderWriter] which provides support for <a
|
||||
* href="https://tools.ietf.org/html/rfc6797">HTTP Strict Transport Security
|
||||
* (HSTS)</a>.
|
||||
*
|
||||
* @param hstsConfig the customization to apply to the header
|
||||
*/
|
||||
fun httpStrictTransportSecurity(hstsConfig: HttpStrictTransportSecurityDsl.() -> Unit) {
|
||||
this.hsts = HttpStrictTransportSecurityDsl().apply(hstsConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows customizing the [XFrameOptionsHeaderWriter] which add the X-Frame-Options
|
||||
* header.
|
||||
*
|
||||
* @param frameOptionsConfig the customization to apply to the header
|
||||
*/
|
||||
fun frameOptions(frameOptionsConfig: FrameOptionsDsl.() -> Unit) {
|
||||
this.frameOptions = FrameOptionsDsl().apply(frameOptionsConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows customizing the [HpkpHeaderWriter] which provides support for <a
|
||||
* href="https://tools.ietf.org/html/rfc7469">HTTP Public Key Pinning (HPKP)</a>.
|
||||
*
|
||||
* @param hpkpConfig the customization to apply to the header
|
||||
*/
|
||||
fun httpPublicKeyPinning(hpkpConfig: HttpPublicKeyPinningDsl.() -> Unit) {
|
||||
this.hpkp = HttpPublicKeyPinningDsl().apply(hpkpConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuration for <a href="https://www.w3.org/TR/CSP2/">Content Security Policy (CSP) Level 2</a>.
|
||||
*
|
||||
* <p>
|
||||
* Calling this method automatically enables (includes) the Content-Security-Policy header in the response
|
||||
* using the supplied security policy directive(s).
|
||||
* </p>
|
||||
*
|
||||
* @param contentSecurityPolicyConfig the customization to apply to the header
|
||||
*/
|
||||
fun contentSecurityPolicy(contentSecurityPolicyConfig: ContentSecurityPolicyDsl.() -> Unit) {
|
||||
this.contentSecurityPolicy = ContentSecurityPolicyDsl().apply(contentSecurityPolicyConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuration for <a href="https://www.w3.org/TR/referrer-policy/">Referrer Policy</a>.
|
||||
*
|
||||
* <p>
|
||||
* Configuration is provided to the [ReferrerPolicyHeaderWriter] which support the writing
|
||||
* of the header as detailed in the W3C Technical Report:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Referrer-Policy</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param referrerPolicyConfig the customization to apply to the header
|
||||
*/
|
||||
fun referrerPolicy(referrerPolicyConfig: ReferrerPolicyDsl.() -> Unit) {
|
||||
this.referrerPolicy = ReferrerPolicyDsl().apply(referrerPolicyConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuration for <a href="https://wicg.github.io/feature-policy/">Feature
|
||||
* Policy</a>.
|
||||
*
|
||||
* <p>
|
||||
* Calling this method automatically enables (includes) the Feature-Policy
|
||||
* header in the response using the supplied policy directive(s).
|
||||
* <p>
|
||||
*
|
||||
* @param policyDirectives policyDirectives the security policy directive(s)
|
||||
*/
|
||||
fun featurePolicy(policyDirectives: String) {
|
||||
this.featurePolicyDirectives = policyDirectives
|
||||
}
|
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>) -> Unit {
|
||||
return { headers ->
|
||||
defaultsDisabled?.also {
|
||||
if (defaultsDisabled!!) {
|
||||
headers.defaultsDisabled()
|
||||
}
|
||||
}
|
||||
contentTypeOptions?.also {
|
||||
headers.contentTypeOptions(contentTypeOptions)
|
||||
}
|
||||
xssProtection?.also {
|
||||
headers.xssProtection(xssProtection)
|
||||
}
|
||||
cacheControl?.also {
|
||||
headers.cacheControl(cacheControl)
|
||||
}
|
||||
hsts?.also {
|
||||
headers.httpStrictTransportSecurity(hsts)
|
||||
}
|
||||
frameOptions?.also {
|
||||
headers.frameOptions(frameOptions)
|
||||
}
|
||||
hpkp?.also {
|
||||
headers.httpPublicKeyPinning(hpkp)
|
||||
}
|
||||
contentSecurityPolicy?.also {
|
||||
headers.contentSecurityPolicy(contentSecurityPolicy)
|
||||
}
|
||||
referrerPolicy?.also {
|
||||
headers.referrerPolicy(referrerPolicy)
|
||||
}
|
||||
featurePolicyDirectives?.also {
|
||||
headers.featurePolicy(featurePolicyDirectives)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.authentication.AuthenticationDetailsSource
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer
|
||||
import org.springframework.security.web.AuthenticationEntryPoint
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] basic authentication using idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property realmName the HTTP Basic realm to use. If [authenticationEntryPoint]
|
||||
* has been invoked, invoking this method will result in an error.
|
||||
* @property authenticationEntryPoint the [AuthenticationEntryPoint] to be populated on
|
||||
* [BasicAuthenticationFilter] in the event that authentication fails.
|
||||
* @property authenticationDetailsSource the custom [AuthenticationDetailsSource] to use for
|
||||
* basic authentication.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class HttpBasicDsl {
|
||||
var realmName: String? = null
|
||||
var authenticationEntryPoint: AuthenticationEntryPoint? = null
|
||||
var authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, *>? = null
|
||||
|
||||
private var disabled = false
|
||||
|
||||
/**
|
||||
* Disables HTTP basic authentication
|
||||
*/
|
||||
fun disable() {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
internal fun get(): (HttpBasicConfigurer<HttpSecurity>) -> Unit {
|
||||
return { httpBasic ->
|
||||
realmName?.also { httpBasic.realmName(realmName) }
|
||||
authenticationEntryPoint?.also { httpBasic.authenticationEntryPoint(authenticationEntryPoint) }
|
||||
authenticationDetailsSource?.also { httpBasic.authenticationDetailsSource(authenticationDetailsSource) }
|
||||
if (disabled) {
|
||||
httpBasic.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-678
@@ -1,678 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.context.ApplicationContext
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||
import org.springframework.util.ClassUtils
|
||||
import javax.servlet.Filter
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
/**
|
||||
* Configures [HttpSecurity] using a [HttpSecurity Kotlin DSL][HttpSecurityDsl].
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* authorizeRequests {
|
||||
* request("/public", permitAll)
|
||||
* request(anyRequest, authenticated)
|
||||
* }
|
||||
* formLogin {
|
||||
* loginPage = "/log-in"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @param httpConfiguration the configurations to apply to [HttpSecurity]
|
||||
*/
|
||||
operator fun HttpSecurity.invoke(httpConfiguration: HttpSecurityDsl.() -> Unit) =
|
||||
HttpSecurityDsl(this, httpConfiguration).build()
|
||||
|
||||
/**
|
||||
* An [HttpSecurity] Kotlin DSL created by [`http { }`][invoke]
|
||||
* in order to configure [HttpSecurity] using idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @param http the [HttpSecurity] which all configurations will be applied to
|
||||
* @param init the configurations to apply to the provided [HttpSecurity]
|
||||
*/
|
||||
@SecurityMarker
|
||||
class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecurityDsl.() -> Unit) {
|
||||
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
|
||||
|
||||
/**
|
||||
* Allows configuring the [HttpSecurity] to only be invoked when matching the
|
||||
* provided pattern.
|
||||
* If Spring MVC is on the classpath, it will use an MVC matcher.
|
||||
* If Spring MVC is not an the classpath, it will use an ant matcher.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* securityMatcher("/private/**")
|
||||
* formLogin {
|
||||
* loginPage = "/log-in"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param pattern one or more patterns used to determine whether this
|
||||
* configuration should be invoked.
|
||||
*/
|
||||
fun securityMatcher(vararg pattern: String) {
|
||||
val mvcPresent = ClassUtils.isPresent(
|
||||
HANDLER_MAPPING_INTROSPECTOR,
|
||||
AuthorizeRequestsDsl::class.java.classLoader)
|
||||
this.http.requestMatchers {
|
||||
if (mvcPresent) {
|
||||
it.mvcMatchers(*pattern)
|
||||
} else {
|
||||
it.antMatchers(*pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuring the [HttpSecurity] to only be invoked when matching the
|
||||
* provided [RequestMatcher].
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* securityMatcher(AntPathRequestMatcher("/private/**"))
|
||||
* formLogin {
|
||||
* loginPage = "/log-in"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param requestMatcher one or more [RequestMatcher] used to determine whether
|
||||
* this configuration should be invoked.
|
||||
*/
|
||||
fun securityMatcher(vararg requestMatcher: RequestMatcher) {
|
||||
this.http.requestMatchers {
|
||||
it.requestMatchers(*requestMatcher)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables form based authentication.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* formLogin {
|
||||
* loginPage = "/log-in"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param formLoginConfiguration custom configurations to be applied
|
||||
* to the form based authentication
|
||||
* @see [FormLoginDsl]
|
||||
*/
|
||||
fun formLogin(formLoginConfiguration: FormLoginDsl.() -> Unit) {
|
||||
val loginCustomizer = FormLoginDsl().apply(formLoginConfiguration).get()
|
||||
this.http.formLogin(loginCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows restricting access based upon the [HttpServletRequest]
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* authorizeRequests {
|
||||
* request("/public", permitAll)
|
||||
* request(anyRequest, authenticated)
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param authorizeRequestsConfiguration custom configuration that specifies
|
||||
* access for requests
|
||||
* @see [AuthorizeRequestsDsl]
|
||||
*/
|
||||
fun authorizeRequests(authorizeRequestsConfiguration: AuthorizeRequestsDsl.() -> Unit) {
|
||||
val authorizeRequestsCustomizer = AuthorizeRequestsDsl().apply(authorizeRequestsConfiguration).get()
|
||||
this.http.authorizeRequests(authorizeRequestsCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables HTTP basic authentication.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* httpBasic {
|
||||
* realmName = "Custom Realm"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param httpBasicConfiguration custom configurations to be applied to the
|
||||
* HTTP basic authentication
|
||||
* @see [HttpBasicDsl]
|
||||
*/
|
||||
fun httpBasic(httpBasicConfiguration: HttpBasicDsl.() -> Unit) {
|
||||
val httpBasicCustomizer = HttpBasicDsl().apply(httpBasicConfiguration).get()
|
||||
this.http.httpBasic(httpBasicCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuring response headers.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* headers {
|
||||
* referrerPolicy {
|
||||
* policy = ReferrerPolicy.SAME_ORIGIN
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param headersConfiguration custom configurations to configure the
|
||||
* response headers
|
||||
* @see [HeadersDsl]
|
||||
*/
|
||||
fun headers(headersConfiguration: HeadersDsl.() -> Unit) {
|
||||
val headersCustomizer = HeadersDsl().apply(headersConfiguration).get()
|
||||
this.http.headers(headersCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables CORS.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* cors {
|
||||
* disable()
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param corsConfiguration custom configurations to configure the
|
||||
* response headers
|
||||
* @see [CorsDsl]
|
||||
*/
|
||||
fun cors(corsConfiguration: CorsDsl.() -> Unit) {
|
||||
val corsCustomizer = CorsDsl().apply(corsConfiguration).get()
|
||||
this.http.cors(corsCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuring session management.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* sessionManagement {
|
||||
* invalidSessionUrl = "/invalid-session"
|
||||
* sessionConcurrency {
|
||||
* maximumSessions = 1
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param sessionManagementConfiguration custom configurations to configure
|
||||
* session management
|
||||
* @see [SessionManagementDsl]
|
||||
*/
|
||||
fun sessionManagement(sessionManagementConfiguration: SessionManagementDsl.() -> Unit) {
|
||||
val sessionManagementCustomizer = SessionManagementDsl().apply(sessionManagementConfiguration).get()
|
||||
this.http.sessionManagement(sessionManagementCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuring a port mapper.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* portMapper {
|
||||
* map(80, 443)
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param portMapperConfiguration custom configurations to configure
|
||||
* the port mapper
|
||||
* @see [PortMapperDsl]
|
||||
*/
|
||||
fun portMapper(portMapperConfiguration: PortMapperDsl.() -> Unit) {
|
||||
val portMapperCustomizer = PortMapperDsl().apply(portMapperConfiguration).get()
|
||||
this.http.portMapper(portMapperCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuring channel security based upon the [HttpServletRequest]
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* requiresChannel {
|
||||
* secure("/public", requiresInsecure)
|
||||
* secure(anyRequest, requiresSecure)
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param requiresChannelConfiguration custom configuration that specifies
|
||||
* channel security
|
||||
* @see [RequiresChannelDsl]
|
||||
*/
|
||||
fun requiresChannel(requiresChannelConfiguration: RequiresChannelDsl.() -> Unit) {
|
||||
val requiresChannelCustomizer = RequiresChannelDsl().apply(requiresChannelConfiguration).get()
|
||||
this.http.requiresChannel(requiresChannelCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds X509 based pre authentication to an application
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* x509 { }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param x509Configuration custom configuration to apply to the
|
||||
* X509 based pre authentication
|
||||
* @see [X509Dsl]
|
||||
*/
|
||||
fun x509(x509Configuration: X509Dsl.() -> Unit) {
|
||||
val x509Customizer = X509Dsl().apply(x509Configuration).get()
|
||||
this.http.x509(x509Customizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables request caching. Specifically this ensures that requests that
|
||||
* are saved (i.e. after authentication is required) are later replayed.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* requestCache { }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param requestCacheConfiguration custom configuration to apply to the
|
||||
* request cache
|
||||
* @see [RequestCacheDsl]
|
||||
*/
|
||||
fun requestCache(requestCacheConfiguration: RequestCacheDsl.() -> Unit) {
|
||||
val requestCacheCustomizer = RequestCacheDsl().apply(requestCacheConfiguration).get()
|
||||
this.http.requestCache(requestCacheCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuring exception handling.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* exceptionHandling {
|
||||
* accessDeniedPage = "/access-denied"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param exceptionHandlingConfiguration custom configuration to apply to the
|
||||
* exception handling
|
||||
* @see [ExceptionHandlingDsl]
|
||||
*/
|
||||
fun exceptionHandling(exceptionHandlingConfiguration: ExceptionHandlingDsl.() -> Unit) {
|
||||
val exceptionHandlingCustomizer = ExceptionHandlingDsl().apply(exceptionHandlingConfiguration).get()
|
||||
this.http.exceptionHandling(exceptionHandlingCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables CSRF protection.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* csrf { }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param csrfConfiguration custom configuration to apply to CSRF
|
||||
* @see [CsrfDsl]
|
||||
*/
|
||||
fun csrf(csrfConfiguration: CsrfDsl.() -> Unit) {
|
||||
val csrfCustomizer = CsrfDsl().apply(csrfConfiguration).get()
|
||||
this.http.csrf(csrfCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides logout support.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* logout {
|
||||
* logoutUrl = "/log-out"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param logoutConfiguration custom configuration to apply to logout
|
||||
* @see [LogoutDsl]
|
||||
*/
|
||||
fun logout(logoutConfiguration: LogoutDsl.() -> Unit) {
|
||||
val logoutCustomizer = LogoutDsl().apply(logoutConfiguration).get()
|
||||
this.http.logout(logoutCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures authentication support using a SAML 2.0 Service Provider.
|
||||
* A [RelyingPartyRegistrationRepository] is required and must be registered with
|
||||
* the [ApplicationContext] or configured via
|
||||
* [Saml2Dsl.relyingPartyRegistrationRepository]
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* saml2Login {
|
||||
* relyingPartyRegistration = getSaml2RelyingPartyRegistration()
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param saml2LoginConfiguration custom configuration to configure the
|
||||
* SAML2 service provider
|
||||
* @see [Saml2Dsl]
|
||||
*/
|
||||
fun saml2Login(saml2LoginConfiguration: Saml2Dsl.() -> Unit) {
|
||||
val saml2LoginCustomizer = Saml2Dsl().apply(saml2LoginConfiguration).get()
|
||||
this.http.saml2Login(saml2LoginCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuring how an anonymous user is represented.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* anonymous {
|
||||
* authorities = listOf(SimpleGrantedAuthority("ROLE_ANON"))
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param anonymousConfiguration custom configuration to configure the
|
||||
* anonymous user
|
||||
* @see [AnonymousDsl]
|
||||
*/
|
||||
fun anonymous(anonymousConfiguration: AnonymousDsl.() -> Unit) {
|
||||
val anonymousCustomizer = AnonymousDsl().apply(anonymousConfiguration).get()
|
||||
this.http.anonymous(anonymousCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.
|
||||
* A [ClientRegistrationRepository] is required and must be registered as a Bean or
|
||||
* configured via [OAuth2LoginDsl.clientRegistrationRepository]
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* oauth2Login {
|
||||
* clientRegistrationRepository = getClientRegistrationRepository()
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param oauth2LoginConfiguration custom configuration to configure the
|
||||
* OAuth 2.0 Login
|
||||
* @see [OAuth2LoginDsl]
|
||||
*/
|
||||
fun oauth2Login(oauth2LoginConfiguration: OAuth2LoginDsl.() -> Unit) {
|
||||
val oauth2LoginCustomizer = OAuth2LoginDsl().apply(oauth2LoginConfiguration).get()
|
||||
this.http.oauth2Login(oauth2LoginCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures OAuth 2.0 client support.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* oauth2Client { }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param oauth2ClientConfiguration custom configuration to configure the
|
||||
* OAuth 2.0 client support
|
||||
* @see [OAuth2ClientDsl]
|
||||
*/
|
||||
fun oauth2Client(oauth2ClientConfiguration: OAuth2ClientDsl.() -> Unit) {
|
||||
val oauth2ClientCustomizer = OAuth2ClientDsl().apply(oauth2ClientConfiguration).get()
|
||||
this.http.oauth2Client(oauth2ClientCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures OAuth 2.0 resource server support.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* oauth2ResourceServer {
|
||||
* jwt { }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param oauth2ResourceServerConfiguration custom configuration to configure the
|
||||
* OAuth 2.0 resource server support
|
||||
* @see [OAuth2ResourceServerDsl]
|
||||
*/
|
||||
fun oauth2ResourceServer(oauth2ResourceServerConfiguration: OAuth2ResourceServerDsl.() -> Unit) {
|
||||
val oauth2ResourceServerCustomizer = OAuth2ResourceServerDsl().apply(oauth2ResourceServerConfiguration).get()
|
||||
this.http.oauth2ResourceServer(oauth2ResourceServerCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the [Filter] at the location of the specified [Filter] class.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* addFilterAt(CustomFilter(), UsernamePasswordAuthenticationFilter::class.java)
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param filter the [Filter] to register
|
||||
* @param atFilter the location of another [Filter] that is already registered
|
||||
* (i.e. known) with Spring Security.
|
||||
*/
|
||||
fun addFilterAt(filter: Filter, atFilter: Class<out Filter>) {
|
||||
this.http.addFilterAt(filter, atFilter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply all configurations to the provided [HttpSecurity]
|
||||
*/
|
||||
internal fun build() {
|
||||
init()
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.web.access.AccessDeniedHandler
|
||||
import org.springframework.security.web.authentication.logout.LogoutHandler
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler
|
||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||
import java.util.*
|
||||
import javax.servlet.http.HttpSession
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] logout support
|
||||
* using idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property clearAuthentication whether the [SecurityContextLogoutHandler] should clear
|
||||
* the [Authentication] at the time of logout.
|
||||
* @property clearAuthentication whether to invalidate the [HttpSession] at the time of logout.
|
||||
* @property logoutUrl the URL that triggers log out to occur.
|
||||
* @property logoutRequestMatcher the [RequestMatcher] that triggers log out to occur.
|
||||
* @property logoutSuccessUrl the URL to redirect to after logout has occurred.
|
||||
* @property logoutSuccessHandler the [LogoutSuccessHandler] to use after logout has occurred.
|
||||
* If this is specified, [logoutSuccessUrl] is ignored.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class LogoutDsl {
|
||||
var clearAuthentication: Boolean? = null
|
||||
var invalidateHttpSession: Boolean? = null
|
||||
var logoutUrl: String? = null
|
||||
var logoutRequestMatcher: RequestMatcher? = null
|
||||
var logoutSuccessUrl: String? = null
|
||||
var logoutSuccessHandler: LogoutSuccessHandler? = null
|
||||
var permitAll: Boolean? = null
|
||||
|
||||
private var logoutHandlers = mutableListOf<LogoutHandler>()
|
||||
private var deleteCookies: Array<out String>? = null
|
||||
private var defaultLogoutSuccessHandlerMappings: LinkedHashMap<RequestMatcher, LogoutSuccessHandler> = linkedMapOf()
|
||||
private var disabled = false
|
||||
|
||||
|
||||
/**
|
||||
* Adds a [LogoutHandler]. The [SecurityContextLogoutHandler] is added as
|
||||
* the last [LogoutHandler] by default.
|
||||
*
|
||||
* @param logoutHandler the [LogoutHandler] to add
|
||||
*/
|
||||
fun addLogoutHandler(logoutHandler: LogoutHandler) {
|
||||
this.logoutHandlers.add(logoutHandler)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying the names of cookies to be removed on logout success.
|
||||
*
|
||||
* @param cookieNamesToClear the names of cookies to be removed on logout success.
|
||||
*/
|
||||
fun deleteCookies(vararg cookieNamesToClear: String) {
|
||||
this.deleteCookies = cookieNamesToClear
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a default [LogoutSuccessHandler] to be used which prefers being
|
||||
* invoked for the provided [RequestMatcher].
|
||||
*
|
||||
* @param logoutHandler the [LogoutSuccessHandler] to use
|
||||
* @param preferredMatcher the [RequestMatcher] for this default
|
||||
* [AccessDeniedHandler]
|
||||
*/
|
||||
fun defaultLogoutSuccessHandlerFor(logoutHandler: LogoutSuccessHandler, preferredMatcher: RequestMatcher) {
|
||||
defaultLogoutSuccessHandlerMappings[preferredMatcher] = logoutHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables logout
|
||||
*/
|
||||
fun disable() {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Grants access to the [logoutSuccessUrl] and the [logoutUrl] for every user.
|
||||
*/
|
||||
fun permitAll() {
|
||||
permitAll = true
|
||||
}
|
||||
|
||||
internal fun get(): (LogoutConfigurer<HttpSecurity>) -> Unit {
|
||||
return { logout ->
|
||||
clearAuthentication?.also { logout.clearAuthentication(clearAuthentication!!) }
|
||||
invalidateHttpSession?.also { logout.invalidateHttpSession(invalidateHttpSession!!) }
|
||||
logoutUrl?.also { logout.logoutUrl(logoutUrl) }
|
||||
logoutRequestMatcher?.also { logout.logoutRequestMatcher(logoutRequestMatcher) }
|
||||
logoutSuccessUrl?.also { logout.logoutSuccessUrl(logoutSuccessUrl) }
|
||||
logoutSuccessHandler?.also { logout.logoutSuccessHandler(logoutSuccessHandler) }
|
||||
deleteCookies?.also { logout.deleteCookies(*deleteCookies!!) }
|
||||
permitAll?.also { logout.permitAll(permitAll!!) }
|
||||
defaultLogoutSuccessHandlerMappings.forEach { (matcher, handler) ->
|
||||
logout.defaultLogoutSuccessHandlerFor(handler, matcher)
|
||||
}
|
||||
logoutHandlers.forEach { logoutHandler ->
|
||||
logout.addLogoutHandler(logoutHandler)
|
||||
}
|
||||
if (disabled) {
|
||||
logout.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.web.servlet.oauth2.client.AuthorizationCodeGrantDsl
|
||||
import org.springframework.security.config.web.servlet.oauth2.login.AuthorizationEndpointDsl
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2ClientConfigurer
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] OAuth 2.0 client support using idiomatic
|
||||
* Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property clientRegistrationRepository the repository of client registrations.
|
||||
* @property authorizedClientRepository the repository for authorized client(s).
|
||||
* @property authorizedClientService the service for authorized client(s).
|
||||
*/
|
||||
@SecurityMarker
|
||||
class OAuth2ClientDsl {
|
||||
var clientRegistrationRepository: ClientRegistrationRepository? = null
|
||||
var authorizedClientRepository: OAuth2AuthorizedClientRepository? = null
|
||||
var authorizedClientService: OAuth2AuthorizedClientService? = null
|
||||
|
||||
private var authorizationCodeGrant: ((OAuth2ClientConfigurer<HttpSecurity>.AuthorizationCodeGrantConfigurer) -> Unit)? = null
|
||||
|
||||
/**
|
||||
* Configures the OAuth 2.0 Authorization Code Grant.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* httpSecurity(http) {
|
||||
* oauth2Client {
|
||||
* authorizationCodeGrant {
|
||||
* authorizationRequestResolver = getAuthorizationRequestResolver()
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param authorizationCodeGrantConfig custom configurations to configure the authorization
|
||||
* code grant
|
||||
* @see [AuthorizationEndpointDsl]
|
||||
*/
|
||||
fun authorizationCodeGrant(authorizationCodeGrantConfig: AuthorizationCodeGrantDsl.() -> Unit) {
|
||||
this.authorizationCodeGrant = AuthorizationCodeGrantDsl().apply(authorizationCodeGrantConfig).get()
|
||||
}
|
||||
|
||||
internal fun get(): (OAuth2ClientConfigurer<HttpSecurity>) -> Unit {
|
||||
return { oauth2Client ->
|
||||
clientRegistrationRepository?.also { oauth2Client.clientRegistrationRepository(clientRegistrationRepository) }
|
||||
authorizedClientRepository?.also { oauth2Client.authorizedClientRepository(authorizedClientRepository) }
|
||||
authorizedClientService?.also { oauth2Client.authorizedClientService(authorizedClientService) }
|
||||
authorizationCodeGrant?.also { oauth2Client.authorizationCodeGrant(authorizationCodeGrant) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-226
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.HttpSecurityBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.web.servlet.oauth2.login.AuthorizationEndpointDsl
|
||||
import org.springframework.security.config.web.servlet.oauth2.login.RedirectionEndpointDsl
|
||||
import org.springframework.security.config.web.servlet.oauth2.login.TokenEndpointDsl
|
||||
import org.springframework.security.config.web.servlet.oauth2.login.UserInfoEndpointDsl
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] OAuth 2.0 login using idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property clientRegistrationRepository the repository of client registrations.
|
||||
* @property authorizedClientRepository the repository for authorized client(s).
|
||||
* @property authorizedClientService the service for authorized client(s).
|
||||
* @property loginPage the login page to redirect to if authentication is required (i.e.
|
||||
* "/login")
|
||||
* @property authenticationSuccessHandler the [AuthenticationSuccessHandler] used after
|
||||
* authentication success
|
||||
* @property authenticationFailureHandler the [AuthenticationFailureHandler] used after
|
||||
* authentication success
|
||||
* @property failureUrl the URL to send users if authentication fails
|
||||
* @property loginProcessingUrl the URL to validate the credentials
|
||||
* @property permitAll whether to grant access to the urls for [failureUrl] as well as
|
||||
* for the [HttpSecurityBuilder], the [loginPage] and [loginProcessingUrl] for every user
|
||||
*/
|
||||
@SecurityMarker
|
||||
class OAuth2LoginDsl {
|
||||
var clientRegistrationRepository: ClientRegistrationRepository? = null
|
||||
var authorizedClientRepository: OAuth2AuthorizedClientRepository? = null
|
||||
var authorizedClientService: OAuth2AuthorizedClientService? = null
|
||||
var loginPage: String? = null
|
||||
var authenticationSuccessHandler: AuthenticationSuccessHandler? = null
|
||||
var authenticationFailureHandler: AuthenticationFailureHandler? = null
|
||||
var failureUrl: String? = null
|
||||
var loginProcessingUrl: String? = null
|
||||
var permitAll: Boolean? = null
|
||||
|
||||
private var defaultSuccessUrlOption: Pair<String, Boolean>? = null
|
||||
private var authorizationEndpoint: ((OAuth2LoginConfigurer<HttpSecurity>.AuthorizationEndpointConfig) -> Unit)? = null
|
||||
private var tokenEndpoint: ((OAuth2LoginConfigurer<HttpSecurity>.TokenEndpointConfig) -> Unit)? = null
|
||||
private var redirectionEndpoint: ((OAuth2LoginConfigurer<HttpSecurity>.RedirectionEndpointConfig) -> Unit)? = null
|
||||
private var userInfoEndpoint: ((OAuth2LoginConfigurer<HttpSecurity>.UserInfoEndpointConfig) -> Unit)? = null
|
||||
|
||||
/**
|
||||
* Grants access to the urls for [failureUrl] as well as for the [HttpSecurityBuilder], the
|
||||
* [loginPage] and [loginProcessingUrl] for every user.
|
||||
*/
|
||||
fun permitAll() {
|
||||
permitAll = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies where users will be redirected after authenticating successfully if
|
||||
* they have not visited a secured page prior to authenticating or [alwaysUse]
|
||||
* is true.
|
||||
*
|
||||
* @param defaultSuccessUrl the default success url
|
||||
* @param alwaysUse true if the [defaultSuccessUrl] should be used after
|
||||
* authentication despite if a protected page had been previously visited
|
||||
*/
|
||||
fun defaultSuccessUrl(defaultSuccessUrl: String, alwaysUse: Boolean) {
|
||||
defaultSuccessUrlOption = Pair(defaultSuccessUrl, alwaysUse)
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the Authorization Server's Authorization Endpoint.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* httpSecurity(http) {
|
||||
* oauth2Login {
|
||||
* authorizationEndpoint {
|
||||
* baseUri = "/auth"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param authorizationEndpointConfig custom configurations to configure the authorization
|
||||
* endpoint
|
||||
* @see [AuthorizationEndpointDsl]
|
||||
*/
|
||||
fun authorizationEndpoint(authorizationEndpointConfig: AuthorizationEndpointDsl.() -> Unit) {
|
||||
this.authorizationEndpoint = AuthorizationEndpointDsl().apply(authorizationEndpointConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the Authorization Server's Token Endpoint.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* httpSecurity(http) {
|
||||
* oauth2Login {
|
||||
* tokenEndpoint {
|
||||
* accessTokenResponseClient = getAccessTokenResponseClient()
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param tokenEndpointConfig custom configurations to configure the token
|
||||
* endpoint
|
||||
* @see [TokenEndpointDsl]
|
||||
*/
|
||||
fun tokenEndpoint(tokenEndpointConfig: TokenEndpointDsl.() -> Unit) {
|
||||
this.tokenEndpoint = TokenEndpointDsl().apply(tokenEndpointConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the Authorization Server's Redirection Endpoint.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* httpSecurity(http) {
|
||||
* oauth2Login {
|
||||
* redirectionEndpoint {
|
||||
* baseUri = "/home"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param redirectionEndpointConfig custom configurations to configure the redirection
|
||||
* endpoint
|
||||
* @see [RedirectionEndpointDsl]
|
||||
*/
|
||||
fun redirectionEndpoint(redirectionEndpointConfig: RedirectionEndpointDsl.() -> Unit) {
|
||||
this.redirectionEndpoint = RedirectionEndpointDsl().apply(redirectionEndpointConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the Authorization Server's UserInfo Endpoint.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* httpSecurity(http) {
|
||||
* oauth2Login {
|
||||
* userInfoEndpoint {
|
||||
* userService = getUserService()
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param userInfoEndpointConfig custom configurations to configure the user info
|
||||
* endpoint
|
||||
* @see [UserInfoEndpointDsl]
|
||||
*/
|
||||
fun userInfoEndpoint(userInfoEndpointConfig: UserInfoEndpointDsl.() -> Unit) {
|
||||
this.userInfoEndpoint = UserInfoEndpointDsl().apply(userInfoEndpointConfig).get()
|
||||
}
|
||||
|
||||
internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>) -> Unit {
|
||||
return { oauth2Login ->
|
||||
clientRegistrationRepository?.also { oauth2Login.clientRegistrationRepository(clientRegistrationRepository) }
|
||||
authorizedClientRepository?.also { oauth2Login.authorizedClientRepository(authorizedClientRepository) }
|
||||
authorizedClientService?.also { oauth2Login.authorizedClientService(authorizedClientService) }
|
||||
loginPage?.also { oauth2Login.loginPage(loginPage) }
|
||||
failureUrl?.also { oauth2Login.failureUrl(failureUrl) }
|
||||
loginProcessingUrl?.also { oauth2Login.loginProcessingUrl(loginProcessingUrl) }
|
||||
permitAll?.also { oauth2Login.permitAll(permitAll!!) }
|
||||
defaultSuccessUrlOption?.also {
|
||||
oauth2Login.defaultSuccessUrl(defaultSuccessUrlOption!!.first, defaultSuccessUrlOption!!.second)
|
||||
}
|
||||
authenticationSuccessHandler?.also { oauth2Login.successHandler(authenticationSuccessHandler) }
|
||||
authenticationFailureHandler?.also { oauth2Login.failureHandler(authenticationFailureHandler) }
|
||||
authorizationEndpoint?.also { oauth2Login.authorizationEndpoint(authorizationEndpoint) }
|
||||
tokenEndpoint?.also { oauth2Login.tokenEndpoint(tokenEndpoint) }
|
||||
redirectionEndpoint?.also { oauth2Login.redirectionEndpoint(redirectionEndpoint) }
|
||||
userInfoEndpoint?.also { oauth2Login.userInfoEndpoint(userInfoEndpoint) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-112
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.web.servlet.oauth2.resourceserver.JwtDsl
|
||||
import org.springframework.security.config.web.servlet.oauth2.resourceserver.OpaqueTokenDsl
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver
|
||||
import org.springframework.security.web.AuthenticationEntryPoint
|
||||
import org.springframework.security.web.access.AccessDeniedHandler
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] OAuth 2.0 resource server support using
|
||||
* idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property accessDeniedHandler the [AccessDeniedHandler] to use for requests authenticating
|
||||
* with <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>s.
|
||||
* @property authenticationEntryPoint the [AuthenticationEntryPoint] to use for requests authenticating
|
||||
* with <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>s.
|
||||
* @property bearerTokenResolver the [BearerTokenResolver] to use for requests authenticating
|
||||
* with <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>s.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class OAuth2ResourceServerDsl {
|
||||
var accessDeniedHandler: AccessDeniedHandler? = null
|
||||
var authenticationEntryPoint: AuthenticationEntryPoint? = null
|
||||
var bearerTokenResolver: BearerTokenResolver? = null
|
||||
|
||||
private var jwt: ((OAuth2ResourceServerConfigurer<HttpSecurity>.JwtConfigurer) -> Unit)? = null
|
||||
private var opaqueToken: ((OAuth2ResourceServerConfigurer<HttpSecurity>.OpaqueTokenConfigurer) -> Unit)? = null
|
||||
|
||||
/**
|
||||
* Enables JWT-encoded bearer token support.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* httpSecurity(http) {
|
||||
* oauth2ResourceServer {
|
||||
* jwt {
|
||||
* jwkSetUri = "https://example.com/oauth2/jwk"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param jwtConfig custom configurations to configure JWT resource server support
|
||||
* @see [JwtDsl]
|
||||
*/
|
||||
fun jwt(jwtConfig: JwtDsl.() -> Unit) {
|
||||
this.jwt = JwtDsl().apply(jwtConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables opaque token support.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* httpSecurity(http) {
|
||||
* oauth2ResourceServer {
|
||||
* opaqueToken { }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param opaqueTokenConfig custom configurations to configure opaque token resource server support
|
||||
* @see [OpaqueTokenDsl]
|
||||
*/
|
||||
fun opaqueToken(opaqueTokenConfig: OpaqueTokenDsl.() -> Unit) {
|
||||
this.opaqueToken = OpaqueTokenDsl().apply(opaqueTokenConfig).get()
|
||||
}
|
||||
|
||||
internal fun get(): (OAuth2ResourceServerConfigurer<HttpSecurity>) -> Unit {
|
||||
return { oauth2ResourceServer ->
|
||||
accessDeniedHandler?.also { oauth2ResourceServer.accessDeniedHandler(accessDeniedHandler) }
|
||||
authenticationEntryPoint?.also { oauth2ResourceServer.authenticationEntryPoint(authenticationEntryPoint) }
|
||||
bearerTokenResolver?.also { oauth2ResourceServer.bearerTokenResolver(bearerTokenResolver) }
|
||||
jwt?.also { oauth2ResourceServer.jwt(jwt) }
|
||||
opaqueToken?.also { oauth2ResourceServer.opaqueToken(opaqueToken) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.PortMapperConfigurer
|
||||
import org.springframework.security.web.PortMapper
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure a [PortMapper] for [HttpSecurity] using idiomatic
|
||||
* Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property portMapper allows specifying the [PortMapper] instance.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class PortMapperDsl {
|
||||
private val mappings = mutableListOf<Pair<Int, Int>>()
|
||||
|
||||
var portMapper: PortMapper? = null
|
||||
|
||||
/**
|
||||
* Adds a mapping to the port mapper.
|
||||
*
|
||||
* @param fromPort the HTTP port number to map from
|
||||
* @param toPort the HTTPS port number to map to
|
||||
*/
|
||||
fun map(fromPort: Int, toPort: Int) {
|
||||
mappings.add(Pair(fromPort, toPort))
|
||||
}
|
||||
|
||||
internal fun get(): (PortMapperConfigurer<HttpSecurity>) -> Unit {
|
||||
return { portMapperConfig ->
|
||||
portMapper?.also {
|
||||
portMapperConfig.portMapper(portMapper)
|
||||
}
|
||||
this.mappings.forEach {
|
||||
portMapperConfig.http(it.first).mapsTo(it.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.RequestCacheConfigurer
|
||||
import org.springframework.security.web.savedrequest.RequestCache
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to enable request caching for [HttpSecurity] using idiomatic
|
||||
* Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property requestCache allows explicit configuration of the [RequestCache] to be used
|
||||
*/
|
||||
@SecurityMarker
|
||||
class RequestCacheDsl {
|
||||
var requestCache: RequestCache? = null
|
||||
|
||||
internal fun get(): (RequestCacheConfigurer<HttpSecurity>) -> Unit {
|
||||
return { requestCacheConfig ->
|
||||
requestCache?.also {
|
||||
requestCacheConfig.requestCache(requestCache)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer
|
||||
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl
|
||||
import org.springframework.security.web.access.channel.ChannelProcessor
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||
import org.springframework.util.ClassUtils
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] channel security using idiomatic
|
||||
* Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property channelProcessors the [ChannelProcessor] instances to use in
|
||||
* [ChannelDecisionManagerImpl]
|
||||
*/
|
||||
class RequiresChannelDsl : AbstractRequestMatcherDsl() {
|
||||
private val channelSecurityRules = mutableListOf<AuthorizationRule>()
|
||||
|
||||
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
|
||||
private val MVC_PRESENT = ClassUtils.isPresent(
|
||||
HANDLER_MAPPING_INTROSPECTOR,
|
||||
RequiresChannelDsl::class.java.classLoader)
|
||||
|
||||
var channelProcessors: List<ChannelProcessor>? = null
|
||||
|
||||
/**
|
||||
* Adds a channel security rule.
|
||||
*
|
||||
* @param matches the [RequestMatcher] to match incoming requests against
|
||||
* @param attribute the configuration attribute to secure the matching request
|
||||
* (i.e. "REQUIRES_SECURE_CHANNEL")
|
||||
*/
|
||||
fun secure(matches: RequestMatcher = AnyRequestMatcher.INSTANCE,
|
||||
attribute: String = "REQUIRES_SECURE_CHANNEL") {
|
||||
channelSecurityRules.add(MatcherAuthorizationRule(matches, attribute))
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a request authorization rule for an endpoint matching the provided
|
||||
* pattern.
|
||||
* If Spring MVC is not an the classpath, it will use an ant matcher.
|
||||
* If Spring MVC is on the classpath, it will use an MVC 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 attribute the configuration attribute to secure the matching request
|
||||
* (i.e. "REQUIRES_SECURE_CHANNEL")
|
||||
*/
|
||||
fun secure(pattern: String, attribute: String = "REQUIRES_SECURE_CHANNEL") {
|
||||
if (MVC_PRESENT) {
|
||||
channelSecurityRules.add(PatternAuthorizationRule(pattern, PatternType.MVC, null, attribute))
|
||||
} else {
|
||||
channelSecurityRules.add(PatternAuthorizationRule(pattern, PatternType.ANT, null, attribute))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a request authorization rule for an endpoint matching the provided
|
||||
* pattern.
|
||||
* If Spring MVC is not an the classpath, it will use an ant matcher.
|
||||
* If Spring MVC is on the classpath, it will use an MVC 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 attribute the configuration attribute to secure the matching request
|
||||
* (i.e. "REQUIRES_SECURE_CHANNEL")
|
||||
*/
|
||||
fun secure(pattern: String, servletPath: String, attribute: String = "REQUIRES_SECURE_CHANNEL") {
|
||||
if (MVC_PRESENT) {
|
||||
channelSecurityRules.add(PatternAuthorizationRule(pattern, PatternType.MVC, servletPath, attribute))
|
||||
} else {
|
||||
channelSecurityRules.add(PatternAuthorizationRule(pattern, PatternType.ANT, servletPath, attribute))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify channel security is active.
|
||||
*/
|
||||
val requiresSecure = "REQUIRES_SECURE_CHANNEL"
|
||||
|
||||
/**
|
||||
* Specify channel security is inactive.
|
||||
*/
|
||||
val requiresInsecure = "REQUIRES_INSECURE_CHANNEL"
|
||||
|
||||
internal fun get(): (ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry) -> Unit {
|
||||
return { channelSecurity ->
|
||||
channelProcessors?.also { channelSecurity.channelProcessors(channelProcessors) }
|
||||
channelSecurityRules.forEach { rule ->
|
||||
when (rule) {
|
||||
is MatcherAuthorizationRule -> channelSecurity.requestMatchers(rule.matcher).requires(rule.rule)
|
||||
is PatternAuthorizationRule -> {
|
||||
when (rule.patternType) {
|
||||
PatternType.ANT -> channelSecurity.antMatchers(rule.pattern).requires(rule.rule)
|
||||
PatternType.MVC -> {
|
||||
val mvcMatchersRequiresChannel = channelSecurity.mvcMatchers(rule.pattern)
|
||||
rule.servletPath?.also { mvcMatchersRequiresChannel.servletPath(rule.servletPath) }
|
||||
mvcMatchersRequiresChannel.requires(rule.rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.HttpSecurityBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LoginConfigurer
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] SAML2 login using idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property relyingPartyRegistrationRepository the [RelyingPartyRegistrationRepository] of relying parties,
|
||||
* each party representing a service provider, SP and this host, and identity provider, IDP pair that
|
||||
* communicate with each other.
|
||||
* @property loginPage the login page to redirect to if authentication is required (i.e.
|
||||
* "/login")
|
||||
* @property authenticationSuccessHandler the [AuthenticationSuccessHandler] used after
|
||||
* authentication success
|
||||
* @property authenticationFailureHandler the [AuthenticationFailureHandler] used after
|
||||
* authentication success
|
||||
* @property failureUrl the URL to send users if authentication fails
|
||||
* @property loginProcessingUrl the URL to validate the credentials
|
||||
* @property permitAll whether to grant access to the urls for [failureUrl] as well as
|
||||
* for the [HttpSecurityBuilder], the [loginPage] and [loginProcessingUrl] for every user
|
||||
*/
|
||||
@SecurityMarker
|
||||
class Saml2Dsl {
|
||||
var relyingPartyRegistrationRepository: RelyingPartyRegistrationRepository? = null
|
||||
var loginPage: String? = null
|
||||
var authenticationSuccessHandler: AuthenticationSuccessHandler? = null
|
||||
var authenticationFailureHandler: AuthenticationFailureHandler? = null
|
||||
var failureUrl: String? = null
|
||||
var loginProcessingUrl: String? = null
|
||||
var permitAll: Boolean? = null
|
||||
|
||||
private var defaultSuccessUrlOption: Pair<String, Boolean>? = null
|
||||
|
||||
/**
|
||||
* Grants access to the urls for [failureUrl] as well as for the [HttpSecurityBuilder], the
|
||||
* [loginPage] and [loginProcessingUrl] for every user.
|
||||
*/
|
||||
fun permitAll() {
|
||||
permitAll = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies where users will be redirected after authenticating successfully if
|
||||
* they have not visited a secured page prior to authenticating or [alwaysUse]
|
||||
* is true.
|
||||
*
|
||||
* @param defaultSuccessUrl the default success url
|
||||
* @param alwaysUse true if the [defaultSuccessUrl] should be used after
|
||||
* authentication despite if a protected page had been previously visited
|
||||
*/
|
||||
fun defaultSuccessUrl(defaultSuccessUrl: String, alwaysUse: Boolean) {
|
||||
defaultSuccessUrlOption = Pair(defaultSuccessUrl, alwaysUse)
|
||||
}
|
||||
|
||||
internal fun get(): (Saml2LoginConfigurer<HttpSecurity>) -> Unit {
|
||||
return { saml2Login ->
|
||||
relyingPartyRegistrationRepository?.also { saml2Login.relyingPartyRegistrationRepository(relyingPartyRegistrationRepository) }
|
||||
loginPage?.also { saml2Login.loginPage(loginPage) }
|
||||
failureUrl?.also { saml2Login.failureUrl(failureUrl) }
|
||||
loginProcessingUrl?.also { saml2Login.loginProcessingUrl(loginProcessingUrl) }
|
||||
permitAll?.also { saml2Login.permitAll(permitAll!!) }
|
||||
defaultSuccessUrlOption?.also {
|
||||
saml2Login.defaultSuccessUrl(defaultSuccessUrlOption!!.first, defaultSuccessUrlOption!!.second)
|
||||
}
|
||||
authenticationSuccessHandler?.also { saml2Login.successHandler(authenticationSuccessHandler) }
|
||||
authenticationFailureHandler?.also { saml2Login.failureHandler(authenticationFailureHandler) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.web.servlet.session.SessionConcurrencyDsl
|
||||
import org.springframework.security.config.web.servlet.session.SessionFixationDsl
|
||||
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer
|
||||
import org.springframework.security.config.http.SessionCreationPolicy
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy
|
||||
import org.springframework.security.web.session.InvalidSessionStrategy
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] session management using idiomatic
|
||||
* Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
*/
|
||||
@SecurityMarker
|
||||
class SessionManagementDsl {
|
||||
var invalidSessionUrl: String? = null
|
||||
var invalidSessionStrategy: InvalidSessionStrategy? = null
|
||||
var sessionAuthenticationErrorUrl: String? = null
|
||||
var sessionAuthenticationFailureHandler: AuthenticationFailureHandler? = null
|
||||
var enableSessionUrlRewriting: Boolean? = null
|
||||
var sessionCreationPolicy: SessionCreationPolicy? = null
|
||||
var sessionAuthenticationStrategy: SessionAuthenticationStrategy? = null
|
||||
private var sessionFixation: ((SessionManagementConfigurer<HttpSecurity>.SessionFixationConfigurer) -> Unit)? = null
|
||||
private var sessionConcurrency: ((SessionManagementConfigurer<HttpSecurity>.ConcurrencyControlConfigurer) -> Unit)? = null
|
||||
|
||||
/**
|
||||
* Enables session fixation protection.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* httpSecurity(http) {
|
||||
* sessionManagement {
|
||||
* sessionFixation { }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param sessionFixationConfig custom configurations to configure session fixation
|
||||
* protection
|
||||
* @see [SessionFixationDsl]
|
||||
*/
|
||||
fun sessionFixation(sessionFixationConfig: SessionFixationDsl.() -> Unit) {
|
||||
this.sessionFixation = SessionFixationDsl().apply(sessionFixationConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the behaviour of multiple sessions for a user.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* httpSecurity(http) {
|
||||
* sessionManagement {
|
||||
* sessionConcurrency {
|
||||
* maximumSessions = 1
|
||||
* maxSessionsPreventsLogin = true
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param sessionConcurrencyConfig custom configurations to configure concurrency
|
||||
* control
|
||||
* @see [SessionConcurrencyDsl]
|
||||
*/
|
||||
fun sessionConcurrency(sessionConcurrencyConfig: SessionConcurrencyDsl.() -> Unit) {
|
||||
this.sessionConcurrency = SessionConcurrencyDsl().apply(sessionConcurrencyConfig).get()
|
||||
}
|
||||
|
||||
internal fun get(): (SessionManagementConfigurer<HttpSecurity>) -> Unit {
|
||||
return { sessionManagement ->
|
||||
invalidSessionUrl?.also { sessionManagement.invalidSessionUrl(invalidSessionUrl) }
|
||||
invalidSessionStrategy?.also { sessionManagement.invalidSessionStrategy(invalidSessionStrategy) }
|
||||
sessionAuthenticationErrorUrl?.also { sessionManagement.sessionAuthenticationErrorUrl(sessionAuthenticationErrorUrl) }
|
||||
sessionAuthenticationFailureHandler?.also { sessionManagement.sessionAuthenticationFailureHandler(sessionAuthenticationFailureHandler) }
|
||||
enableSessionUrlRewriting?.also { sessionManagement.enableSessionUrlRewriting(enableSessionUrlRewriting!!) }
|
||||
sessionCreationPolicy?.also { sessionManagement.sessionCreationPolicy(sessionCreationPolicy) }
|
||||
sessionAuthenticationStrategy?.also { sessionManagement.sessionAuthenticationStrategy(sessionAuthenticationStrategy) }
|
||||
sessionFixation?.also { sessionManagement.sessionFixation(sessionFixation) }
|
||||
sessionConcurrency?.also { sessionManagement.sessionConcurrency(sessionConcurrency) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.security.authentication.AuthenticationDetailsSource
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.X509Configurer
|
||||
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.authentication.preauth.PreAuthenticatedAuthenticationToken
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] X509 based pre authentication
|
||||
* using idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property x509AuthenticationFilter the entire [X509AuthenticationFilter]. If
|
||||
* this is specified, the properties on [X509Configurer] will not be populated
|
||||
* on the {@link X509AuthenticationFilter}.
|
||||
* @property x509PrincipalExtractor the [X509PrincipalExtractor]
|
||||
* @property authenticationDetailsSource the [X509PrincipalExtractor]
|
||||
* @property userDetailsService shortcut for invoking
|
||||
* [authenticationUserDetailsService] with a [UserDetailsByNameServiceWrapper]
|
||||
* @property authenticationUserDetailsService the [AuthenticationUserDetailsService] to use
|
||||
* @property subjectPrincipalRegex the regex to extract the principal from the certificate
|
||||
*/
|
||||
@SecurityMarker
|
||||
class X509Dsl {
|
||||
var x509AuthenticationFilter: X509AuthenticationFilter? = null
|
||||
var x509PrincipalExtractor: X509PrincipalExtractor? = null
|
||||
var authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails>? = null
|
||||
var userDetailsService: UserDetailsService? = null
|
||||
var authenticationUserDetailsService: AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken>? = null
|
||||
var subjectPrincipalRegex: String? = null
|
||||
|
||||
internal fun get(): (X509Configurer<HttpSecurity>) -> Unit {
|
||||
return { x509 ->
|
||||
x509AuthenticationFilter?.also { x509.x509AuthenticationFilter(x509AuthenticationFilter) }
|
||||
x509PrincipalExtractor?.also { x509.x509PrincipalExtractor(x509PrincipalExtractor) }
|
||||
authenticationDetailsSource?.also { x509.authenticationDetailsSource(authenticationDetailsSource) }
|
||||
userDetailsService?.also { x509.userDetailsService(userDetailsService) }
|
||||
authenticationUserDetailsService?.also { x509.authenticationUserDetailsService(authenticationUserDetailsService) }
|
||||
subjectPrincipalRegex?.also { x509.subjectPrincipalRegex(subjectPrincipalRegex) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.headers
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the [HttpSecurity] cache control headers using idiomatic
|
||||
* Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
*/
|
||||
@SecurityMarker
|
||||
class CacheControlDsl {
|
||||
private var disabled = false
|
||||
|
||||
/**
|
||||
* Disable cache control headers.
|
||||
*/
|
||||
fun disable() {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.CacheControlConfig) -> Unit {
|
||||
return { cacheControl ->
|
||||
if (disabled) {
|
||||
cacheControl.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.headers
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the [HttpSecurity] Content-Security-Policy header using
|
||||
* idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property policyDirectives the security policy directive(s) to be used in the response header.
|
||||
* @property reportOnly includes the Content-Security-Policy-Report-Only header in the response.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class ContentSecurityPolicyDsl {
|
||||
var policyDirectives: String? = null
|
||||
var reportOnly: Boolean? = null
|
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.ContentSecurityPolicyConfig) -> Unit {
|
||||
return { contentSecurityPolicy ->
|
||||
policyDirectives?.also {
|
||||
contentSecurityPolicy.policyDirectives(policyDirectives)
|
||||
}
|
||||
reportOnly?.also {
|
||||
if (reportOnly!!) {
|
||||
contentSecurityPolicy.reportOnly()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.headers
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] X-Content-Type-Options header using idiomatic
|
||||
* Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
*/
|
||||
@SecurityMarker
|
||||
class ContentTypeOptionsDsl {
|
||||
private var disabled = false
|
||||
|
||||
/**
|
||||
* Disable the X-Content-Type-Options header.
|
||||
*/
|
||||
fun disable() {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.ContentTypeOptionsConfig) -> Unit {
|
||||
return { contentTypeOptions ->
|
||||
if (disabled) {
|
||||
contentTypeOptions.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.headers
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the [HttpSecurity] X-Frame-Options header using
|
||||
* idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property sameOrigin allow any request that comes from the same origin to frame this
|
||||
* application.
|
||||
* @property deny deny framing any content from this application.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class FrameOptionsDsl {
|
||||
var sameOrigin: Boolean? = null
|
||||
var deny: Boolean? = null
|
||||
|
||||
private var disabled = false
|
||||
|
||||
/**
|
||||
* Disable the X-Frame-Options header.
|
||||
*/
|
||||
fun disable() {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.FrameOptionsConfig) -> Unit {
|
||||
return { frameOptions ->
|
||||
sameOrigin?.also {
|
||||
if (sameOrigin!!) {
|
||||
frameOptions.sameOrigin()
|
||||
}
|
||||
}
|
||||
deny?.also {
|
||||
if (deny!!) {
|
||||
frameOptions.deny()
|
||||
}
|
||||
}
|
||||
if (disabled) {
|
||||
frameOptions.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.headers
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the [HttpSecurity] HTTP Public Key Pinning header using
|
||||
* idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property pins the value for the pin- directive of the Public-Key-Pins header.
|
||||
* @property maxAgeInSeconds the value (in seconds) for the max-age directive of the
|
||||
* Public-Key-Pins header.
|
||||
* @property includeSubDomains if true, the pinning policy applies to this pinned host
|
||||
* as well as any subdomains of the host's domain name.
|
||||
* @property reportOnly if true, the browser should not terminate the connection with
|
||||
* the server.
|
||||
* @property reportUri the URI to which the browser should report pin validation failures.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class HttpPublicKeyPinningDsl {
|
||||
var pins: Map<String, String>? = null
|
||||
var maxAgeInSeconds: Long? = null
|
||||
var includeSubDomains: Boolean? = null
|
||||
var reportOnly: Boolean? = null
|
||||
var reportUri: String? = null
|
||||
|
||||
private var disabled = false
|
||||
|
||||
/**
|
||||
* Disable the HTTP Public Key Pinning header.
|
||||
*/
|
||||
fun disable() {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.HpkpConfig) -> Unit {
|
||||
return { hpkp ->
|
||||
pins?.also {
|
||||
hpkp.withPins(pins)
|
||||
}
|
||||
maxAgeInSeconds?.also {
|
||||
hpkp.maxAgeInSeconds(maxAgeInSeconds!!)
|
||||
}
|
||||
includeSubDomains?.also {
|
||||
hpkp.includeSubDomains(includeSubDomains!!)
|
||||
}
|
||||
reportOnly?.also {
|
||||
hpkp.reportOnly(reportOnly!!)
|
||||
}
|
||||
reportUri?.also {
|
||||
hpkp.reportUri(reportUri)
|
||||
}
|
||||
if (disabled) {
|
||||
hpkp.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.headers
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the [HttpSecurity] HTTP Strict Transport Security header using
|
||||
* idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property maxAgeInSeconds the value (in seconds) for the max-age directive of the
|
||||
* Strict-Transport-Security header.
|
||||
* @property requestMatcher the [RequestMatcher] used to determine if the
|
||||
* "Strict-Transport-Security" header should be added. If true the header is added,
|
||||
* else the header is not added.
|
||||
* @property includeSubDomains if true, subdomains should be considered HSTS Hosts too.
|
||||
* @property preload if true, preload will be included in HSTS Header.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class HttpStrictTransportSecurityDsl {
|
||||
var maxAgeInSeconds: Long? = null
|
||||
var requestMatcher: RequestMatcher? = null
|
||||
var includeSubDomains: Boolean? = null
|
||||
var preload: Boolean? = null
|
||||
|
||||
private var disabled = false
|
||||
|
||||
/**
|
||||
* Disable the HTTP Strict Transport Security header.
|
||||
*/
|
||||
fun disable() {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.HstsConfig) -> Unit {
|
||||
return { hsts ->
|
||||
maxAgeInSeconds?.also { hsts.maxAgeInSeconds(maxAgeInSeconds!!) }
|
||||
requestMatcher?.also { hsts.requestMatcher(requestMatcher) }
|
||||
includeSubDomains?.also { hsts.includeSubDomains(includeSubDomains!!) }
|
||||
preload?.also { hsts.preload(preload!!) }
|
||||
if (disabled) {
|
||||
hsts.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.headers
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the [HttpSecurity] referrer policy header using
|
||||
* idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property policy the policy to be used in the response header.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class ReferrerPolicyDsl {
|
||||
var policy: ReferrerPolicyHeaderWriter.ReferrerPolicy? = null
|
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.ReferrerPolicyConfig) -> Unit {
|
||||
return { referrerPolicy ->
|
||||
policy?.also {
|
||||
referrerPolicy.policy(policy)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.headers
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the [HttpSecurity] XSS protection header using
|
||||
* idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property block whether to specify the mode as blocked
|
||||
* @property xssProtectionEnabled if true, the header value will contain a value of 1.
|
||||
* If false, will explicitly disable specify that X-XSS-Protection is disabled.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class XssProtectionConfigDsl {
|
||||
var block: Boolean? = null
|
||||
var xssProtectionEnabled: Boolean? = null
|
||||
|
||||
private var disabled = false
|
||||
|
||||
/**
|
||||
* Do not include the X-XSS-Protection header in the response.
|
||||
*/
|
||||
fun disable() {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
internal fun get(): (HeadersConfigurer<HttpSecurity>.XXssConfig) -> Unit {
|
||||
return { xssProtection ->
|
||||
block?.also { xssProtection.block(block!!) }
|
||||
xssProtectionEnabled?.also { xssProtection.xssProtectionEnabled(xssProtectionEnabled!!) }
|
||||
|
||||
if (disabled) {
|
||||
xssProtection.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.oauth2.client
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2ClientConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest
|
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure OAuth 2.0 Authorization Code Grant.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]'s.
|
||||
* @property authorizationRequestRepository the repository used for storing [OAuth2AuthorizationRequest]'s.
|
||||
* @property accessTokenResponseClient the client used for requesting the access token credential
|
||||
* from the Token Endpoint.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class AuthorizationCodeGrantDsl {
|
||||
var authorizationRequestResolver: OAuth2AuthorizationRequestResolver? = null
|
||||
var authorizationRequestRepository: AuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null
|
||||
var accessTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>? = null
|
||||
|
||||
internal fun get(): (OAuth2ClientConfigurer<HttpSecurity>.AuthorizationCodeGrantConfigurer) -> Unit {
|
||||
return { authorizationCodeGrant ->
|
||||
authorizationRequestResolver?.also { authorizationCodeGrant.authorizationRequestResolver(authorizationRequestResolver) }
|
||||
authorizationRequestRepository?.also { authorizationCodeGrant.authorizationRequestRepository(authorizationRequestRepository) }
|
||||
accessTokenResponseClient?.also { authorizationCodeGrant.accessTokenResponseClient(accessTokenResponseClient) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.oauth2.login
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the Authorization Server's Authorization Endpoint using
|
||||
* idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property baseUri the base URI used for authorization requests.
|
||||
* @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]'s.
|
||||
* @property authorizationRequestRepository the repository used for storing [OAuth2AuthorizationRequest]'s.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class AuthorizationEndpointDsl {
|
||||
var baseUri: String? = null
|
||||
var authorizationRequestResolver: OAuth2AuthorizationRequestResolver? = null
|
||||
var authorizationRequestRepository: AuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null
|
||||
|
||||
internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.AuthorizationEndpointConfig) -> Unit {
|
||||
return { authorizationEndpoint ->
|
||||
baseUri?.also { authorizationEndpoint.baseUri(baseUri) }
|
||||
authorizationRequestResolver?.also { authorizationEndpoint.authorizationRequestResolver(authorizationRequestResolver) }
|
||||
authorizationRequestRepository?.also { authorizationEndpoint.authorizationRequestRepository(authorizationRequestRepository) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.oauth2.login
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the Authorization Server's Redirection Endpoint using
|
||||
* idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property baseUri the URI where the authorization response will be processed.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class RedirectionEndpointDsl {
|
||||
var baseUri: String? = null
|
||||
|
||||
internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.RedirectionEndpointConfig) -> Unit {
|
||||
return { redirectionEndpoint ->
|
||||
baseUri?.also { redirectionEndpoint.baseUri(baseUri) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.oauth2.login
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the Authorization Server's Token Endpoint using
|
||||
* idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property accessTokenResponseClient the client used for requesting the access token credential
|
||||
* from the Token Endpoint.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class TokenEndpointDsl {
|
||||
var accessTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>? = null
|
||||
|
||||
internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.TokenEndpointConfig) -> Unit {
|
||||
return { tokenEndpoint ->
|
||||
accessTokenResponseClient?.also { tokenEndpoint.accessTokenResponseClient(accessTokenResponseClient) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.oauth2.login
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper
|
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the Authorization Server's UserInfo Endpoint using
|
||||
* idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property userService the OAuth 2.0 service used for obtaining the user attributes of the End-User
|
||||
* from the UserInfo Endpoint.
|
||||
* @property oidcUserService the OpenID Connect 1.0 service used for obtaining the user attributes of the
|
||||
* End-User from the UserInfo Endpoint.
|
||||
* @property userAuthoritiesMapper the [GrantedAuthoritiesMapper] used for mapping [OAuth2User.getAuthorities]
|
||||
*/
|
||||
@SecurityMarker
|
||||
class UserInfoEndpointDsl {
|
||||
var userService: OAuth2UserService<OAuth2UserRequest, OAuth2User>? = null
|
||||
var oidcUserService: OAuth2UserService<OidcUserRequest, OidcUser>? = null
|
||||
var userAuthoritiesMapper: GrantedAuthoritiesMapper? = null
|
||||
|
||||
private var customUserTypePair: Pair<Class<out OAuth2User>, String>? = null
|
||||
|
||||
/**
|
||||
* Sets a custom [OAuth2User] type and associates it to the provided
|
||||
* client [ClientRegistration.getRegistrationId] registration identifier.
|
||||
*
|
||||
* @param customUserType a custom [OAuth2User] type
|
||||
* @param clientRegistrationId the client registration identifier
|
||||
*/
|
||||
fun customUserType(customUserType: Class<out OAuth2User>, clientRegistrationId: String) {
|
||||
customUserTypePair = Pair(customUserType, clientRegistrationId)
|
||||
}
|
||||
|
||||
internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.UserInfoEndpointConfig) -> Unit {
|
||||
return { userInfoEndpoint ->
|
||||
userService?.also { userInfoEndpoint.userService(userService) }
|
||||
oidcUserService?.also { userInfoEndpoint.oidcUserService(oidcUserService) }
|
||||
userAuthoritiesMapper?.also { userInfoEndpoint.userAuthoritiesMapper(userAuthoritiesMapper) }
|
||||
customUserTypePair?.also { userInfoEndpoint.customUserType(customUserTypePair!!.first, customUserTypePair!!.second) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.oauth2.resourceserver
|
||||
|
||||
import org.springframework.core.convert.converter.Converter
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
import org.springframework.security.oauth2.jwt.Jwt
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure JWT Resource Server Support using idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property jwtAuthenticationConverter the [Converter] to use for converting a [Jwt] into
|
||||
* an [AbstractAuthenticationToken].
|
||||
* @property jwtDecoder the [JwtDecoder] to use.
|
||||
* @property jwkSetUri configures a [JwtDecoder] using a
|
||||
* <a target="_blank" href="https://tools.ietf.org/html/rfc7517">JSON Web Key (JWK)</a> URL
|
||||
*/
|
||||
@SecurityMarker
|
||||
class JwtDsl {
|
||||
private var _jwtDecoder: JwtDecoder? = null
|
||||
private var _jwkSetUri: String? = null
|
||||
|
||||
var jwtAuthenticationConverter: Converter<Jwt, out AbstractAuthenticationToken>? = null
|
||||
var jwtDecoder: JwtDecoder?
|
||||
get() = _jwtDecoder
|
||||
set(value) {
|
||||
_jwtDecoder = value
|
||||
_jwkSetUri = null
|
||||
}
|
||||
var jwkSetUri: String?
|
||||
get() = _jwkSetUri
|
||||
set(value) {
|
||||
_jwkSetUri = value
|
||||
_jwtDecoder = null
|
||||
}
|
||||
|
||||
internal fun get(): (OAuth2ResourceServerConfigurer<HttpSecurity>.JwtConfigurer) -> Unit {
|
||||
return { jwt ->
|
||||
jwtAuthenticationConverter?.also { jwt.jwtAuthenticationConverter(jwtAuthenticationConverter) }
|
||||
jwtDecoder?.also { jwt.decoder(jwtDecoder) }
|
||||
jwkSetUri?.also { jwt.jwkSetUri(jwkSetUri) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.oauth2.resourceserver
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure opaque token Resource Server Support using idiomatic Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property introspectionUri the URI of the Introspection endpoint.
|
||||
* @property introspector the [OpaqueTokenIntrospector] to use.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class OpaqueTokenDsl {
|
||||
private var _introspectionUri: String? = null
|
||||
private var _introspector: OpaqueTokenIntrospector? = null
|
||||
private var clientCredentials: Pair<String, String>? = null
|
||||
|
||||
var introspectionUri: String?
|
||||
get() = _introspectionUri
|
||||
set(value) {
|
||||
_introspectionUri = value
|
||||
_introspector = null
|
||||
}
|
||||
var introspector: OpaqueTokenIntrospector?
|
||||
get() = _introspector
|
||||
set(value) {
|
||||
_introspector = value
|
||||
_introspectionUri = null
|
||||
clientCredentials = null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configures the credentials for Introspection endpoint.
|
||||
*
|
||||
* @param clientId the clientId part of the credentials.
|
||||
* @param clientSecret the clientSecret part of the credentials.
|
||||
*/
|
||||
fun introspectionClientCredentials(clientId: String, clientSecret: String) {
|
||||
clientCredentials = Pair(clientId, clientSecret)
|
||||
_introspector = null
|
||||
}
|
||||
|
||||
internal fun get(): (OAuth2ResourceServerConfigurer<HttpSecurity>.OpaqueTokenConfigurer) -> Unit {
|
||||
return { opaqueToken ->
|
||||
introspectionUri?.also { opaqueToken.introspectionUri(introspectionUri) }
|
||||
introspector?.also { opaqueToken.introspector(introspector) }
|
||||
clientCredentials?.also { opaqueToken.introspectionClientCredentials(clientCredentials!!.first, clientCredentials!!.second) }
|
||||
}
|
||||
}
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.session
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
import org.springframework.security.core.session.SessionRegistry
|
||||
import org.springframework.security.web.session.SessionInformationExpiredStrategy
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the behaviour of multiple sessions using idiomatic
|
||||
* Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property maximumSessions controls the maximum number of sessions for a user.
|
||||
* @property expiredUrl the URL to redirect to if a user tries to access a resource and
|
||||
* their session has been expired due to too many sessions for the current user.
|
||||
* @property expiredSessionStrategy determines the behaviour when an expired session
|
||||
* is detected.
|
||||
* @property maxSessionsPreventsLogin if true, prevents a user from authenticating when the
|
||||
* [maximumSessions] has been reached. Otherwise (default), the user who authenticates
|
||||
* is allowed access and an existing user's session is expired.
|
||||
* @property sessionRegistry the [SessionRegistry] implementation used.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class SessionConcurrencyDsl {
|
||||
var maximumSessions: Int? = null
|
||||
var expiredUrl: String? = null
|
||||
var expiredSessionStrategy: SessionInformationExpiredStrategy? = null
|
||||
var maxSessionsPreventsLogin: Boolean? = null
|
||||
var sessionRegistry: SessionRegistry? = null
|
||||
|
||||
internal fun get(): (SessionManagementConfigurer<HttpSecurity>.ConcurrencyControlConfigurer) -> Unit {
|
||||
return { sessionConcurrencyControl ->
|
||||
maximumSessions?.also {
|
||||
sessionConcurrencyControl.maximumSessions(maximumSessions!!)
|
||||
}
|
||||
expiredUrl?.also {
|
||||
sessionConcurrencyControl.expiredUrl(expiredUrl)
|
||||
}
|
||||
expiredSessionStrategy?.also {
|
||||
sessionConcurrencyControl.expiredSessionStrategy(expiredSessionStrategy)
|
||||
}
|
||||
maxSessionsPreventsLogin?.also {
|
||||
sessionConcurrencyControl.maxSessionsPreventsLogin(maxSessionsPreventsLogin!!)
|
||||
}
|
||||
sessionRegistry?.also {
|
||||
sessionConcurrencyControl.sessionRegistry(sessionRegistry)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.session
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer
|
||||
import org.springframework.security.config.web.servlet.SecurityMarker
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpSession
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure session fixation protection using idiomatic
|
||||
* Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
*/
|
||||
@SecurityMarker
|
||||
class SessionFixationDsl {
|
||||
private var strategy: SessionFixationStrategy? = null
|
||||
|
||||
/**
|
||||
* Specifies that a new session should be created, but the session attributes from
|
||||
* the original [HttpSession] should not be retained.
|
||||
*/
|
||||
fun newSession() {
|
||||
this.strategy = SessionFixationStrategy.NEW
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that a new session should be created and the session attributes from
|
||||
* the original [HttpSession] should be retained.
|
||||
*/
|
||||
fun migrateSession() {
|
||||
this.strategy = SessionFixationStrategy.MIGRATE
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that the Servlet container-provided session fixation protection
|
||||
* should be used. When a session authenticates, the Servlet method
|
||||
* [HttpServletRequest.changeSessionId] is called to change the session ID
|
||||
* and retain all session attributes.
|
||||
*/
|
||||
fun changeSessionId() {
|
||||
this.strategy = SessionFixationStrategy.CHANGE_ID
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that no session fixation protection should be enabled.
|
||||
*/
|
||||
fun none() {
|
||||
this.strategy = SessionFixationStrategy.NONE
|
||||
}
|
||||
|
||||
internal fun get(): (SessionManagementConfigurer<HttpSecurity>.SessionFixationConfigurer) -> Unit {
|
||||
return { sessionFixation ->
|
||||
strategy?.also {
|
||||
when (strategy) {
|
||||
SessionFixationStrategy.NEW -> sessionFixation.newSession()
|
||||
SessionFixationStrategy.MIGRATE -> sessionFixation.migrateSession()
|
||||
SessionFixationStrategy.CHANGE_ID -> sessionFixation.changeSessionId()
|
||||
SessionFixationStrategy.NONE -> sessionFixation.none()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum class SessionFixationStrategy {
|
||||
NEW, MIGRATE, CHANGE_ID, NONE
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-5.3.xsd
|
||||
http\://www.springframework.org/schema/security/spring-security-5.3.xsd=org/springframework/security/config/spring-security-5.3.xsd
|
||||
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-5.2.xsd
|
||||
http\://www.springframework.org/schema/security/spring-security-5.2.xsd=org/springframework/security/config/spring-security-5.2.xsd
|
||||
http\://www.springframework.org/schema/security/spring-security-5.1.xsd=org/springframework/security/config/spring-security-5.1.xsd
|
||||
http\://www.springframework.org/schema/security/spring-security-5.0.xsd=org/springframework/security/config/spring-security-5.0.xsd
|
||||
@@ -14,8 +13,7 @@ http\://www.springframework.org/schema/security/spring-security-2.0.xsd=org/spri
|
||||
http\://www.springframework.org/schema/security/spring-security-2.0.1.xsd=org/springframework/security/config/spring-security-2.0.1.xsd
|
||||
http\://www.springframework.org/schema/security/spring-security-2.0.2.xsd=org/springframework/security/config/spring-security-2.0.2.xsd
|
||||
http\://www.springframework.org/schema/security/spring-security-2.0.4.xsd=org/springframework/security/config/spring-security-2.0.4.xsd
|
||||
https\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-5.3.xsd
|
||||
https\://www.springframework.org/schema/security/spring-security-5.3.xsd=org/springframework/security/config/spring-security-5.3.xsd
|
||||
https\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-5.2.xsd
|
||||
https\://www.springframework.org/schema/security/spring-security-5.2.xsd=org/springframework/security/config/spring-security-5.2.xsd
|
||||
https\://www.springframework.org/schema/security/spring-security-5.1.xsd=org/springframework/security/config/spring-security-5.1.xsd
|
||||
https\://www.springframework.org/schema/security/spring-security-5.0.xsd=org/springframework/security/config/spring-security-5.0.xsd
|
||||
|
||||
-1095
File diff suppressed because it is too large
Load Diff
-3177
File diff suppressed because it is too large
Load Diff
+106
@@ -0,0 +1,106 @@
|
||||
package org.springframework.security.config
|
||||
|
||||
import groovy.xml.MarkupBuilder
|
||||
import org.mockito.Mockito
|
||||
import org.springframework.context.ApplicationListener
|
||||
import org.springframework.context.support.AbstractRefreshableApplicationContext
|
||||
import org.springframework.mock.web.MockServletContext
|
||||
import org.springframework.security.CollectingAppListener
|
||||
import org.springframework.security.config.util.InMemoryXmlApplicationContext
|
||||
import org.springframework.security.config.util.InMemoryXmlWebApplicationContext
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import spock.lang.Specification
|
||||
|
||||
import javax.servlet.ServletContext
|
||||
|
||||
import static org.springframework.security.config.ConfigTestUtils.AUTH_PROVIDER_XML
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
abstract class AbstractXmlConfigTests extends Specification {
|
||||
AbstractRefreshableApplicationContext appContext;
|
||||
Writer writer;
|
||||
MarkupBuilder xml;
|
||||
ApplicationListener appListener;
|
||||
|
||||
def setup() {
|
||||
writer = new StringWriter()
|
||||
xml = new MarkupBuilder(writer)
|
||||
appListener = new CollectingAppListener()
|
||||
}
|
||||
|
||||
def cleanup() {
|
||||
if (appContext != null) {
|
||||
appContext.close();
|
||||
appContext = null;
|
||||
}
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
def mockBean(Class clazz, String id = clazz.simpleName) {
|
||||
xml.'b:bean'(id: id, 'class': Mockito.class.name, 'factory-method':'mock') {
|
||||
'b:constructor-arg'(value : clazz.name)
|
||||
'b:constructor-arg'(value : id)
|
||||
}
|
||||
}
|
||||
|
||||
def bean(String name, Class clazz) {
|
||||
xml.'b:bean'(id: name, 'class': clazz.name)
|
||||
}
|
||||
|
||||
def bean(String name, String clazz) {
|
||||
xml.'b:bean'(id: name, 'class': clazz)
|
||||
}
|
||||
|
||||
def bean(String name, String clazz, List constructorArgs) {
|
||||
xml.'b:bean'(id: name, 'class': clazz) {
|
||||
constructorArgs.each { val ->
|
||||
'b:constructor-arg'(value: val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def bean(String name, String clazz, Map properties, Map refs) {
|
||||
xml.'b:bean'(id: name, 'class': clazz) {
|
||||
properties.each {key, val ->
|
||||
'b:property'(name: key, value: val)
|
||||
}
|
||||
refs.each {key, val ->
|
||||
'b:property'(name: key, ref: val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def createAppContext() {
|
||||
createAppContext(AUTH_PROVIDER_XML)
|
||||
}
|
||||
|
||||
def createAppContext(String extraXml) {
|
||||
appContext = new InMemoryXmlApplicationContext(writer.toString() + extraXml);
|
||||
appContext.addApplicationListener(appListener);
|
||||
}
|
||||
|
||||
def createWebAppContext() {
|
||||
createWebAppContext(AUTH_PROVIDER_XML);
|
||||
}
|
||||
|
||||
def createWebAppContext(ServletContext servletContext) {
|
||||
createWebAppContext(AUTH_PROVIDER_XML, servletContext);
|
||||
}
|
||||
|
||||
def createWebAppContext(String extraXml) {
|
||||
createWebAppContext(extraXml, null);
|
||||
}
|
||||
|
||||
def createWebAppContext(String extraXml, ServletContext servletContext) {
|
||||
appContext = new InMemoryXmlWebApplicationContext(writer.toString() + extraXml);
|
||||
appContext.addApplicationListener(appListener);
|
||||
if (servletContext != null) {
|
||||
appContext.setServletContext(servletContext);
|
||||
} else {
|
||||
appContext.setServletContext(new MockServletContext());
|
||||
}
|
||||
appContext.refresh();
|
||||
}
|
||||
}
|
||||
+6
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -13,17 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.samples
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
/**
|
||||
* @author Eleftheria Stein
|
||||
* Exists for mocking purposes to ensure that the Type information is found.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
class KotlinApplication
|
||||
public interface AnyObjectPostProcessor extends ObjectPostProcessor<Object> {
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
runApplication<KotlinApplication>(*args)
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.annotation;
|
||||
|
||||
import javax.servlet.Filter
|
||||
|
||||
import spock.lang.AutoCleanup
|
||||
import spock.lang.Specification
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.mock.web.MockServletContext
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
|
||||
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.authority.AuthorityUtils
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.core.context.SecurityContextImpl
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
|
||||
import org.springframework.security.web.csrf.CsrfToken
|
||||
import org.springframework.security.web.csrf.DefaultCsrfToken
|
||||
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
abstract class BaseSpringSpec extends Specification {
|
||||
boolean allowCircularReferences = false
|
||||
@AutoCleanup
|
||||
ConfigurableApplicationContext context
|
||||
@AutoCleanup
|
||||
ConfigurableApplicationContext oppContext
|
||||
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
CsrfToken csrfToken
|
||||
AuthenticationManagerBuilder authenticationBldr
|
||||
|
||||
def setup() {
|
||||
authenticationBldr = createAuthenticationManagerBuilder()
|
||||
setupWeb(null)
|
||||
}
|
||||
|
||||
def setupWeb(httpSession = null) {
|
||||
request = new MockHttpServletRequest(method:"GET")
|
||||
if(httpSession) {
|
||||
request.session = httpSession
|
||||
}
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
setupCsrf()
|
||||
}
|
||||
|
||||
def setupCsrf(csrfTokenValue="BaseSpringSpec_CSRFTOKEN",req=request,resp=response) {
|
||||
csrfToken = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf",csrfTokenValue)
|
||||
new HttpSessionCsrfTokenRepository().saveToken(csrfToken, req, resp)
|
||||
req.setParameter(csrfToken.parameterName, csrfToken.token)
|
||||
}
|
||||
|
||||
def cleanup() {
|
||||
SecurityContextHolder.clearContext()
|
||||
}
|
||||
|
||||
def loadConfig(Class<?>... configs) {
|
||||
context = new AnnotationConfigWebApplicationContext()
|
||||
context.setAllowCircularReferences(allowCircularReferences)
|
||||
context.register(configs)
|
||||
context.setServletContext(new MockServletContext())
|
||||
context.refresh()
|
||||
context
|
||||
}
|
||||
|
||||
def findFilter(Class<?> filter, int index = 0) {
|
||||
filterChain(index).filters.find { filter.isAssignableFrom(it.class)}
|
||||
}
|
||||
|
||||
def filterChain(int index=0) {
|
||||
filterChains()[index]
|
||||
}
|
||||
|
||||
def filterChains() {
|
||||
context.getBean(FilterChainProxy).filterChains
|
||||
}
|
||||
|
||||
Filter getSpringSecurityFilterChain() {
|
||||
context.getBean("springSecurityFilterChain",Filter.class)
|
||||
}
|
||||
|
||||
def getResponseHeaders() {
|
||||
def headers = [:]
|
||||
response.headerNames.each { name ->
|
||||
headers.put(name, response.getHeaderValues(name).join(','))
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
AuthenticationManager authenticationManager() {
|
||||
context.getBean(AuthenticationManager)
|
||||
}
|
||||
|
||||
AuthenticationManager getAuthenticationManager() {
|
||||
try {
|
||||
authenticationManager().delegateBuilder.getObject()
|
||||
} catch(NoSuchBeanDefinitionException e) {
|
||||
} catch(MissingPropertyException e) {}
|
||||
findFilter(FilterSecurityInterceptor).authenticationManager
|
||||
}
|
||||
|
||||
List<AuthenticationProvider> authenticationProviders() {
|
||||
List<AuthenticationProvider> providers = new ArrayList<AuthenticationProvider>()
|
||||
AuthenticationManager authenticationManager = getAuthenticationManager()
|
||||
while(authenticationManager?.providers) {
|
||||
providers.addAll(authenticationManager.providers)
|
||||
authenticationManager = authenticationManager.parent
|
||||
}
|
||||
providers
|
||||
}
|
||||
|
||||
AuthenticationProvider findAuthenticationProvider(Class<?> provider) {
|
||||
authenticationProviders().find { provider.isAssignableFrom(it.class) }
|
||||
}
|
||||
|
||||
def getCurrentAuthentication() {
|
||||
new HttpSessionSecurityContextRepository().loadContext(new HttpRequestResponseHolder(request, response)).authentication
|
||||
}
|
||||
|
||||
def login(String username="user", String role="ROLE_USER") {
|
||||
login(new UsernamePasswordAuthenticationToken(username, null, AuthorityUtils.createAuthorityList(role)))
|
||||
}
|
||||
|
||||
def login(Authentication auth) {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository()
|
||||
HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response)
|
||||
repo.loadContext(requestResponseHolder)
|
||||
repo.saveContext(new SecurityContextImpl(authentication:auth), requestResponseHolder.request, requestResponseHolder.response)
|
||||
}
|
||||
|
||||
def createAuthenticationManagerBuilder() {
|
||||
oppContext = new AnnotationConfigApplicationContext(ObjectPostProcessorConfiguration, AuthenticationConfiguration)
|
||||
AuthenticationManagerBuilder auth = new AuthenticationManagerBuilder(objectPostProcessor)
|
||||
auth.inMemoryAuthentication().and()
|
||||
}
|
||||
|
||||
def getObjectPostProcessor() {
|
||||
oppContext.getBean(ObjectPostProcessor)
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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 javax.servlet.Filter
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.config.AbstractXmlConfigTests
|
||||
import org.springframework.security.config.BeanIds
|
||||
import org.springframework.security.web.FilterInvocation
|
||||
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
abstract class AbstractHttpConfigTests extends AbstractXmlConfigTests {
|
||||
final int AUTO_CONFIG_FILTERS = 15;
|
||||
|
||||
def httpAutoConfig(Closure c) {
|
||||
xml.http(['auto-config': 'true', 'use-expressions':false], c)
|
||||
}
|
||||
|
||||
def httpAutoConfig(String matcher, Closure c) {
|
||||
xml.http(['auto-config': 'true', 'use-expressions':false, 'request-matcher': matcher], c)
|
||||
}
|
||||
|
||||
def interceptUrl(String path, String authz) {
|
||||
xml.'intercept-url'(pattern: path, access: authz)
|
||||
}
|
||||
|
||||
def interceptUrl(String path, String httpMethod, String authz) {
|
||||
xml.'intercept-url'(pattern: path, method: httpMethod, access: authz)
|
||||
}
|
||||
|
||||
Filter getFilter(Class type) {
|
||||
List filters = getFilters("/any");
|
||||
|
||||
for (f in filters) {
|
||||
if (f.class.isAssignableFrom(type)) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
List getFilters(String url) {
|
||||
springSecurityFilterChain.getFilters(url)
|
||||
}
|
||||
|
||||
Filter getSpringSecurityFilterChain() {
|
||||
appContext.getBean(BeanIds.FILTER_CHAIN_PROXY)
|
||||
}
|
||||
|
||||
FilterInvocation createFilterinvocation(String path, String method) {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "");
|
||||
request.setMethod(method);
|
||||
request.setRequestURI(null);
|
||||
request.setServletPath(path);
|
||||
|
||||
return new FilterInvocation(request, new MockHttpServletResponse(), new MockFilterChain());
|
||||
}
|
||||
|
||||
def basicLogin(HttpServletRequest request, String username="user",String password="password") {
|
||||
def credentials = username + ":" + password
|
||||
request.addHeader("Authorization", "Basic " + credentials.bytes.encodeBase64())
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.issue50;
|
||||
|
||||
import org.spockframework.util.Assert;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -32,7 +33,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
|
||||
+7
-11
@@ -17,6 +17,7 @@
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -43,12 +44,12 @@ public class ReactiveMethodSecurityConfigurationTests {
|
||||
DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler;
|
||||
|
||||
@Test
|
||||
public void rolePrefixWithGrantedAuthorityDefaults() throws NoSuchMethodException {
|
||||
public void rolePrefixWithGrantedAuthorityDefaults() {
|
||||
this.spring.register(WithRolePrefixConfiguration.class).autowire();
|
||||
|
||||
TestingAuthenticationToken authentication = new TestingAuthenticationToken(
|
||||
"principal", "credential", "CUSTOM_ABC");
|
||||
MockMethodInvocation methodInvocation = new MockMethodInvocation(new Foo(), Foo.class, "bar", String.class);
|
||||
MockMethodInvocation methodInvocation = mock(MockMethodInvocation.class);
|
||||
|
||||
EvaluationContext context = this.methodSecurityExpressionHandler
|
||||
.createEvaluationContext(authentication, methodInvocation);
|
||||
@@ -62,12 +63,12 @@ public class ReactiveMethodSecurityConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rolePrefixWithDefaultConfig() throws NoSuchMethodException {
|
||||
public void rolePrefixWithDefaultConfig() {
|
||||
this.spring.register(ReactiveMethodSecurityConfiguration.class).autowire();
|
||||
|
||||
TestingAuthenticationToken authentication = new TestingAuthenticationToken(
|
||||
"principal", "credential", "ROLE_ABC");
|
||||
MockMethodInvocation methodInvocation = new MockMethodInvocation(new Foo(), Foo.class, "bar", String.class);
|
||||
MockMethodInvocation methodInvocation = mock(MockMethodInvocation.class);
|
||||
|
||||
EvaluationContext context = this.methodSecurityExpressionHandler
|
||||
.createEvaluationContext(authentication, methodInvocation);
|
||||
@@ -88,12 +89,12 @@ public class ReactiveMethodSecurityConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rolePrefixWithGrantedAuthorityDefaultsAndSubclassWithProxyingEnabled() throws NoSuchMethodException {
|
||||
public void rolePrefixWithGrantedAuthorityDefaultsAndSubclassWithProxyingEnabled() {
|
||||
this.spring.register(SubclassConfig.class).autowire();
|
||||
|
||||
TestingAuthenticationToken authentication = new TestingAuthenticationToken(
|
||||
"principal", "credential", "ROLE_ABC");
|
||||
MockMethodInvocation methodInvocation = new MockMethodInvocation(new Foo(), Foo.class, "bar", String.class);
|
||||
MockMethodInvocation methodInvocation = mock(MockMethodInvocation.class);
|
||||
|
||||
EvaluationContext context = this.methodSecurityExpressionHandler
|
||||
.createEvaluationContext(authentication, methodInvocation);
|
||||
@@ -107,9 +108,4 @@ public class ReactiveMethodSecurityConfigurationTests {
|
||||
@Configuration
|
||||
static class SubclassConfig extends ReactiveMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
private static class Foo {
|
||||
public void bar(String param){
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-32
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.rsocket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import io.rsocket.RSocketFactory;
|
||||
import io.rsocket.frame.decoder.PayloadDecoder;
|
||||
import io.rsocket.transport.netty.server.CloseableChannel;
|
||||
@@ -23,6 +27,8 @@ import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -37,20 +43,12 @@ import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.TestJwts;
|
||||
import org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;
|
||||
import org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;
|
||||
import org.springframework.security.rsocket.metadata.BearerTokenAuthenticationEncoder;
|
||||
import org.springframework.security.rsocket.metadata.BasicAuthenticationEncoder;
|
||||
import org.springframework.security.rsocket.metadata.BearerTokenMetadata;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static io.rsocket.metadata.WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -97,7 +95,7 @@ public class JwtITests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void routeWhenBearerThenAuthorized() {
|
||||
public void routeWhenAuthorized() {
|
||||
BearerTokenMetadata credentials =
|
||||
new BearerTokenMetadata("token");
|
||||
when(this.decoder.decode(any())).thenReturn(Mono.just(jwt()));
|
||||
@@ -114,26 +112,6 @@ public class JwtITests {
|
||||
assertThat(hiRob).isEqualTo("Hi rob");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void routeWhenAuthenticationBearerThenAuthorized() {
|
||||
MimeType authenticationMimeType = MimeTypeUtils.parseMimeType(MESSAGE_RSOCKET_AUTHENTICATION.getString());
|
||||
|
||||
BearerTokenMetadata credentials =
|
||||
new BearerTokenMetadata("token");
|
||||
when(this.decoder.decode(any())).thenReturn(Mono.just(jwt()));
|
||||
this.requester = requester()
|
||||
.setupMetadata(credentials, authenticationMimeType)
|
||||
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
|
||||
.block();
|
||||
|
||||
String hiRob = this.requester.route("secure.retrieve-mono")
|
||||
.data("rob")
|
||||
.retrieveMono(String.class)
|
||||
.block();
|
||||
|
||||
assertThat(hiRob).isEqualTo("Hi rob");
|
||||
}
|
||||
|
||||
private Jwt jwt() {
|
||||
return TestJwts.jwt()
|
||||
.claim(IdTokenClaimNames.ISS, "https://issuer.example.com")
|
||||
@@ -167,7 +145,7 @@ public class JwtITests {
|
||||
@Bean
|
||||
public RSocketStrategies rsocketStrategies() {
|
||||
return RSocketStrategies.builder()
|
||||
.encoder(new BearerTokenAuthenticationEncoder())
|
||||
.encoder(new BasicAuthenticationEncoder())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -176,7 +154,7 @@ public class JwtITests {
|
||||
rsocket
|
||||
.authorizePayload(authorize ->
|
||||
authorize
|
||||
.anyRequest().authenticated()
|
||||
.route("secure.admin.*").authenticated()
|
||||
.anyExchange().permitAll()
|
||||
)
|
||||
.jwt(Customizer.withDefaults());
|
||||
|
||||
-192
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* 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.annotation.rsocket;
|
||||
|
||||
import io.rsocket.RSocketFactory;
|
||||
import io.rsocket.exceptions.ApplicationErrorException;
|
||||
import io.rsocket.frame.decoder.PayloadDecoder;
|
||||
import io.rsocket.transport.netty.server.CloseableChannel;
|
||||
import io.rsocket.transport.netty.server.TcpServerTransport;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.messaging.rsocket.RSocketRequester;
|
||||
import org.springframework.messaging.rsocket.RSocketStrategies;
|
||||
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;
|
||||
import org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;
|
||||
import org.springframework.security.rsocket.metadata.SimpleAuthenticationEncoder;
|
||||
import org.springframework.security.rsocket.metadata.UsernamePasswordMetadata;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.rsocket.metadata.WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringRunner.class)
|
||||
public class SimpleAuthenticationITests {
|
||||
@Autowired
|
||||
RSocketMessageHandler handler;
|
||||
|
||||
@Autowired
|
||||
SecuritySocketAcceptorInterceptor interceptor;
|
||||
|
||||
@Autowired
|
||||
ServerController controller;
|
||||
|
||||
private CloseableChannel server;
|
||||
|
||||
private RSocketRequester requester;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.server = RSocketFactory.receive()
|
||||
.frameDecoder(PayloadDecoder.ZERO_COPY)
|
||||
.addSocketAcceptorPlugin(this.interceptor)
|
||||
.acceptor(this.handler.responder())
|
||||
.transport(TcpServerTransport.create("localhost", 0))
|
||||
.start()
|
||||
.block();
|
||||
}
|
||||
|
||||
@After
|
||||
public void dispose() {
|
||||
this.requester.rsocket().dispose();
|
||||
this.server.dispose();
|
||||
this.controller.payloads.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retrieveMonoWhenSecureThenDenied() throws Exception {
|
||||
this.requester = RSocketRequester.builder()
|
||||
.rsocketStrategies(this.handler.getRSocketStrategies())
|
||||
.connectTcp("localhost", this.server.address().getPort())
|
||||
.block();
|
||||
|
||||
String data = "rob";
|
||||
assertThatCode(() -> this.requester.route("secure.retrieve-mono")
|
||||
.data(data)
|
||||
.retrieveMono(String.class)
|
||||
.block()
|
||||
)
|
||||
.isInstanceOf(ApplicationErrorException.class);
|
||||
assertThat(this.controller.payloads).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retrieveMonoWhenAuthorizedThenGranted() {
|
||||
MimeType authenticationMimeType = MimeTypeUtils.parseMimeType(MESSAGE_RSOCKET_AUTHENTICATION.getString());
|
||||
|
||||
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("rob", "password");
|
||||
this.requester = RSocketRequester.builder()
|
||||
.setupMetadata(credentials, authenticationMimeType)
|
||||
.rsocketStrategies(this.handler.getRSocketStrategies())
|
||||
.connectTcp("localhost", this.server.address().getPort())
|
||||
.block();
|
||||
String data = "rob";
|
||||
String hiRob = this.requester.route("secure.retrieve-mono")
|
||||
.metadata(credentials, authenticationMimeType)
|
||||
.data(data)
|
||||
.retrieveMono(String.class)
|
||||
.block();
|
||||
|
||||
assertThat(hiRob).isEqualTo("Hi rob");
|
||||
assertThat(this.controller.payloads).containsOnly(data);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableRSocketSecurity
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public ServerController controller() {
|
||||
return new ServerController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RSocketMessageHandler messageHandler() {
|
||||
RSocketMessageHandler handler = new RSocketMessageHandler();
|
||||
handler.setRSocketStrategies(rsocketStrategies());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RSocketStrategies rsocketStrategies() {
|
||||
return RSocketStrategies.builder()
|
||||
.encoder(new SimpleAuthenticationEncoder())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
|
||||
rsocket
|
||||
.authorizePayload(authorize ->
|
||||
authorize
|
||||
.anyRequest().authenticated()
|
||||
.anyExchange().permitAll()
|
||||
)
|
||||
.simpleAuthentication(Customizer.withDefaults());
|
||||
return rsocket.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
MapReactiveUserDetailsService uds() {
|
||||
UserDetails rob = User.withDefaultPasswordEncoder()
|
||||
.username("rob")
|
||||
.password("password")
|
||||
.roles("USER", "ADMIN")
|
||||
.build();
|
||||
return new MapReactiveUserDetailsService(rob);
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class ServerController {
|
||||
private List<String> payloads = new ArrayList<>();
|
||||
|
||||
@MessageMapping("**")
|
||||
String retrieveMono(String payload) {
|
||||
add(payload);
|
||||
return "Hi " + payload;
|
||||
}
|
||||
|
||||
private void add(String p) {
|
||||
this.payloads.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+2
-60
@@ -18,39 +18,23 @@ package org.springframework.security.config.annotation.web;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
||||
import org.springframework.security.web.context.request.async.SecurityContextCallableProcessingInterceptor;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
|
||||
import org.springframework.web.context.request.async.WebAsyncManager;
|
||||
import org.springframework.web.context.request.async.WebAsyncUtils;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.powermock.api.mockito.PowerMockito.spy;
|
||||
import static org.powermock.api.mockito.PowerMockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -58,17 +42,11 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
||||
*
|
||||
*/
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({ SpringFactoriesLoader.class, WebAsyncManager.class })
|
||||
@PowerMockIgnore({ "org.w3c.dom.*", "org.xml.sax.*", "org.apache.xerces.*", "javax.xml.parsers.*", "javax.xml.transform.*" })
|
||||
@PrepareForTest({ SpringFactoriesLoader.class })
|
||||
@PowerMockIgnore({ "org.w3c.dom.*", "org.xml.sax.*", "org.apache.xerces.*", "javax.xml.parsers.*" })
|
||||
public class WebSecurityConfigurerAdapterPowermockTests {
|
||||
ConfigurableWebApplicationContext context;
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
if (context != null) {
|
||||
@@ -119,40 +97,4 @@ public class WebSecurityConfigurerAdapterPowermockTests {
|
||||
this.configure = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadConfigWhenDefaultConfigThenWebAsyncManagerIntegrationFilterAdded() throws Exception {
|
||||
this.spring.register(WebAsyncPopulatedByDefaultConfig.class).autowire();
|
||||
|
||||
WebAsyncManager webAsyncManager = mock(WebAsyncManager.class);
|
||||
|
||||
this.mockMvc.perform(get("/").requestAttr(WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE, webAsyncManager));
|
||||
|
||||
ArgumentCaptor<CallableProcessingInterceptor> callableProcessingInterceptorArgCaptor =
|
||||
ArgumentCaptor.forClass(CallableProcessingInterceptor.class);
|
||||
verify(webAsyncManager, atLeastOnce()).registerCallableInterceptor(any(), callableProcessingInterceptorArgCaptor.capture());
|
||||
|
||||
CallableProcessingInterceptor callableProcessingInterceptor =
|
||||
callableProcessingInterceptorArgCaptor.getAllValues().stream()
|
||||
.filter(e -> SecurityContextCallableProcessingInterceptor.class.isAssignableFrom(e.getClass()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
assertThat(callableProcessingInterceptor).isNotNull();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class WebAsyncPopulatedByDefaultConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser(PasswordEncodedUser.user());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+49
-59
@@ -25,6 +25,11 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -33,7 +38,6 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
@@ -41,25 +45,26 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.context.request.async.SecurityContextCallableProcessingInterceptor;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.accept.ContentNegotiationStrategy;
|
||||
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
|
||||
import org.springframework.web.context.request.async.WebAsyncManager;
|
||||
import org.springframework.web.context.request.async.WebAsyncUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.ThrowableAssert.catchThrowable;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
@@ -70,6 +75,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
* @author Rob Winch
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
@PrepareForTest({WebAsyncManager.class})
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PowerMockIgnore({ "org.w3c.dom.*", "org.xml.sax.*", "org.apache.xerces.*", "javax.xml.parsers.*", "javax.xml.transform.*" })
|
||||
public class WebSecurityConfigurerAdapterTests {
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
@@ -106,6 +114,42 @@ public class WebSecurityConfigurerAdapterTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadConfigWhenDefaultConfigThenWebAsyncManagerIntegrationFilterAdded() throws Exception {
|
||||
this.spring.register(WebAsyncPopulatedByDefaultConfig.class).autowire();
|
||||
|
||||
WebAsyncManager webAsyncManager = mock(WebAsyncManager.class);
|
||||
|
||||
this.mockMvc.perform(get("/").requestAttr(WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE, webAsyncManager));
|
||||
|
||||
ArgumentCaptor<CallableProcessingInterceptor> callableProcessingInterceptorArgCaptor =
|
||||
ArgumentCaptor.forClass(CallableProcessingInterceptor.class);
|
||||
verify(webAsyncManager, atLeastOnce()).registerCallableInterceptor(any(), callableProcessingInterceptorArgCaptor.capture());
|
||||
|
||||
CallableProcessingInterceptor callableProcessingInterceptor =
|
||||
callableProcessingInterceptorArgCaptor.getAllValues().stream()
|
||||
.filter(e -> SecurityContextCallableProcessingInterceptor.class.isAssignableFrom(e.getClass()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
assertThat(callableProcessingInterceptor).isNotNull();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class WebAsyncPopulatedByDefaultConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser(PasswordEncodedUser.user());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadConfigWhenRequestAuthenticateThenAuthenticationEventPublished() throws Exception {
|
||||
this.spring.register(InMemoryAuthWithWebSecurityConfigurerAdapter.class).autowire();
|
||||
@@ -371,58 +415,4 @@ public class WebSecurityConfigurerAdapterTests {
|
||||
@Order
|
||||
static class LowestPriorityWebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
}
|
||||
|
||||
// gh-7515
|
||||
@Test
|
||||
public void performWhenUsingAuthenticationEventPublisherBeanThenUses() throws Exception {
|
||||
this.spring.register(CustomAuthenticationEventPublisherBean.class).autowire();
|
||||
|
||||
AuthenticationEventPublisher authenticationEventPublisher =
|
||||
this.spring.getContext().getBean(AuthenticationEventPublisher.class);
|
||||
|
||||
this.mockMvc.perform(get("/")
|
||||
.with(httpBasic("user", "password")));
|
||||
|
||||
verify(authenticationEventPublisher).publishAuthenticationSuccess(any(Authentication.class));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomAuthenticationEventPublisherBean extends WebSecurityConfigurerAdapter {
|
||||
@Bean
|
||||
@Override
|
||||
public UserDetailsService userDetailsService() {
|
||||
return new InMemoryUserDetailsManager(PasswordEncodedUser.user());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationEventPublisher authenticationEventPublisher() {
|
||||
return mock(AuthenticationEventPublisher.class);
|
||||
}
|
||||
}
|
||||
|
||||
// gh-4400
|
||||
@Test
|
||||
public void performWhenUsingAuthenticationEventPublisherInDslThenUses() throws Exception {
|
||||
this.spring.register(CustomAuthenticationEventPublisherDsl.class).autowire();
|
||||
|
||||
AuthenticationEventPublisher authenticationEventPublisher =
|
||||
CustomAuthenticationEventPublisherDsl.EVENT_PUBLISHER;
|
||||
|
||||
this.mockMvc.perform(get("/")
|
||||
.with(httpBasic("user", "password"))); // fails since no providers configured
|
||||
|
||||
verify(authenticationEventPublisher).publishAuthenticationFailure(
|
||||
any(AuthenticationException.class),
|
||||
any(Authentication.class));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomAuthenticationEventPublisherDsl extends WebSecurityConfigurerAdapter {
|
||||
static AuthenticationEventPublisher EVENT_PUBLISHER = mock(AuthenticationEventPublisher.class);
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.authenticationEventPublisher(EVENT_PUBLISHER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -98,7 +98,7 @@ public class NamespaceRememberMeTests {
|
||||
.andReturn();
|
||||
|
||||
rememberMe = result.getResponse().getCookie("remember-me");
|
||||
assertThat(rememberMe).isNotNull().extracting(Cookie::getMaxAge).isEqualTo(0);
|
||||
assertThat(rememberMe).isNotNull().extracting("maxAge").containsExactly(0);
|
||||
|
||||
this.mvc.perform(post("/authentication-class").with(csrf())
|
||||
.cookie(rememberMe))
|
||||
@@ -292,7 +292,7 @@ public class NamespaceRememberMeTests {
|
||||
.with(rememberMeLogin()))
|
||||
.andReturn().getResponse().getCookie("remember-me");
|
||||
|
||||
assertThat(expiredRememberMe).extracting(Cookie::getMaxAge).isEqualTo(314);
|
||||
assertThat(expiredRememberMe).extracting("maxAge").containsExactly(314);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -320,8 +320,8 @@ public class NamespaceRememberMeTests {
|
||||
.with(rememberMeLogin()))
|
||||
.andReturn().getResponse().getCookie("remember-me");
|
||||
|
||||
assertThat(expiredRememberMe).extracting(Cookie::getMaxAge)
|
||||
.isEqualTo(AbstractRememberMeServices.TWO_WEEKS_S);
|
||||
assertThat(expiredRememberMe).extracting("maxAge")
|
||||
.containsExactly(AbstractRememberMeServices.TWO_WEEKS_S);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -331,7 +331,7 @@ public class NamespaceRememberMeTests {
|
||||
.with(rememberMeLogin()))
|
||||
.andReturn().getResponse().getCookie("remember-me");
|
||||
|
||||
assertThat(secureCookie).extracting(Cookie::getSecure).isEqualTo(true);
|
||||
assertThat(secureCookie).extracting("secure").containsExactly(true);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -357,7 +357,7 @@ public class NamespaceRememberMeTests {
|
||||
.secure(true))
|
||||
.andReturn().getResponse().getCookie("remember-me");
|
||||
|
||||
assertThat(secureCookie).extracting(Cookie::getSecure).isEqualTo(true);
|
||||
assertThat(secureCookie).extracting("secure").containsExactly(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -139,7 +139,7 @@ public class UrlAuthorizationsTests {
|
||||
FilterSecurityInterceptor interceptor = getFilter(FilterSecurityInterceptor.class);
|
||||
assertThat(interceptor).isNotNull();
|
||||
assertThat(interceptor).extracting("accessDecisionManager")
|
||||
.isInstanceOf(AffirmativeBased.class);
|
||||
.first().isInstanceOf(AffirmativeBased.class);
|
||||
}
|
||||
|
||||
private <T extends Filter> T getFilter(Class<T> filterType) {
|
||||
|
||||
+3
-2
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -92,7 +93,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.security.oauth2.core.oidc.TestOidcIdTokens.idToken;
|
||||
import static org.springframework.security.oauth2.jwt.TestJwts.jwt;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
@@ -1016,7 +1016,8 @@ public class OAuth2LoginConfigurerTests {
|
||||
}
|
||||
|
||||
private static OAuth2UserService<OidcUserRequest, OidcUser> createOidcUserService() {
|
||||
OidcIdToken idToken = idToken().build();
|
||||
OidcIdToken idToken = new OidcIdToken("token123", Instant.now(),
|
||||
Instant.now().plusSeconds(3600), Collections.singletonMap(IdTokenClaimNames.SUB, "sub123"));
|
||||
return request -> new DefaultOidcUser(
|
||||
Collections.singleton(new OidcUserAuthority(idToken)), idToken);
|
||||
}
|
||||
|
||||
+5
-142
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -28,19 +28,10 @@ import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import com.nimbusds.jose.JWSAlgorithm;
|
||||
import com.nimbusds.jose.JWSHeader;
|
||||
import com.nimbusds.jose.JWSObject;
|
||||
import com.nimbusds.jose.Payload;
|
||||
import com.nimbusds.jose.crypto.RSASSASigner;
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import net.minidev.json.JSONObject;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import org.hamcrest.core.AllOf;
|
||||
@@ -73,7 +64,6 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationManagerResolver;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
@@ -89,13 +79,11 @@ import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
|
||||
import org.springframework.security.oauth2.jose.TestKeys;
|
||||
import org.springframework.security.oauth2.jwt.BadJwtException;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.jwt.JwtClaimNames;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.JwtException;
|
||||
import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
|
||||
@@ -103,7 +91,6 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
||||
@@ -140,8 +127,6 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.security.config.Customizer.withDefaults;
|
||||
import static org.springframework.security.oauth2.core.TestOAuth2AccessTokens.noScopes;
|
||||
import static org.springframework.security.oauth2.jwt.JwtClaimNames.ISS;
|
||||
import static org.springframework.security.oauth2.jwt.JwtClaimNames.SUB;
|
||||
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSetUri;
|
||||
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withPublicKey;
|
||||
import static org.springframework.security.oauth2.jwt.TestJwts.jwt;
|
||||
@@ -164,7 +149,7 @@ import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
public class OAuth2ResourceServerConfigurerTests {
|
||||
private static final String JWT_TOKEN = "token";
|
||||
private static final String JWT_SUBJECT = "mock-test-subject";
|
||||
private static final Map<String, Object> JWT_CLAIMS = Collections.singletonMap(SUB, JWT_SUBJECT);
|
||||
private static final Map<String, Object> JWT_CLAIMS = Collections.singletonMap(JwtClaimNames.SUB, JWT_SUBJECT);
|
||||
private static final Jwt JWT = jwt().build();
|
||||
private static final String JWK_SET_URI = "https://mock.org";
|
||||
private static final JwtAuthenticationToken JWT_AUTHENTICATION_TOKEN =
|
||||
@@ -257,7 +242,7 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
|
||||
this.mvc.perform(get("/").with(bearerToken(token)))
|
||||
.andExpect(status().isUnauthorized())
|
||||
.andExpect(header().string("WWW-Authenticate", "Bearer"));
|
||||
.andExpect(invalidTokenHeader("An error occurred while attempting to decode the Jwt: Malformed Jwk set"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -270,7 +255,7 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
|
||||
this.mvc.perform(get("/").with(bearerToken(token)))
|
||||
.andExpect(status().isUnauthorized())
|
||||
.andExpect(header().string("WWW-Authenticate", "Bearer"));
|
||||
.andExpect(invalidTokenHeader("An error occurred while attempting to decode the Jwt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1107,22 +1092,6 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
.andExpect(invalidTokenHeader("algorithm"));
|
||||
}
|
||||
|
||||
// gh-7793
|
||||
@Test
|
||||
public void requestWhenUsingCustomAuthenticationEventPublisherThenUses() throws Exception{
|
||||
this.spring.register(CustomAuthenticationEventPublisher.class).autowire();
|
||||
|
||||
when(bean(JwtDecoder.class).decode(anyString()))
|
||||
.thenThrow(new BadJwtException("problem"));
|
||||
|
||||
this.mvc.perform(get("/").with(bearerToken("token")));
|
||||
|
||||
verifyBean(AuthenticationEventPublisher.class)
|
||||
.publishAuthenticationFailure(
|
||||
any(OAuth2AuthenticationException.class),
|
||||
any(Authentication.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenCustomJwtAuthenticationManagerThenUsed() throws Exception {
|
||||
this.spring.register(JwtAuthenticationManagerConfig.class, BasicController.class).autowire();
|
||||
@@ -1376,50 +1345,6 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
verify(http, never()).authenticationProvider(any(AuthenticationProvider.class));
|
||||
}
|
||||
|
||||
// -- authentication manager resolver
|
||||
|
||||
@Test
|
||||
public void getWhenMultipleIssuersThenUsesIssuerClaimToDifferentiate() throws Exception {
|
||||
this.spring.register(WebServerConfig.class, MultipleIssuersConfig.class, BasicController.class).autowire();
|
||||
|
||||
MockWebServer server = this.spring.getContext().getBean(MockWebServer.class);
|
||||
String metadata = "{\n"
|
||||
+ " \"issuer\": \"%s\", \n"
|
||||
+ " \"jwks_uri\": \"%s/.well-known/jwks.json\" \n"
|
||||
+ "}";
|
||||
String jwkSet = jwkSet();
|
||||
String issuerOne = server.url("/issuerOne").toString();
|
||||
String issuerTwo = server.url("/issuerTwo").toString();
|
||||
String issuerThree = server.url("/issuerThree").toString();
|
||||
String jwtOne = jwtFromIssuer(issuerOne);
|
||||
String jwtTwo = jwtFromIssuer(issuerTwo);
|
||||
String jwtThree = jwtFromIssuer(issuerThree);
|
||||
|
||||
mockWebServer(String.format(metadata, issuerOne, issuerOne));
|
||||
mockWebServer(jwkSet);
|
||||
|
||||
this.mvc.perform(get("/authenticated")
|
||||
.with(bearerToken(jwtOne)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("test-subject"));
|
||||
|
||||
mockWebServer(String.format(metadata, issuerTwo, issuerTwo));
|
||||
mockWebServer(jwkSet);
|
||||
|
||||
this.mvc.perform(get("/authenticated")
|
||||
.with(bearerToken(jwtTwo)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("test-subject"));
|
||||
|
||||
mockWebServer(String.format(metadata, issuerThree, issuerThree));
|
||||
mockWebServer(jwkSet);
|
||||
|
||||
this.mvc.perform(get("/authenticated")
|
||||
.with(bearerToken(jwtThree)))
|
||||
.andExpect(status().isUnauthorized())
|
||||
.andExpect(invalidTokenHeader("Invalid issuer"));
|
||||
}
|
||||
|
||||
// -- Incorrect Configuration
|
||||
|
||||
@Test
|
||||
@@ -2063,31 +1988,6 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomAuthenticationEventPublisher extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.oauth2ResourceServer()
|
||||
.jwt();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
JwtDecoder jwtDecoder() {
|
||||
return mock(JwtDecoder.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
AuthenticationEventPublisher authenticationEventPublisher() {
|
||||
return mock(AuthenticationEventPublisher.class);
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class OpaqueTokenConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
@@ -2199,26 +2099,6 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class MultipleIssuersConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
MockWebServer web;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
String issuerOne = this.web.url("/issuerOne").toString();
|
||||
String issuerTwo = this.web.url("/issuerTwo").toString();
|
||||
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver =
|
||||
new JwtIssuerAuthenticationManagerResolver(issuerOne, issuerTwo);
|
||||
|
||||
// @formatter:off
|
||||
http
|
||||
.oauth2ResourceServer()
|
||||
.authenticationManagerResolver(authenticationManagerResolver);
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class AuthenticationManagerResolverPlusOtherConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
@@ -2406,23 +2286,6 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
", error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\"");
|
||||
}
|
||||
|
||||
private String jwkSet() {
|
||||
return new JWKSet(new RSAKey.Builder(TestKeys.DEFAULT_PUBLIC_KEY)
|
||||
.keyID("1").build()).toString();
|
||||
}
|
||||
|
||||
private String jwtFromIssuer(String issuer) throws Exception {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put(ISS, issuer);
|
||||
claims.put(SUB, "test-subject");
|
||||
claims.put("scope", "message:read");
|
||||
JWSObject jws = new JWSObject(
|
||||
new JWSHeader.Builder(JWSAlgorithm.RS256).keyID("1").build(),
|
||||
new Payload(new JSONObject(claims)));
|
||||
jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
|
||||
return jws.serialize();
|
||||
}
|
||||
|
||||
private void mockWebServer(String response) {
|
||||
this.web.enqueue(new MockResponse()
|
||||
.setResponseCode(200)
|
||||
|
||||
-263
@@ -1,263 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.annotation.web.configurers.saml2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.opensaml.saml.saml2.core.Assertion;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
|
||||
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.security.config.annotation.web.configurers.saml2.TestRelyingPartyRegistrations.saml2AuthenticationConfiguration;
|
||||
|
||||
/**
|
||||
* Tests for different Java configuration for {@link Saml2LoginConfigurer}
|
||||
*/
|
||||
public class Saml2LoginConfigurerTests {
|
||||
|
||||
private static final Converter<Assertion, Collection<? extends GrantedAuthority>>
|
||||
AUTHORITIES_EXTRACTOR = a -> Arrays.asList(new SimpleGrantedAuthority("TEST"));
|
||||
private static final GrantedAuthoritiesMapper AUTHORITIES_MAPPER =
|
||||
authorities -> Arrays.asList(new SimpleGrantedAuthority("TEST CONVERTED"));
|
||||
private static final Duration RESPONSE_TIME_VALIDATION_SKEW = Duration.ZERO;
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private FilterChainProxy springSecurityFilterChain;
|
||||
|
||||
@Autowired
|
||||
private RelyingPartyRegistrationRepository repository;
|
||||
|
||||
@Autowired
|
||||
SecurityContextRepository securityContextRepository;
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired(required = false)
|
||||
MockMvc mvc;
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
private MockHttpServletResponse response;
|
||||
private MockFilterChain filterChain;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.request = new MockHttpServletRequest("POST", "");
|
||||
this.request.setServletPath("/login/saml2/sso/test-rp");
|
||||
this.response = new MockHttpServletResponse();
|
||||
this.filterChain = new MockFilterChain();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saml2LoginWhenConfiguringAuthenticationManagerThenTheManagerIsUsed() throws Exception {
|
||||
// setup application context
|
||||
this.spring.register(Saml2LoginConfigWithCustomAuthenticationManager.class).autowire();
|
||||
performSaml2Login("ROLE_AUTH_MANAGER");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saml2LoginWhenConfiguringAuthenticationDefaultsUsingCustomizerThenTheProviderIsConfigured() throws Exception {
|
||||
// setup application context
|
||||
this.spring.register(Saml2LoginConfigWithAuthenticationDefaultsWithPostProcessor.class).autowire();
|
||||
validateSaml2WebSsoAuthenticationFilterConfiguration();
|
||||
}
|
||||
|
||||
private void validateSaml2WebSsoAuthenticationFilterConfiguration() {
|
||||
// get the OpenSamlAuthenticationProvider
|
||||
Saml2WebSsoAuthenticationFilter filter = getSaml2SsoFilter(this.springSecurityFilterChain);
|
||||
AuthenticationManager manager =
|
||||
(AuthenticationManager) ReflectionTestUtils.getField(filter, "authenticationManager");
|
||||
ProviderManager pm = (ProviderManager) manager;
|
||||
AuthenticationProvider provider = pm.getProviders()
|
||||
.stream()
|
||||
.filter(p -> p instanceof OpenSamlAuthenticationProvider)
|
||||
.findFirst()
|
||||
.get();
|
||||
Assert.assertSame(AUTHORITIES_EXTRACTOR, ReflectionTestUtils.getField(provider, "authoritiesExtractor"));
|
||||
Assert.assertSame(AUTHORITIES_MAPPER, ReflectionTestUtils.getField(provider, "authoritiesMapper"));
|
||||
Assert.assertSame(RESPONSE_TIME_VALIDATION_SKEW, ReflectionTestUtils.getField(provider, "responseTimeValidationSkew"));
|
||||
}
|
||||
|
||||
private Saml2WebSsoAuthenticationFilter getSaml2SsoFilter(FilterChainProxy chain) {
|
||||
return (Saml2WebSsoAuthenticationFilter) chain.getFilters("/login/saml2/sso/test")
|
||||
.stream()
|
||||
.filter(f -> f instanceof Saml2WebSsoAuthenticationFilter)
|
||||
.findFirst()
|
||||
.get();
|
||||
}
|
||||
|
||||
private void performSaml2Login(String expected) throws IOException, ServletException {
|
||||
// setup authentication parameters
|
||||
this.request.setParameter(
|
||||
"SAMLResponse",
|
||||
Base64.getEncoder().encodeToString(
|
||||
"saml2-xml-response-object".getBytes()
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
// perform test
|
||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
// assertions
|
||||
Authentication authentication = this.securityContextRepository
|
||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
||||
.getAuthentication();
|
||||
Assert.assertNotNull("Expected a valid authentication object.", authentication);
|
||||
assertThat(authentication.getAuthorities()).hasSize(1);
|
||||
assertThat(authentication.getAuthorities()).first()
|
||||
.isInstanceOf(SimpleGrantedAuthority.class).hasToString(expected);
|
||||
}
|
||||
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(Saml2LoginConfigBeans.class)
|
||||
static class Saml2LoginConfigWithCustomAuthenticationManager extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.saml2Login()
|
||||
.authenticationManager(
|
||||
getAuthenticationManagerMock("ROLE_AUTH_MANAGER")
|
||||
);
|
||||
super.configure(http);
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(Saml2LoginConfigBeans.class)
|
||||
static class Saml2LoginConfigWithAuthenticationDefaultsWithPostProcessor extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
ObjectPostProcessor<OpenSamlAuthenticationProvider> processor
|
||||
= new ObjectPostProcessor<OpenSamlAuthenticationProvider>() {
|
||||
@Override
|
||||
public <O extends OpenSamlAuthenticationProvider> O postProcess(O provider) {
|
||||
provider.setResponseTimeValidationSkew(RESPONSE_TIME_VALIDATION_SKEW);
|
||||
provider.setAuthoritiesMapper(AUTHORITIES_MAPPER);
|
||||
provider.setAuthoritiesExtractor(AUTHORITIES_EXTRACTOR);
|
||||
return provider;
|
||||
}
|
||||
};
|
||||
|
||||
http.saml2Login()
|
||||
.addObjectPostProcessor(processor)
|
||||
;
|
||||
super.configure(http);
|
||||
}
|
||||
}
|
||||
|
||||
private static AuthenticationManager getAuthenticationManagerMock(String role) {
|
||||
return new AuthenticationManager() {
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication)
|
||||
throws AuthenticationException {
|
||||
if (!supports(authentication.getClass())) {
|
||||
throw new AuthenticationServiceException("not supported");
|
||||
}
|
||||
return new Saml2Authentication(
|
||||
() -> "auth principal",
|
||||
"saml2 response",
|
||||
Collections.singletonList(
|
||||
new SimpleGrantedAuthority(role)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return authentication.isAssignableFrom(Saml2AuthenticationToken.class);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static class Saml2LoginConfigBeans {
|
||||
|
||||
@Bean
|
||||
SecurityContextRepository securityContextRepository() {
|
||||
return new HttpSessionSecurityContextRepository();
|
||||
}
|
||||
|
||||
@Bean
|
||||
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
|
||||
RelyingPartyRegistrationRepository repository = mock(RelyingPartyRegistrationRepository.class);
|
||||
when(repository.findByRegistrationId(anyString())).thenReturn(
|
||||
saml2AuthenticationConfiguration()
|
||||
);
|
||||
return repository;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.annotation.web.configurers.saml2;
|
||||
|
||||
import org.springframework.security.saml2.credentials.Saml2X509Credential;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
|
||||
|
||||
import static org.springframework.security.config.annotation.web.configurers.saml2.TestSaml2Credentials.signingCredential;
|
||||
import static org.springframework.security.config.annotation.web.configurers.saml2.TestSaml2Credentials.verificationCertificate;
|
||||
|
||||
/**
|
||||
* Preconfigured test data for {@link RelyingPartyRegistration} objects
|
||||
*/
|
||||
public class TestRelyingPartyRegistrations {
|
||||
|
||||
static RelyingPartyRegistration saml2AuthenticationConfiguration() {
|
||||
//remote IDP entity ID
|
||||
String idpEntityId = "https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php";
|
||||
//remote WebSSO Endpoint - Where to Send AuthNRequests to
|
||||
String webSsoEndpoint = "https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php";
|
||||
//local registration ID
|
||||
String registrationId = "simplesamlphp";
|
||||
//local entity ID - autogenerated based on URL
|
||||
String localEntityIdTemplate = "{baseUrl}/saml2/service-provider-metadata/{registrationId}";
|
||||
//local signing (and decryption key)
|
||||
Saml2X509Credential signingCredential = signingCredential();
|
||||
//IDP certificate for verification of incoming messages
|
||||
Saml2X509Credential idpVerificationCertificate = verificationCertificate();
|
||||
String acsUrlTemplate = "{baseUrl}" + Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
|
||||
return RelyingPartyRegistration.withRegistrationId(registrationId)
|
||||
.providerDetails(c -> c.entityId(idpEntityId))
|
||||
.providerDetails(c -> c.webSsoUrl(webSsoEndpoint))
|
||||
.credentials(c -> c.add(signingCredential))
|
||||
.credentials(c -> c.add(idpVerificationCertificate))
|
||||
.localEntityIdTemplate(localEntityIdTemplate)
|
||||
.assertionConsumerServiceUrlTemplate(acsUrlTemplate)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.annotation.web.configurers.saml2;
|
||||
|
||||
import org.springframework.security.converter.RsaKeyConverters;
|
||||
import org.springframework.security.saml2.credentials.Saml2X509Credential;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import static org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.DECRYPTION;
|
||||
import static org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.SIGNING;
|
||||
import static org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.VERIFICATION;
|
||||
|
||||
/**
|
||||
* Preconfigured SAML credentials for SAML integration tests.
|
||||
*/
|
||||
public class TestSaml2Credentials {
|
||||
|
||||
static Saml2X509Credential verificationCertificate() {
|
||||
String certificate = "-----BEGIN CERTIFICATE-----\n" +
|
||||
"MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD\n" +
|
||||
"VQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYD\n" +
|
||||
"VQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwX\n" +
|
||||
"c2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0Bw\n" +
|
||||
"aXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJ\n" +
|
||||
"BgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAa\n" +
|
||||
"BgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQD\n" +
|
||||
"DBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlr\n" +
|
||||
"QHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62\n" +
|
||||
"E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz\n" +
|
||||
"2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWW\n" +
|
||||
"RDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQ\n" +
|
||||
"nX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5\n" +
|
||||
"cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gph\n" +
|
||||
"iJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5\n" +
|
||||
"ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTAD\n" +
|
||||
"AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduO\n" +
|
||||
"nRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+v\n" +
|
||||
"ZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLu\n" +
|
||||
"xbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6z\n" +
|
||||
"V9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3\n" +
|
||||
"lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\n" +
|
||||
"-----END CERTIFICATE-----";
|
||||
return new Saml2X509Credential(
|
||||
x509Certificate(certificate),
|
||||
VERIFICATION
|
||||
);
|
||||
}
|
||||
|
||||
static X509Certificate x509Certificate(String source) {
|
||||
try {
|
||||
final CertificateFactory factory = CertificateFactory.getInstance("X.509");
|
||||
return (X509Certificate) factory.generateCertificate(
|
||||
new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8))
|
||||
);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static Saml2X509Credential signingCredential() {
|
||||
String key = "-----BEGIN PRIVATE KEY-----\n" +
|
||||
"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\n" +
|
||||
"VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\n" +
|
||||
"cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\n" +
|
||||
"Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\n" +
|
||||
"x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\n" +
|
||||
"wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\n" +
|
||||
"vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\n" +
|
||||
"8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\n" +
|
||||
"oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\n" +
|
||||
"EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\n" +
|
||||
"KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\n" +
|
||||
"YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\n" +
|
||||
"9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\n" +
|
||||
"INrtuLp4YHbgk1mi\n" +
|
||||
"-----END PRIVATE KEY-----";
|
||||
String certificate = "-----BEGIN CERTIFICATE-----\n" +
|
||||
"MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\n" +
|
||||
"VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\n" +
|
||||
"A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\n" +
|
||||
"DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\n" +
|
||||
"MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\n" +
|
||||
"MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\n" +
|
||||
"TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\n" +
|
||||
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\n" +
|
||||
"vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\n" +
|
||||
"+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\n" +
|
||||
"y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\n" +
|
||||
"XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\n" +
|
||||
"qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\n" +
|
||||
"RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\n" +
|
||||
"-----END CERTIFICATE-----";
|
||||
PrivateKey pk = RsaKeyConverters.pkcs8().convert(new ByteArrayInputStream(key.getBytes()));
|
||||
X509Certificate cert = x509Certificate(certificate);
|
||||
return new Saml2X509Credential(pk, cert, SIGNING, DECRYPTION);
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -50,7 +50,7 @@ public class XsdDocumentedTests {
|
||||
String referenceLocation = "../docs/manual/src/docs/asciidoc/_includes/servlet/appendix/namespace.adoc";
|
||||
|
||||
String schema31xDocumentLocation = "org/springframework/security/config/spring-security-3.1.xsd";
|
||||
String schemaDocumentLocation = "org/springframework/security/config/spring-security-5.3.xsd";
|
||||
String schemaDocumentLocation = "org/springframework/security/config/spring-security-5.2.xsd";
|
||||
|
||||
XmlSupport xml = new XmlSupport();
|
||||
|
||||
@@ -142,8 +142,8 @@ public class XsdDocumentedTests {
|
||||
|
||||
String[] schemas = resource.getFile().getParentFile().list((dir, name) -> name.endsWith(".xsd"));
|
||||
|
||||
assertThat(schemas.length).isEqualTo(15)
|
||||
.withFailMessage("the count is equal to 15, if not then schemaDocument needs updating");
|
||||
assertThat(schemas.length).isEqualTo(14)
|
||||
.withFailMessage("the count is equal to 14, if not then schemaDocument needs updating");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
-219
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses.accessTokenResponse;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for {@link OAuth2ClientBeanDefinitionParser}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SecurityTestExecutionListeners
|
||||
public class OAuth2ClientBeanDefinitionParserTests {
|
||||
private static final String CONFIG_LOCATION_PREFIX = "classpath:org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests";
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private ClientRegistrationRepository clientRegistrationRepository;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2AuthorizedClientRepository authorizedClientRepository;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2AuthorizedClientService authorizedClientService;
|
||||
|
||||
@Autowired(required = false)
|
||||
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mvc;
|
||||
|
||||
@Test
|
||||
public void requestWhenAuthorizeThenRedirect() throws Exception {
|
||||
this.spring.configLocations(xml("Minimal")).autowire();
|
||||
|
||||
MvcResult result = this.mvc.perform(get("/oauth2/authorization/google"))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andReturn();
|
||||
assertThat(result.getResponse().getRedirectedUrl()).matches(
|
||||
"https://accounts.google.com/o/oauth2/v2/auth\\?" +
|
||||
"response_type=code&client_id=google-client-id&" +
|
||||
"scope=scope1%20scope2&state=.{15,}&redirect_uri=http://localhost/callback/google");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenCustomClientRegistrationRepositoryThenCalled() throws Exception {
|
||||
this.spring.configLocations(xml("CustomClientRegistrationRepository")).autowire();
|
||||
|
||||
ClientRegistration clientRegistration = CommonOAuth2Provider.GOOGLE.getBuilder("google")
|
||||
.clientId("google-client-id")
|
||||
.clientSecret("google-client-secret")
|
||||
.redirectUriTemplate("http://localhost/callback/google")
|
||||
.scope("scope1", "scope2")
|
||||
.build();
|
||||
when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(clientRegistration);
|
||||
|
||||
MvcResult result = this.mvc.perform(get("/oauth2/authorization/google"))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andReturn();
|
||||
assertThat(result.getResponse().getRedirectedUrl()).matches(
|
||||
"https://accounts.google.com/o/oauth2/v2/auth\\?" +
|
||||
"response_type=code&client_id=google-client-id&" +
|
||||
"scope=scope1%20scope2&state=.{15,}&redirect_uri=http://localhost/callback/google");
|
||||
|
||||
verify(this.clientRegistrationRepository).findByRegistrationId(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenCustomAuthorizationRequestResolverThenCalled() throws Exception {
|
||||
this.spring.configLocations(xml("CustomConfiguration")).autowire();
|
||||
|
||||
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("google");
|
||||
|
||||
OAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest(clientRegistration);
|
||||
when(this.authorizationRequestResolver.resolve(any())).thenReturn(authorizationRequest);
|
||||
|
||||
this.mvc.perform(get("/oauth2/authorization/google"))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl(
|
||||
"https://accounts.google.com/o/oauth2/v2/auth?" +
|
||||
"response_type=code&client_id=google-client-id&" +
|
||||
"scope=scope1%20scope2&state=state&redirect_uri=http://localhost/callback/google"));
|
||||
|
||||
verify(this.authorizationRequestResolver).resolve(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenAuthorizationResponseMatchThenProcess() throws Exception {
|
||||
this.spring.configLocations(xml("CustomConfiguration")).autowire();
|
||||
|
||||
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("google");
|
||||
|
||||
OAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest(clientRegistration);
|
||||
when(this.authorizationRequestRepository.loadAuthorizationRequest(any()))
|
||||
.thenReturn(authorizationRequest);
|
||||
when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))
|
||||
.thenReturn(authorizationRequest);
|
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build();
|
||||
when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse);
|
||||
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("code", "code123");
|
||||
params.add("state", authorizationRequest.getState());
|
||||
this.mvc.perform(get(authorizationRequest.getRedirectUri()).params(params))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl(authorizationRequest.getRedirectUri()));
|
||||
|
||||
ArgumentCaptor<OAuth2AuthorizedClient> authorizedClientCaptor =
|
||||
ArgumentCaptor.forClass(OAuth2AuthorizedClient.class);
|
||||
verify(this.authorizedClientRepository).saveAuthorizedClient(
|
||||
authorizedClientCaptor.capture(), any(), any(), any());
|
||||
OAuth2AuthorizedClient authorizedClient = authorizedClientCaptor.getValue();
|
||||
assertThat(authorizedClient.getClientRegistration()).isEqualTo(clientRegistration);
|
||||
assertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());
|
||||
}
|
||||
|
||||
@WithMockUser
|
||||
@Test
|
||||
public void requestWhenCustomAuthorizedClientServiceThenCalled() throws Exception {
|
||||
this.spring.configLocations(xml("CustomAuthorizedClientService")).autowire();
|
||||
|
||||
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("google");
|
||||
|
||||
OAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest(clientRegistration);
|
||||
when(this.authorizationRequestRepository.loadAuthorizationRequest(any()))
|
||||
.thenReturn(authorizationRequest);
|
||||
when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))
|
||||
.thenReturn(authorizationRequest);
|
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build();
|
||||
when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse);
|
||||
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("code", "code123");
|
||||
params.add("state", authorizationRequest.getState());
|
||||
this.mvc.perform(get(authorizationRequest.getRedirectUri()).params(params))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl(authorizationRequest.getRedirectUri()));
|
||||
|
||||
verify(this.authorizedClientService).saveAuthorizedClient(any(), any());
|
||||
}
|
||||
|
||||
private static OAuth2AuthorizationRequest createAuthorizationRequest(ClientRegistration clientRegistration) {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());
|
||||
return OAuth2AuthorizationRequest.authorizationCode()
|
||||
.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
|
||||
.clientId(clientRegistration.getClientId())
|
||||
.redirectUri(clientRegistration.getRedirectUriTemplate())
|
||||
.scopes(clientRegistration.getScopes())
|
||||
.state("state")
|
||||
.attributes(attributes)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static String xml(String configName) {
|
||||
return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml";
|
||||
}
|
||||
}
|
||||
-495
@@ -1,495 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
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.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.oauth2.core.user.TestOAuth2Users;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
||||
import org.springframework.security.oauth2.jwt.TestJwts;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses.accessTokenResponse;
|
||||
import static org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses.oidcAccessTokenResponse;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Tests for {@link OAuth2LoginBeanDefinitionParser}.
|
||||
*
|
||||
* @author Ruby Hartono
|
||||
*/
|
||||
public class OAuth2LoginBeanDefinitionParserTests {
|
||||
private static final String CONFIG_LOCATION_PREFIX = "classpath:org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests";
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private ClientRegistrationRepository clientRegistrationRepository;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2AuthorizedClientRepository authorizedClientRepository;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2AuthorizedClientService authorizedClientService;
|
||||
|
||||
@Autowired(required = false)
|
||||
private ApplicationListener<AuthenticationSuccessEvent> authenticationSuccessListener;
|
||||
|
||||
@Autowired(required = false)
|
||||
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService;
|
||||
|
||||
@Autowired(required = false)
|
||||
private JwtDecoderFactory<ClientRegistration> jwtDecoderFactory;
|
||||
|
||||
@Autowired(required = false)
|
||||
private GrantedAuthoritiesMapper userAuthoritiesMapper;
|
||||
|
||||
@Autowired(required = false)
|
||||
private AuthenticationFailureHandler authenticationFailureHandler;
|
||||
|
||||
@Autowired(required = false)
|
||||
private AuthenticationSuccessHandler authenticationSuccessHandler;
|
||||
|
||||
@Autowired(required = false)
|
||||
private RequestCache requestCache;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mvc;
|
||||
|
||||
@Test
|
||||
public void requestLoginWhenMultiClientRegistrationThenReturnLoginPageWithClients() throws Exception {
|
||||
this.spring.configLocations(this.xml("MultiClientRegistration")).autowire();
|
||||
|
||||
MvcResult result = this.mvc.perform(get("/login"))
|
||||
.andExpect(status().is2xxSuccessful())
|
||||
.andReturn();
|
||||
|
||||
assertThat(result.getResponse().getContentAsString())
|
||||
.contains("<a href=\"/oauth2/authorization/google-login\">Google</a>");
|
||||
assertThat(result.getResponse().getContentAsString())
|
||||
.contains("<a href=\"/oauth2/authorization/github-login\">Github</a>");
|
||||
}
|
||||
|
||||
// gh-5347
|
||||
@Test
|
||||
public void requestWhenSingleClientRegistrationThenAutoRedirect() throws Exception {
|
||||
this.spring.configLocations(this.xml("SingleClientRegistration")).autowire();
|
||||
|
||||
this.mvc.perform(get("/"))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("http://localhost/oauth2/authorization/google-login"));
|
||||
|
||||
verify(requestCache).saveRequest(any(), any());
|
||||
}
|
||||
|
||||
// gh-5347
|
||||
@Test
|
||||
public void requestWhenSingleClientRegistrationAndRequestFaviconNotAuthenticatedThenRedirectDefaultLoginPage()
|
||||
throws Exception {
|
||||
this.spring.configLocations(this.xml("SingleClientRegistration")).autowire();
|
||||
|
||||
this.mvc.perform(get("/favicon.ico").accept(new MediaType("image", "*")))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("http://localhost/login"));
|
||||
}
|
||||
|
||||
// gh-6812
|
||||
@Test
|
||||
public void requestWhenSingleClientRegistrationAndRequestXHRNotAuthenticatedThenDoesNotRedirectForAuthorization()
|
||||
throws Exception {
|
||||
this.spring.configLocations(this.xml("SingleClientRegistration")).autowire();
|
||||
|
||||
this.mvc.perform(get("/").header("X-Requested-With", "XMLHttpRequest"))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("http://localhost/login"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenAuthorizationRequestNotFoundThenThrowAuthenticationException() throws Exception {
|
||||
this.spring.configLocations(this.xml("SingleClientRegistration-WithCustomAuthenticationFailureHandler"))
|
||||
.autowire();
|
||||
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("code", "code123");
|
||||
params.add("state", "state123");
|
||||
this.mvc.perform(get("/login/oauth2/code/google").params(params));
|
||||
|
||||
ArgumentCaptor<AuthenticationException> exceptionCaptor = ArgumentCaptor
|
||||
.forClass(AuthenticationException.class);
|
||||
verify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), exceptionCaptor.capture());
|
||||
AuthenticationException exception = exceptionCaptor.getValue();
|
||||
assertThat(exception).isInstanceOf(OAuth2AuthenticationException.class);
|
||||
assertThat(((OAuth2AuthenticationException) exception).getError().getErrorCode())
|
||||
.isEqualTo("authorization_request_not_found");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenAuthorizationResponseValidThenAuthenticate() throws Exception {
|
||||
this.spring.configLocations(this.xml("MultiClientRegistration-WithCustomConfiguration")).autowire();
|
||||
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "github-login");
|
||||
OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()
|
||||
.attributes(attributes).build();
|
||||
when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest);
|
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build();
|
||||
when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse);
|
||||
|
||||
OAuth2User oauth2User = TestOAuth2Users.create();
|
||||
when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User);
|
||||
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("code", "code123");
|
||||
params.add("state", authorizationRequest.getState());
|
||||
this.mvc.perform(get("/login/oauth2/code/github-login").params(params))
|
||||
.andExpect(status().is2xxSuccessful());
|
||||
|
||||
ArgumentCaptor<Authentication> authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);
|
||||
verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture());
|
||||
Authentication authentication = authenticationCaptor.getValue();
|
||||
assertThat(authentication.getPrincipal()).isInstanceOf(OAuth2User.class);
|
||||
}
|
||||
|
||||
// gh-6009
|
||||
@Test
|
||||
public void requestWhenAuthorizationResponseValidThenAuthenticationSuccessEventPublished() throws Exception {
|
||||
this.spring.configLocations(this.xml("MultiClientRegistration-WithCustomConfiguration")).autowire();
|
||||
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "github-login");
|
||||
OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()
|
||||
.attributes(attributes).build();
|
||||
when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest);
|
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build();
|
||||
when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse);
|
||||
|
||||
OAuth2User oauth2User = TestOAuth2Users.create();
|
||||
when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User);
|
||||
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("code", "code123");
|
||||
params.add("state", authorizationRequest.getState());
|
||||
this.mvc.perform(get("/login/oauth2/code/github-login").params(params));
|
||||
|
||||
verify(authenticationSuccessListener).onApplicationEvent(any(AuthenticationSuccessEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenOidcAuthenticationResponseValidThenJwtDecoderFactoryCalled() throws Exception {
|
||||
this.spring.configLocations(this.xml("SingleClientRegistration-WithJwtDecoderFactoryAndDefaultSuccessHandler"))
|
||||
.autowire();
|
||||
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login");
|
||||
OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.oidcRequest()
|
||||
.attributes(attributes).build();
|
||||
when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest);
|
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = oidcAccessTokenResponse().build();
|
||||
when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse);
|
||||
|
||||
Jwt jwt = TestJwts.user();
|
||||
when(this.jwtDecoderFactory.createDecoder(any())).thenReturn(token -> jwt);
|
||||
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("code", "code123");
|
||||
params.add("state", authorizationRequest.getState());
|
||||
this.mvc.perform(get("/login/oauth2/code/google-login").params(params))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("/"));
|
||||
|
||||
verify(this.jwtDecoderFactory).createDecoder(any());
|
||||
verify(this.requestCache).getRequest(any(), any());
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Test
|
||||
public void requestWhenCustomGrantedAuthoritiesMapperThenCalled() throws Exception {
|
||||
this.spring.configLocations(this.xml("MultiClientRegistration-WithCustomGrantedAuthorities")).autowire();
|
||||
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "github-login");
|
||||
OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()
|
||||
.attributes(attributes).build();
|
||||
when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest);
|
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build();
|
||||
when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse);
|
||||
|
||||
OAuth2User oauth2User = TestOAuth2Users.create();
|
||||
when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User);
|
||||
|
||||
when(this.userAuthoritiesMapper.mapAuthorities(any())).thenReturn(
|
||||
(Collection) AuthorityUtils.createAuthorityList("ROLE_OAUTH2_USER"));
|
||||
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("code", "code123");
|
||||
params.add("state", authorizationRequest.getState());
|
||||
this.mvc.perform(get("/login/oauth2/code/github-login").params(params))
|
||||
.andExpect(status().is2xxSuccessful());
|
||||
|
||||
ArgumentCaptor<Authentication> authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);
|
||||
verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture());
|
||||
Authentication authentication = authenticationCaptor.getValue();
|
||||
assertThat(authentication.getPrincipal()).isInstanceOf(OAuth2User.class);
|
||||
assertThat(authentication.getAuthorities()).hasSize(1);
|
||||
assertThat(authentication.getAuthorities()).first().isInstanceOf(SimpleGrantedAuthority.class)
|
||||
.hasToString("ROLE_OAUTH2_USER");
|
||||
|
||||
// re-setup for OIDC test
|
||||
attributes = new HashMap<>();
|
||||
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "google-login");
|
||||
authorizationRequest = TestOAuth2AuthorizationRequests.oidcRequest()
|
||||
.attributes(attributes).build();
|
||||
when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest);
|
||||
|
||||
accessTokenResponse = oidcAccessTokenResponse().build();
|
||||
when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse);
|
||||
|
||||
Jwt jwt = TestJwts.user();
|
||||
when(this.jwtDecoderFactory.createDecoder(any())).thenReturn(token -> jwt);
|
||||
|
||||
when(this.userAuthoritiesMapper.mapAuthorities(any()))
|
||||
.thenReturn((Collection) AuthorityUtils.createAuthorityList("ROLE_OIDC_USER"));
|
||||
|
||||
this.mvc.perform(get("/login/oauth2/code/google-login").params(params))
|
||||
.andExpect(status().is2xxSuccessful());
|
||||
|
||||
authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);
|
||||
verify(authenticationSuccessHandler, times(2)).onAuthenticationSuccess(any(), any(),
|
||||
authenticationCaptor.capture());
|
||||
authentication = authenticationCaptor.getValue();
|
||||
assertThat(authentication.getPrincipal()).isInstanceOf(OidcUser.class);
|
||||
assertThat(authentication.getAuthorities()).hasSize(1);
|
||||
assertThat(authentication.getAuthorities()).first().isInstanceOf(SimpleGrantedAuthority.class)
|
||||
.hasToString("ROLE_OIDC_USER");
|
||||
}
|
||||
|
||||
// gh-5488
|
||||
@Test
|
||||
public void requestWhenCustomLoginProcessingUrlThenProcessAuthentication() throws Exception {
|
||||
this.spring.configLocations(this.xml("MultiClientRegistration-WithCustomLoginProcessingUrl")).autowire();
|
||||
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, "github-login");
|
||||
OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()
|
||||
.attributes(attributes).build();
|
||||
when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest);
|
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build();
|
||||
when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse);
|
||||
|
||||
OAuth2User oauth2User = TestOAuth2Users.create();
|
||||
when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User);
|
||||
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("code", "code123");
|
||||
params.add("state", authorizationRequest.getState());
|
||||
this.mvc.perform(get("/login/oauth2/github-login").params(params))
|
||||
.andExpect(status().is2xxSuccessful());
|
||||
|
||||
ArgumentCaptor<Authentication> authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);
|
||||
verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture());
|
||||
Authentication authentication = authenticationCaptor.getValue();
|
||||
assertThat(authentication.getPrincipal()).isInstanceOf(OAuth2User.class);
|
||||
}
|
||||
|
||||
// gh-5521
|
||||
@Test
|
||||
public void requestWhenCustomAuthorizationRequestResolverThenCalled() throws Exception {
|
||||
this.spring.configLocations(this.xml("SingleClientRegistration-WithCustomAuthorizationRequestResolver"))
|
||||
.autowire();
|
||||
|
||||
this.mvc.perform(get("/oauth2/authorization/google-login"))
|
||||
.andExpect(status().is3xxRedirection());
|
||||
|
||||
verify(authorizationRequestResolver).resolve(any());
|
||||
}
|
||||
|
||||
// gh-5347
|
||||
@Test
|
||||
public void requestWhenMultiClientRegistrationThenRedirectDefaultLoginPage() throws Exception {
|
||||
this.spring.configLocations(this.xml("MultiClientRegistration")).autowire();
|
||||
|
||||
this.mvc.perform(get("/"))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("http://localhost/login"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenCustomLoginPageThenRedirectCustomLoginPage() throws Exception {
|
||||
this.spring.configLocations(this.xml("SingleClientRegistration-WithCustomLoginPage")).autowire();
|
||||
|
||||
this.mvc.perform(get("/"))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("http://localhost/custom-login"));
|
||||
}
|
||||
|
||||
// gh-6802
|
||||
@Test
|
||||
public void requestWhenSingleClientRegistrationAndFormLoginConfiguredThenRedirectDefaultLoginPage()
|
||||
throws Exception {
|
||||
this.spring.configLocations(this.xml("SingleClientRegistration-WithFormLogin")).autowire();
|
||||
|
||||
this.mvc.perform(get("/"))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("http://localhost/login"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenCustomClientRegistrationRepositoryThenCalled() throws Exception {
|
||||
this.spring.configLocations(this.xml("WithCustomClientRegistrationRepository")).autowire();
|
||||
|
||||
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();
|
||||
when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(clientRegistration);
|
||||
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());
|
||||
OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()
|
||||
.attributes(attributes).build();
|
||||
when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest);
|
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build();
|
||||
when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse);
|
||||
|
||||
OAuth2User oauth2User = TestOAuth2Users.create();
|
||||
when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User);
|
||||
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("code", "code123");
|
||||
params.add("state", authorizationRequest.getState());
|
||||
this.mvc.perform(get("/login/oauth2/code/" + clientRegistration.getRegistrationId()).params(params));
|
||||
|
||||
verify(clientRegistrationRepository).findByRegistrationId(clientRegistration.getRegistrationId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenCustomAuthorizedClientRepositoryThenCalled() throws Exception {
|
||||
this.spring.configLocations(this.xml("WithCustomAuthorizedClientRepository")).autowire();
|
||||
|
||||
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();
|
||||
when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(clientRegistration);
|
||||
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());
|
||||
OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()
|
||||
.attributes(attributes).build();
|
||||
when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest);
|
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build();
|
||||
when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse);
|
||||
|
||||
OAuth2User oauth2User = TestOAuth2Users.create();
|
||||
when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User);
|
||||
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("code", "code123");
|
||||
params.add("state", authorizationRequest.getState());
|
||||
this.mvc.perform(get("/login/oauth2/code/" + clientRegistration.getRegistrationId()).params(params));
|
||||
|
||||
verify(authorizedClientRepository).saveAuthorizedClient(any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenCustomAuthorizedClientServiceThenCalled() throws Exception {
|
||||
this.spring.configLocations(this.xml("WithCustomAuthorizedClientService")).autowire();
|
||||
|
||||
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();
|
||||
when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(clientRegistration);
|
||||
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());
|
||||
OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()
|
||||
.attributes(attributes).build();
|
||||
when(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any())).thenReturn(authorizationRequest);
|
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponse().build();
|
||||
when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse);
|
||||
|
||||
OAuth2User oauth2User = TestOAuth2Users.create();
|
||||
when(this.oauth2UserService.loadUser(any())).thenReturn(oauth2User);
|
||||
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("code", "code123");
|
||||
params.add("state", authorizationRequest.getState());
|
||||
this.mvc.perform(get("/login/oauth2/code/" + clientRegistration.getRegistrationId()).params(params));
|
||||
|
||||
verify(authorizedClientService).saveAuthorizedClient(any(), any());
|
||||
}
|
||||
|
||||
private String xml(String configName) {
|
||||
return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml";
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user