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

Compare commits

...

109 Commits

Author SHA1 Message Date
Rob Winch 783e426183 Release 5.0.0.M2 2017-06-15 16:15:57 -05:00
Rob Winch ca6348800e HttpSecurity.authorizeExchange() allows Method Chaining
Fixes gh-4397
2017-06-15 15:50:30 -05:00
Rob Winch fb85ad6bd7 Revert "Work Around SPR-15651"
This reverts commit 6c286696b4.
2017-06-15 15:25:36 -05:00
Rob Winch dcef3d6ebb Remove springIoVersion for release preparations 2017-06-15 13:34:51 -05:00
Rob Winch 89763a5b73 Update to spring-build-conventions-0.0.2.RELEASE 2017-06-15 13:19:20 -05:00
Rob Winch 9d19b7337e Ensure Unique Names
Issue: gh-4394
2017-06-15 13:00:59 -05:00
Rob Winch fda0220fad Provide default reactive HttpSecurity
Fixes gh-4396
2017-06-15 13:00:19 -05:00
Rob Winch 9141a8a7c0 Add Multiple Reactive HttpSecurity
Fixes gh-4395
2017-06-15 13:00:19 -05:00
Rob Winch 406e1e6951 Extract out HttpSecurityConfiguration
Fixes gh-4394
2017-06-15 13:00:19 -05:00
Rob Winch 2deb1cda9f Delete SecurityWebFilterChain in favor of DefaultWebFilterChain
Fixesh gh-4393
2017-06-15 13:00:19 -05:00
stonio dbda7e35a4 Update index.adoc - add missing callout
To fix WARNING: index.adoc: line 988: no callouts refer to list item 1
2017-06-15 12:49:20 -05:00
stonio 335a01577a Typo "he" -> "the" 2017-06-15 12:47:41 -05:00
Matt Raible 3624ba5b4e Remove Okta Add Authorization Server section
Fixes gh-4391
2017-06-15 11:51:56 -04:00
Rob Winch a34bf01754 HelloWebfluxApplicationTests do not start a server 2017-06-13 15:16:01 -05:00
Rob Winch 30132892a0 Polish UserDetailsResourceFactoryBean Support
Issues: gh-4380 gh-4381 gh-4382
2017-06-13 15:15:21 -05:00
Rob Winch 6db418f47b Use RequestPath
Fixes gh-4389
2017-06-12 12:42:15 -05:00
Rob Winch 6c286696b4 Work Around SPR-15651
Issue gh-4386
2017-06-09 22:26:02 -05:00
Rob Winch 337317a060 WebFlux now uses ParsingPathMatcher
Fixes gh-4388
2017-06-09 22:25:45 -05:00
Rob Winch 554768f1e4 Remove use of HttpRequestPathHelper
Fixes gh-4387
2017-06-09 22:25:11 -05:00
Rob Winch 6428cb411e Add UserDetailsRepositoryResourceFactoryBean
Add the ability to easily create a UserDetailsRepository from a Properties
in the standard Spring Security user format.

Fixes gh-4382
2017-06-09 16:07:18 -05:00
Rob Winch 4cb77e5386 Add UserDetailsManagerResourceFactoryBean
Add the ability to easily create a UserDetailsManager from a Properties
in the standard Spring Security user format.

Fixes gh-4381
2017-06-09 16:07:18 -05:00
Rob Winch 256d14ede0 Add UserDetailsResourceFactoryBean
Add the ability to create a Collection<UserDetails> from a Properties
Resource using the standard Spring Security user format.

Fixes gh-4380
2017-06-09 16:07:18 -05:00
Rob Winch d09fb5b500 Move UserDetailsRepository to core.userdetails
Fixes gh-4383
2017-06-09 16:07:09 -05:00
Rob Winch fd244eb95c Add Artifactory Deploy to Jenkinsfile 2017-06-08 17:26:13 -05:00
Rob Winch 32751a2ce7 Update to spring-build-conventions 0.0.2.BUILD-SNAPSHOT 2017-06-08 17:26:11 -05:00
Joe Grandja eb6bd9bea9 Update groupId io.projectreactor.addons -> io.projectreactor
Fixes gh-4377
2017-06-08 14:06:51 -04:00
Rob Winch 5dee8534cd Update SecurityJackson2Modules
Fixes gh-4370
2017-06-07 23:05:13 -05:00
Thai Ha 489ffcfce5 UserInfoTypeConverter is registered if client is registered
Fixes gh-4364
2017-06-02 16:03:33 -04:00
Joe Grandja 6c0ecea494 Use java.util.Function instead of Converter
Fixes gh-4323
2017-06-01 17:25:39 -04:00
Rob Winch ac6a86fb5f Fixes for changes in SPR-15397 2017-06-01 14:26:27 -05:00
Joe Grandja ae17cc255b Implement protected resource call flow in oauth2login sample
Fixes gh-4362
2017-06-01 14:32:29 -04:00
Rob Winch e5eda24054 Add ServerWebExchangeMatcherEntry 2017-05-31 16:13:20 -05:00
Rob Winch 915de03f42 Polish ExchangeMutatorWebFilter Support
Issue gh-4343
2017-05-31 13:23:55 -05:00
Rob Winch 6aa7f05579 Polish ExchangeMutatorWebFilter usage 2017-05-30 16:37:21 -05:00
Joe Grandja 545339c663 Change AuthorizationGrantType from enum to class
Fixes gh-4291
2017-05-30 16:22:53 -04:00
Joe Grandja 4476df93e9 Change ResponseType from enum to class
Fixes gh-4292
2017-05-30 16:11:57 -04:00
Joe Grandja 336e247e70 Change AccessToken.TokenType from enum to class
Fixes gh-4293
2017-05-30 15:50:58 -04:00
Joe Grandja 435e389609 Change ClientAuthenticationMethod from enum to class
Fixes gh-4313
2017-05-30 14:41:59 -04:00
Joe Grandja e5ceeb4e22 Resolve compile errors -> MockServerExchangeMutator removed
Fixes gh-4356
2017-05-26 07:17:32 -04:00
Joe Grandja 68368c87ca Resolve compile errors -> WebTestClient methods removed
Fixes gh-4355
2017-05-25 11:14:29 -04:00
Joe Grandja 86d0fd77a7 Fix HelloWebflux tests
Fixes gh-4353
2017-05-24 21:27:09 -04:00
Matt Raible dd8952f700 Update Okta instructions in oauth2Login sample
Fixes gh-4352
2017-05-24 16:15:53 -04:00
Joe Grandja 3ccf6764c1 Handle unsuccessful UserInfo response
Fixes gh-4351
2017-05-24 15:43:21 -04:00
Joe Grandja 521feb9a1b Update Boot samples to 2.0.0.M1
Fixes gh-4339
2017-05-24 11:32:00 -04:00
Rob Winch bc141febdb Demo mock support with RouterFunction 2017-05-23 16:29:30 -05:00
Rob Winch a79a81cd24 Simplify webflux samples
Remove the custom user from the hellowebflux and hellowebfluxfn samples.
2017-05-23 15:59:16 -05:00
Rob Winch 3440909fc9 ServerWebExchangeMatcher returns Mono<MatchResult> 2017-05-23 15:32:12 -05:00
Rob Winch 39f7a14126 Merge pull request #4347 from thomasdarimont/issue/fix-javadoc-for-HeadersConfigurer
Fix JavaDoc for HeadersConfigurer
2017-05-22 10:08:35 -05:00
Thomas Darimont 9e6b10ce46 Fix JavaDoc for HeadersConfigurer
Corrected copy-paste error.
2017-05-22 00:32:19 +02:00
Rob Winch 247635ed92 WebFluxSecurityConfiguration defaults HTTP Basic
Fixes gh-4346
2017-05-19 21:50:06 -05:00
Rob Winch 1cec497a50 Add method chaining for AuthorizeExchangeBuilder
Fixes gh-4345
2017-05-19 21:25:50 -05:00
Rob Winch 0428cdd934 Add @EnableWebFluxSecurity
Fixes gh-4344
2017-05-19 21:11:42 -05:00
Rob Winch 7bc98db23c Add WebTestClient test support
SecurityExchangeMutators

Fixes gh-4343
2017-05-19 15:09:23 -05:00
Rob Winch 7b703a3b98 SecurityContextRepositoryServerWebExchange defaults to principal
This is necessary for the test support to work

Issue gh-4343
2017-05-19 14:41:20 -05:00
Rob Winch 3c7c256fc9 WebTestHandler now returns wrapped ServerWebExchange
For easier testing WebTestHandler now obtains the wrapped
ServerWebExchange objects.
2017-05-19 08:48:58 -05:00
Rob Winch d9c7f42b4c Rename Application->HelloWebFluxApplication 2017-05-17 15:34:07 -05:00
Rob Winch 93dd10917c Update to Boot 2.0.0.M1
Fixes gh-4339
2017-05-16 15:54:58 -05:00
Rob Winch f01989ff49 Add hellowebfluxfn
Fixes gh-4338
2017-05-16 15:39:39 -05:00
Rob Winch 07234f6255 Switch back to Spring Framework 5.0.0.BUILD-SNAPSHOT 2017-05-16 15:12:09 -05:00
Rob Winch 22c1685d70 Use AssertionsForClassType 2017-05-15 13:48:12 -05:00
Rob Winch 7b8ed826b8 Build against Cairo SNAPSHOTs again 2017-05-15 13:47:53 -05:00
Rob Winch e5f2e72843 Sort gradle.properties 2017-05-15 13:32:43 -05:00
Rob Winch e631805635 Remove RedirectMatcher
This is no longer necessary now that Spring 5 is the minimum version.

Fixes gh-4092
2017-05-11 14:38:18 -05:00
Rob Winch d81b436e5d Remove pom.xml from build
Gradle is easy enough to import into IDEs, so pom.xml should no
longer be necessary.

This commit removes the pom.xml files from the build.

Fixes gh-4283
2017-05-11 14:32:36 -05:00
Rob Winch 0e4fd349a9 Next developmenet version 2017-05-10 01:09:35 -05:00
Rob Winch f279346dac Release 5.0.0.M1 2017-05-10 01:08:50 -05:00
Rob Winch c05fc951d8 Disable OSSRH Deploy
For the release disable OSSRH Deploy since we will release only
to Artifactory for the milestone. We cannot deploy to OSSRH because
we have RC dependencies that are not in Maven Central.
2017-05-10 00:30:35 -05:00
Rob Winch 221d4f8fb1 Document WebFlux What Is New
Issue gh-4325
2017-05-10 00:28:16 -05:00
Vedran Pavic e9427e421b Update Spring version 2017-05-10 00:27:36 -05:00
Vedran Pavic 85719fcd64 Use Base64 implementation provided by Java 8 2017-05-10 00:27:36 -05:00
Rob Winch fe37d0f5f9 Update to spring-build-conventions 0.0.1.RELEASE 2017-05-10 00:14:47 -05:00
Rob Winch b4f2777755 Add WebFlux
Fixes gh-4128
2017-05-10 00:13:02 -05:00
Rob Winch 051e3fb079 Add UserBuilders.withUserDetails 2017-05-10 00:12:12 -05:00
Rob Winch 14f5ebcc3b Update to Spring Data Kay M3 2017-05-09 09:37:28 -05:00
Rob Winch 4fdf343045 Remove springIoVersion 2017-05-09 02:39:49 -05:00
Rob Winch 514f0053cc Use dependency-management.gradle 2017-05-09 02:39:49 -05:00
Rob Winch 5dcb6e37b1 Update to boot 1.5.3.RELEASE 2017-05-09 02:35:46 -05:00
Rob Winch 861e7994ff crypto uses spring-jcl 2017-05-09 02:35:46 -05:00
Rob Winch 5c7ff5c24a Update to spring 5.0.0.RC1 2017-05-09 02:35:46 -05:00
Joe Grandja a458b682d6 Add package/class level javadoc in oauth2-client
Fixes gh-4295
2017-05-04 12:37:35 -04:00
Joe Grandja d04bfaca5a Update oauth2login README with GitHub/Facebook/Okta
* Add instructions to the oauth2login sample README for setting up
OAuth 2.0 Login for GitHub, Facebook, and Okta
* Change base package for oauth2login sample

Fixes gh-4287, Fixes gh-4288, Fixes gh-4289
2017-05-04 12:37:35 -04:00
Rob Winch 9d2ba5be42 Add spock dependencies to dependency-management.properties
Spring Boot removed sock from its dependency management via
https://github.com/spring-projects/spring-boot/issues/9043

This commit adds spock to this project's dependency management
2017-05-04 10:10:40 -05:00
Rob Winch 021ee0d317 Spring Data findOne->findById 2017-05-04 09:38:03 -05:00
Rob Winch a199819acb Merge pull request #4319 from rwinch/tests-configuration
Build Leverages tests-configuration plugin
2017-05-01 21:01:15 -05:00
Rob Winch 18e2457ddf Polish Travis
* Add --refresh-dependencies to .travis.yml
* Add --no-daemon to .travis.yml
2017-05-01 20:31:01 -05:00
Rob Winch e1ef0477fb Build Leverages tests-configuration plugin 2017-05-01 17:10:20 -05:00
Rob Winch d57b0b11ab Merge pull request #4317 from rwinch/travis-logs
Polish Logging for Tests
2017-05-01 14:45:00 -05:00
Rob Winch 7fcc3f58e4 Polish Logging for Tests
This should drastically reduce the log size for integration tests
fixing the build for Travis which limits the console output.
2017-05-01 14:00:40 -05:00
Rob Winch c1d399b300 Polish MavenBomPlugin 2017-04-28 13:23:18 -05:00
Joe Grandja 829c386756 Add support for OAuth 2.0 Login
Fixes gh-3907
2017-04-28 10:58:59 -04:00
Rob Winch a38352c4cc Use plugins-snapshot 2017-04-25 15:46:52 -05:00
Rob Winch d108bf58bf Remove commons-logging
Issue: gh-4308
2017-04-24 19:40:22 -05:00
Rob Winch 85d244598c Use spring-build-conventions:0.0.1.BUILD-SNAPSHOT 2017-04-21 21:43:54 -05:00
Rob Winch 014f3c8f21 Add Jenkinsfile
We should add a Jenkinsfile so we can build the project in GitHub

Fixes gh-4306
2017-04-21 10:55:05 -05:00
Rob Winch dd6fc48dd8 Standardize Build
The build now uses spring build conventions to simplify the build

Fixes gh-4284
2017-04-21 10:55:05 -05:00
Rob Winch 5a65da400d Use ReflectionTestUtils rather than Whitebox
This is better because it no longer uses Mockito's internal API

Fixes gh-4305
2017-04-21 10:54:58 -05:00
Rob Winch 5ba051aa70 Reset browser to accomidate every domain
Fixes gh-4304
2017-04-21 10:52:31 -05:00
Rob Winch 366c371912 Update to Gradle 3.5
Fixes gh-4303
2017-04-21 10:51:49 -05:00
Rob Winch c5fedb6f8d Remove unused sandbox projects
Fixes gh-4302
2017-04-21 10:51:35 -05:00
Rob Winch d59f128210 Fix asciidoc in manual
Fixes gh-4301
2017-04-21 10:50:30 -05:00
Rob Winch 59a0ba285f Fix Sonar Issues
Fixes gh-4300
2017-04-21 10:49:59 -05:00
Rob Winch 9d9aadb80f Fix DefaultSavedRequestMixinTests with Spring 5
Previously DefaultSavedRequestMixinTests
serializeDefaultRequestBuildWithConstructorTest broke in Spring 5
because Spring 5's MockHttpServletRequest.setCookie now automatically adds
the Cookie header.

This commit ensures that the Cookie header is not added by overriding the
class we are writing.

Fixes gh-4272
2017-04-12 15:51:26 -05:00
Joe Grandja 71e491fcf0 Provide abstraction for an Authenticated Principal 2017-04-10 16:18:40 -04:00
Joe Grandja 2ce174dbf0 Update poms to 5.0.0.BUILD-SNAPSHOT 2017-04-07 16:49:50 -04:00
Joe Grandja 2b81983f7c Update to Java 8 compatibility
* Spring IO Athens-BUILD-SNAPSHOT -> Cairo-BUILD-SNAPSHOT
* CGLib 3.1 -> 3.2.5 latest release Issue related to ASM https://github.com/cglib/cglib/issues/20
* AssertJ 2.2.0 -> 3.6.2 latest release
* PowerMock 1.6.2 -> 1.6.5 latest release is 1.6.6 but has regression Issue https://github.com/powermock/powermock/issues/717
* Update maven-compiler-plugin source/target to 1.8
2017-04-07 16:49:38 -04:00
Rob Winch 8a05548dcb Use Spring IO Brussels-SR1 2017-03-21 14:50:21 -05:00
Rob Winch 4f265c8fbc Update taglib version 2017-03-08 19:49:25 -06:00
Rob Winch 3ca78df15f Fix Security version Test 2017-03-08 19:12:39 -06:00
Rob Winch 78c5c733e5 Update to 5.0.0.BUILD-SNAPSHOT 2017-03-08 16:06:08 -06:00
473 changed files with 17955 additions and 15734 deletions
+1 -1
View File
@@ -13,4 +13,4 @@ cache:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
script: ./gradlew build
script: ./gradlew build --refresh-dependencies --no-daemon
Vendored
+113
View File
@@ -0,0 +1,113 @@
def projectProperties = [
[$class: 'BuildDiscarderProperty',
strategy: [$class: 'LogRotator', numToKeepStr: '5']],
pipelineTriggers([cron('@daily')])
]
properties(projectProperties)
def SUCCESS = hudson.model.Result.SUCCESS.toString()
currentBuild.result = SUCCESS
try {
parallel check: {
stage('Check') {
node {
checkout scm
try {
sh "./gradlew clean check --refresh-dependencies --no-daemon"
} catch(Exception e) {
currentBuild.result = 'FAILED: check'
throw e
} finally {
junit '**/build/*-results/*.xml'
}
}
}
},
sonar: {
stage('Sonar') {
node {
checkout scm
withCredentials([string(credentialsId: 'spring-sonar.login', variable: 'SONAR_LOGIN')]) {
try {
sh "./gradlew clean sonarqube -PexcludeProjects='**/samples/**' -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon"
} catch(Exception e) {
currentBuild.result = 'FAILED: sonar'
throw e
}
}
}
}
},
springio: {
stage('Spring IO') {
node {
checkout scm
try {
sh "./gradlew clean springIoCheck -PplatformVersion=Cairo-BUILD-SNAPSHOT -PexcludeProjects='**/samples/**' --refresh-dependencies --no-daemon --stacktrace"
} catch(Exception e) {
currentBuild.result = 'FAILED: springio'
throw e
} finally {
junit '**/build/spring-io*-results/*.xml'
}
}
}
}
if(currentBuild.result == 'SUCCESS') {
parallel artifactory: {
stage('Artifactory Deploy') {
node {
checkout scm
withCredentials([usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')]) {
sh "./gradlew check artifactoryPublish -x check -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --no-daemon --stacktrace"
}
}
}
},
docs: {
stage('Deploy Docs') {
node {
checkout scm
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
sh "./gradlew deployDocs -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace"
}
}
}
},
schema: {
stage('Deploy Schema') {
node {
checkout scm
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
sh "./gradlew deploySchema -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace"
}
}
}
}
}
} finally {
def buildStatus = currentBuild.result
def buildNotSuccess = !SUCCESS.equals(buildStatus)
def lastBuildNotSuccess = !SUCCESS.equals(currentBuild.previousBuild?.result)
if(buildNotSuccess || lastBuildNotSuccess) {
stage('Notifiy') {
node {
final def RECIPIENTS = [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']]
def subject = "${buildStatus}: Build ${env.JOB_NAME} ${env.BUILD_NUMBER} status is now ${buildStatus}"
def details = """The build status changed to ${buildStatus}. For details see ${env.BUILD_URL}"""
emailext (
subject: subject,
body: details,
recipientProviders: RECIPIENTS,
to: "$SPRING_SECURITY_TEAM_EMAILS"
)
}
}
}
}
-19
View File
@@ -1,19 +0,0 @@
// Acl Module build file
dependencies {
compile project(':spring-security-core'),
springCoreDependency,
'aopalliance:aopalliance:1.0',
"org.springframework:spring-aop:$springVersion",
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-tx:$springVersion",
"org.springframework:spring-jdbc:$springVersion"
optional "net.sf.ehcache:ehcache:$ehcacheVersion"
testCompile "org.springframework:spring-beans:$springVersion",
"org.springframework:spring-context-support:$springVersion",
"org.springframework:spring-test:$springVersion"
testRuntime "org.hsqldb:hsqldb:$hsqlVersion"
}
-171
View File
@@ -1,171 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<name>spring-security-acl</name>
<description>spring-security-acl</description>
<url>http://spring.io/spring-security</url>
<organization>
<name>spring.io</name>
<url>http://spring.io/</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>rwinch</id>
<name>Rob Winch</name>
<email>rwinch@gopivotal.com</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
<url>https://github.com/spring-projects/spring-security</url>
</scm>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.9.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
+19
View File
@@ -0,0 +1,19 @@
apply plugin: 'io.spring.convention.spring-module'
dependencies {
compile project(':spring-security-core')
compile 'aopalliance:aopalliance'
compile 'org.springframework:spring-aop'
compile 'org.springframework:spring-context'
compile 'org.springframework:spring-core'
compile 'org.springframework:spring-jdbc'
compile 'org.springframework:spring-tx'
optional 'net.sf.ehcache:ehcache'
testCompile 'org.springframework:spring-beans'
testCompile 'org.springframework:spring-context-support'
testCompile 'org.springframework:spring-test'
testRuntime 'org.hsqldb:hsqldb'
}
-10
View File
@@ -1,10 +0,0 @@
dependencies {
compile project(':spring-security-core'),
springCoreDependency,
"org.springframework:spring-beans:$springVersion",
"org.springframework:spring-context:$springVersion"
testCompile 'aopalliance:aopalliance:1.0',
"org.springframework:spring-aop:$springVersion"
}
-150
View File
@@ -1,150 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<name>spring-security-aspects</name>
<description>spring-security-aspects</description>
<url>http://spring.io/spring-security</url>
<organization>
<name>spring.io</name>
<url>http://spring.io/</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>rwinch</id>
<name>Rob Winch</name>
<email>rwinch@gopivotal.com</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
<url>https://github.com/spring-projects/spring-security</url>
</scm>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.4</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
+12
View File
@@ -0,0 +1,12 @@
apply plugin: 'io.spring.convention.spring-module'
apply plugin: 'aspectj'
dependencies {
compile project(':spring-security-core')
compile 'org.springframework:spring-beans'
compile 'org.springframework:spring-context'
compile 'org.springframework:spring-core'
testCompile 'aopalliance:aopalliance'
testCompile 'org.springframework:spring-aop'
}
-9
View File
@@ -1,9 +0,0 @@
apply plugin: 'maven-bom'
apply from: "$rootDir/gradle/maven-deployment.gradle"
generatePom.enabled = false
sonarqube.skipProject = true
mavenBom {
projects = coreModuleProjects
}
+9
View File
@@ -0,0 +1,9 @@
apply plugin: 'maven-bom'
sonarqube.skipProject = true
project.rootProject.allprojects.each { p ->
p.plugins.withType(io.spring.gradle.convention.SpringMavenPlugin) {
project.mavenBom.projects.add(p)
}
}
+11 -184
View File
@@ -1,192 +1,19 @@
buildscript {
repositories {
maven { url "https://repo.spring.io/plugins-release" }
maven { url "https://repo.spring.io/plugins-snapshot" }
}
dependencies {
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.7")
classpath("io.spring.gradle:spring-io-plugin:0.0.6.RELEASE")
classpath("com.bmuschko:gradle-tomcat-plugin:2.2.4")
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
classpath('org.asciidoctor:asciidoctor-gradle-plugin:1.5.1')
classpath("io.spring.gradle:docbook-reference-plugin:0.3.1")
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.0.BUILD-SNAPSHOT")
classpath 'io.spring.gradle:spring-build-conventions:0.0.2.RELEASE'
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
}
repositories {
maven { url 'https://repo.spring.io/plugins-snapshot' }
}
}
apply plugin: 'io.spring.convention.root'
plugins {
id "org.sonarqube" version "2.1-rc1"
}
apply plugin: 'base'
group = 'org.springframework.security'
description = 'Spring Security'
allprojects {
apply plugin: 'idea'
apply plugin: 'eclipse'
ext.snapshotBuild = version.contains("SNAPSHOT")
ext.releaseBuild = version.contains("SNAPSHOT")
ext.milestoneBuild = !(snapshotBuild || releaseBuild)
ext.releaseBuild = version.endsWith('RELEASE')
ext.snapshotBuild = version.endsWith('SNAPSHOT')
ext.springVersion = '4.3.5.RELEASE'
ext.springLdapVersion = '2.2.0.RELEASE'
group = 'org.springframework.security'
repositories {
mavenCentral()
maven { url "https://repo.spring.io/libs-snapshot" }
maven { url "https://repo.spring.io/plugins-release" }
maven { url "http://repo.terracotta.org/maven2/" }
}
}
sonarqube {
properties {
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.projectName", "Spring Security"
property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec"
property "sonar.links.homepage", 'https://www.springsource.org/spring-security'
property "sonar.links.ci", 'https://build.springsource.org/browse/SEC-B32X'
property "sonar.links.issue", 'https://jira.springsource.org/browse/SEC'
property "sonar.links.scm", 'https://github.com/SpringSource/spring-security'
property "sonar.links.scm_dev", 'https://github.com/SpringSource/spring-security.git'
property "sonar.java.coveragePlugin", "jacoco"
}
}
// Set up different subproject lists for individual configuration
ext.javaProjects = subprojects.findAll { project -> project.name != 'docs' && project.name != 'manual' && project.name != 'guides' && project.name != 'spring-security-bom' }
ext.sampleProjects = subprojects.findAll { project -> project.name.startsWith('spring-security-samples') }
ext.itestProjects = subprojects.findAll { project -> project.name.startsWith('itest') }
ext.coreModuleProjects = javaProjects - sampleProjects - itestProjects
ext.aspectjProjects = [project(':spring-security-aspects'), project(':spring-security-samples-xml-aspectj'), project(':spring-security-samples-javaconfig-aspectj')]
configure(allprojects - javaProjects) {
task afterEclipseImport {
ext.srcFile = file('.classpath')
inputs.file srcFile
outputs.dir srcFile
onlyIf { !srcFile.exists() }
doLast {
srcFile << """<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>
"""
}
}
}
configure(subprojects - coreModuleProjects - project(':spring-security-samples-javaconfig-messages') - project(':spring-security-bom')) {
tasks.findByPath("artifactoryPublish")?.enabled = false
sonarqube {
skipProject = true
}
}
configure(javaProjects) {
ext.TOMCAT_GRADLE = "$rootDir/gradle/tomcat.gradle"
ext.WAR_SAMPLE_GRADLE = "$rootDir/gradle/war-sample.gradle"
ext.BOOT_SAMPLE_GRADLE = "$rootDir/gradle/boot-sample.gradle"
apply from: "$rootDir/gradle/javaprojects.gradle"
if(!project.name.contains('gae')) {
apply from: "$rootDir/gradle/checkstyle.gradle"
}
apply from: "$rootDir/gradle/ide.gradle"
apply from: "$rootDir/gradle/release-checks.gradle"
apply from: "$rootDir/gradle/maven-deployment.gradle"
}
configure(coreModuleProjects) {
apply plugin: 'emma'
apply plugin: 'spring-io'
ext.springIoVersion = project.hasProperty('platformVersion') ? platformVersion : 'Athens-BUILD-SNAPSHOT'
configurations {
jacoco //Configuration Group used by Sonar to provide Code Coverage using JaCoCo
}
dependencyManagement {
springIoTestRuntime {
imports {
mavenBom("io.spring.platform:platform-bom:${springIoVersion}") {
bomProperties([
'mockito.version': '1.10.19'
])
}
}
}
}
dependencies {
jacoco "org.jacoco:org.jacoco.agent:0.7.5.201505241946:runtime"
}
test {
jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=${project.group}.*"
}
integrationTest {
jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=${project.group}.*"
}
}
configure (aspectjProjects) {
apply plugin: 'java'
apply plugin: 'aspectj'
}
task coreBuild {
dependsOn coreModuleProjects*.tasks*.matching { task -> task.name == 'build' }
}
task coreInstall {
dependsOn coreModuleProjects*.tasks*.matching { task -> task.name == 'install' }
}
// Task for creating the distro zip
task dist(type: Zip) {
dependsOn { subprojects*.tasks*.matching { task -> task.name.endsWith('generatePom') } }
classifier = 'dist'
evaluationDependsOn(':docs')
evaluationDependsOn(':docs:manual')
def zipRootDir = "${project.name}-$version"
into(zipRootDir) {
from(rootDir) {
include '*.adoc'
include '*.txt'
}
into('docs') {
with(project(':docs').apiSpec)
with(project(':docs:manual').spec)
with(project(':docs:guides').spec)
}
project.coreModuleProjects*.tasks*.withType(AbstractArchiveTask).flatten().each{ archiveTask ->
if(archiveTask!=dist){
into("$zipRootDir/dist") {
from archiveTask.outputs.files
}
}
}
sampleProjects.each { project->
into("$zipRootDir/samples/$project.name") {
from(project.projectDir) {
include "src/main/**"
include "pom.xml"
}
}
}
}
}
artifacts {
archives dist
archives project(':docs').docsZip
archives project(':docs').schemaZip
}
dependencyManagementExport.projects = subprojects.findAll { !it.name.contains('-boot') }
@@ -13,7 +13,9 @@ public class MavenBomPlugin implements Plugin<Project> {
public void apply(Project project) {
project.plugins.apply(JavaPlugin)
project.plugins.apply(MavenPlugin)
project.group = project.rootProject.group
project.task(MAVEN_BOM_TASK_NAME, type: MavenBomTask, group: 'Generate', description: 'Configures the pom as a Maven Build of Materials (BOM)')
project.install.dependsOn project.mavenBom
}
}
}
+2 -2
View File
@@ -6,7 +6,7 @@ import org.gradle.api.tasks.*
public class MavenBomTask extends DefaultTask {
Set<Project> projects
Set<Project> projects = []
File bomFile
@@ -52,4 +52,4 @@ public class MavenBomTask extends DefaultTask {
}
}
}
}
}
@@ -26,15 +26,11 @@ class AspectJPlugin implements Plugin<Project> {
void apply(Project project) {
project.plugins.apply(JavaPlugin)
if (!project.hasProperty('aspectjVersion')) {
throw new GradleException("You must set the property 'aspectjVersion' before applying the aspectj plugin")
}
if (project.configurations.findByName('ajtools') == null) {
project.configurations.create('ajtools')
project.dependencies {
ajtools "org.aspectj:aspectjtools:${project.aspectjVersion}"
optional "org.aspectj:aspectjrt:${project.aspectjVersion}"
ajtools "org.aspectj:aspectjtools"
optional "org.aspectj:aspectjrt"
}
}
-17
View File
@@ -1,17 +0,0 @@
dependencies {
compile project(':spring-security-core'),
project(':spring-security-web'),
springCoreDependency,
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-beans:$springVersion",
"org.springframework:spring-web:$springVersion",
"org.jasig.cas.client:cas-client-core:$casClientVersion"
optional "net.sf.ehcache:ehcache:$ehcacheVersion",
"com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion"
testCompile "org.skyscreamer:jsonassert:$jsonassertVersion"
provided "javax.servlet:javax.servlet-api:$servletApiVersion"
}
-175
View File
@@ -1,175 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<name>spring-security-cas</name>
<description>spring-security-cas</description>
<url>http://spring.io/spring-security</url>
<organization>
<name>spring.io</name>
<url>http://spring.io/</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>rwinch</id>
<name>Rob Winch</name>
<email>rwinch@gopivotal.com</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
<url>https://github.com/spring-projects/spring-security</url>
</scm>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.4.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.4</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.9.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
+18
View File
@@ -0,0 +1,18 @@
apply plugin: 'io.spring.convention.spring-module'
dependencies {
compile project(':spring-security-core')
compile project(':spring-security-web')
compile 'org.jasig.cas.client:cas-client-core'
compile 'org.springframework:spring-beans'
compile 'org.springframework:spring-context'
compile 'org.springframework:spring-core'
compile 'org.springframework:spring-web'
optional 'com.fasterxml.jackson.core:jackson-databind'
optional 'net.sf.ehcache:ehcache'
provided 'javax.servlet:javax.servlet-api'
testCompile 'org.skyscreamer:jsonassert'
}
-77
View File
@@ -1,77 +0,0 @@
import javax.security.auth.login.ConfigurationSpi;
// Config Module build file
apply plugin: 'groovy'
apply plugin: 'trang'
compileTestJava.dependsOn(':spring-security-core:compileTestJava')
dependencies {
// NB: Don't add other compile time dependencies to the config module as this breaks tooling
compile project(':spring-security-core'),
springCoreDependency,
'aopalliance:aopalliance:1.0',
"org.springframework:spring-aop:$springVersion",
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-beans:$springVersion"
optional project(':spring-security-web'),
project(':spring-security-ldap'),
project(':spring-security-openid'),
project(':spring-security-messaging'),
"org.springframework:spring-web:$springVersion",
"org.springframework:spring-websocket:$springVersion",
"org.springframework:spring-webmvc:$springVersion",
"org.aspectj:aspectjweaver:$aspectjVersion",
"org.springframework:spring-jdbc:$springVersion",
"org.springframework:spring-tx:$springVersion"
provided "javax.servlet:javax.servlet-api:$servletApiVersion"
testCompile project(':spring-security-cas'),
project(':spring-security-core').sourceSets.test.output,
project(':spring-security-aspects'),
'javax.annotation:jsr250-api:1.0',
"org.springframework.ldap:spring-ldap-core:$springLdapVersion",
"org.springframework:spring-expression:$springVersion",
"org.springframework:spring-jdbc:$springVersion",
"org.springframework:spring-orm:$springVersion",
"org.springframework:spring-tx:$springVersion",
"org.slf4j:jcl-over-slf4j:$slf4jVersion",
"org.eclipse.persistence:javax.persistence:$javaPersistenceVersion",
"org.hibernate:hibernate-entitymanager:$hibernateVersion",
"org.codehaus.groovy:groovy-all:$groovyVersion",
"org.apache.directory.server:apacheds-core:$apacheDsVersion",
"org.apache.directory.server:apacheds-core-entry:$apacheDsVersion",
"org.apache.directory.server:apacheds-protocol-shared:$apacheDsVersion",
"org.apache.directory.server:apacheds-protocol-ldap:$apacheDsVersion",
"org.apache.directory.server:apacheds-server-jndi:$apacheDsVersion",
'org.apache.directory.shared:shared-ldap:0.9.15',
'ldapsdk:ldapsdk:4.1',
powerMockDependencies,
"org.hsqldb:hsqldb:$hsqlVersion",
spockDependencies
testCompile('org.openid4java:openid4java-nodeps:0.9.6') {
exclude group: 'com.google.code.guice', module: 'guice'
}
testCompile("org.springframework.data:spring-data-jpa:$springDataJpaVersion") {
exclude group: 'org.aspectj', module: 'aspectjrt'
}
testRuntime "org.hsqldb:hsqldb:$hsqlVersion",
"cglib:cglib-nodep:$cglibVersion"
}
test {
inputs.file file("$rootDir/docs/manual/src/docbook/appendix-namespace.xml")
}
rncToXsd {
rncDir = file('src/main/resources/org/springframework/security/config/')
xsdDir = rncDir
xslFile = new File(rncDir, 'spring-security.xsl')
}
build.dependsOn rncToXsd
-410
View File
@@ -1,410 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<name>spring-security-config</name>
<description>spring-security-config</description>
<url>http://spring.io/spring-security</url>
<organization>
<name>spring.io</name>
<url>http://spring.io/</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>rwinch</id>
<name>Rob Winch</name>
<email>rwinch@gopivotal.com</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
<url>https://github.com/spring-projects/spring-security</url>
</scm>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.4</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-messaging</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-openid</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ldapsdk</groupId>
<artifactId>ldapsdk</artifactId>
<version>4.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core</artifactId>
<version>1.5.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core-entry</artifactId>
<version>1.5.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-protocol-ldap</artifactId>
<version>1.5.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-protocol-shared</artifactId>
<version>1.5.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-jndi</artifactId>
<version>1.5.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.shared</groupId>
<artifactId>shared-ldap</artifactId>
<version>0.9.15</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.0.11.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openid4java</groupId>
<artifactId>openid4java-nodeps</artifactId>
<version>0.9.6</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.code.guice</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-support</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-common</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-reflect</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>0.7-groovy-2.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>junit-dep</artifactId>
<groupId>junit</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>0.7-groovy-2.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>junit-dep</artifactId>
<groupId>junit</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.10.2.RELEASE</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>aspectjrt</artifactId>
<groupId>org.aspectj</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
<version>2.2.0.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
+68
View File
@@ -0,0 +1,68 @@
apply plugin: 'io.spring.convention.spring-module'
apply plugin: 'trang'
dependencies {
// NB: Don't add other compile time dependencies to the config module as this breaks tooling
compile project(':spring-security-core')
compile 'aopalliance:aopalliance:1.0'
compile 'org.springframework:spring-aop'
compile 'org.springframework:spring-beans'
compile 'org.springframework:spring-context'
compile 'org.springframework:spring-core'
optional project(':spring-security-ldap')
optional project(':spring-security-messaging')
optional project(':spring-security-oauth2-client')
optional project(':spring-security-openid')
optional project(':spring-security-web')
optional project(':spring-security-webflux')
optional 'org.aspectj:aspectjweaver'
optional 'org.springframework:spring-jdbc'
optional 'org.springframework:spring-tx'
optional 'org.springframework:spring-webmvc'
optional'org.springframework:spring-web'
optional'org.springframework:spring-websocket'
provided 'javax.servlet:javax.servlet-api'
testCompile project(':spring-security-aspects')
testCompile project(':spring-security-cas')
testCompile project(path : ':spring-security-core', configuration : 'tests')
testCompile project(path : ':spring-security-webflux', configuration : 'tests')
testCompile apachedsDependencies
testCompile powerMockDependencies
testCompile spockDependencies
testCompile 'javax.annotation:jsr250-api:1.0'
testCompile 'ldapsdk:ldapsdk:4.1'
testCompile 'org.codehaus.groovy:groovy-all'
testCompile 'org.eclipse.persistence:javax.persistence'
testCompile 'org.hibernate:hibernate-entitymanager'
testCompile 'org.hsqldb:hsqldb'
testCompile ('org.openid4java:openid4java-nodeps') {
exclude group: 'com.google.code.guice', module: 'guice'
}
testCompile 'org.slf4j:jcl-over-slf4j'
testCompile 'org.springframework.ldap:spring-ldap-core'
testCompile 'org.springframework:spring-expression'
testCompile 'org.springframework:spring-jdbc'
testCompile 'org.springframework:spring-orm'
testCompile 'org.springframework:spring-tx'
testCompile ('org.springframework.data:spring-data-jpa') {
exclude group: 'org.aspectj', module: 'aspectjrt'
}
testRuntime 'cglib:cglib-nodep'
testRuntime 'org.hsqldb:hsqldb'
}
test {
inputs.file file("$rootDir/docs/manual/src/docbook/appendix-namespace.xml")
}
rncToXsd {
rncDir = file('src/main/resources/org/springframework/security/config/')
xsdDir = rncDir
xslFile = new File(rncDir, 'spring-security.xsl')
}
build.dependsOn rncToXsd
@@ -77,6 +77,10 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
order += STEP;
put(LogoutFilter.class, order);
order += STEP;
filterToOrder.put(
"org.springframework.security.oauth2.client.authentication.AuthorizationCodeRequestRedirectFilter",
order);
order += STEP;
put(X509AuthenticationFilter.class, order);
order += STEP;
put(AbstractPreAuthenticatedProcessingFilter.class, order);
@@ -84,6 +88,10 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter",
order);
order += STEP;
filterToOrder.put(
"org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationProcessingFilter",
order);
order += STEP;
put(UsernamePasswordAuthenticationFilter.class, order);
order += STEP;
put(ConcurrentSessionFilter.class, order);
@@ -61,6 +61,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.PortMapperImpl;
@@ -896,6 +897,158 @@ public final class HttpSecurity extends
return getOrApply(new FormLoginConfigurer<HttpSecurity>());
}
/**
* Configures authentication against an external <i>OAuth 2.0</i> or <i>OpenID Connect 1.0</i> Provider.
* <br>
* <br>
*
* The <i>&quot;authentication flow&quot;</i> is realized using the <b>Authorization Code Grant</b>,
* as specified in the <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">OAuth 2.0 Authorization Framework</a>.
* <br>
* <br>
*
* As a prerequisite to using this feature, the developer must register a <i>Client</i> with an <i>Authorization Server</i>.
* The output of the <i>Client Registration</i> process results in a number of properties that are then used for configuring
* an instance of a {@link org.springframework.security.oauth2.client.registration.ClientRegistration}.
* Properties specific to a <i>Client</i> include: <i>client_id</i>, <i>client_secret</i>, <i>scope</i>, <i>redirect_uri</i>, etc.
* There are also properties specific to the <i>Provider</i>, for example,
* <i>Authorization Endpoint URI</i>, <i>Token Endpoint URI</i>, <i>UserInfo Endpoint URI</i>, etc.
* <br>
* <br>
*
* Multiple client support is provided for use cases where the application provides the user the option
* for <i>&quot;Logging in&quot;</i> against one or more Providers, for example, <i>Google</i>, <i>GitHub</i>, <i>Facebook</i>, etc.
* <br>
* <br>
*
* {@link org.springframework.security.oauth2.client.registration.ClientRegistration}(s) are composed within a
* {@link org.springframework.security.oauth2.client.registration.ClientRegistrationRepository}.
* An instance of {@link org.springframework.security.oauth2.client.registration.ClientRegistrationRepository} is <b>required</b>
* and may be supplied via the {@link ApplicationContext} or configured using
* {@link OAuth2LoginConfigurer#clients(org.springframework.security.oauth2.client.registration.ClientRegistrationRepository)}.
* <br>
* <br>
*
* The default configuration provides an auto-generated login page at <code>&quot;/login&quot;</code> and
* redirects to <code>&quot;/login?error&quot;</code> when an authentication error occurs.
* The login page will display each of the clients (composed within the
* {@link org.springframework.security.oauth2.client.registration.ClientRegistrationRepository})
* with an anchor link to <code>&quot;/oauth2/authorization/code/{clientAlias}&quot;</code>.
* Clicking through the link will initiate the <i>&quot;Authorization Request&quot;</i> flow
* redirecting the end-user's user-agent to the <i>Authorization Endpoint</i> of the <i>Provider</i>.
* Assuming the <i>Resource Owner</i> (end-user) grants the <i>Client</i> access, the <i>Authorization Server</i>
* will redirect the end-user's user-agent to the <i>Redirection Endpoint</i> containing the <i>Authorization Code</i>
* - the <i>Redirection Endpoint</i> is automatically configured for the application and
* defaults to <code>&quot;/oauth2/authorize/code/{clientAlias}&quot;</code>.
*
* <p>
* At this point in the <i>&quot;authentication flow&quot;</i>, the configured
* {@link org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger}
* will exchange the <i>Authorization Code</i> for an <i>Access Token</i> and then use it to access the protected resource
* at the <i>UserInfo Endpoint</i> (via {@link org.springframework.security.oauth2.client.user.OAuth2UserService})
* in order to retrieve the details of the <i>Resource Owner</i> (end-user) and establish the <i>&quot;authenticated&quot;</i> session.
*
* <h2>Example Configurations</h2>
*
* The minimal configuration defaults to automatically generating a login page at <code>&quot;/login&quot;</code>
* and redirecting to <code>&quot;/login?error&quot;</code> when an authentication error occurs or redirecting to
* <code>&quot;/&quot;</code> when an authenticated session is established.
*
* <pre>
* &#064;EnableWebSecurity
* public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
*
* &#064;Override
* protected void configure(HttpSecurity http) throws Exception {
* http
* .authorizeRequests()
* .anyRequest().authenticated()
* .and()
* .oauth2Login();
* }
*
* &#064;Bean
* public ClientRegistrationRepository clientRegistrationRepository() {
* // ClientRegistrationRepositoryImpl must be composed of at least one ClientRegistration instance
* return new ClientRegistrationRepositoryImpl();
* }
* }
* </pre>
*
* The following shows the configuration options available for customizing the defaults.
*
* <pre>
* &#064;EnableWebSecurity
* public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
*
* &#064;Override
* protected void configure(HttpSecurity http) throws Exception {
* http
* .authorizeRequests()
* .anyRequest().authenticated()
* .and()
* .oauth2Login()
* .clients(this.clientRegistrationRepository())
* .authorizationRequestBuilder(this.authorizationRequestBuilder())
* .authorizationCodeTokenExchanger(this.authorizationCodeTokenExchanger())
* .userInfoEndpoint()
* .userInfoService(this.userInfoService())
* .userInfoEndpoint()
* // Provide a mapping between a Converter implementation and a UserInfo Endpoint URI
* .userInfoTypeConverter(this.userInfoConverter(),
* new URI("https://www.googleapis.com/oauth2/v3/userinfo"));
* }
*
* &#064;Bean
* public ClientRegistrationRepository clientRegistrationRepository() {
* // ClientRegistrationRepositoryImpl must be composed of at least one ClientRegistration instance
* return new ClientRegistrationRepositoryImpl();
* }
*
* &#064;Bean
* public AuthorizationRequestUriBuilder authorizationRequestBuilder() {
* // Custom URI builder for the &quot;Authorization Request&quot;
* return new AuthorizationRequestUriBuilderImpl();
* }
*
* &#064;Bean
* public AuthorizationGrantTokenExchanger&lt;AuthorizationCodeAuthenticationToken&gt; authorizationCodeTokenExchanger() {
* // Custom implementation that exchanges an &quot;Authorization Code Grant&quot; for an &quot;Access Token&quot;
* return new AuthorizationCodeTokenExchangerImpl();
* }
*
* &#064;Bean
* public OAuth2UserService userInfoService() {
* // Custom implementation that retrieves the details of the authenticated user at the &quot;UserInfo Endpoint&quot;
* return new OAuth2UserServiceImpl();
* }
*
* &#064;Bean
* public Converter&lt;ClientHttpResponse, UserInfo&gt; userInfoConverter() {
* // Default converter implementation for UserInfo
* return new org.springframework.security.oauth2.client.user.converter.UserInfoConverter();
* }
* }
* </pre>
*
* @author Joe Grandja
* @since 5.0
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant Flow</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.1">Section 4.1.1 Authorization Request</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.2">Section 4.1.2 Authorization Response</a>
* @see org.springframework.security.oauth2.client.registration.ClientRegistration
* @see org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
* @see org.springframework.security.oauth2.client.authentication.AuthorizationRequestUriBuilder
* @see org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger
* @see org.springframework.security.oauth2.client.user.OAuth2UserService
*
* @return the {@link OAuth2LoginConfigurer} for further customizations
* @throws Exception
*/
public OAuth2LoginConfigurer<HttpSecurity> oauth2Login() throws Exception {
return getOrApply(new OAuth2LoginConfigurer<HttpSecurity>());
}
/**
* Configures channel security. In order for this configuration to be useful at least
* one mapping to a required channel must be provided.
@@ -213,7 +213,7 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
* Forward Authentication Failure Handler
*
* @param forwardUrl the target URL in case of failure
* @return he {@link FormLoginConfigurer} for additional customization
* @return the {@link FormLoginConfigurer} for additional customization
*/
public FormLoginConfigurer<H> failureForwardUrl(String forwardUrl) {
failureHandler(new ForwardAuthenticationFailureHandler(forwardUrl));
@@ -224,7 +224,7 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
* Forward Authentication Success Handler
*
* @param forwardUrl the target URL in case of success
* @return he {@link FormLoginConfigurer} for additional customization
* @return the {@link FormLoginConfigurer} for additional customization
*/
public FormLoginConfigurer<H> successForwardUrl(String forwardUrl) {
successHandler(new ForwardAuthenticationSuccessHandler(forwardUrl));
@@ -227,7 +227,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends
}
/**
* Allows completing configuration of Strict Transport Security and continuing
* Allows completing configuration of X-XSS-Protection and continuing
* configuration of headers.
*
* @return the {@link HeadersConfigurer} for additional configuration
@@ -282,7 +282,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends
}
/**
* Allows completing configuration of Strict Transport Security and continuing
* Allows completing configuration of Cache Control and continuing
* configuration of headers.
*
* @return the {@link HeadersConfigurer} for additional configuration
@@ -0,0 +1,123 @@
/*
* Copyright 2012-2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationProcessingFilter;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationProvider;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
import org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger;
import org.springframework.security.oauth2.client.authentication.nimbus.NimbusAuthorizationCodeTokenExchanger;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.user.OAuth2UserService;
import org.springframework.security.oauth2.client.user.nimbus.NimbusOAuth2UserService;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
/**
* @author Joe Grandja
*/
final class AuthorizationCodeAuthenticationFilterConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractAuthenticationFilterConfigurer<H, AuthorizationCodeAuthenticationFilterConfigurer<H>, AuthorizationCodeAuthenticationProcessingFilter> {
private AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
private OAuth2UserService userInfoService;
private Map<URI, Function<ClientHttpResponse, ? extends OAuth2User>> userInfoTypeConverters = new HashMap<>();
AuthorizationCodeAuthenticationFilterConfigurer() {
super(new AuthorizationCodeAuthenticationProcessingFilter(), null);
}
AuthorizationCodeAuthenticationFilterConfigurer<H> clientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) {
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
Assert.notEmpty(clientRegistrationRepository.getRegistrations(), "clientRegistrationRepository cannot be empty");
this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
return this;
}
AuthorizationCodeAuthenticationFilterConfigurer<H> authorizationCodeTokenExchanger(
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger) {
Assert.notNull(authorizationCodeTokenExchanger, "authorizationCodeTokenExchanger cannot be null");
this.authorizationCodeTokenExchanger = authorizationCodeTokenExchanger;
return this;
}
AuthorizationCodeAuthenticationFilterConfigurer<H> userInfoService(OAuth2UserService userInfoService) {
Assert.notNull(userInfoService, "userInfoService cannot be null");
this.userInfoService = userInfoService;
return this;
}
AuthorizationCodeAuthenticationFilterConfigurer<H> userInfoTypeConverter(Function<ClientHttpResponse, ? extends OAuth2User> userInfoConverter, URI userInfoUri) {
Assert.notNull(userInfoConverter, "userInfoConverter cannot be null");
Assert.notNull(userInfoUri, "userInfoUri cannot be null");
this.userInfoTypeConverters.put(userInfoUri, userInfoConverter);
return this;
}
String getLoginUrl() {
return super.getLoginPage();
}
String getLoginFailureUrl() {
return super.getFailureUrl();
}
@Override
public void init(H http) throws Exception {
AuthorizationCodeAuthenticationProvider authenticationProvider = new AuthorizationCodeAuthenticationProvider(
this.getAuthorizationCodeTokenExchanger(), this.getUserInfoService());
authenticationProvider = this.postProcess(authenticationProvider);
http.authenticationProvider(authenticationProvider);
super.init(http);
}
@Override
public void configure(H http) throws Exception {
AuthorizationCodeAuthenticationProcessingFilter authFilter = this.getAuthenticationFilter();
authFilter.setClientRegistrationRepository(OAuth2LoginConfigurer.getClientRegistrationRepository(this.getBuilder()));
super.configure(http);
}
@Override
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
return this.getAuthenticationFilter().getAuthorizeRequestMatcher();
}
private AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> getAuthorizationCodeTokenExchanger() {
if (this.authorizationCodeTokenExchanger == null) {
this.authorizationCodeTokenExchanger = new NimbusAuthorizationCodeTokenExchanger();
}
return this.authorizationCodeTokenExchanger;
}
private OAuth2UserService getUserInfoService() {
if (this.userInfoService == null) {
this.userInfoService = new NimbusOAuth2UserService(this.userInfoTypeConverters);
}
return this.userInfoService;
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2012-2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeRequestRedirectFilter;
import org.springframework.security.oauth2.client.authentication.AuthorizationRequestUriBuilder;
import org.springframework.security.oauth2.client.authentication.DefaultAuthorizationRequestUriBuilder;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.util.Assert;
/**
* @author Joe Grandja
*/
final class AuthorizationCodeRequestRedirectFilterConfigurer<B extends HttpSecurityBuilder<B>> extends
AbstractHttpConfigurer<AuthorizationCodeRequestRedirectFilterConfigurer<B>, B> {
private AuthorizationRequestUriBuilder authorizationRequestBuilder;
AuthorizationCodeRequestRedirectFilterConfigurer<B> clientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) {
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
Assert.notEmpty(clientRegistrationRepository.getRegistrations(), "clientRegistrationRepository cannot be empty");
this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
return this;
}
AuthorizationCodeRequestRedirectFilterConfigurer<B> authorizationRequestBuilder(AuthorizationRequestUriBuilder authorizationRequestBuilder) {
Assert.notNull(authorizationRequestBuilder, "authorizationRequestBuilder cannot be null");
this.authorizationRequestBuilder = authorizationRequestBuilder;
return this;
}
@Override
public void configure(B http) throws Exception {
AuthorizationCodeRequestRedirectFilter filter = new AuthorizationCodeRequestRedirectFilter(
OAuth2LoginConfigurer.getClientRegistrationRepository(this.getBuilder()),
this.getAuthorizationRequestBuilder());
http.addFilter(this.postProcess(filter));
}
private AuthorizationRequestUriBuilder getAuthorizationRequestBuilder() {
if (this.authorizationRequestBuilder == null) {
this.authorizationRequestBuilder = new DefaultAuthorizationRequestUriBuilder();
}
return this.authorizationRequestBuilder;
}
}
@@ -0,0 +1,154 @@
/*
* Copyright 2012-2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
import org.springframework.context.ApplicationContext;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeRequestRedirectFilter;
import org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger;
import org.springframework.security.oauth2.client.authentication.AuthorizationRequestUriBuilder;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.client.user.OAuth2UserService;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import java.net.URI;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author Joe Grandja
*/
public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> extends
AbstractHttpConfigurer<OAuth2LoginConfigurer<B>, B> {
private final AuthorizationCodeRequestRedirectFilterConfigurer<B> authorizationCodeRequestRedirectFilterConfigurer;
private final AuthorizationCodeAuthenticationFilterConfigurer<B> authorizationCodeAuthenticationFilterConfigurer;
private final UserInfoEndpointConfig userInfoEndpointConfig;
public OAuth2LoginConfigurer() {
this.authorizationCodeRequestRedirectFilterConfigurer = new AuthorizationCodeRequestRedirectFilterConfigurer<>();
this.authorizationCodeAuthenticationFilterConfigurer = new AuthorizationCodeAuthenticationFilterConfigurer<>();
this.userInfoEndpointConfig = new UserInfoEndpointConfig();
}
public OAuth2LoginConfigurer<B> clients(ClientRegistration... clientRegistrations) {
Assert.notEmpty(clientRegistrations, "clientRegistrations cannot be empty");
return clients(new InMemoryClientRegistrationRepository(Arrays.asList(clientRegistrations)));
}
public OAuth2LoginConfigurer<B> clients(ClientRegistrationRepository clientRegistrationRepository) {
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
Assert.notEmpty(clientRegistrationRepository.getRegistrations(), "clientRegistrationRepository cannot be empty");
this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
return this;
}
public OAuth2LoginConfigurer<B> authorizationRequestBuilder(AuthorizationRequestUriBuilder authorizationRequestBuilder) {
Assert.notNull(authorizationRequestBuilder, "authorizationRequestBuilder cannot be null");
this.authorizationCodeRequestRedirectFilterConfigurer.authorizationRequestBuilder(authorizationRequestBuilder);
return this;
}
public OAuth2LoginConfigurer<B> authorizationCodeTokenExchanger(
AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger) {
Assert.notNull(authorizationCodeTokenExchanger, "authorizationCodeTokenExchanger cannot be null");
this.authorizationCodeAuthenticationFilterConfigurer.authorizationCodeTokenExchanger(authorizationCodeTokenExchanger);
return this;
}
public UserInfoEndpointConfig userInfoEndpoint() {
return this.userInfoEndpointConfig;
}
public class UserInfoEndpointConfig {
private UserInfoEndpointConfig() {
}
public OAuth2LoginConfigurer<B> userInfoService(OAuth2UserService userInfoService) {
Assert.notNull(userInfoService, "userInfoService cannot be null");
OAuth2LoginConfigurer.this.authorizationCodeAuthenticationFilterConfigurer.userInfoService(userInfoService);
return this.and();
}
public OAuth2LoginConfigurer<B> userInfoTypeConverter(Function<ClientHttpResponse, ? extends OAuth2User> userInfoConverter, URI userInfoUri) {
Assert.notNull(userInfoConverter, "userInfoConverter cannot be null");
Assert.notNull(userInfoUri, "userInfoUri cannot be null");
OAuth2LoginConfigurer.this.authorizationCodeAuthenticationFilterConfigurer.userInfoTypeConverter(userInfoConverter, userInfoUri);
return this.and();
}
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
}
@Override
public void init(B http) throws Exception {
this.authorizationCodeRequestRedirectFilterConfigurer.setBuilder(http);
this.authorizationCodeAuthenticationFilterConfigurer.setBuilder(http);
this.authorizationCodeRequestRedirectFilterConfigurer.init(http);
this.authorizationCodeAuthenticationFilterConfigurer.init(http);
this.initDefaultLoginFilter(http);
}
@Override
public void configure(B http) throws Exception {
this.authorizationCodeRequestRedirectFilterConfigurer.configure(http);
this.authorizationCodeAuthenticationFilterConfigurer.configure(http);
}
static <B extends HttpSecurityBuilder<B>> ClientRegistrationRepository getClientRegistrationRepository(B http) {
ClientRegistrationRepository clientRegistrationRepository = http.getSharedObject(ClientRegistrationRepository.class);
if (clientRegistrationRepository == null) {
clientRegistrationRepository = getDefaultClientRegistrationRepository(http);
http.setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
}
return clientRegistrationRepository;
}
private static <B extends HttpSecurityBuilder<B>> ClientRegistrationRepository getDefaultClientRegistrationRepository(B http) {
return http.getSharedObject(ApplicationContext.class).getBean(ClientRegistrationRepository.class);
}
private void initDefaultLoginFilter(B http) {
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http.getSharedObject(DefaultLoginPageGeneratingFilter.class);
if (loginPageGeneratingFilter != null && !this.authorizationCodeAuthenticationFilterConfigurer.isCustomLoginPage()) {
ClientRegistrationRepository clientRegistrationRepository = getClientRegistrationRepository(this.getBuilder());
if (!CollectionUtils.isEmpty(clientRegistrationRepository.getRegistrations())) {
Map<String, String> oauth2AuthenticationUrlToClientName = clientRegistrationRepository.getRegistrations().stream()
.collect(Collectors.toMap(e -> AuthorizationCodeRequestRedirectFilter.AUTHORIZATION_BASE_URI + "/" + e.getClientAlias(),
e -> e.getClientName()));
loginPageGeneratingFilter.setOauth2LoginEnabled(true);
loginPageGeneratingFilter.setOauth2AuthenticationUrlToClientName(oauth2AuthenticationUrlToClientName);
loginPageGeneratingFilter.setLoginPageUrl(this.authorizationCodeAuthenticationFilterConfigurer.getLoginUrl());
loginPageGeneratingFilter.setFailureUrl(this.authorizationCodeAuthenticationFilterConfigurer.getLoginFailureUrl());
}
}
}
}
@@ -0,0 +1,37 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.annotation.web.reactive;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.*;
/**
* @author Rob Winch
* @since 5.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({HttpSecurityConfiguration.class, WebFluxSecurityConfiguration.class})
@Configuration
public @interface EnableWebFluxSecurity {
}
@@ -0,0 +1,82 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.annotation.web.reactive;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryAuthenticationManager;
import org.springframework.security.config.web.server.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsRepository;
import org.springframework.security.web.reactive.result.method.annotation.AuthenticationPrincipalArgumentResolver;
import org.springframework.security.web.server.context.WebSessionSecurityContextRepository;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
import static org.springframework.security.config.web.server.HttpSecurity.http;
/**
* @author Rob Winch
* @since 5.0
*/
public class HttpSecurityConfiguration implements WebFluxConfigurer {
private static final String BEAN_NAME_PREFIX = "org.springframework.security.config.annotation.web.reactive.HttpSecurityConfiguration.";
private static final String HTTPSECURITY_BEAN_NAME = BEAN_NAME_PREFIX + "httpSecurity";
@Autowired(required = false)
private ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry();
@Autowired(required = false)
private ReactiveAuthenticationManager authenticationManager;
@Autowired(required = false)
private UserDetailsRepository userDetailsRepository;
@Override
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
configurer.addCustomResolver(authenticationPrincipalArgumentResolver());
}
@Bean
public AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver() {
return new AuthenticationPrincipalArgumentResolver(adapterRegistry);
}
@Bean(HTTPSECURITY_BEAN_NAME)
@Scope("prototype")
public HttpSecurity httpSecurity() {
HttpSecurity http = http();
http.httpBasic();
http.authenticationManager(authenticationManager());
http.securityContextRepository(new WebSessionSecurityContextRepository());
return http;
}
private ReactiveAuthenticationManager authenticationManager() {
if(authenticationManager != null) {
return authenticationManager;
}
if(userDetailsRepository != null) {
return new UserDetailsRepositoryAuthenticationManager(userDetailsRepository);
}
return null;
}
}
@@ -0,0 +1,69 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.annotation.web.reactive;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.HttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.WebFilterChainFilter;
import org.springframework.util.ObjectUtils;
import java.util.Arrays;
import java.util.List;
/**
* @author Rob Winch
* @since 5.0
*/
@Configuration
public class WebFluxSecurityConfiguration {
private static final String BEAN_NAME_PREFIX = "org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.";
private static final String SPRING_SECURITY_WEBFILTERCHAINFILTER_BEAN_NAME = BEAN_NAME_PREFIX + "WebFilterChainFilter";
@Autowired(required = false)
private List<SecurityWebFilterChain> securityWebFilterChains;
@Autowired
ApplicationContext context;
@Bean(SPRING_SECURITY_WEBFILTERCHAINFILTER_BEAN_NAME)
public WebFilterChainFilter springSecurityWebFilterChainFilter() {
return WebFilterChainFilter.fromSecurityWebFilterChainsList(getSecurityWebFilterChains());
}
private List<SecurityWebFilterChain> getSecurityWebFilterChains() {
List<SecurityWebFilterChain> result = securityWebFilterChains;
if(ObjectUtils.isEmpty(result)) {
return defaultSecurityWebFilterChains();
}
return result;
}
private List<SecurityWebFilterChain> defaultSecurityWebFilterChains() {
HttpSecurity http = context.getBean(HttpSecurity.class);
http
.authorizeExchange()
.anyExchange().authenticated();
return Arrays.asList(http.build());
}
}
@@ -0,0 +1,100 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.core.userdetails;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.security.core.userdetails.MapUserDetailsRepository;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
/**
* Constructs an {@link MapUserDetailsRepository} from a resource using {@link UserDetailsResourceFactoryBean}.
*
* @author Rob Winch
* @since 5.0
* @see UserDetailsResourceFactoryBean
*/
public class UserDetailsRepositoryResourceFactoryBean implements ResourceLoaderAware, FactoryBean<MapUserDetailsRepository> {
private UserDetailsResourceFactoryBean userDetails = new UserDetailsResourceFactoryBean();
@Override
public MapUserDetailsRepository getObject() throws Exception {
Collection<UserDetails> users = userDetails.getObject();
return new MapUserDetailsRepository(users);
}
@Override
public Class<?> getObjectType() {
return MapUserDetailsRepository.class;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
userDetails.setResourceLoader(resourceLoader);
}
/**
* Sets a the location of a Resource that is a Properties file in the format defined in {@link UserDetailsResourceFactoryBean}
*
* @param resourceLocation the location of the properties file that contains the users (i.e. "classpath:users.properties")
* @return the UserDetailsResourceFactoryBean
*/
public void setResourceLocation(String resourceLocation) {
this.userDetails.setResourceLocation(resourceLocation);
}
/**
* Sets a a Resource that is a Properties file in the format defined in {@link UserDetailsResourceFactoryBean}
*
* @param resource the Resource to use
*/
public void setResource(Resource resource) {
this.userDetails.setResource(resource);
}
/**
* Create a UserDetailsRepositoryResourceFactoryBean with the location of a Resource that is a Properties file in the
* format defined in {@link UserDetailsResourceFactoryBean}
*
* @param resourceLocatiton the location of the properties file that contains the users (i.e. "classpath:users.properties")
* @return the UserDetailsResourceFactoryBean
*/
public static UserDetailsRepositoryResourceFactoryBean fromResourceLocation(String resourceLocatiton) {
UserDetailsRepositoryResourceFactoryBean result = new UserDetailsRepositoryResourceFactoryBean();
result.setResourceLocation(resourceLocatiton);
return result;
}
/**
* Create a UserDetailsRepositoryResourceFactoryBean with a Resource that is a Properties file in the
* format defined in {@link UserDetailsResourceFactoryBean}
*
* @param propertiesResource the Resource that is a properties file that contains the users
* @return the UserDetailsResourceFactoryBean
*/
public static UserDetailsRepositoryResourceFactoryBean fromResource(Resource propertiesResource) {
UserDetailsRepositoryResourceFactoryBean result = new UserDetailsRepositoryResourceFactoryBean();
result.setResource(propertiesResource);
return result;
}
}
@@ -0,0 +1,156 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.core.userdetails;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.memory.UserAttribute;
import org.springframework.security.core.userdetails.memory.UserAttributeEditor;
import org.springframework.util.Assert;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Properties;
/**
* Parses a Resource that is a Properties file in the format of:
*
* <code>
* username=password[,enabled|disabled],roles...
* </code>
*
* The enabled and disabled properties are optional with enabled being the default. For example:
*
* <code>
* user=password,ROLE_USER
* admin=secret,ROLE_USER,ROLE_ADMIN
* disabled_user=does_not_matter,disabled,ROLE_USER
* </code>
*
* @author Rob Winch
* @since 5.0
*/
public class UserDetailsResourceFactoryBean implements ResourceLoaderAware, FactoryBean<Collection<UserDetails>> {
private ResourceLoader resourceLoader = new DefaultResourceLoader();
private String resourceLocation;
private Resource resource;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader,"resourceLoader cannot be null");
this.resourceLoader = resourceLoader;
}
@Override
public Collection<UserDetails> getObject() throws Exception {
Properties userProperties = new Properties();
Resource resource = getProperitesResource();
try(InputStream in = resource.getInputStream()){
userProperties.load(in);
}
Collection<UserDetails> users = new ArrayList<>(userProperties.size());
Enumeration<?> names = userProperties.propertyNames();
UserAttributeEditor editor = new UserAttributeEditor();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
String property = userProperties.getProperty(name);
editor.setAsText(property);
UserAttribute attr = (UserAttribute) editor.getValue();
if(attr == null) {
throw new IllegalStateException("The entry with username '" + name + "' and value '" + property + "' could not be converted to a UserDetails.");
}
UserDetails user = User.withUsername(name)
.password(attr.getPassword())
.disabled(!attr.isEnabled())
.authorities(attr.getAuthorities())
.build();
users.add(user);
}
return users;
}
@Override
public Class<?> getObjectType() {
return Collection.class;
}
/**
* Sets a the location of a Resource that is a Properties file in the format defined in {@link UserDetailsResourceFactoryBean}
*
* @param resourceLocation the location of the properties file that contains the users (i.e. "classpath:users.properties")
*/
public void setResourceLocation(String resourceLocation) {
this.resourceLocation = resourceLocation;
}
/**
* Sets a a Resource that is a Properties file in the format defined in {@link UserDetailsResourceFactoryBean}
*
* @param resource the Resource to use
*/
public void setResource(Resource resource) {
this.resource = resource;
}
private Resource getProperitesResource() {
Resource result = resource;
if(result == null && resourceLocation != null) {
result = resourceLoader.getResource(resourceLocation);
}
Assert.notNull(result, "resource cannot be null if resourceLocation is null");
return result;
}
/**
* Create a UserDetailsResourceFactoryBean with the location of a Resource that is a Properties file in the
* format defined in {@link UserDetailsResourceFactoryBean}
*
* @param resourceLocatiton the location of the properties file that contains the users (i.e. "classpath:users.properties")
* @return the UserDetailsResourceFactoryBean
*/
public static UserDetailsResourceFactoryBean fromResourceLocation(String resourceLocatiton) {
UserDetailsResourceFactoryBean result = new UserDetailsResourceFactoryBean();
result.setResourceLocation(resourceLocatiton);
return result;
}
/**
* Create a UserDetailsResourceFactoryBean with a Resource that is a Properties file in the
* format defined in {@link UserDetailsResourceFactoryBean}
*
* @param propertiesResource the Resource that is a properties file that contains the users
* @return the UserDetailsResourceFactoryBean
*/
public static UserDetailsResourceFactoryBean fromResource(Resource propertiesResource) {
UserDetailsResourceFactoryBean result = new UserDetailsResourceFactoryBean();
result.setResource(propertiesResource);
return result;
}
}
@@ -0,0 +1,101 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.provisioning;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.security.config.core.userdetails.UserDetailsResourceFactoryBean;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import java.util.Collection;
/**
* Constructs an {@link InMemoryUserDetailsManager} from a resource using {@link UserDetailsResourceFactoryBean}.
*
* @author Rob Winch
* @since 5.0
* @see UserDetailsResourceFactoryBean
*/
public class UserDetailsManagerResourceFactoryBean implements ResourceLoaderAware, FactoryBean<InMemoryUserDetailsManager> {
private UserDetailsResourceFactoryBean userDetails = new UserDetailsResourceFactoryBean();
@Override
public InMemoryUserDetailsManager getObject() throws Exception {
Collection<UserDetails> users = userDetails.getObject();
return new InMemoryUserDetailsManager(users);
}
@Override
public Class<?> getObjectType() {
return InMemoryUserDetailsManager.class;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
userDetails.setResourceLoader(resourceLoader);
}
/**
* Sets a the location of a Resource that is a Properties file in the format defined in {@link UserDetailsResourceFactoryBean}
*
* @param resourceLocation the location of the properties file that contains the users (i.e. "classpath:users.properties")
* @return the UserDetailsResourceFactoryBean
*/
public void setResourceLocation(String resourceLocation) {
this.userDetails.setResourceLocation(resourceLocation);
}
/**
* Sets a a Resource that is a Properties file in the format defined in {@link UserDetailsResourceFactoryBean}
*
* @param resource the Resource to use
*/
public void setResource(Resource resource) {
this.userDetails.setResource(resource);
}
/**
* Create a UserDetailsServiceResourceFactoryBean with the location of a Resource that is a Properties file in the
* format defined in {@link UserDetailsResourceFactoryBean}
*
* @param resourceLocation the location of the properties file that contains the users (i.e. "classpath:users.properties")
* @return the UserDetailsResourceFactoryBean
*/
public static UserDetailsManagerResourceFactoryBean fromResourceLocation(String resourceLocation) {
UserDetailsManagerResourceFactoryBean result = new UserDetailsManagerResourceFactoryBean();
result.setResourceLocation(resourceLocation);
return result;
}
/**
* Create a UserDetailsServiceResourceFactoryBean with a Resource that is a Properties file in the
* format defined in {@link UserDetailsResourceFactoryBean}
*
* @param resource the Resource that is a properties file that contains the users
* @return the UserDetailsResourceFactoryBean
*/
public static UserDetailsManagerResourceFactoryBean fromResource(Resource resource) {
UserDetailsManagerResourceFactoryBean result = new UserDetailsManagerResourceFactoryBean();
result.setResource(resource);
return result;
}
}
@@ -0,0 +1,116 @@
/*
* Copyright 2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.web.server;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import java.util.List;
/**
* @author Rob Winch
* @since 5.0
*/
abstract class AbstractServerWebExchangeMatcherRegistry<T> {
/**
* Maps any request.
*
* @return the object that is chained after creating the {@link ServerWebExchangeMatcher}
*/
public T anyExchange() {
return matcher(ServerWebExchangeMatchers.anyExchange());
}
/**
* Maps a {@link List} of
* {@link org.springframework.security.web.server.util.matcher.PathMatcherServerWebExchangeMatcher}
* instances.
*
* @param method the {@link HttpMethod} to use for any
* {@link HttpMethod}.
*
* @return the object that is chained after creating the {@link ServerWebExchangeMatcher}
*/
public T pathMatchers(HttpMethod method) {
return pathMatchers(method, new String[] { "/**" });
}
/**
* Maps a {@link List} of
* {@link org.springframework.security.web.server.util.matcher.PathMatcherServerWebExchangeMatcher}
* instances.
*
* @param method the {@link HttpMethod} to use or {@code null} for any
* {@link HttpMethod}.
* @param antPatterns the ant patterns to create. If {@code null} or empty, then matches on nothing.
* {@link org.springframework.security.web.server.util.matcher.PathMatcherServerWebExchangeMatcher} from
*
* @return the object that is chained after creating the {@link ServerWebExchangeMatcher}
*/
public T pathMatchers(HttpMethod method, String... antPatterns) {
return matcher(ServerWebExchangeMatchers.pathMatchers(method, antPatterns));
}
/**
* Maps a {@link List} of
* {@link org.springframework.security.web.server.util.matcher.PathMatcherServerWebExchangeMatcher}
* instances that do not care which {@link HttpMethod} is used.
*
* @param antPatterns the ant patterns to create
* {@link org.springframework.security.web.server.util.matcher.PathMatcherServerWebExchangeMatcher} from
*
* @return the object that is chained after creating the {@link ServerWebExchangeMatcher}
*/
public T pathMatchers(String... antPatterns) {
return matcher(ServerWebExchangeMatchers.pathMatchers(antPatterns));
}
/**
* Associates a list of {@link ServerWebExchangeMatcher} instances
*
* @param matchers the {@link ServerWebExchangeMatcher} instances
*
* @return the object that is chained after creating the {@link ServerWebExchangeMatcher}
*/
public T matchers(ServerWebExchangeMatcher... matchers) {
return registerMatcher(new OrServerWebExchangeMatcher(matchers));
}
/**
* Subclasses should implement this method for returning the object that is chained to
* the creation of the {@link ServerWebExchangeMatcher} instances.
*
* @param matcher the {@link ServerWebExchangeMatcher} instances that were created
* @return the chained Object for the subclass which allows association of something
* else to the {@link ServerWebExchangeMatcher}
*/
protected abstract T registerMatcher(ServerWebExchangeMatcher matcher);
/**
* Associates a {@link ServerWebExchangeMatcher} instances
*
* @param matcher the {@link ServerWebExchangeMatcher} instance
*
* @return the object that is chained after creating the {@link ServerWebExchangeMatcher}
*/
private T matcher(ServerWebExchangeMatcher matcher) {
return registerMatcher(matcher);
}
}
@@ -0,0 +1,128 @@
/*
* Copyright 2002-2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.web.server;
import org.springframework.security.web.server.header.CacheControlHttpHeadersWriter;
import org.springframework.security.web.server.header.CompositeHttpHeadersWriter;
import org.springframework.security.web.server.header.ContentTypeOptionsHttpHeadersWriter;
import org.springframework.security.web.server.header.HttpHeaderWriterWebFilter;
import org.springframework.security.web.server.header.HttpHeadersWriter;
import org.springframework.security.web.server.header.StrictTransportSecurityHttpHeadersWriter;
import org.springframework.security.web.server.header.XFrameOptionsHttpHeadersWriter;
import org.springframework.security.web.server.header.XXssProtectionHttpHeadersWriter;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Rob Winch
* @since 5.0
*/
public class HeaderBuilder {
private final List<HttpHeadersWriter> writers;
private CacheControlHttpHeadersWriter cacheControl = new CacheControlHttpHeadersWriter();
private ContentTypeOptionsHttpHeadersWriter contentTypeOptions = new ContentTypeOptionsHttpHeadersWriter();
private StrictTransportSecurityHttpHeadersWriter hsts = new StrictTransportSecurityHttpHeadersWriter();
private XFrameOptionsHttpHeadersWriter frameOptions = new XFrameOptionsHttpHeadersWriter();
private XXssProtectionHttpHeadersWriter xss = new XXssProtectionHttpHeadersWriter();
public HeaderBuilder() {
this.writers = new ArrayList<>(Arrays.asList(cacheControl, contentTypeOptions, hsts, frameOptions, xss));
}
public CacheSpec cache() {
return new CacheSpec();
}
public ContentTypeOptionsSpec contentTypeOptions() {
return new ContentTypeOptionsSpec();
}
public FrameOptionsSpec frameOptions() {
return new FrameOptionsSpec();
}
public HstsSpec hsts() {
return new HstsSpec();
}
public HttpHeaderWriterWebFilter build() {
HttpHeadersWriter writer = new CompositeHttpHeadersWriter(writers);
return new HttpHeaderWriterWebFilter(writer);
}
public XssProtectionSpec xssProtection() {
return new XssProtectionSpec();
}
public class CacheSpec {
public void disable() {
writers.remove(cacheControl);
}
private CacheSpec() {}
}
public class ContentTypeOptionsSpec {
public void disable() {
writers.remove(contentTypeOptions);
}
private ContentTypeOptionsSpec() {}
}
public class FrameOptionsSpec {
public void mode(XFrameOptionsHttpHeadersWriter.Mode mode) {
frameOptions.setMode(mode);
}
public void disable() {
writers.remove(frameOptions);
}
private FrameOptionsSpec() {}
}
public class HstsSpec {
public void maxAge(Duration maxAge) {
hsts.setMaxAge(maxAge);
}
public void includeSubdomains(boolean includeSubDomains) {
hsts.setIncludeSubDomains(includeSubDomains);
}
public void disable() {
writers.remove(hsts);
}
private HstsSpec() {}
}
public class XssProtectionSpec {
public void disable() {
writers.remove(xss);
}
private XssProtectionSpec() {}
}
}
@@ -0,0 +1,58 @@
/*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.web.server;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.web.server.AuthenticationEntryPoint;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.security.web.server.HttpBasicAuthenticationConverter;
import org.springframework.security.web.server.authentication.DefaultAuthenticationSuccessHandler;
import org.springframework.security.web.server.authentication.www.HttpBasicAuthenticationEntryPoint;
import org.springframework.security.web.server.context.SecurityContextRepository;
/**
* @author Rob Winch
* @since 5.0
*/
public class HttpBasicBuilder {
private ReactiveAuthenticationManager authenticationManager;
private SecurityContextRepository securityContextRepository;
private AuthenticationEntryPoint entryPoint = new HttpBasicAuthenticationEntryPoint();
public HttpBasicBuilder authenticationManager(ReactiveAuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
return this;
}
public HttpBasicBuilder securityContextRepository(SecurityContextRepository securityContextRepository) {
this.securityContextRepository = securityContextRepository;
return this;
}
public AuthenticationWebFilter build() {
AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(authenticationManager);
authenticationFilter.setEntryPoint(entryPoint);
authenticationFilter.setAuthenticationConverter(new HttpBasicAuthenticationConverter());
if(securityContextRepository != null) {
DefaultAuthenticationSuccessHandler handler = new DefaultAuthenticationSuccessHandler();
handler.setSecurityContextRepository(securityContextRepository);
authenticationFilter.setAuthenticationSuccessHandler(handler);
}
return authenticationFilter;
}
}
@@ -0,0 +1,210 @@
/*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.web.server;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
import org.springframework.security.authorization.AuthorityAuthorizationManager;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.web.server.MatcherSecurityWebFilterChain;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.security.web.server.authorization.AuthorizationWebFilter;
import org.springframework.security.web.server.authorization.DelegatingReactiveAuthorizationManager;
import org.springframework.security.web.server.context.SecurityContextRepositoryWebFilter;
import org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter;
import org.springframework.security.web.server.context.SecurityContextRepository;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcherEntry;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import org.springframework.util.Assert;
import org.springframework.web.server.WebFilter;
import reactor.core.publisher.Mono;
/**
* @author Rob Winch
* @since 5.0
*/
public class HttpSecurity {
private ServerWebExchangeMatcher securityMatcher = ServerWebExchangeMatchers.anyExchange();
private AuthorizeExchangeBuilder authorizeExchangeBuilder;
private HeaderBuilder headers = new HeaderBuilder();
private HttpBasicBuilder httpBasic;
private ReactiveAuthenticationManager authenticationManager;
private Optional<SecurityContextRepository> securityContextRepository = Optional.empty();
/**
* The ServerExchangeMatcher that determines which requests apply to this HttpSecurity instance.
*
* @param matcher the ServerExchangeMatcher that determines which requests apply to this HttpSecurity instance.
* Default is all requests.
*/
public HttpSecurity securityMatcher(ServerWebExchangeMatcher matcher) {
Assert.notNull(matcher, "matcher cannot be null");
this.securityMatcher = matcher;
return this;
}
/**
* Gets the ServerExchangeMatcher that determines which requests apply to this HttpSecurity instance.
* @return the ServerExchangeMatcher that determines which requests apply to this HttpSecurity instance.
*/
private ServerWebExchangeMatcher getSecurityMatcher() {
return this.securityMatcher;
}
public HttpSecurity securityContextRepository(SecurityContextRepository securityContextRepository) {
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
this.securityContextRepository = Optional.of(securityContextRepository);
return this;
}
public HttpBasicBuilder httpBasic() {
if(httpBasic == null) {
httpBasic = new HttpBasicBuilder();
}
return httpBasic;
}
public HeaderBuilder headers() {
return headers;
}
public AuthorizeExchangeBuilder authorizeExchange() {
if(authorizeExchangeBuilder == null) {
authorizeExchangeBuilder = new AuthorizeExchangeBuilder();
}
return authorizeExchangeBuilder;
}
public HttpSecurity authenticationManager(ReactiveAuthenticationManager manager) {
this.authenticationManager = manager;
return this;
}
public SecurityWebFilterChain build() {
List<WebFilter> filters = new ArrayList<>();
if(headers != null) {
filters.add(headers.build());
}
securityContextRepositoryWebFilter().ifPresent( f-> filters.add(f));
if(httpBasic != null) {
httpBasic.authenticationManager(authenticationManager);
securityContextRepository.ifPresent( scr -> httpBasic.securityContextRepository(scr)) ;
filters.add(httpBasic.build());
}
if(authorizeExchangeBuilder != null) {
filters.add(new ExceptionTranslationWebFilter());
filters.add(authorizeExchangeBuilder.build());
}
return new MatcherSecurityWebFilterChain(getSecurityMatcher(), filters);
}
public static HttpSecurity http() {
return new HttpSecurity();
}
private Optional<SecurityContextRepositoryWebFilter> securityContextRepositoryWebFilter() {
return securityContextRepository
.flatMap( r -> Optional.of(new SecurityContextRepositoryWebFilter(r)));
}
public class HttpBasicSpec extends HttpBasicBuilder {
public HttpSecurity disable() {
httpBasic = null;
return HttpSecurity.this;
}
}
private HttpSecurity() {}
/**
* @author Rob Winch
* @since 5.0
*/
public class AuthorizeExchangeBuilder extends AbstractServerWebExchangeMatcherRegistry<AuthorizeExchangeBuilder.Access> {
private DelegatingReactiveAuthorizationManager.Builder managerBldr = DelegatingReactiveAuthorizationManager.builder();
private ServerWebExchangeMatcher matcher;
private boolean anyExchangeRegistered;
public HttpSecurity and() {
return HttpSecurity.this;
}
@Override
public Access anyExchange() {
Access result = super.anyExchange();
anyExchangeRegistered = true;
return result;
}
@Override
protected Access registerMatcher(ServerWebExchangeMatcher matcher) {
if(anyExchangeRegistered) {
throw new IllegalStateException("Cannot register " + matcher + " which would be unreachable because anyExchange() has already been registered.");
}
if(this.matcher != null) {
throw new IllegalStateException("The matcher " + matcher + " does not have an access rule defined");
}
this.matcher = matcher;
return new Access();
}
protected WebFilter build() {
if(this.matcher != null) {
throw new IllegalStateException("The matcher " + matcher + " does not have an access rule defined");
}
return new AuthorizationWebFilter(managerBldr.build());
}
public final class Access {
public AuthorizeExchangeBuilder permitAll() {
return access( (a,e) -> Mono.just(new AuthorizationDecision(true)));
}
public AuthorizeExchangeBuilder denyAll() {
return access( (a,e) -> Mono.just(new AuthorizationDecision(false)));
}
public AuthorizeExchangeBuilder hasRole(String role) {
return access(AuthorityAuthorizationManager.hasRole(role));
}
public AuthorizeExchangeBuilder hasAuthority(String authority) {
return access(AuthorityAuthorizationManager.hasAuthority(authority));
}
public AuthorizeExchangeBuilder authenticated() {
return access(AuthenticatedAuthorizationManager.authenticated());
}
public AuthorizeExchangeBuilder access(ReactiveAuthorizationManager<AuthorizationContext> manager) {
managerBldr.add(new ServerWebExchangeMatcherEntry<>(matcher, manager));
matcher = null;
return AuthorizeExchangeBuilder.this;
}
}
}
}
@@ -346,7 +346,7 @@ public class NamespaceHttpTests extends BaseSpringSpec {
}
}
// http@request-matcher is not available (instead request matcher instances are used)
// http@request-matcher is not available (instead request securityMatcher instances are used)
def "http@request-matcher-ref ant"() {
when:
@@ -78,7 +78,7 @@ public class DefaultLoginPageConfigurerTests extends BaseSpringSpec {
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
<p><font color='red'>Your login attempt was not successful, try again.<br/><br/>Reason: Bad credentials</font></p><h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
<p style='color:red;'>Your login attempt was not successful, try again.<br/><br/>Reason: Bad credentials</p><h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
<table>
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
@@ -107,7 +107,7 @@ public class DefaultLoginPageConfigurerTests extends BaseSpringSpec {
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to default success page"
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
<p><font color='green'>You have been logged out</font></p><h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
<p style='color:green;'>You have been logged out</p><h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
<table>
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,7 +42,6 @@ import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.web.AuthenticationEntryPoint
import org.springframework.security.web.FilterChainProxy
import org.springframework.security.web.FilterInvocation
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2017 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.
@@ -38,7 +38,6 @@ import org.springframework.security.config.annotation.web.configuration.BaseWebC
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextImpl
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.web.AuthenticationEntryPoint
import org.springframework.security.web.FilterChainProxy
import org.springframework.security.web.FilterInvocation
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2017 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,7 +28,6 @@ import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.mock.web.MockServletContext
import org.springframework.security.access.SecurityConfig
import org.springframework.security.crypto.codec.Base64
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@@ -383,7 +382,7 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
def login(MockHttpServletRequest request, String username, String password) {
String toEncode = username + ':' + password
request.addHeader('Authorization','Basic ' + new String(Base64.encode(toEncode.getBytes('UTF-8'))))
request.addHeader('Authorization','Basic ' + Base64.encoder.encodeToString(toEncode.getBytes('UTF-8')))
}
@RestController
@@ -26,10 +26,10 @@ import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.internal.WhiteboxImpl;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.messaging.Message;
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.ClassUtils;
/**
@@ -87,7 +87,7 @@ public class SecurityNamespaceHandlerTests {
Log logger = mock(Log.class);
SecurityNamespaceHandler handler = new SecurityNamespaceHandler();
WhiteboxImpl.setInternalState(handler, Log.class, logger);
ReflectionTestUtils.setField(handler, "logger", logger);
handler.init();
@@ -0,0 +1,125 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.annotation.web.reactive;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.web.server.HttpSecurity;
import org.springframework.security.core.userdetails.MapUserDetailsRepository;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsRepository;
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.WebFilterChainFilter;
import org.springframework.security.web.server.util.matcher.PathMatcherServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.mockito.Mockito.mock;
/**
* @author Rob Winch
* @since 5.0
*/
@RunWith(Enclosed.class)
public class EnableWebFluxSecurityTests {
@RunWith(SpringRunner.class)
public static class Defaults {
@Autowired
WebFilterChainFilter springSecurityFilterChain;
@Test
public void defaultRequiresAuthentication() {
WebTestClient client = WebTestClientBuilder.bindToWebFilters(springSecurityFilterChain).build();
client.get()
.uri("/")
.exchange()
.expectStatus().isUnauthorized()
.expectBody().isEmpty();
}
@EnableWebFluxSecurity
static class Config {
@Bean
public UserDetailsRepository userDetailsRepository() {
return new MapUserDetailsRepository(User.withUsername("user")
.password("password")
.roles("USER")
.build()
);
}
}
}
@RunWith(SpringRunner.class)
public static class MultiHttpSecurity {
@Autowired
WebFilterChainFilter springSecurityFilterChain;
@Test
public void multiWorks() {
WebTestClient client = WebTestClientBuilder.bindToWebFilters(springSecurityFilterChain).build();
client.get()
.uri("/api/test")
.exchange()
.expectStatus().isUnauthorized()
.expectBody().isEmpty();
client.get()
.uri("/test")
.exchange()
.expectStatus().isOk();
}
@EnableWebFluxSecurity
static class Config {
@Order(Ordered.HIGHEST_PRECEDENCE)
@Bean
public SecurityWebFilterChain apiHttpSecurity(HttpSecurity http) {
http
.securityMatcher(new PathMatcherServerWebExchangeMatcher("/api/**"))
.authorizeExchange()
.anyExchange().denyAll();
return http.build();
}
@Bean
public SecurityWebFilterChain httpSecurity(HttpSecurity http) {
return http.build();
}
@Bean
public UserDetailsRepository userDetailsRepository() {
return new MapUserDetailsRepository(User.withUsername("user")
.password("password")
.roles("USER")
.build()
);
}
}
}
}
@@ -0,0 +1,54 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.core.userdetails;
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.security.core.userdetails.UserDetailsRepository;
import org.springframework.security.util.InMemoryResource;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
/**
* @author Rob Winch
* @since 5.0
*/
@RunWith(SpringRunner.class)
public class UserDetailsRepositoryResourceFactoryBeanPropertiesResourceITests {
@Autowired
UserDetailsRepository users;
@Test
public void loadUserByUsernameWhenUserFoundThenNotNull() {
assertThat(users.findByUsername("user").block()).isNotNull();
}
@Configuration
static class Config {
@Bean
public UserDetailsRepositoryResourceFactoryBean userDetailsService() {
return UserDetailsRepositoryResourceFactoryBean.fromResource(new InMemoryResource("user=password,ROLE_USER"));
}
}
}
@@ -0,0 +1,53 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.core.userdetails;
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.security.core.userdetails.UserDetailsRepository;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
/**
* @author Rob Winch
* @since 5.0
*/
@RunWith(SpringRunner.class)
public class UserDetailsRepositoryResourceFactoryBeanPropertiesResourceLocationITests {
@Autowired
UserDetailsRepository users;
@Test
public void loadUserByUsernameWhenUserFoundThenNotNull() {
assertThat(users.findByUsername("user").block()).isNotNull();
}
@Configuration
static class Config {
@Bean
public UserDetailsRepositoryResourceFactoryBean userDetailsService() {
return UserDetailsRepositoryResourceFactoryBean.fromResourceLocation("classpath:users.properties");
}
}
}
@@ -0,0 +1,100 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.core.userdetails;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.io.ResourceLoader;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.util.InMemoryResource;
import java.util.Collection;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* @author Rob Winch
* @since 5.0
*/
@RunWith(MockitoJUnitRunner.class)
public class UserDetailsResourceFactoryBeanTest {
@Mock
ResourceLoader resourceLoader;
UserDetailsResourceFactoryBean factory = new UserDetailsResourceFactoryBean();
@Test
public void setResourceLoaderWhenNullThenThrowsException() throws Exception {
assertThatThrownBy(() -> factory.setResourceLoader(null) )
.isInstanceOf(IllegalArgumentException.class)
.hasStackTraceContaining("resourceLoader cannot be null");
}
@Test
public void getObjectWhenPropertiesResourceLocationNullThenThrowsIllegalStateException() throws Exception {
factory.setResourceLoader(resourceLoader);
assertThatThrownBy(() -> factory.getObject() )
.isInstanceOf(IllegalArgumentException.class)
.hasStackTraceContaining("resource cannot be null if resourceLocation is null");
}
@Test
public void getObjectWhenPropertiesResourceLocationSingleUserThenThrowsGetsSingleUser() throws Exception {
factory.setResourceLocation("classpath:users.properties");
Collection<UserDetails> users = factory.getObject();
assertLoaded();
}
@Test
public void getObjectWhenPropertiesResourceSingleUserThenThrowsGetsSingleUser() throws Exception {
factory.setResource(new InMemoryResource("user=password,ROLE_USER"));
assertLoaded();
}
@Test
public void getObjectWhenInvalidUserThenThrowsMeaningfulException() throws Exception {
factory.setResource(new InMemoryResource("user=invalidFormatHere"));
assertThatThrownBy(() -> factory.getObject() )
.isInstanceOf(IllegalStateException.class)
.hasStackTraceContaining("user")
.hasStackTraceContaining("invalidFormatHere");
}
private void assertLoaded() throws Exception {
Collection<UserDetails> users = factory.getObject();
UserDetails expectedUser = User.withUsername("user")
.password("password")
.authorities("ROLE_USER")
.build();
assertThat(users).containsExactly(expectedUser);
}
}
@@ -28,8 +28,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.internal.util.reflection.Whitebox;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.AuthenticationEntryPoint;
@@ -74,7 +74,8 @@ public class DefaultFilterChainValidatorTests {
AnyRequestMatcher.INSTANCE, aaf, etf, fsi);
fcp = new FilterChainProxy(securityChain);
validator = new DefaultFilterChainValidator();
Whitebox.setInternalState(validator, "logger", logger);
ReflectionTestUtils.setField(validator, "logger", logger);
}
// SEC-1878
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.security.config.http;
import java.lang.reflect.Method;
import java.util.Base64;
import javax.servlet.Filter;
import javax.servlet.http.HttpServletResponse;
@@ -31,7 +32,6 @@ import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
import org.springframework.security.crypto.codec.Base64;
import static org.assertj.core.api.Assertions.assertThat;
@@ -87,7 +87,7 @@ public class NamespaceHttpBasicTests {
// @formatter:on
this.request.addHeader("Authorization",
"Basic " + new String(Base64.encode("user:test".getBytes("UTF-8"))));
"Basic " + Base64.getEncoder().encodeToString("user:test".getBytes("UTF-8")));
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
@@ -0,0 +1,54 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.provisioning;
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.security.provisioning.UserDetailsManager;
import org.springframework.security.util.InMemoryResource;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
/**
* @author Rob Winch
* @since 5.0
*/
@RunWith(SpringRunner.class)
public class UserDetailsManagerResourceFactoryBeanPropertiesResourceITests {
@Autowired
UserDetailsManager users;
@Test
public void loadUserByUsernameWhenUserFoundThenNotNull() {
assertThat(users.loadUserByUsername("user")).isNotNull();
}
@Configuration
static class Config {
@Bean
public UserDetailsManagerResourceFactoryBean userDetailsService() {
return UserDetailsManagerResourceFactoryBean.fromResource(new InMemoryResource("user=password,ROLE_USER"));
}
}
}
@@ -0,0 +1,53 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.provisioning;
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.security.provisioning.UserDetailsManager;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
/**
* @author Rob Winch
* @since 5.0
*/
@RunWith(SpringRunner.class)
public class UserDetailsManagerResourceFactoryBeanPropertiesResourceLocationITests {
@Autowired
UserDetailsManager users;
@Test
public void loadUserByUsernameWhenUserFoundThenNotNull() {
assertThat(users.loadUserByUsername("user")).isNotNull();
}
@Configuration
static class Config {
@Bean
public UserDetailsManagerResourceFactoryBean userDetailsService() {
return UserDetailsManagerResourceFactoryBean.fromResourceLocation("classpath:users.properties");
}
}
}
@@ -0,0 +1,112 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.web.server;
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
import org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter;
import org.springframework.test.web.reactive.server.WebTestClient;
/**
* @author Rob Winch
* @since 5.0
*/
public class AuthorizeExchangeBuilderTests {
HttpSecurity.AuthorizeExchangeBuilder authorization = HttpSecurity.http().new AuthorizeExchangeBuilder();
@Test
public void antMatchersWhenMethodAndPatternsThenDiscriminatesByMethod() {
authorization.pathMatchers(HttpMethod.POST, "/a", "/b").denyAll();
authorization.anyExchange().permitAll();
WebTestClient client = buildClient();
client.get()
.uri("/a")
.exchange()
.expectStatus().isOk();
client.get()
.uri("/b")
.exchange()
.expectStatus().isOk();
client.post()
.uri("/a")
.exchange()
.expectStatus().isUnauthorized();
client.post()
.uri("/b")
.exchange()
.expectStatus().isUnauthorized();
}
@Test
public void antMatchersWhenPatternsThenAnyMethod() {
authorization.pathMatchers("/a", "/b").denyAll();
authorization.anyExchange().permitAll();
WebTestClient client = buildClient();
client.get()
.uri("/a")
.exchange()
.expectStatus().isUnauthorized();
client.get()
.uri("/b")
.exchange()
.expectStatus().isUnauthorized();
client.post()
.uri("/a")
.exchange()
.expectStatus().isUnauthorized();
client.post()
.uri("/b")
.exchange()
.expectStatus().isUnauthorized();
}
@Test(expected = IllegalStateException.class)
public void antMatchersWhenNoAccessAndAnotherMatcherThenThrowsException() {
authorization.pathMatchers("/incomplete");
authorization.pathMatchers("/throws-exception");
}
@Test(expected = IllegalStateException.class)
public void anyExchangeWhenFollowedByMatcherThenThrowsException() {
authorization.anyExchange().denyAll();
authorization.pathMatchers("/never-reached");
}
@Test(expected = IllegalStateException.class)
public void buildWhenMatcherDefinedWithNoAccessThenThrowsException() {
authorization.pathMatchers("/incomplete");
authorization.build();
}
private WebTestClient buildClient() {
return WebTestClientBuilder.bindToWebFilters(new ExceptionTranslationWebFilter(), authorization.build()).build();
}
}
@@ -0,0 +1,144 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.web.server;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
import org.springframework.security.web.server.header.ContentTypeOptionsHttpHeadersWriter;
import org.springframework.security.web.server.header.StrictTransportSecurityHttpHeadersWriter;
import org.springframework.security.web.server.header.XFrameOptionsHttpHeadersWriter;
import org.springframework.security.web.server.header.XXssProtectionHttpHeadersWriter;
import org.springframework.test.web.reactive.server.FluxExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
/**
* @author Rob Winch
* @since 5.0
*/
public class HeaderBuilderTests {
HeaderBuilder headers = new HeaderBuilder();
HttpHeaders expectedHeaders = new HttpHeaders();
Set<String> ignoredHeaderNames = Collections.singleton(HttpHeaders.CONTENT_TYPE);
@Before
public void setup() {
expectedHeaders.add(StrictTransportSecurityHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, "max-age=31536000 ; includeSubDomains");
expectedHeaders.add(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate");
expectedHeaders.add(HttpHeaders.PRAGMA, "no-cache");
expectedHeaders.add(HttpHeaders.EXPIRES, "0");
expectedHeaders.add(ContentTypeOptionsHttpHeadersWriter.X_CONTENT_OPTIONS, "nosniff");
expectedHeaders.add(XFrameOptionsHttpHeadersWriter.X_FRAME_OPTIONS, "DENY");
expectedHeaders.add(XXssProtectionHttpHeadersWriter.X_XSS_PROTECTION, "1 ; mode=block");
}
@Test
public void headersWhenDefaultsThenAllDefaultsWritten() {
assertHeaders();
}
@Test
public void headersWhenCacheDisableThenCacheNotWritten() {
expectedHeaders.remove(HttpHeaders.CACHE_CONTROL);
expectedHeaders.remove(HttpHeaders.PRAGMA);
expectedHeaders.remove(HttpHeaders.EXPIRES);
headers.cache().disable();
assertHeaders();
}
@Test
public void headersWhenContentOptionsDisableThenContentTypeOptionsNotWritten() {
expectedHeaders.remove(ContentTypeOptionsHttpHeadersWriter.X_CONTENT_OPTIONS);
headers.contentTypeOptions().disable();
assertHeaders();
}
@Test
public void headersWhenHstsDisableThenHstsNotWritten() {
expectedHeaders.remove(StrictTransportSecurityHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);
headers.hsts().disable();
assertHeaders();
}
@Test
public void headersWhenHstsCustomThenCustomHstsWritten() {
expectedHeaders.remove(StrictTransportSecurityHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);
expectedHeaders.add(StrictTransportSecurityHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, "max-age=60");
headers.hsts().maxAge(Duration.ofSeconds(60));
headers.hsts().includeSubdomains(false);
assertHeaders();
}
@Test
public void headersWhenFrameOptionsDisableThenFrameOptionsNotWritten() {
expectedHeaders.remove(XFrameOptionsHttpHeadersWriter.X_FRAME_OPTIONS);
headers.frameOptions().disable();
assertHeaders();
}
@Test
public void headersWhenFrameOptionsModeThenFrameOptionsCustomMode() {
expectedHeaders.remove(XFrameOptionsHttpHeadersWriter.X_FRAME_OPTIONS);
expectedHeaders.add(XFrameOptionsHttpHeadersWriter.X_FRAME_OPTIONS, "SAMEORIGIN");
headers.frameOptions().mode(XFrameOptionsHttpHeadersWriter.Mode.SAMEORIGIN);
assertHeaders();
}
@Test
public void headersWhenXssProtectionDisableThenXssProtectionNotWritten() {
expectedHeaders.remove("X-Xss-Protection");
headers.xssProtection().disable();
assertHeaders();
}
private void assertHeaders() {
WebTestClient client = buildClient();
FluxExchangeResult<String> response = client.get()
.uri("https://example.com/")
.exchange()
.returnResult(String.class);
Map<String,List<String>> responseHeaders = response.getResponseHeaders();
ignoredHeaderNames.stream().forEach(responseHeaders::remove);
assertThat(responseHeaders).describedAs(response.toString()).isEqualTo(expectedHeaders);
}
private WebTestClient buildClient() {
return WebTestClientBuilder.bindToWebFilters(headers.build()).build();
}
}
@@ -0,0 +1,122 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.config.web.server;
import org.apache.http.HttpHeaders;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
import org.springframework.security.web.server.WebFilterChainFilter;
import org.springframework.security.web.server.context.SecurityContextRepository;
import org.springframework.security.web.server.context.WebSessionSecurityContextRepository;
import org.springframework.test.web.reactive.server.EntityExchangeResult;
import org.springframework.test.web.reactive.server.FluxExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Mono;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
/**
* @author Rob Winch
* @since 5.0
*/
@RunWith(MockitoJUnitRunner.class)
public class HttpSecurityTests {
@Mock
SecurityContextRepository contextRepository;
@Mock
ReactiveAuthenticationManager authenticationManager;
HttpSecurity http;
@Before
public void setup() {
http = HttpSecurity.http();
}
@Test
public void defaults() {
http.securityContextRepository(this.contextRepository);
WebTestClient client = buildClient();
FluxExchangeResult<String> result = client.get()
.uri("/")
.exchange()
.expectHeader().valueMatches(HttpHeaders.CACHE_CONTROL, ".+")
.returnResult(String.class);
assertThat(result.getResponseCookies()).isEmpty();
// there is no need to try and load the SecurityContext by default
verifyZeroInteractions(contextRepository);
}
@Test
public void basic() {
given(this.authenticationManager.authenticate(any())).willReturn(Mono.just(new TestingAuthenticationToken("rob", "rob", "ROLE_USER", "ROLE_ADMIN")));
http.securityContextRepository(new WebSessionSecurityContextRepository());
http.httpBasic();
http.authenticationManager(authenticationManager);
HttpSecurity.AuthorizeExchangeBuilder authorize = http.authorizeExchange();
authorize.anyExchange().authenticated();
WebTestClient client = buildClient();
EntityExchangeResult<String> result = client
.filter(basicAuthentication("rob", "rob"))
.get()
.uri("/")
.exchange()
.expectStatus().isOk()
.expectHeader().valueMatches(HttpHeaders.CACHE_CONTROL, ".+")
.expectBody(String.class).consumeWith(b -> assertThat(b.getResponseBody()).isEqualTo("ok"))
.returnResult();
assertThat(result.getResponseCookies().getFirst("SESSION")).isNotNull();
}
@Test
public void basicWhenNoCredentialsThenUnauthorized() {
http.authorizeExchange().anyExchange().authenticated();
WebTestClient client = buildClient();
client
.get()
.uri("/")
.exchange()
.expectStatus().isUnauthorized()
.expectHeader().valueMatches(HttpHeaders.CACHE_CONTROL, ".+")
.expectBody().isEmpty();
}
private WebTestClient buildClient() {
WebFilterChainFilter springSecurityFilterChain = WebFilterChainFilter.fromSecurityWebFilterChains(http.build());
return WebTestClientBuilder.bindToWebFilters(springSecurityFilterChain).build();
}
}
@@ -0,0 +1,19 @@
#
# /*
# * Copyright 2002-2017 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
# *
# * http://www.apache.org/licenses/LICENSE-2.0
# *
# * Unless required by applicable law or agreed to in writing, software
# * distributed under the License is distributed on an "AS IS" BASIS,
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# * See the License for the specific language governing permissions and
# * limitations under the License.
# */
#
user=password,ROLE_USER
-53
View File
@@ -1,53 +0,0 @@
// Core build file
// We don't define a module dependency on crypto to avoid creating a transitive dependency
def cryptoProject = project(':spring-security-crypto')
def cryptoClasses = cryptoProject.sourceSets.main.output
configurations {
included
compile.extendsFrom included
testCompile.exclude group: 'org.mockito', module: 'mockito-all'
}
dependencies {
compile springCoreDependency,
'aopalliance:aopalliance:1.0',
"org.springframework:spring-aop:$springVersion",
"org.springframework:spring-beans:$springVersion",
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-expression:$springVersion"
optional "net.sf.ehcache:ehcache:$ehcacheVersion",
'javax.annotation:jsr250-api:1.0',
"org.aspectj:aspectjrt:$aspectjVersion",
"org.springframework:spring-jdbc:$springVersion",
"org.springframework:spring-tx:$springVersion",
"com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion"
included cryptoProject
testCompile "commons-collections:commons-collections:$commonsCollectionsVersion",
"org.springframework:spring-test:$springVersion",
"org.slf4j:jcl-over-slf4j:$slf4jVersion",
powerMockDependencies,
"org.skyscreamer:jsonassert:$jsonassertVersion"
testRuntime "org.hsqldb:hsqldb:$hsqlVersion"
}
classes.doLast {
copy {
from cryptoClasses
into sourceSets.main.output.classesDir
}
}
sourceJar.from cryptoProject.sourceSets.main.java
configure(project.tasks.withType(Test)) {
systemProperties['springSecurityVersion'] = version
systemProperties['springVersion'] = springVersion
}
-314
View File
@@ -1,314 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<name>spring-security-core</name>
<description>spring-security-core</description>
<url>http://spring.io/spring-security</url>
<organization>
<name>spring.io</name>
<url>http://spring.io/</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>rwinch</id>
<name>Rob Winch</name>
<email>rwinch@gopivotal.com</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
<url>https://github.com/spring-projects/spring-security</url>
</scm>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.4</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.9.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.4</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>2.2.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-support</artifactId>
<version>1.6.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>1.6.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-common</artifactId>
<version>1.6.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-reflect</artifactId>
<version>1.6.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.3.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.7</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-all</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
+50
View File
@@ -0,0 +1,50 @@
apply plugin: 'io.spring.convention.spring-module'
def includeProject = project(':spring-security-crypto')
configurations {
included
compile.extendsFrom included
}
dependencies {
compile 'aopalliance:aopalliance'
compile 'org.springframework:spring-aop'
compile 'org.springframework:spring-beans'
compile 'org.springframework:spring-context'
compile 'org.springframework:spring-core'
compile 'org.springframework:spring-expression'
included includeProject
optional 'com.fasterxml.jackson.core:jackson-databind'
optional 'io.projectreactor:reactor-core'
optional 'javax.annotation:jsr250-api'
optional 'net.sf.ehcache:ehcache'
optional 'org.aspectj:aspectjrt'
optional 'org.springframework:spring-jdbc'
optional 'org.springframework:spring-tx'
testCompile powerMockDependencies
testCompile 'commons-collections:commons-collections'
testCompile 'io.projectreactor:reactor-test'
testCompile 'org.skyscreamer:jsonassert'
testCompile 'org.slf4j:jcl-over-slf4j'
testCompile 'org.springframework:spring-test'
testRuntime 'org.hsqldb:hsqldb'
}
classes.doLast {
copy {
from includeProject.sourceSets.main.output
into sourceSets.main.output.classesDir
}
}
tasks.sourcesJar.from {includeProject.sourceSets.main.java}
configure(project.tasks.withType(Test)) {
systemProperties['springSecurityVersion'] = version
systemProperties['springVersion'] = project.dependencyManagement.managedVersions['org.springframework:spring-core']
}
@@ -24,8 +24,8 @@ import java.util.Collections;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.AuthenticatedPrincipal;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
/**
* Base class for <code>Authentication</code> objects.
@@ -79,8 +79,8 @@ public abstract class AbstractAuthenticationToken implements Authentication,
}
public String getName() {
if (this.getPrincipal() instanceof UserDetails) {
return ((UserDetails) this.getPrincipal()).getUsername();
if (this.getPrincipal() instanceof AuthenticatedPrincipal) {
return ((AuthenticatedPrincipal) this.getPrincipal()).getName();
}
if (getPrincipal() instanceof Principal) {
@@ -0,0 +1,30 @@
/*
* Copyright 2002-2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authentication;
import org.springframework.security.core.Authentication;
import reactor.core.publisher.Mono;
/**
*
* @author Rob Winch
* @since 5.0
*/
public interface ReactiveAuthenticationManager {
Mono<Authentication> authenticate(Authentication authentication);
}
@@ -0,0 +1,53 @@
/*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authentication;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
/**
* Adapts an AuthenticationManager to the reactive APIs. This is somewhat necessary because many of the ways that
* credentials are stored (i.e. JDBC, LDAP, etc) do not have reactive implementations. What's more is it is generally
* considered best practice to store passwords in a hash that is intentionally slow which would block ever request
* from coming in unless it was put on another thread.
*
* @author Rob Winch
* @since 5.0
*/
public class ReactiveAuthenticationManagerAdapter implements ReactiveAuthenticationManager {
private final AuthenticationManager authenticationManager;
public ReactiveAuthenticationManagerAdapter(AuthenticationManager authenticationManager) {
Assert.notNull(authenticationManager, "authenticationManager cannot be null");
this.authenticationManager = authenticationManager;
}
@Override
public Mono<Authentication> authenticate(Authentication token) {
return Mono.just(token)
.publishOn(Schedulers.elastic())
.flatMap( t -> {
try {
return Mono.just(authenticationManager.authenticate(t));
} catch(Throwable error) {
return Mono.error(error);
}
})
.filter( a -> a.isAuthenticated());
}
}
@@ -0,0 +1,48 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.authentication;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsRepository;
import org.springframework.util.Assert;
import reactor.core.publisher.Mono;
/**
* @author Rob Winch
* @since 5.0
*/
public class UserDetailsRepositoryAuthenticationManager implements ReactiveAuthenticationManager {
private final UserDetailsRepository repository;
public UserDetailsRepositoryAuthenticationManager(UserDetailsRepository userDetailsRepository) {
Assert.notNull(userDetailsRepository, "userDetailsRepository cannot be null");
this.repository = userDetailsRepository;
}
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
final String username = authentication.getName();
return repository
.findByUsername(username)
.filter( u -> u.getPassword().equals(authentication.getCredentials()))
.switchIfEmpty( Mono.error(new BadCredentialsException("Invalid Credentials")) )
.map( u -> new UsernamePasswordAuthenticationToken(u, u.getPassword(), u.getAuthorities()) );
}
}
@@ -17,8 +17,8 @@
package org.springframework.security.authentication.encoding;
import java.security.MessageDigest;
import java.util.Base64;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.util.Assert;
@@ -107,13 +107,13 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
prefix = forceLowerCasePrefix ? SSHA_PREFIX_LC : SSHA_PREFIX;
}
return prefix + Utf8.decode(Base64.encode(hash));
return prefix + Utf8.decode(Base64.getEncoder().encode(hash));
}
private byte[] extractSalt(String encPass) {
String encPassNoLabel = encPass.substring(6);
byte[] hashAndSalt = Base64.decode(encPassNoLabel.getBytes());
byte[] hashAndSalt = Base64.getDecoder().decode(encPassNoLabel.getBytes());
int saltLength = hashAndSalt.length - SHA_LENGTH;
byte[] salt = new byte[saltLength];
System.arraycopy(hashAndSalt, SHA_LENGTH, salt, 0, saltLength);
@@ -15,7 +15,8 @@
*/
package org.springframework.security.authentication.encoding;
import org.springframework.security.crypto.codec.Base64;
import java.util.Base64;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.codec.Utf8;
@@ -57,7 +58,7 @@ public class Md4PasswordEncoder extends BaseDigestPasswordEncoder {
byte[] resBuf = md4.digest();
if (getEncodeHashAsBase64()) {
return Utf8.decode(Base64.encode(resBuf));
return Utf8.decode(Base64.getEncoder().encode(resBuf));
}
else {
return new String(Hex.encode(resBuf));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,8 @@ package org.springframework.security.authentication.encoding;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.util.Assert;
@@ -106,7 +106,7 @@ public class MessageDigestPasswordEncoder extends BaseDigestPasswordEncoder {
}
if (getEncodeHashAsBase64()) {
return Utf8.decode(Base64.encode(digest));
return Utf8.decode(Base64.getEncoder().encode(digest));
}
else {
return new String(Hex.encode(digest));
@@ -0,0 +1,42 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.authorization;
import org.springframework.security.core.Authentication;
import reactor.core.publisher.Mono;
/**
* @author Rob Winch
* @since 5.0
*/
public class AuthenticatedAuthorizationManager<T> implements ReactiveAuthorizationManager<T> {
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {
return authentication
.map(a -> new AuthorizationDecision(a.isAuthenticated()))
.defaultIfEmpty(new AuthorizationDecision(false));
}
public static <T> AuthenticatedAuthorizationManager<T> authenticated() {
return new AuthenticatedAuthorizationManager<>();
}
private AuthenticatedAuthorizationManager() {}
}
@@ -0,0 +1,56 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.authorization;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
import reactor.core.publisher.Mono;
/**
* @author Rob Winch
* @since 5.0
*/
public class AuthorityAuthorizationManager<T> implements ReactiveAuthorizationManager<T> {
private final String authority;
private AuthorityAuthorizationManager(String authority) {
this.authority = authority;
}
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {
return authentication
.filter(a -> a.isAuthenticated())
.flatMapIterable( a -> a.getAuthorities())
.map( g-> g.getAuthority())
.hasElement(this.authority)
.map( hasAuthority -> new AuthorizationDecision(hasAuthority))
.defaultIfEmpty(new AuthorizationDecision(false));
}
public static <T> AuthorityAuthorizationManager<T> hasAuthority(String authority) {
Assert.notNull(authority, "authority cannot be null");
return new AuthorityAuthorizationManager<>(authority);
}
public static <T> AuthorityAuthorizationManager<T> hasRole(String role) {
Assert.notNull(role, "role cannot be null");
return hasAuthority("ROLE_" + role);
}
}
@@ -0,0 +1,35 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.authorization;
/**
* @author Rob Winch
* @since 5.0
*/
public class AuthorizationDecision {
private final boolean granted;
public AuthorizationDecision(boolean granted) {
this.granted = granted;
}
public boolean isGranted() {
return granted;
}
}
@@ -0,0 +1,39 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.authorization;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import reactor.core.publisher.Mono;
/**
*
* @author Rob Winch
* @since 5.0
*/
public interface ReactiveAuthorizationManager<T> {
Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object);
default Mono<Void> verify(Mono<Authentication> authentication, T object) {
return check(authentication, object)
.filter( d -> d.isGranted())
.switchIfEmpty( Mono.error(new AccessDeniedException("Access Denied")) )
.flatMap( d -> Mono.empty() );
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2012-2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.core;
import org.springframework.security.authentication.AuthenticationManager;
/**
* Representation of an authenticated <code>Principal</code> once an
* {@link Authentication} request has been successfully authenticated
* by the {@link AuthenticationManager#authenticate(Authentication)} method.
*
* Implementors typically provide their own representation of a <code>Principal</code>,
* which usually contains information describing the <code>Principal</code> entity,
* such as, first/middle/last name, address, email, phone, id, etc.
*
* This interface allows implementors to expose specific attributes
* of their custom representation of <code>Principal</code> in a generic way.
*
* @author Joe Grandja
* @since 5.0
* @see Authentication#getPrincipal()
* @see org.springframework.security.core.userdetails.UserDetails
*/
public interface AuthenticatedPrincipal {
/**
* Returns the name of the authenticated <code>Principal</code>. Never <code>null</code>.
*
* @return the name of the authenticated <code>Principal</code>
*/
String getName();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -38,9 +38,9 @@ public class SpringSecurityCoreVersion {
* N.B. Classes are not intended to be serializable between different versions. See
* SEC-1709 for why we still need a serial version.
*/
public static final long SERIAL_VERSION_UID = 420L;
public static final long SERIAL_VERSION_UID = 500L;
static final String MIN_SPRING_VERSION = "4.3.5.RELEASE";
static final String MIN_SPRING_VERSION = "5.0.0.RC2";
static {
performVersionChecks();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,10 +16,10 @@
package org.springframework.security.core.token;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Date;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.util.Assert;
@@ -89,7 +89,7 @@ public class KeyBasedPersistenceTokenService implements TokenService, Initializi
// Compute key
String sha512Hex = Sha512DigestUtils.shaHex(content + ":" + serverSecret);
String keyPayload = content + ":" + sha512Hex;
String key = Utf8.decode(Base64.encode(Utf8.encode(keyPayload)));
String key = Utf8.decode(Base64.getEncoder().encode(Utf8.encode(keyPayload)));
return new DefaultToken(key, creationTime, extendedInformation);
}
@@ -99,7 +99,7 @@ public class KeyBasedPersistenceTokenService implements TokenService, Initializi
return null;
}
String[] tokens = StringUtils.delimitedListToStringArray(
Utf8.decode(Base64.decode(Utf8.encode(key))), ":");
Utf8.decode(Base64.getDecoder().decode(Utf8.encode(key))), ":");
Assert.isTrue(tokens.length >= 4, "Expected 4 or more tokens but found "
+ tokens.length);
@@ -0,0 +1,61 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.core.userdetails;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.util.Assert;
import reactor.core.publisher.Mono;
/**
*
* @author Rob Winch
* @since 5.0
*/
public class MapUserDetailsRepository implements UserDetailsRepository {
private final Map<String,UserDetails> users;
public MapUserDetailsRepository(Map<String,UserDetails> users) {
this.users = users;
}
public MapUserDetailsRepository(UserDetails... users) {
this(Arrays.asList(users));
}
public MapUserDetailsRepository(Collection<UserDetails> users) {
Assert.notEmpty(users, "users cannot be null or empty");
this.users = users.stream().collect(Collectors.toMap( u -> getKey(u.getName()), Function.identity()));
}
@Override
public Mono<UserDetails> findByUsername(String username) {
String key = getKey(username);
UserDetails result = users.get(key);
return result == null ? Mono.empty() : Mono.just(User.withUserDetails(result).build());
}
private String getKey(String username) {
return username.toLowerCase();
}
}
@@ -248,6 +248,16 @@ public class User implements UserDetails, CredentialsContainer {
return new UserBuilder().username(username);
}
public static UserBuilder withUserDetails(UserDetails userDetails) {
return withUsername(userDetails.getUsername())
.password(userDetails.getPassword())
.accountExpired(!userDetails.isAccountNonExpired())
.accountLocked(!userDetails.isAccountNonLocked())
.authorities(userDetails.getAuthorities())
.credentialsExpired(!userDetails.isCredentialsNonExpired())
.disabled(!userDetails.isEnabled());
}
/**
* Builds the user to be added. At minimum the username, password, and authorities
* should provided. The remaining attributes have reasonable defaults.
@@ -351,7 +361,7 @@ public class User implements UserDetails, CredentialsContainer {
* additional attributes for this user)
* @see #roles(String...)
*/
public UserBuilder authorities(List<? extends GrantedAuthority> authorities) {
public UserBuilder authorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = new ArrayList<GrantedAuthority>(authorities);
return this;
}
@@ -18,6 +18,7 @@ package org.springframework.security.core.userdetails;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.AuthenticatedPrincipal;
import java.io.Serializable;
import java.util.Collection;
@@ -41,7 +42,7 @@ import java.util.Collection;
*
* @author Ben Alex
*/
public interface UserDetails extends Serializable {
public interface UserDetails extends AuthenticatedPrincipal, Serializable {
// ~ Methods
// ========================================================================================================
@@ -60,8 +61,7 @@ public interface UserDetails extends Serializable {
String getPassword();
/**
* Returns the username used to authenticate the user. Cannot return <code>null</code>
* .
* Returns the username used to authenticate the user. Cannot return <code>null</code>.
*
* @return the username (never <code>null</code>)
*/
@@ -100,4 +100,14 @@ public interface UserDetails extends Serializable {
* @return <code>true</code> if the user is enabled, <code>false</code> otherwise
*/
boolean isEnabled();
/**
* Returns the name of the user. Cannot return <code>null</code>.
* The default implementation of this method returns {@link #getUsername()}.
*
* @return the name of the user (never <code>null</code>)
*/
default String getName() {
return getUsername();
}
}
@@ -0,0 +1,28 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.core.userdetails;
import org.springframework.security.core.userdetails.UserDetails;
import reactor.core.publisher.Mono;
public interface UserDetailsRepository {
Mono<UserDetails> findByUsername(String username);
}
@@ -16,17 +16,18 @@
package org.springframework.security.jackson2;
import com.fasterxml.jackson.annotation.JacksonAnnotation;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.jsontype.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.io.IOException;
import java.util.*;
/**
* This utility class will find all the SecurityModules in classpath.
@@ -65,7 +66,7 @@ public final class SecurityJackson2Modules {
if(mapper != null) {
TypeResolverBuilder<?> typeBuilder = mapper.getDeserializationConfig().getDefaultTyper(null);
if (typeBuilder == null) {
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
mapper.setDefaultTyping(createWhitelistedDefaultTyping());
}
}
}
@@ -103,4 +104,111 @@ public final class SecurityJackson2Modules {
}
return modules;
}
/**
* Creates a TypeResolverBuilder that performs whitelisting.
* @return a TypeResolverBuilder that performs whitelisting.
*/
private static TypeResolverBuilder<? extends TypeResolverBuilder> createWhitelistedDefaultTyping() {
TypeResolverBuilder<? extends TypeResolverBuilder> result = new WhitelistTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL);
result = result.init(JsonTypeInfo.Id.CLASS, null);
result = result.inclusion(JsonTypeInfo.As.PROPERTY);
return result;
}
/**
* An implementation of {@link ObjectMapper.DefaultTypeResolverBuilder} that overrides the {@link TypeIdResolver}
* with {@link WhitelistTypeIdResolver}.
* @author Rob Winch
*/
static class WhitelistTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder {
public WhitelistTypeResolverBuilder(ObjectMapper.DefaultTyping defaultTyping) {
super(defaultTyping);
}
protected TypeIdResolver idResolver(MapperConfig<?> config,
JavaType baseType, Collection<NamedType> subtypes, boolean forSer, boolean forDeser) {
TypeIdResolver result = super.idResolver(config, baseType, subtypes, forSer, forDeser);
return new WhitelistTypeIdResolver(result);
}
}
/**
* A {@link TypeIdResolver} that delegates to an existing implementation and throws an IllegalStateException if the
* class being looked up is not whitelisted, does not provide an explicit mixin, and is not annotated with Jackson
* mappings. See https://github.com/spring-projects/spring-security/issues/4370
*/
static class WhitelistTypeIdResolver implements TypeIdResolver {
private static final Set<String> WHITELIST_CLASS_NAMES = Collections.unmodifiableSet(new HashSet(Arrays.asList(
"java.util.ArrayList",
"java.util.Collections$EmptyMap",
"java.util.Date",
"java.util.TreeMap",
"org.springframework.security.core.context.SecurityContextImpl"
)));
private final TypeIdResolver delegate;
WhitelistTypeIdResolver(TypeIdResolver delegate) {
this.delegate = delegate;
}
@Override
public void init(JavaType baseType) {
delegate.init(baseType);
}
@Override
public String idFromValue(Object value) {
return delegate.idFromValue(value);
}
@Override
public String idFromValueAndType(Object value, Class<?> suggestedType) {
return delegate.idFromValueAndType(value, suggestedType);
}
@Override
public String idFromBaseType() {
return delegate.idFromBaseType();
}
@Override
public JavaType typeFromId(DatabindContext context, String id) throws IOException {
DeserializationConfig config = (DeserializationConfig) context.getConfig();
JavaType result = delegate.typeFromId(context, id);
String className = result.getRawClass().getName();
if(isWhitelisted(className)) {
return delegate.typeFromId(context, id);
}
boolean isExplicitMixin = config.findMixInClassFor(result.getRawClass()) != null;
if(isExplicitMixin) {
return result;
}
JacksonAnnotation jacksonAnnotation = AnnotationUtils.findAnnotation(result.getRawClass(), JacksonAnnotation.class);
if(jacksonAnnotation != null) {
return result;
}
throw new IllegalArgumentException("The class with " + id + " and name of " + className + " is not whitelisted. " +
"If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. " +
"If the serialization is only done by a trusted source, you can also enable default typing. " +
"See https://github.com/spring-projects/spring-security/issues/4370 for details");
}
private boolean isWhitelisted(String id) {
return WHITELIST_CLASS_NAMES.contains(id);
}
@Override
public String getDescForKnownTypeIds() {
return delegate.getDescForKnownTypeIds();
}
@Override
public JsonTypeInfo.Id getMechanism() {
return delegate.getMechanism();
}
}
}
@@ -17,8 +17,10 @@
package org.springframework.security.authentication;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.junit.*;
import org.springframework.security.core.AuthenticatedPrincipal;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -135,6 +137,18 @@ public class AbstractAuthenticationTokenTests {
assertThat(token.toString().lastIndexOf("Not granted any authorities") != -1).isTrue();
}
@Test
public void testGetNameWhenPrincipalIsAuthenticatedPrincipal() {
String principalName = "test";
AuthenticatedPrincipal principal = mock(AuthenticatedPrincipal.class);
when(principal.getName()).thenReturn(principalName);
MockAuthenticationImpl token = new MockAuthenticationImpl(principal, "Password", authorities);
assertThat(token.getName()).isEqualTo(principalName);
verify(principal, times(1)).getName();
}
// ~ Inner Classes
// ==================================================================================================
@@ -0,0 +1,89 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.authentication;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.Authentication;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;
/**
* @author Rob Winch
* @since 5.0
*/
@RunWith(MockitoJUnitRunner.class)
public class ReactiveAuthenticationManagerAdapterTests {
@Mock
AuthenticationManager delegate;
@Mock
Authentication authentication;
ReactiveAuthenticationManagerAdapter manager;
@Before
public void setup() {
manager = new ReactiveAuthenticationManagerAdapter(delegate);
}
@Test(expected = IllegalArgumentException.class)
public void constructorNullAuthenticationManager() {
new ReactiveAuthenticationManagerAdapter(null);
}
@Test
public void authenticateWhenSuccessThenSucces() {
when(delegate.authenticate(any())).thenReturn(authentication);
when(authentication.isAuthenticated()).thenReturn(true);
Authentication result = manager.authenticate(authentication).block();
assertThat(result).isEqualTo(authentication);
}
@Test
public void authenticateWhenReturnNotAuthenticatedThenError() {
when(delegate.authenticate(any())).thenReturn(authentication);
Authentication result = manager.authenticate(authentication).block();
assertThat(result).isNull();
}
@Test
public void authenticateWhenBadCredentialsThenError() {
when(delegate.authenticate(any())).thenThrow(new BadCredentialsException("Failed"));
when(authentication.isAuthenticated()).thenReturn(true);
Mono<Authentication> result = manager.authenticate(authentication);
StepVerifier.create(result)
.expectError(BadCredentialsException.class)
.verify();
}
}
@@ -0,0 +1,97 @@
/*
* Copyright 2002-2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authentication;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsRepository;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
/**
* @author Rob Winch
* @since 5.0
*/
@RunWith(MockitoJUnitRunner.class)
public class UserDetailsRepositoryAuthenticationManagerTests {
@Mock
UserDetailsRepository repository;
UserDetailsRepositoryAuthenticationManager manager;
String username;
String password;
@Before
public void setup() {
manager = new UserDetailsRepositoryAuthenticationManager(repository);
username = "user";
password = "pass";
}
@Test(expected = IllegalArgumentException.class)
public void constructorNullUserDetailsRepository() {
UserDetailsRepository udr = null;
new UserDetailsRepositoryAuthenticationManager(udr);
}
@Test
public void authenticateWhenUserNotFoundThenBadCredentials() {
when(repository.findByUsername(username)).thenReturn(Mono.empty());
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
Mono<Authentication> authentication = manager.authenticate(token);
StepVerifier
.create(authentication)
.expectError(BadCredentialsException.class)
.verify();
}
@Test
public void authenticateWhenPasswordNotEqualThenBadCredentials() {
User user = new User(username, password, AuthorityUtils.createAuthorityList("ROLE_USER"));
when(repository.findByUsername(user.getUsername())).thenReturn(Mono.just(user));
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password + "INVALID");
Mono<Authentication> authentication = manager.authenticate(token);
StepVerifier
.create(authentication)
.expectError(BadCredentialsException.class)
.verify();
}
@Test
public void authenticateWhenSuccessThenSuccess() {
User user = new User(username, password, AuthorityUtils.createAuthorityList("ROLE_USER"));
when(repository.findByUsername(user.getUsername())).thenReturn(Mono.just(user));
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
Authentication authentication = manager.authenticate(token).block();
assertThat(authentication).isEqualTo(authentication);
}
}
@@ -0,0 +1,77 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.authorization;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.security.core.Authentication;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
/**
* @author Rob Winch
* @since 5.0
*/
@RunWith(MockitoJUnitRunner.class)
public class AuthenticatedAuthorizationManagerTests {
@Mock
Authentication authentication;
AuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();
@Test
public void checkWhenAuthenticatedThenReturnTrue() {
when(authentication.isAuthenticated()).thenReturn(true);
boolean granted = manager.check(Mono.just(authentication), null).block().isGranted();
assertThat(granted).isTrue();
}
@Test
public void checkWhenNotAuthenticatedThenReturnFalse() {
boolean granted = manager.check(Mono.just(authentication), null).block().isGranted();
assertThat(granted).isFalse();
}
@Test
public void checkWhenEmptyThenReturnFalse() {
boolean granted = manager.check(Mono.empty(), null).block().isGranted();
assertThat(granted).isFalse();
}
@Test
public void checkWhenErrorThenError() {
Mono<AuthorizationDecision> result = manager.check(Mono.error(new RuntimeException("ooops")), null);
StepVerifier
.create(result)
.expectError()
.verify();
}
}
@@ -0,0 +1,133 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.authorization;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.util.Collection;
import java.util.Collections;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static org.springframework.security.core.authority.AuthorityUtils.createAuthorityList;
/**
* @author Rob Winch
* @since 5.0
*/
@RunWith(MockitoJUnitRunner.class)
public class AuthorityAuthorizationManagerTests {
@Mock
Authentication authentication;
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAuthority("ADMIN");
@Test
public void checkWhenHasAuthorityAndNotAuthenticatedThenReturnFalse() {
boolean granted = manager.check(Mono.just(authentication), null).block().isGranted();
assertThat(granted).isFalse();
}
@Test
public void checkWhenHasAuthorityAndEmptyThenReturnFalse() {
boolean granted = manager.check(Mono.empty(), null).block().isGranted();
assertThat(granted).isFalse();
}
@Test
public void checkWhenHasAuthorityAndErrorThenError() {
Mono<AuthorizationDecision> result = manager.check(Mono.error(new RuntimeException("ooops")), null);
StepVerifier
.create(result)
.expectError()
.verify();
}
@Test
public void checkWhenHasAuthorityAndAuthenticatedAndNoAuthoritiesThenReturnFalse() {
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getAuthorities()).thenReturn(Collections.emptyList());
boolean granted = manager.check(Mono.just(authentication), null).block().isGranted();
assertThat(granted).isFalse();
}
@Test
public void checkWhenHasAuthorityAndAuthenticatedAndWrongAuthoritiesThenReturnFalse() {
authentication = new TestingAuthenticationToken("rob", "secret", "ROLE_ADMIN");
boolean granted = manager.check(Mono.just(authentication), null).block().isGranted();
assertThat(granted).isFalse();
}
@Test
public void checkWhenHasAuthorityAndAuthorizedThenReturnTrue() {
authentication = new TestingAuthenticationToken("rob", "secret", "ADMIN");
boolean granted = manager.check(Mono.just(authentication), null).block().isGranted();
assertThat(granted).isTrue();
}
@Test
public void checkWhenHasRoleAndAuthorizedThenReturnTrue() {
manager = AuthorityAuthorizationManager.hasRole("ADMIN");
authentication = new TestingAuthenticationToken("rob", "secret", "ROLE_ADMIN");
boolean granted = manager.check(Mono.just(authentication), null).block().isGranted();
assertThat(granted).isTrue();
}
@Test
public void checkWhenHasRoleAndNotAuthorizedThenReturnTrue() {
manager = AuthorityAuthorizationManager.hasRole("ADMIN");
authentication = new TestingAuthenticationToken("rob", "secret", "ADMIN");
boolean granted = manager.check(Mono.just(authentication), null).block().isGranted();
assertThat(granted).isFalse();
}
@Test(expected = IllegalArgumentException.class)
public void hasRoleWhenNullThenException() {
String role = null;
AuthorityAuthorizationManager.hasRole(role);
}
@Test(expected = IllegalArgumentException.class)
public void hasAuthorityWhenNullThenException() {
String authority = null;
AuthorityAuthorizationManager.hasAuthority(authority);
}
}
@@ -15,7 +15,7 @@
*/
package org.springframework.security.concurrent;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -15,11 +15,11 @@
*/
package org.springframework.security.core;
import org.junit.Test;
import java.io.DataInputStream;
import java.io.InputStream;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -29,7 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class JavaVersionTests {
private static final int JDK6_CLASS_VERSION = 50;
private static final int JDK8_CLASS_VERSION = 52;
@Test
public void authenticationCorrectJdkCompatibility() throws Exception {
@@ -45,7 +45,7 @@ public class JavaVersionTests {
data.readInt();
data.readShort(); // minor
int major = data.readShort();
assertThat(major).isEqualTo(JDK6_CLASS_VERSION);
assertThat(major).isEqualTo(JDK8_CLASS_VERSION);
}
finally {
try {
@@ -0,0 +1,79 @@
/*
*
* * Copyright 2002-2017 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
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.springframework.security.core.userdetails;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import org.junit.Test;
import org.springframework.security.core.userdetails.MapUserDetailsRepository;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import reactor.core.publisher.Mono;
public class MapUserDetailsRepositoryTests {
private static final UserDetails USER_DETAILS = User.withUsername("user")
.password("password")
.roles("USER")
.build();
private MapUserDetailsRepository users = new MapUserDetailsRepository(Arrays.asList(USER_DETAILS));
@Test(expected = IllegalArgumentException.class)
public void constructorNullUsers() {
Collection<UserDetails> users = null;
new MapUserDetailsRepository(users);
}
@Test(expected = IllegalArgumentException.class)
public void constructorEmptyUsers() {
Collection<UserDetails> users = Collections.emptyList();
new MapUserDetailsRepository(users);
}
@Test
public void findByUsernameWhenFoundThenReturns() {
assertThat((users.findByUsername(USER_DETAILS.getUsername()).block())).isEqualTo(USER_DETAILS);
}
@Test
public void findByUsernameWhenDifferentCaseThenReturns() {
assertThat((users.findByUsername("uSeR").block())).isEqualTo(USER_DETAILS);
}
@Test
public void findByUsernameWhenClearCredentialsThenFindByUsernameStillHasCredentials() {
User foundUser = users.findByUsername(USER_DETAILS.getUsername()).cast(User.class).block();
assertThat(foundUser.getPassword()).isNotEmpty();
foundUser.eraseCredentials();
assertThat(foundUser.getPassword()).isNull();
foundUser = users.findByUsername(USER_DETAILS.getUsername()).cast(User.class).block();
assertThat(foundUser.getPassword()).isNotEmpty();
}
@Test
public void findByUsernameWhenNotFoundThenEmpty() {
assertThat((users.findByUsername("notfound"))).isEqualTo(Mono.empty());
}
}
@@ -0,0 +1,81 @@
/*
* Copyright 2012-2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.core.userdetails;
import org.junit.Test;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import java.util.Collection;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests {@link UserDetails}
*
* @author Joe Grandja
*/
public class UserDetailsTest {
@Test
public void getNameWhenCalledThenDefaultToGetUsername() {
UserDetails userDetails = new MockUserDetails("joeg");
assertThat(userDetails.getName()).isEqualTo(userDetails.getUsername());
}
private class MockUserDetails implements UserDetails {
private final String username;
private MockUserDetails(String username) {
this.username = username;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.NO_AUTHORITIES;
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
}
@@ -146,4 +146,35 @@ public class UserTests {
out.writeObject(user);
out.close();
}
@Test
public void withUserDetailsWhenAllEnabled() throws Exception {
User expected = new User("rob","pass", true, true, true, true, ROLE_12);
UserDetails actual = User.withUserDetails(expected).build();
assertThat(actual.getUsername()).isEqualTo(expected.getUsername());
assertThat(actual.getPassword()).isEqualTo(expected.getPassword());
assertThat(actual.getAuthorities()).isEqualTo(expected.getAuthorities());
assertThat(actual.isAccountNonExpired()).isEqualTo(expected.isAccountNonExpired());
assertThat(actual.isAccountNonLocked()).isEqualTo(expected.isAccountNonLocked());
assertThat(actual.isCredentialsNonExpired()).isEqualTo(expected.isCredentialsNonExpired());
assertThat(actual.isEnabled()).isEqualTo(expected.isEnabled());
}
@Test
public void withUserDetailsWhenAllDisabled() throws Exception {
User expected = new User("rob","pass", false, false, false, false, ROLE_12);
UserDetails actual = User.withUserDetails(expected).build();
assertThat(actual.getUsername()).isEqualTo(expected.getUsername());
assertThat(actual.getPassword()).isEqualTo(expected.getPassword());
assertThat(actual.getAuthorities()).isEqualTo(expected.getAuthorities());
assertThat(actual.isAccountNonExpired()).isEqualTo(expected.isAccountNonExpired());
assertThat(actual.isAccountNonLocked()).isEqualTo(expected.isAccountNonLocked());
assertThat(actual.isCredentialsNonExpired()).isEqualTo(expected.isCredentialsNonExpired());
assertThat(actual.isEnabled()).isEqualTo(expected.isEnabled());
}
}
@@ -0,0 +1,123 @@
/*
* Copyright 2015-2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.jackson2;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonIgnoreType;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
import java.lang.annotation.*;
import static org.assertj.core.api.Assertions.*;
/**
* @author Rob Winch
* @since 5.0
*/
public class SecurityJackson2ModulesTests {
private ObjectMapper mapper;
@Before
public void setup() {
mapper = new ObjectMapper();
SecurityJackson2Modules.enableDefaultTyping(mapper);
}
@Test
public void readValueWhenNotWhitelistedOrMappedThenThrowsException() throws Exception {
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelisted\",\"property\":\"bar\"}";
assertThatThrownBy(() -> {
mapper.readValue(content, Object.class);
}
).hasStackTraceContaining("whitelisted");
}
@Test
public void readValueWhenExplicitDefaultTypingAfterSecuritySetupThenReadsAsSpecificType() throws Exception {
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelisted\",\"property\":\"bar\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotWhitelisted.class);
}
@Test
public void readValueWhenExplicitDefaultTypingBeforeSecuritySetupThenReadsAsSpecificType() throws Exception {
mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
SecurityJackson2Modules.enableDefaultTyping(mapper);
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelisted\",\"property\":\"bar\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotWhitelisted.class);
}
@Test
public void readValueWhenAnnotatedThenReadsAsSpecificType() throws Exception {
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelistedButAnnotated\",\"property\":\"bar\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotWhitelistedButAnnotated.class);
}
@Test
public void readValueWhenMixinProvidedThenReadsAsSpecificType() throws Exception {
mapper.addMixIn(NotWhitelisted.class, NotWhitelistedMixin.class);
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelisted\",\"property\":\"bar\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotWhitelisted.class);
}
@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NotJacksonAnnotation {}
@NotJacksonAnnotation
static class NotWhitelisted {
private String property = "bar";
public String getProperty() {
return property;
}
public void setProperty(String property) {
}
}
@JsonIgnoreType(false)
static class NotWhitelistedButAnnotated {
private String property = "bar";
public String getProperty() {
return property;
}
public void setProperty(String property) {
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class NotWhitelistedMixin {
}
}
-3
View File
@@ -1,3 +0,0 @@
dependencies {
optional 'org.bouncycastle:bcpkix-jdk15on:1.54'
}
-112
View File
@@ -1,112 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<name>spring-security-crypto</name>
<description>spring-security-crypto</description>
<url>http://spring.io/spring-security</url>
<organization>
<name>spring.io</name>
<url>http://spring.io/</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>rwinch</id>
<name>Rob Winch</name>
<email>rwinch@gopivotal.com</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
<url>https://github.com/spring-projects/spring-security</url>
</scm>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.54</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
+6
View File
@@ -0,0 +1,6 @@
apply plugin: 'io.spring.convention.spring-module'
dependencies {
optional 'org.springframework:spring-jcl'
optional 'org.bouncycastle:bcpkix-jdk15on'
}
@@ -1,646 +0,0 @@
/*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.crypto.codec;
/**
* Base64 encoder which is a reduced version of Robert Harder's public domain
* implementation (version 2.3.7). See <a
* href="http://iharder.net/base64">http://iharder.net/base64</a> for more information.
* <p>
* For internal use only.
*
* @author Luke Taylor
* @since 3.0
*/
public final class Base64 {
/** No options specified. Value is zero. */
public final static int NO_OPTIONS = 0;
/** Specify encoding in first bit. Value is one. */
public final static int ENCODE = 1;
/** Specify decoding in first bit. Value is zero. */
public final static int DECODE = 0;
/** Do break lines when encoding. Value is 8. */
public final static int DO_BREAK_LINES = 8;
/**
* Encode using Base64-like encoding that is URL- and Filename-safe as described in
* Section 4 of RFC3548: <a
* href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs
* .org/rfcs/rfc3548.html</a>. It is important to note that data encoded this way is
* <em>not</em> officially valid Base64, or at the very least should not be called
* Base64 without also specifying that is was encoded using the URL- and Filename-safe
* dialect.
*/
public final static int URL_SAFE = 16;
/**
* Encode using the special "ordered" dialect of Base64 described here: <a
* href="http://www.faqs.org/qa/rfcc-1940.html"
* >http://www.faqs.org/qa/rfcc-1940.html</a>.
*/
public final static int ORDERED = 32;
/** Maximum line length (76) of Base64 output. */
private final static int MAX_LINE_LENGTH = 76;
/** The equals sign (=) as a byte. */
private final static byte EQUALS_SIGN = (byte) '=';
/** The new line character (\n) as a byte. */
private final static byte NEW_LINE = (byte) '\n';
private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */
/** The 64 valid Base64 values. */
/* Host platform me be something funny like EBCDIC, so we hardcode these values. */
private final static byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B',
(byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H',
(byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
(byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
(byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
(byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
(byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
(byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
(byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
(byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
(byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
(byte) '+', (byte) '/' };
/**
* Translates a Base64 value to either its 6-bit reconstruction value or a negative
* number indicating some other meaning.
**/
private final static byte[] _STANDARD_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9,
-9, // Decimal 0 - 8
-5, -5, // Whitespace: Tab and Linefeed
-9, -9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
-9, -9, -9, -9, -9, // Decimal 27 - 31
-5, // Whitespace: Space
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
62, // Plus sign at decimal 43
-9, -9, -9, // Decimal 44 - 46
63, // Slash at decimal 47
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
-9, -9, -9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9, -9, -9, // Decimal 62 - 64
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
-9, -9, -9, -9, -9, -9, // Decimal 91 - 96
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
-9, -9, -9, -9, -9 // Decimal 123 - 127
, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
};
/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */
/**
* Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: <a
* href
* ="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
* Notice that the last two bytes become "hyphen" and "underscore" instead of "plus"
* and "slash."
*/
private final static byte[] _URL_SAFE_ALPHABET = { (byte) 'A', (byte) 'B',
(byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H',
(byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
(byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
(byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
(byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
(byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
(byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
(byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
(byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
(byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
(byte) '-', (byte) '_' };
/**
* Used in decoding URL- and Filename-safe dialects of Base64.
*/
private final static byte[] _URL_SAFE_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9,
-9, // Decimal 0 - 8
-5, -5, // Whitespace: Tab and Linefeed
-9, -9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
-9, -9, -9, -9, -9, // Decimal 27 - 31
-5, // Whitespace: Space
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
-9, // Plus sign at decimal 43
-9, // Decimal 44
62, // Minus sign at decimal 45
-9, // Decimal 46
-9, // Slash at decimal 47
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
-9, -9, -9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9, -9, -9, // Decimal 62 - 64
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
-9, -9, -9, -9, // Decimal 91 - 94
63, // Underscore at decimal 95
-9, // Decimal 96
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
-9, -9, -9, -9, -9 // Decimal 123 - 127
, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
};
/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */
/**
* I don't get the point of this technique, but someone requested it, and it is
* described here: <a
* href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/
* qa/rfcc-1940.html</a>.
*/
private final static byte[] _ORDERED_ALPHABET = { (byte) '-', (byte) '0', (byte) '1',
(byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
(byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D',
(byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J',
(byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
(byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V',
(byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) '_', (byte) 'a',
(byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g',
(byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm',
(byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's',
(byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
(byte) 'z' };
/**
* Used in decoding the "ordered" dialect of Base64.
*/
private final static byte[] _ORDERED_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9,
-9, // Decimal 0 - 8
-5, -5, // Whitespace: Tab and Linefeed
-9, -9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
-9, -9, -9, -9, -9, // Decimal 27 - 31
-5, // Whitespace: Space
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
-9, // Plus sign at decimal 43
-9, // Decimal 44
0, // Minus sign at decimal 45
-9, // Decimal 46
-9, // Slash at decimal 47
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine
-9, -9, -9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9, -9, -9, // Decimal 62 - 64
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A' through 'M'
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N' through 'Z'
-9, -9, -9, -9, // Decimal 91 - 94
37, // Underscore at decimal 95
-9, // Decimal 96
38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a' through 'm'
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n' through 'z'
-9, -9, -9, -9, -9 // Decimal 123 - 127
, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
};
public static byte[] decode(byte[] bytes) {
return decode(bytes, 0, bytes.length, NO_OPTIONS);
}
public static byte[] encode(byte[] bytes) {
return encodeBytesToBytes(bytes, 0, bytes.length, NO_OPTIONS);
}
public static boolean isBase64(byte[] bytes) {
try {
decode(bytes);
}
catch (InvalidBase64CharacterException e) {
return false;
}
return true;
}
/**
* Returns one of the _SOMETHING_ALPHABET byte arrays depending on the options
* specified. It's possible, though silly, to specify ORDERED <b>and</b> URLSAFE in
* which case one of them will be picked, though there is no guarantee as to which one
* will be picked.
*/
private static byte[] getAlphabet(int options) {
if ((options & URL_SAFE) == URL_SAFE) {
return _URL_SAFE_ALPHABET;
}
else if ((options & ORDERED) == ORDERED) {
return _ORDERED_ALPHABET;
}
else {
return _STANDARD_ALPHABET;
}
}
/**
* Returns one of the _SOMETHING_DECODABET byte arrays depending on the options
* specified. It's possible, though silly, to specify ORDERED and URL_SAFE in which
* case one of them will be picked, though there is no guarantee as to which one will
* be picked.
*/
private static byte[] getDecodabet(int options) {
if ((options & URL_SAFE) == URL_SAFE) {
return _URL_SAFE_DECODABET;
}
else if ((options & ORDERED) == ORDERED) {
return _ORDERED_DECODABET;
}
else {
return _STANDARD_DECODABET;
}
}
/* ******** E N C O D I N G M E T H O D S ******** */
/**
* <p>
* Encodes up to three bytes of the array <var>source</var> and writes the resulting
* four Base64 bytes to <var>destination</var>. The source and destination arrays can
* be manipulated anywhere along their length by specifying <var>srcOffset</var> and
* <var>destOffset</var>. This method does not check to make sure your arrays are
* large enough to accomodate <var>srcOffset</var> + 3 for the <var>source</var> array
* or <var>destOffset</var> + 4 for the <var>destination</var> array. The actual
* number of significant bytes in your array is given by <var>numSigBytes</var>.
* </p>
* <p>
* This is the lowest level of the encoding methods with all possible parameters.
* </p>
*
* @param source the array to convert
* @param srcOffset the index where conversion begins
* @param numSigBytes the number of significant bytes in your array
* @param destination the array to hold the conversion
* @param destOffset the index where output will be put
* @return the <var>destination</var> array
* @since 1.3
*/
private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes,
byte[] destination, int destOffset, int options) {
byte[] ALPHABET = getAlphabet(options);
// 1 2 3
// 01234567890123456789012345678901 Bit position
// --------000000001111111122222222 Array position from threeBytes
// --------| || || || | Six bit groups to index ALPHABET
// >>18 >>12 >> 6 >> 0 Right shift necessary
// 0x3f 0x3f 0x3f Additional AND
// Create buffer with zero-padding if there are only one or two
// significant bytes passed in the array.
// We have to shift left 24 in order to flush out the 1's that appear
// when Java treats a value as negative that is cast from a byte to an int.
int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
| (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
| (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
switch (numSigBytes) {
case 3:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
return destination;
case 2:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
case 1:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = EQUALS_SIGN;
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
default:
return destination;
}
}
/**
*
* @param source The data to convert
* @param off Offset in array where conversion should begin
* @param len Length of data to convert
* @param options Specified options
* @return The Base64-encoded data as a String
* @see Base64#DO_BREAK_LINES
* @throws java.io.IOException if there is an error
* @throws NullPointerException if source array is null
* @throws IllegalArgumentException if source array, offset, or length are invalid
* @since 2.3.1
*/
private static byte[] encodeBytesToBytes(byte[] source, int off, int len, int options) {
if (source == null) {
throw new NullPointerException("Cannot serialize a null array.");
} // end if: null
if (off < 0) {
throw new IllegalArgumentException("Cannot have negative offset: " + off);
} // end if: off < 0
if (len < 0) {
throw new IllegalArgumentException("Cannot have length offset: " + len);
} // end if: len < 0
if (off + len > source.length) {
throw new IllegalArgumentException(String.format(
"Cannot have offset of %d and length of %d with array of length %d",
off, len, source.length));
} // end if: off < 0
boolean breakLines = (options & DO_BREAK_LINES) > 0;
// int len43 = len * 4 / 3;
// byte[] outBuff = new byte[ ( len43 ) // Main 4:3
// + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
// + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
// Try to determine more precisely how big the array needs to be.
// If we get it right, we don't have to do an array copy, and
// we save a bunch of memory.
int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0); // Bytes needed for actual
// encoding
if (breakLines) {
encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters
}
byte[] outBuff = new byte[encLen];
int d = 0;
int e = 0;
int len2 = len - 2;
int lineLength = 0;
for (; d < len2; d += 3, e += 4) {
encode3to4(source, d + off, 3, outBuff, e, options);
lineLength += 4;
if (breakLines && lineLength >= MAX_LINE_LENGTH) {
outBuff[e + 4] = NEW_LINE;
e++;
lineLength = 0;
} // end if: end of line
} // en dfor: each piece of array
if (d < len) {
encode3to4(source, d + off, len - d, outBuff, e, options);
e += 4;
} // end if: some padding needed
// Only resize array if we didn't guess it right.
if (e <= outBuff.length - 1) {
byte[] finalOut = new byte[e];
System.arraycopy(outBuff, 0, finalOut, 0, e);
// System.err.println("Having to resize array from " + outBuff.length + " to "
// + e );
return finalOut;
}
else {
// System.err.println("No need to resize array.");
return outBuff;
}
}
/* ******** D E C O D I N G M E T H O D S ******** */
/**
* Decodes four bytes from array <var>source</var> and writes the resulting bytes (up
* to three of them) to <var>destination</var>. The source and destination arrays can
* be manipulated anywhere along their length by specifying <var>srcOffset</var> and
* <var>destOffset</var>. This method does not check to make sure your arrays are
* large enough to accomodate <var>srcOffset</var> + 4 for the <var>source</var> array
* or <var>destOffset</var> + 3 for the <var>destination</var> array. This method
* returns the actual number of bytes that were converted from the Base64 encoding.
* <p>
* This is the lowest level of the decoding methods with all possible parameters.
* </p>
*
*
* @param source the array to convert
* @param srcOffset the index where conversion begins
* @param destination the array to hold the conversion
* @param destOffset the index where output will be put
* @param options alphabet type is pulled from this (standard, url-safe, ordered)
* @return the number of decoded bytes converted
* @throws NullPointerException if source or destination arrays are null
* @throws IllegalArgumentException if srcOffset or destOffset are invalid or there is
* not enough room in the array.
* @since 1.3
*/
private static int decode4to3(final byte[] source, final int srcOffset,
final byte[] destination, final int destOffset, final int options) {
// Lots of error checking and exception throwing
if (source == null) {
throw new NullPointerException("Source array was null.");
} // end if
if (destination == null) {
throw new NullPointerException("Destination array was null.");
} // end if
if (srcOffset < 0 || srcOffset + 3 >= source.length) {
throw new IllegalArgumentException(
String.format(
"Source array with length %d cannot have offset of %d and still process four bytes.",
source.length, srcOffset));
} // end if
if (destOffset < 0 || destOffset + 2 >= destination.length) {
throw new IllegalArgumentException(
String.format(
"Destination array with length %d cannot have offset of %d and still store three bytes.",
destination.length, destOffset));
} // end if
byte[] DECODABET = getDecodabet(options);
// Example: Dk==
if (source[srcOffset + 2] == EQUALS_SIGN) {
// Two ways to do the same thing. Don't know which way I like best.
// int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
// | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
| ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
destination[destOffset] = (byte) (outBuff >>> 16);
return 1;
}
// Example: DkL=
else if (source[srcOffset + 3] == EQUALS_SIGN) {
// Two ways to do the same thing. Don't know which way I like best.
// int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
| ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
| ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
destination[destOffset] = (byte) (outBuff >>> 16);
destination[destOffset + 1] = (byte) (outBuff >>> 8);
return 2;
}
// Example: DkLE
else {
// Two ways to do the same thing. Don't know which way I like best.
// int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
// | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
| ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
| ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
| ((DECODABET[source[srcOffset + 3]] & 0xFF));
destination[destOffset] = (byte) (outBuff >> 16);
destination[destOffset + 1] = (byte) (outBuff >> 8);
destination[destOffset + 2] = (byte) (outBuff);
return 3;
}
}
/**
* Low-level access to decoding ASCII characters in the form of a byte array.
* <strong>Ignores GUNZIP option, if it's set.</strong> This is not generally a
* recommended method, although it is used internally as part of the decoding process.
* Special case: if len = 0, an empty array is returned. Still, if you need more speed
* and reduced memory footprint (and aren't gzipping), consider this method.
*
* @param source The Base64 encoded data
* @param off The offset of where to begin decoding
* @param len The length of characters to decode
* @param options Can specify options such as alphabet type to use
* @return decoded data
* @throws IllegalArgumentException If bogus characters exist in source data
*/
private static byte[] decode(final byte[] source, final int off, final int len,
final int options) {
// Lots of error checking and exception throwing
if (source == null) {
throw new NullPointerException("Cannot decode null source array.");
} // end if
if (off < 0 || off + len > source.length) {
throw new IllegalArgumentException(
String.format(
"Source array with length %d cannot have offset of %d and process %d bytes.",
source.length, off, len));
} // end if
if (len == 0) {
return new byte[0];
}
else if (len < 4) {
throw new IllegalArgumentException(
"Base64-encoded string must have at least four characters, but length specified was "
+ len);
} // end if
byte[] DECODABET = getDecodabet(options);
int len34 = len * 3 / 4; // Estimate on array size
byte[] outBuff = new byte[len34]; // Upper limit on size of output
int outBuffPosn = 0; // Keep track of where we're writing
byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space
int b4Posn = 0; // Keep track of four byte input buffer
int i = 0; // Source array counter
byte sbiDecode = 0; // Special value from DECODABET
for (i = off; i < off + len; i++) { // Loop through source
sbiDecode = DECODABET[source[i] & 0xFF];
// White space, Equals sign, or legit Base64 character
// Note the values such as -5 and -9 in the
// DECODABETs at the top of the file.
if (sbiDecode >= WHITE_SPACE_ENC) {
if (sbiDecode >= EQUALS_SIGN_ENC) {
b4[b4Posn++] = source[i]; // Save non-whitespace
if (b4Posn > 3) { // Time to decode?
outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options);
b4Posn = 0;
// If that was the equals sign, break out of 'for' loop
if (source[i] == EQUALS_SIGN) {
break;
}
}
}
}
else {
// There's a bad input character in the Base64 stream.
throw new InvalidBase64CharacterException(String.format(
"Bad Base64 input character decimal %d in array position %d",
((int) source[i]) & 0xFF, i));
}
}
byte[] out = new byte[outBuffPosn];
System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
return out;
}
}
class InvalidBase64CharacterException extends IllegalArgumentException {
InvalidBase64CharacterException(String message) {
super(message);
}
}
@@ -1,186 +1,187 @@
/*
* Copyright 2002-2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.crypto.scrypt;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.crypto.generators.SCrypt;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.security.crypto.keygen.KeyGenerators;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* <p>
* Implementation of PasswordEncoder that uses the SCrypt hashing function.
* Clients can optionally supply a cpu cost parameter, a memory cost parameter
* and a parallelization parameter.
* </p>
*
* <p>
* A few <a href=
* "http://bouncy-castle.1462172.n4.nabble.com/Java-Bouncy-Castle-scrypt-implementation-td4656832.html">
* warnings</a>:
* </p>
*
* <ul>
* <li>The currently implementation uses Bouncy castle which does not exploit
* parallelism/optimizations that password crackers will, so there is an
* unnecessary asymmetry between attacker and defender.</li>
* <li>Scrypt is based on Salsa20 which performs poorly in Java (on par with
* AES) but performs awesome (~4-5x faster) on SIMD capable platforms</li>
* <li>While there are some that would disagree, consider reading -
* <a href="http://blog.ircmaxell.com/2014/03/why-i-dont-recommend-scrypt.html">
* Why I Don't Recommend Scrypt</a> (for password storage)</li>
* </ul>
*
* @author Shazin Sadakath
* @author Rob Winch
*
*/
public class SCryptPasswordEncoder implements PasswordEncoder {
private final Log logger = LogFactory.getLog(getClass());
private final int cpuCost;
private final int memoryCost;
private final int parallelization;
private final int keyLength;
private final BytesKeyGenerator saltGenerator;
public SCryptPasswordEncoder() {
this(16384, 8, 1, 32, 64);
}
/**
* Creates a new instance
*
* @param cpuCost
* cpu cost of the algorithm (as defined in scrypt this is N).
* must be power of 2 greater than 1. Default is currently 16,348
* or 2^14)
* @param memoryCost
* memory cost of the algorithm (as defined in scrypt this is r)
* Default is currently 8.
* @param parallelization
* the parallelization of the algorithm (as defined in scrypt
* this is p) Default is currently 1. Note that the
* implementation does not currently take advantage of
* parallelization.
* @param keyLength
* key length for the algorithm (as defined in scrypt this is
* dkLen). The default is currently 32.
* @param saltLength
* salt length (as defined in scrypt this is the length of S).
* The default is currently 64.
*/
public SCryptPasswordEncoder(int cpuCost, int memoryCost, int parallelization, int keyLength, int saltLength) {
if (cpuCost <= 1) {
throw new IllegalArgumentException("Cpu cost parameter must be > 1.");
}
if (memoryCost == 1 && cpuCost > 65536) {
throw new IllegalArgumentException("Cpu cost parameter must be > 1 and < 65536.");
}
if (memoryCost < 1) {
throw new IllegalArgumentException("Memory cost must be >= 1.");
}
int maxParallel = Integer.MAX_VALUE / (128 * memoryCost * 8);
if (parallelization < 1 || parallelization > maxParallel) {
throw new IllegalArgumentException("Parallelisation parameter p must be >= 1 and <= " + maxParallel
+ " (based on block size r of " + memoryCost + ")");
}
if (keyLength < 1 || keyLength > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Key length must be >= 1 and <= " + Integer.MAX_VALUE);
}
if (saltLength < 1 || saltLength > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Salt length must be >= 1 and <= " + Integer.MAX_VALUE);
}
this.cpuCost = cpuCost;
this.memoryCost = memoryCost;
this.parallelization = parallelization;
this.keyLength = keyLength;
this.saltGenerator = KeyGenerators.secureRandom(saltLength);
}
public String encode(CharSequence rawPassword) {
return digest(rawPassword, saltGenerator.generateKey());
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (encodedPassword == null || encodedPassword.length() < keyLength) {
logger.warn("Empty encoded password");
return false;
}
return decodeAndCheckMatches(rawPassword, encodedPassword);
}
private boolean decodeAndCheckMatches(CharSequence rawPassword, String encodedPassword) {
String[] parts = encodedPassword.split("\\$");
if (parts.length != 4) {
return false;
}
long params = Long.parseLong(parts[1], 16);
byte[] salt = decodePart(parts[2]);
byte[] derived = decodePart(parts[3]);
int cpuCost = (int) Math.pow(2, params >> 16 & 0xffff);
int memoryCost = (int) params >> 8 & 0xff;
int parallelization = (int) params & 0xff;
byte[] generated = SCrypt.generate(Utf8.encode(rawPassword), salt, cpuCost, memoryCost, parallelization,
keyLength);
if (derived.length != generated.length) {
return false;
}
int result = 0;
for (int i = 0; i < derived.length; i++) {
result |= derived[i] ^ generated[i];
}
return result == 0;
}
private String digest(CharSequence rawPassword, byte[] salt) {
byte[] derived = SCrypt.generate(Utf8.encode(rawPassword), salt, cpuCost, memoryCost, parallelization, keyLength);
String params = Long
.toString(((int) (Math.log(cpuCost) / Math.log(2)) << 16L) | memoryCost << 8 | parallelization, 16);
StringBuilder sb = new StringBuilder((salt.length + derived.length) * 2);
sb.append("$").append(params).append('$');
sb.append(encodePart(salt)).append('$');
sb.append(encodePart(derived));
return sb.toString();
}
private byte[] decodePart(String part) {
return Base64.decode(Utf8.encode(part));
}
private String encodePart(byte[] part) {
return Utf8.decode(Base64.encode(part));
}
}
/*
* Copyright 2002-2017 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.crypto.scrypt;
import java.util.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.crypto.generators.SCrypt;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.security.crypto.keygen.KeyGenerators;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* <p>
* Implementation of PasswordEncoder that uses the SCrypt hashing function.
* Clients can optionally supply a cpu cost parameter, a memory cost parameter
* and a parallelization parameter.
* </p>
*
* <p>
* A few <a href=
* "http://bouncy-castle.1462172.n4.nabble.com/Java-Bouncy-Castle-scrypt-implementation-td4656832.html">
* warnings</a>:
* </p>
*
* <ul>
* <li>The currently implementation uses Bouncy castle which does not exploit
* parallelism/optimizations that password crackers will, so there is an
* unnecessary asymmetry between attacker and defender.</li>
* <li>Scrypt is based on Salsa20 which performs poorly in Java (on par with
* AES) but performs awesome (~4-5x faster) on SIMD capable platforms</li>
* <li>While there are some that would disagree, consider reading -
* <a href="http://blog.ircmaxell.com/2014/03/why-i-dont-recommend-scrypt.html">
* Why I Don't Recommend Scrypt</a> (for password storage)</li>
* </ul>
*
* @author Shazin Sadakath
* @author Rob Winch
*
*/
public class SCryptPasswordEncoder implements PasswordEncoder {
private final Log logger = LogFactory.getLog(getClass());
private final int cpuCost;
private final int memoryCost;
private final int parallelization;
private final int keyLength;
private final BytesKeyGenerator saltGenerator;
public SCryptPasswordEncoder() {
this(16384, 8, 1, 32, 64);
}
/**
* Creates a new instance
*
* @param cpuCost
* cpu cost of the algorithm (as defined in scrypt this is N).
* must be power of 2 greater than 1. Default is currently 16,348
* or 2^14)
* @param memoryCost
* memory cost of the algorithm (as defined in scrypt this is r)
* Default is currently 8.
* @param parallelization
* the parallelization of the algorithm (as defined in scrypt
* this is p) Default is currently 1. Note that the
* implementation does not currently take advantage of
* parallelization.
* @param keyLength
* key length for the algorithm (as defined in scrypt this is
* dkLen). The default is currently 32.
* @param saltLength
* salt length (as defined in scrypt this is the length of S).
* The default is currently 64.
*/
public SCryptPasswordEncoder(int cpuCost, int memoryCost, int parallelization, int keyLength, int saltLength) {
if (cpuCost <= 1) {
throw new IllegalArgumentException("Cpu cost parameter must be > 1.");
}
if (memoryCost == 1 && cpuCost > 65536) {
throw new IllegalArgumentException("Cpu cost parameter must be > 1 and < 65536.");
}
if (memoryCost < 1) {
throw new IllegalArgumentException("Memory cost must be >= 1.");
}
int maxParallel = Integer.MAX_VALUE / (128 * memoryCost * 8);
if (parallelization < 1 || parallelization > maxParallel) {
throw new IllegalArgumentException("Parallelisation parameter p must be >= 1 and <= " + maxParallel
+ " (based on block size r of " + memoryCost + ")");
}
if (keyLength < 1 || keyLength > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Key length must be >= 1 and <= " + Integer.MAX_VALUE);
}
if (saltLength < 1 || saltLength > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Salt length must be >= 1 and <= " + Integer.MAX_VALUE);
}
this.cpuCost = cpuCost;
this.memoryCost = memoryCost;
this.parallelization = parallelization;
this.keyLength = keyLength;
this.saltGenerator = KeyGenerators.secureRandom(saltLength);
}
public String encode(CharSequence rawPassword) {
return digest(rawPassword, saltGenerator.generateKey());
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (encodedPassword == null || encodedPassword.length() < keyLength) {
logger.warn("Empty encoded password");
return false;
}
return decodeAndCheckMatches(rawPassword, encodedPassword);
}
private boolean decodeAndCheckMatches(CharSequence rawPassword, String encodedPassword) {
String[] parts = encodedPassword.split("\\$");
if (parts.length != 4) {
return false;
}
long params = Long.parseLong(parts[1], 16);
byte[] salt = decodePart(parts[2]);
byte[] derived = decodePart(parts[3]);
int cpuCost = (int) Math.pow(2, params >> 16 & 0xffff);
int memoryCost = (int) params >> 8 & 0xff;
int parallelization = (int) params & 0xff;
byte[] generated = SCrypt.generate(Utf8.encode(rawPassword), salt, cpuCost, memoryCost, parallelization,
keyLength);
if (derived.length != generated.length) {
return false;
}
int result = 0;
for (int i = 0; i < derived.length; i++) {
result |= derived[i] ^ generated[i];
}
return result == 0;
}
private String digest(CharSequence rawPassword, byte[] salt) {
byte[] derived = SCrypt.generate(Utf8.encode(rawPassword), salt, cpuCost, memoryCost, parallelization, keyLength);
String params = Long
.toString(((int) (Math.log(cpuCost) / Math.log(2)) << 16L) | memoryCost << 8 | parallelization, 16);
StringBuilder sb = new StringBuilder((salt.length + derived.length) * 2);
sb.append("$").append(params).append('$');
sb.append(encodePart(salt)).append('$');
sb.append(encodePart(derived));
return sb.toString();
}
private byte[] decodePart(String part) {
return Base64.getDecoder().decode(Utf8.encode(part));
}
private String encodePart(byte[] part) {
return Utf8.decode(Base64.getEncoder().encode(part));
}
}
@@ -1,51 +0,0 @@
/*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.crypto.codec;
import static org.assertj.core.api.Assertions.*;
import org.junit.*;
/**
* @author Luke Taylor
*/
public class Base64Tests {
@Test
public void isBase64ReturnsTrueForValidBase64() {
new Base64(); // unused
assertThat(Base64.isBase64(new byte[] { (byte) 'A', (byte) 'B', (byte) 'C',
(byte) 'D' })).isTrue();
}
@Test
public void isBase64ReturnsFalseForInvalidBase64() throws Exception {
// Include invalid '`' character
assertThat(Base64.isBase64(new byte[] { (byte) 'A', (byte) 'B', (byte) 'C',
(byte) '`' })).isFalse();
}
@Test(expected = NullPointerException.class)
public void isBase64RejectsNull() {
Base64.isBase64(null);
}
@Test(expected = IllegalArgumentException.class)
public void isBase64RejectsInvalidLength() {
Base64.isBase64(new byte[] { (byte) 'A' });
}
}
-6
View File
@@ -1,6 +0,0 @@
dependencies {
compile project(':spring-security-core'),
springCoreDependency,
"org.springframework.data:spring-data-commons:$springDataCommonsVersion"
}
-128
View File
@@ -1,128 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-data</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<name>spring-security-data</name>
<description>spring-security-data</description>
<url>http://spring.io/spring-security</url>
<organization>
<name>spring.io</name>
<url>http://spring.io/</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>rwinch</id>
<name>Rob Winch</name>
<email>rwinch@gopivotal.com</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
<url>https://github.com/spring-projects/spring-security</url>
</scm>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>1.12.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.2.3.BUILD-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/snapshot</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
+7
View File
@@ -0,0 +1,7 @@
apply plugin: 'io.spring.convention.spring-module'
dependencies {
compile project(':spring-security-core')
compile 'org.springframework.data:spring-data-commons'
compile 'org.springframework:spring-core'
}
-159
View File
@@ -1,159 +0,0 @@
// Docbook and Javadoc building and uploading tasks
apply plugin: 'base'
task docs {
dependsOn 'manual:reference', 'apidocs', 'guides:asciidoctor'
}
project('manual') {
apply plugin: 'base'
apply plugin: 'org.asciidoctor.gradle.asciidoctor'
apply plugin: 'docbook-reference'
ext.expandPlaceholders = ""
asciidoctorj {
version = '1.5.2'
}
asciidoctor {
backends = ['docbook5']
def ghTag = snapshotBuild ? 'master' : project.version
def ghUrl = "https://github.com/spring-projects/spring-security/tree/$ghTag"
options = [
eruby: 'erubis',
attributes: [
copycss : '',
icons : 'font',
'source-highlighter': 'prettify',
sectanchors : '',
toc2: '',
idprefix: '',
idseparator: '-',
doctype: 'book',
numbered: '',
'spring-security-version' : project.version,
'spring-version' : springVersion,
revnumber : project.version,
'gh-url': ghUrl,
'gh-samples-url': "$ghUrl/samples",
docinfo : ""
]
]
}
reference {
sourceDir = new File(asciidoctor.outputDir , 'docbook5')
pdfFilename = "spring-security-reference.pdf"
epubFilename = "spring-security-reference.epub"
expandPlaceholders = ""
}
afterEvaluate {
tasks.findAll { it.name.startsWith("reference") }.each{ it.dependsOn.add("asciidoctor") }
}
ext.spec = copySpec {
from (reference) {
into 'reference'
}
}
}
task apidocs(type: Javadoc) {
destinationDir = new File(buildDir, 'apidocs')
title = "Spring Security $version API"
logging.captureStandardError LogLevel.INFO
logging.captureStandardOutput LogLevel.INFO
source coreModuleProjects.collect { project ->
project.sourceSets.main.allJava
}
classpath = files(coreModuleProjects.collect { project ->
project.sourceSets.main.compileClasspath
})
options {
outputLevel = org.gradle.external.javadoc.JavadocOutputLevel.QUIET
links = [
"http://static.springframework.org/spring/docs/3.2.x/javadoc-api",
"http://static.springsource.org/spring-ldap/docs/1.3.x/apidocs/",
"http://download.oracle.com/javase/6/docs/api/"
]
groups = [
'Spring Security Core':[
'org.springframework.security.core*',
'org.springframework.security.authentication*',
'org.springframework.security.access*',
'org.springframework.security.remoting*',
'org.springframework.security.provisioning*',
'org.springframework.security.util*'],
'Spring Security Web':['org.springframework.security.web*'],
'Spring Security LDAP':['org.springframework.security.ldap*'],
'Spring Security Crypto':['org.springframework.security.crypto*'],
'Spring Security OpenID':['org.springframework.security.openid*'],
'Spring Security CAS':['org.springframework.security.cas*'],
'Spring Security ACL':['org.springframework.security.acls*'],
'Spring Security Config':['org.springframework.security.config*'],
'Spring Security Taglibs':['org.springframework.security.taglibs*'],
]
addStringOption('-quiet')
}
}
if (JavaVersion.current().isJava8Compatible()) {
// Turn off doclint in JDK 8 Javadoc (too strict on checks)
apidocs.options.addStringOption('Xdoclint:none', '-quiet')
}
ext.apiSpec = copySpec {
into('apidocs') {
from(apidocs.destinationDir)
}
}
assemble.dependsOn = [apidocs, 'manual:asciidoctor']
task docsZip(type: Zip) {
dependsOn docs
evaluationDependsOn('guides')
group = 'Distribution'
baseName = rootProject.name
classifier = 'docs'
description = "Builds -${classifier} archive containing api and reference " +
"for deployment at static.springframework.org/spring-security/site/docs."
with(project(':docs').apiSpec)
with(project(':docs:manual').spec)
with(project(':docs:guides').spec)
}
task schemaZip(type: Zip) {
group = 'Distribution'
baseName = rootProject.name
classifier = 'schema'
description = "Builds -${classifier} archive containing all " +
"XSDs for deployment at static.springframework.org/schema."
coreModuleProjects.each { module ->
def Properties schemas = new Properties();
module.sourceSets.main.resources.find {
it.path.endsWith('META-INF/spring.schemas')
}?.withInputStream { schemas.load(it) }
for (def key : schemas.keySet()) {
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
assert shortName != key
File xsdFile = module.sourceSets.main.resources.find {
it.path.endsWith(schemas.get(key))
}
assert xsdFile != null
into (shortName) {
from xsdFile.path
}
}
}
}
@@ -0,0 +1,11 @@
apply plugin: 'io.spring.convention.docs'
asciidoctor {
def ghTag = snapshotBuild ? 'master' : project.version
def ghUrl = "https://github.com/spring-projects/spring-security/tree/$ghTag"
attributes 'spring-security-version' : project.version,
'spring-version' : project(':spring-security-core').dependencyManagement.managedVersions['org.springframework:spring-core'],
revnumber : project.version,
'gh-url': ghUrl,
'gh-samples-url': "$ghUrl/samples"
}

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