1
0
mirror of synced 2026-07-11 22:00:04 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Rob Winch c474fdfe71 rm .github/workflows for unsupported branch 2025-05-02 12:34:56 -05:00
701 changed files with 7395 additions and 47207 deletions
-1
View File
@@ -24,7 +24,6 @@ atlassian-ide-plugin.xml
.checkstyle
s101plugin.state
.attach_pid*
.~lock.*#
!.idea/checkstyle-idea.xml
!.idea/externalDependencies.xml
+1 -1
View File
@@ -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
View File
@@ -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
}
}
}
}
}
-4
View File
@@ -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();
}
}
});
}
}
+3 -10
View File
@@ -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
@@ -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
}
}
@@ -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_")
}
}
}
@@ -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"))
}
}
@@ -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")
}
}
@@ -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;
}
}
@@ -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
}
}
@@ -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"));
}
}
@@ -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
@@ -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";
}
@@ -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.*");
}
@@ -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;
}
/**
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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
@@ -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());
@@ -47,7 +47,6 @@ class SecuritySocketAcceptorInterceptorConfiguration {
}
rsocket
.basicAuthentication(Customizer.withDefaults())
.simpleAuthentication(Customizer.withDefaults())
.authorizePayload(authz ->
authz
.setup().authenticated()
@@ -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.
*
@@ -2331,10 +2331,6 @@ public final class HttpSecurity extends
* &#064;Configuration
* &#064;EnableWebSecurity
* public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
*
* &#064;Value("${spring.security.oauth2.resourceserver.jwt.key-value}")
* RSAPublicKey key;
*
* &#064;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())
* )
* );
* }
*
* &#064;Bean
* public JwtDecoder jwtDecoder() {
* return NimbusJwtDecoder.withPublicKey(this.key).build();
* return JwtDecoders.fromOidcIssuerLocation(issuerUri);
* }
* }
* </pre>
@@ -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
*
@@ -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;
}
@@ -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
@@ -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
}
}
}
@@ -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 &lt;http&gt;
@@ -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;
}
@@ -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,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;
@@ -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);
@@ -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;
}
}
@@ -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;
}
}
}
@@ -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 &lt;http&gt;'s &lt;oauth2-resource-server&gt; 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 &lt;oauth2-resource-server&gt; element and return the corresponding
* {@link BearerTokenAuthenticationFilter}
*
* @param oauth2ResourceServer the &lt;oauth2-resource-server&gt; 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,
@@ -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 + "'";
}
}
@@ -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
@@ -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
}
}
@@ -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()
}
}
}
}
@@ -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()
}
}
}
}
@@ -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()
}
}
}
}
@@ -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)
}
}
}
}
@@ -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()
}
}
}
}
@@ -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/&ast;&ast;")
* 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/&ast;&ast;"))
* 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()
}
}
}
}
@@ -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) }
}
}
}
@@ -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) }
}
}
}
@@ -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) }
}
}
}
@@ -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)
}
}
}
}
@@ -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)
}
}
}
}
@@ -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) }
}
}
}
@@ -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) }
}
}
}
@@ -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()
}
}
}
}
@@ -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()
}
}
}
}
}
@@ -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()
}
}
}
}
@@ -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()
}
}
}
}
@@ -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()
}
}
}
}
@@ -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()
}
}
}
}
@@ -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)
}
}
}
}
@@ -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()
}
}
}
}
@@ -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) }
}
}
}
@@ -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) }
}
}
}
@@ -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) }
}
}
}
@@ -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) }
}
}
}
@@ -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) }
}
}
}
@@ -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) }
}
}
}
@@ -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) }
}
}
}
@@ -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)
}
}
}
}
@@ -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
@@ -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();
}
}
@@ -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)
}
@@ -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)
}
}
@@ -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())
}
}
@@ -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
@@ -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){
}
}
}
@@ -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());
@@ -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);
}
}
}
@@ -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) {
}
}
}
@@ -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);
}
}
}
@@ -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
@@ -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) {
@@ -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);
}
@@ -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)
@@ -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;
}
}
}
@@ -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();
}
}
@@ -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);
}
}
@@ -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");
}
/**
@@ -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";
}
}
@@ -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