Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 64682ab6b8 | |||
| 2484f72292 | |||
| 1693625acb | |||
| 0f7645aa50 | |||
| f79fe3ce46 | |||
| 853765c55c | |||
| 59ea0f0711 | |||
| 7da0c74095 | |||
| e8d58498b0 | |||
| 5802393073 | |||
| bab5b73e71 | |||
| 717bf4819b | |||
| fd31e52fb1 | |||
| 4732d08080 | |||
| 2c69092bdd | |||
| 8f671f807c | |||
| ed90e4143e | |||
| 3747a14258 | |||
| 10fec400eb | |||
| 315c0b3f03 | |||
| 43677a6964 | |||
| 6d25f479b0 | |||
| 8f03a04b9a | |||
| faefcc73e9 | |||
| dfacad020b | |||
| 0af0ee3339 | |||
| dca42b5602 | |||
| 657f0475ec | |||
| 8e9f7e2f10 | |||
| bf5a3a1aee | |||
| 201690ceba | |||
| e1f33d577f | |||
| 879adcae5f | |||
| eed8538071 | |||
| 3df9ad1e8f | |||
| dac46dc57c | |||
| b5b2e2c50e | |||
| 72c99af0d4 | |||
| 742e5fd7a9 | |||
| 795c073f48 | |||
| 58c9cbb471 | |||
| e40bc262bb | |||
| b0d865e28f | |||
| 0f7f0042ac | |||
| e0f1121d5b | |||
| ea93ed4165 | |||
| 96acf33019 | |||
| 35dbc0e6a8 | |||
| 0526c5233c | |||
| 60c011a976 | |||
| 916a19ccbd | |||
| 8138fac3f9 | |||
| 62fadfd28a | |||
| 166f48e6ab | |||
| 0f97086c86 | |||
| fe5c6d6621 | |||
| c642de537a | |||
| fd27c07b55 | |||
| e0d95c7c8a | |||
| 9d76c98791 | |||
| e923371724 | |||
| 216512bc54 | |||
| 8445e91b22 | |||
| 13ccb83d6f | |||
| 127d9eece9 | |||
| 7891787a53 | |||
| 2d8b6650db | |||
| 78995ff0a9 | |||
| 7974f3161d | |||
| c35c1c0643 | |||
| 2162221589 | |||
| 040fb6aa3c | |||
| b152218ee0 | |||
| 544e421157 | |||
| 0f612bf637 | |||
| c683bc10bf | |||
| 11ab23f4f4 | |||
| 0065b55a75 | |||
| d6f9d2e34a | |||
| 5dedbb6283 | |||
| 4cad151b57 | |||
| 72080bb5fe | |||
| 5854f00977 | |||
| 93118f4e91 | |||
| 8796850816 | |||
| cee2ea9c60 | |||
| 1159c9f302 | |||
| 6c5ce1237d | |||
| f81b58112b | |||
| 0e02b489c8 | |||
| d1669b909f | |||
| cb8041ba67 | |||
| 6f74162a1f | |||
| 99a0baadfa | |||
| 1f9a0e579f | |||
| 9e7d08c9e7 | |||
| 82168faf9d | |||
| 9d0f8977a9 | |||
| 5ae615f3b4 | |||
| d2b0077392 | |||
| 092c5aecf7 | |||
| a5d56d8724 | |||
| 7c0da854da | |||
| 0f546dcb07 | |||
| cb576d16e1 | |||
| 3b4df40f47 | |||
| 6cbf71bd72 | |||
| cd63329b63 | |||
| cc7f504f96 | |||
| a094563052 | |||
| da19435f21 | |||
| be50cd8ada | |||
| 21efbb6ba7 | |||
| c7cf6fdd73 | |||
| 8129bf2ce0 | |||
| d48079eb19 | |||
| 45f1179b52 | |||
| e11dfa7578 | |||
| 6cc0f6c054 | |||
| 22ea835643 | |||
| 9f51d68e92 | |||
| 32af1884f7 | |||
| a88035196a | |||
| 0db94b1d36 | |||
| 9e8994a2b7 | |||
| 8b2faff7ad | |||
| 469bc20e6d | |||
| 947d11f433 | |||
| b3a60a83f6 | |||
| 5bc7e4171c | |||
| 80e96b0f7b |
+3
-1
@@ -20,4 +20,6 @@ build/
|
||||
.gradle/
|
||||
atlassian-ide-plugin.xml
|
||||
!etc/eclipse/.checkstyle
|
||||
.checkstyle
|
||||
.checkstyle
|
||||
classes/
|
||||
s101plugin.state
|
||||
|
||||
+7
-13
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-acl</artifactId>
|
||||
<version>4.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<name>spring-security-acl</name>
|
||||
<description>spring-security-acl</description>
|
||||
<url>http://spring.io/spring-security</url>
|
||||
@@ -35,7 +35,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>4.3.5.RELEASE</version>
|
||||
<version>4.3.22.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -51,7 +51,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>4.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -95,14 +95,14 @@
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
<artifactId>ehcache</artifactId>
|
||||
<version>2.9.0</version>
|
||||
<version>2.10.6</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<version>1.1.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -120,7 +120,7 @@
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<version>2.3.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -132,7 +132,7 @@
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.7</version>
|
||||
<version>1.7.25</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -151,12 +151,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
@@ -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.
|
||||
@@ -61,14 +61,14 @@ public class AuditLoggerTests {
|
||||
public void nonAuditableAceIsIgnored() {
|
||||
AccessControlEntry ace = mock(AccessControlEntry.class);
|
||||
logger.logIfNeeded(true, ace);
|
||||
assertThat(bytes.size()).isEqualTo(0);
|
||||
assertThat(bytes.size()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successIsNotLoggedIfAceDoesntRequireSuccessAudit() throws Exception {
|
||||
when(ace.isAuditSuccess()).thenReturn(false);
|
||||
logger.logIfNeeded(true, ace);
|
||||
assertThat(bytes.size()).isEqualTo(0);
|
||||
assertThat(bytes.size()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -76,20 +76,20 @@ public class AuditLoggerTests {
|
||||
when(ace.isAuditSuccess()).thenReturn(true);
|
||||
|
||||
logger.logIfNeeded(true, ace);
|
||||
assertThat(bytes.toString().startsWith("GRANTED due to ACE")).isTrue();
|
||||
assertThat(bytes.toString()).startsWith("GRANTED due to ACE");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failureIsntLoggedIfAceDoesntRequireFailureAudit() throws Exception {
|
||||
when(ace.isAuditFailure()).thenReturn(false);
|
||||
logger.logIfNeeded(false, ace);
|
||||
assertThat(bytes.size()).isEqualTo(0);
|
||||
assertThat(bytes.size()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failureIsLoggedIfAceRequiresFailureAudit() throws Exception {
|
||||
when(ace.isAuditFailure()).thenReturn(true);
|
||||
logger.logIfNeeded(false, ace);
|
||||
assertThat(bytes.toString().startsWith("DENIED due to ACE")).isTrue();
|
||||
assertThat(bytes.toString()).startsWith("DENIED due to ACE");
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -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.
|
||||
@@ -137,9 +137,9 @@ public class ObjectIdentityImplTests {
|
||||
|
||||
String string = "SOME_STRING";
|
||||
assertThat(string).isNotSameAs(obj);
|
||||
assertThat(obj.equals(null)).isFalse();
|
||||
assertThat(obj.equals("DIFFERENT_OBJECT_TYPE")).isFalse();
|
||||
assertThat(obj.equals(new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(2)))).isFalse();
|
||||
assertThat(obj).isNotNull();
|
||||
assertThat(obj).isNotEqualTo("DIFFERENT_OBJECT_TYPE");
|
||||
assertThat(obj).isNotEqualTo(new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(2)));
|
||||
assertThat(obj).isNotEqualTo(new ObjectIdentityImpl(
|
||||
"org.springframework.security.acls.domain.ObjectIdentityImplTests$MockOtherIdDomainObject",
|
||||
Long.valueOf(1)));
|
||||
@@ -151,7 +151,7 @@ public class ObjectIdentityImplTests {
|
||||
public void hashcodeIsDifferentForDifferentJavaTypes() throws Exception {
|
||||
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, Long.valueOf(1));
|
||||
ObjectIdentity obj2 = new ObjectIdentityImpl(String.class, Long.valueOf(1));
|
||||
assertThat(obj.hashCode() == obj2.hashCode()).isFalse();
|
||||
assertThat(obj.hashCode()).isNotEqualTo(obj2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -175,7 +175,7 @@ public class ObjectIdentityImplTests {
|
||||
public void stringAndNumericIdsAreNotEqual() throws Exception {
|
||||
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, "1000");
|
||||
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, Long.valueOf(1000));
|
||||
assertThat(obj.equals(obj2)).isFalse();
|
||||
assertThat(obj).isNotEqualTo(obj2);
|
||||
}
|
||||
|
||||
// ~ Inner Classes
|
||||
|
||||
+4
-4
@@ -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.
|
||||
@@ -104,17 +104,17 @@ public class SpringCacheBasedAclCacheTests {
|
||||
// Try to evict an entry that doesn't exist
|
||||
myCache.evictFromCache(Long.valueOf(3));
|
||||
myCache.evictFromCache(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102)));
|
||||
assertThat(4).isEqualTo(realCache.size());
|
||||
assertThat(realCache).hasSize(4);
|
||||
|
||||
myCache.evictFromCache(Long.valueOf(1));
|
||||
assertThat(2).isEqualTo(realCache.size());
|
||||
assertThat(realCache).hasSize(2);
|
||||
|
||||
// Check the second object inserted
|
||||
assertThat(acl2).isEqualTo(myCache.getFromCache(Long.valueOf(2)));
|
||||
assertThat(acl2).isEqualTo(myCache.getFromCache(identity2));
|
||||
|
||||
myCache.evictFromCache(identity2);
|
||||
assertThat(0).isEqualTo(realCache.size());
|
||||
assertThat(realCache).isEmpty();
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
|
||||
+6
-12
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-aspects</artifactId>
|
||||
<version>4.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<name>spring-security-aspects</name>
|
||||
<description>spring-security-aspects</description>
|
||||
<url>http://spring.io/spring-security</url>
|
||||
@@ -35,7 +35,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>4.3.5.RELEASE</version>
|
||||
<version>4.3.22.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -45,7 +45,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>4.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -79,7 +79,7 @@
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
<version>1.8.4</version>
|
||||
<version>1.8.13</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
@@ -92,7 +92,7 @@
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<version>1.1.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -116,7 +116,7 @@
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.7</version>
|
||||
<version>1.7.25</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -130,12 +130,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
+9
-5
@@ -4,13 +4,14 @@ buildscript {
|
||||
maven { url "https://repo.spring.io/plugins-snapshot" }
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.github.ben-manes:gradle-versions-plugin:0.17.0"
|
||||
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("org.springframework.boot:spring-boot-gradle-plugin:1.5.18.RELEASE")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +26,12 @@ description = 'Spring Security'
|
||||
allprojects {
|
||||
apply plugin: 'idea'
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: "com.github.ben-manes.versions"
|
||||
|
||||
ext.releaseBuild = version.endsWith('RELEASE')
|
||||
ext.snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
ext.springVersion = '4.3.5.RELEASE'
|
||||
ext.springLdapVersion = '2.2.0.RELEASE'
|
||||
ext.springVersion = '4.3.22.RELEASE'
|
||||
ext.springLdapVersion = '2.3.2.RELEASE'
|
||||
|
||||
group = 'org.springframework.security'
|
||||
|
||||
@@ -59,6 +61,7 @@ sonarqube {
|
||||
// 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.sampleBootProjects = subprojects.findAll { project -> project.name.startsWith('spring-security-samples-boot') }
|
||||
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')]
|
||||
@@ -106,7 +109,7 @@ configure(coreModuleProjects) {
|
||||
apply plugin: 'emma'
|
||||
apply plugin: 'spring-io'
|
||||
|
||||
ext.springIoVersion = project.hasProperty('platformVersion') ? platformVersion : 'Athens-BUILD-SNAPSHOT'
|
||||
ext.springIoVersion = project.hasProperty('platformVersion') ? platformVersion : 'Brussels-SR9'
|
||||
|
||||
configurations {
|
||||
jacoco //Configuration Group used by Sonar to provide Code Coverage using JaCoCo
|
||||
@@ -117,6 +120,7 @@ configure(coreModuleProjects) {
|
||||
imports {
|
||||
mavenBom("io.spring.platform:platform-bom:${springIoVersion}") {
|
||||
bomProperties([
|
||||
'assertj.version': '2.6.0',
|
||||
'mockito.version': '1.10.19'
|
||||
])
|
||||
}
|
||||
@@ -124,7 +128,7 @@ configure(coreModuleProjects) {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
jacoco "org.jacoco:org.jacoco.agent:0.7.5.201505241946:runtime"
|
||||
jacoco "org.jacoco:org.jacoco.agent:0.7.9:runtime"
|
||||
}
|
||||
test {
|
||||
jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=${project.group}.*"
|
||||
|
||||
+9
-15
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-cas</artifactId>
|
||||
<version>4.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<name>spring-security-cas</name>
|
||||
<description>spring-security-cas</description>
|
||||
<url>http://spring.io/spring-security</url>
|
||||
@@ -35,7 +35,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>4.3.5.RELEASE</version>
|
||||
<version>4.3.22.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -51,13 +51,13 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>4.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<version>4.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -89,7 +89,7 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.8.4</version>
|
||||
<version>2.8.11.3</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
@@ -103,7 +103,7 @@
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
<artifactId>ehcache</artifactId>
|
||||
<version>2.9.0</version>
|
||||
<version>2.10.6</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
@@ -116,7 +116,7 @@
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<version>1.1.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -140,13 +140,13 @@
|
||||
<dependency>
|
||||
<groupId>org.skyscreamer</groupId>
|
||||
<artifactId>jsonassert</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.4.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.7</version>
|
||||
<version>1.7.25</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -155,12 +155,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
+3
-3
@@ -55,7 +55,7 @@ public class CasAuthenticationTokenMixinTests {
|
||||
|
||||
public static final String AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", [" + AUTHORITY_JSON + "]]";
|
||||
|
||||
public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.ArrayList\", [" + AUTHORITY_JSON + "]]";
|
||||
public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", [" + AUTHORITY_JSON + "]]";
|
||||
|
||||
// @formatter:off
|
||||
public static final String USER_JSON = "{"
|
||||
@@ -84,14 +84,14 @@ public class CasAuthenticationTokenMixinTests {
|
||||
+ "\"principal\": {"
|
||||
+ "\"@class\": \"org.jasig.cas.client.authentication.AttributePrincipalImpl\", "
|
||||
+ "\"name\": \"assertName\", "
|
||||
+ "\"attributes\": {\"@class\": \"java.util.Collections$EmptyMap\"}, "
|
||||
+ "\"attributes\": {\"@class\": \"java.util.HashMap\"}, "
|
||||
+ "\"proxyGrantingTicket\": null, "
|
||||
+ "\"proxyRetriever\": null"
|
||||
+ "}, "
|
||||
+ "\"validFromDate\": [\"java.util.Date\", " + START_DATE.getTime() + "], "
|
||||
+ "\"validUntilDate\": [\"java.util.Date\", " + END_DATE.getTime() + "],"
|
||||
+ "\"authenticationDate\": [\"java.util.Date\", " + START_DATE.getTime() + "], "
|
||||
+ "\"attributes\": {\"@class\": \"java.util.Collections$EmptyMap\"}" +
|
||||
+ "\"attributes\": {\"@class\": \"java.util.HashMap\"}" +
|
||||
"}"
|
||||
+ "}";
|
||||
|
||||
|
||||
+3
-6
@@ -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.
|
||||
@@ -57,10 +57,7 @@ public class GrantedAuthorityFromAssertionAttributesUserDetailsServiceTests {
|
||||
assertion, "ticket");
|
||||
UserDetails user = uds.loadUserDetails(token);
|
||||
Set<String> roles = AuthorityUtils.authorityListToSet(user.getAuthorities());
|
||||
assertThat(roles.size()).isEqualTo(4);
|
||||
assertThat(roles).contains("role_a1");
|
||||
assertThat(roles).contains("role_a2");
|
||||
assertThat(roles).contains("role_b");
|
||||
assertThat(roles).contains("role_c");
|
||||
assertThat(roles).containsOnly(
|
||||
"role_a1", "role_a2", "role_b", "role_c");
|
||||
}
|
||||
}
|
||||
|
||||
+6
-8
@@ -16,11 +16,6 @@
|
||||
|
||||
package org.springframework.security.cas.web;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
@@ -35,9 +30,13 @@ import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Tests {@link CasAuthenticationFilter}.
|
||||
*
|
||||
@@ -146,8 +145,7 @@ public class CasAuthenticationFilterTests {
|
||||
.createAuthorityList("ROLE_ANONYMOUS")));
|
||||
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken("un", "principal", AuthorityUtils
|
||||
.createAuthorityList("ROLE_ANONYMOUS")));
|
||||
new TestingAuthenticationToken("un", "principal"));
|
||||
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken("un", "principal", "ROLE_ANONYMOUS"));
|
||||
|
||||
+23
-29
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<version>4.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<name>spring-security-config</name>
|
||||
<description>spring-security-config</description>
|
||||
<url>http://spring.io/spring-security</url>
|
||||
@@ -35,7 +35,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>4.3.5.RELEASE</version>
|
||||
<version>4.3.22.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -51,7 +51,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>4.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -90,35 +90,35 @@
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<version>1.8.4</version>
|
||||
<version>1.8.13</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>
|
||||
<version>4.2.11.RELEASE</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>
|
||||
<version>4.2.11.RELEASE</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>
|
||||
<version>4.2.11.RELEASE</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>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
@@ -161,13 +161,13 @@
|
||||
<dependency>
|
||||
<groupId>cglib</groupId>
|
||||
<artifactId>cglib-nodep</artifactId>
|
||||
<version>3.1</version>
|
||||
<version>3.2.10</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<version>1.1.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -245,13 +245,13 @@
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-entitymanager</artifactId>
|
||||
<version>5.0.11.Final</version>
|
||||
<version>5.0.12.Final</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<version>2.3.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -275,7 +275,7 @@
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-mockito</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.6.5</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -287,37 +287,37 @@
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-support</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.6.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-core</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.6.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.6.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4-common</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.6.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-reflect</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.6.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.7</version>
|
||||
<version>1.7.25</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -347,7 +347,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-jpa</artifactId>
|
||||
<version>1.10.2.RELEASE</version>
|
||||
<version>1.11.18.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -359,19 +359,19 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.ldap</groupId>
|
||||
<artifactId>spring-ldap-core</artifactId>
|
||||
<version>2.2.0.RELEASE</version>
|
||||
<version>2.3.2.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-aspects</artifactId>
|
||||
<version>4.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-cas</artifactId>
|
||||
<version>4.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -390,12 +390,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
+9
-2
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
@@ -42,6 +43,8 @@ import java.util.List;
|
||||
* @since 3.2
|
||||
*/
|
||||
public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
|
||||
|
||||
private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
|
||||
|
||||
private ApplicationContext context;
|
||||
@@ -160,8 +163,12 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
String... mvcPatterns) {
|
||||
boolean isServlet30 = ClassUtils.isPresent("javax.servlet.ServletRegistration", getClass().getClassLoader());
|
||||
ObjectPostProcessor<Object> opp = this.context.getBean(ObjectPostProcessor.class);
|
||||
HandlerMappingIntrospector introspector = new HandlerMappingIntrospector(
|
||||
this.context);
|
||||
if(!this.context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
|
||||
throw new NoSuchBeanDefinitionException("A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME +" of type " + HandlerMappingIntrospector.class.getName()
|
||||
+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
|
||||
}
|
||||
HandlerMappingIntrospector introspector = this.context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,
|
||||
HandlerMappingIntrospector.class);
|
||||
List<MvcRequestMatcher> matchers = new ArrayList<MvcRequestMatcher>(
|
||||
mvcPatterns.length);
|
||||
for (String mvcPattern : mvcPatterns) {
|
||||
|
||||
+6
-2
@@ -25,6 +25,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.http.HttpMethod;
|
||||
@@ -47,7 +48,7 @@ import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.debug.DebugFilter;
|
||||
import org.springframework.security.web.firewall.DefaultHttpFirewall;
|
||||
import org.springframework.security.web.firewall.StrictHttpFirewall;
|
||||
import org.springframework.security.web.firewall.HttpFirewall;
|
||||
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
@@ -159,7 +160,7 @@ public final class WebSecurity extends
|
||||
|
||||
/**
|
||||
* Allows customizing the {@link HttpFirewall}. The default is
|
||||
* {@link DefaultHttpFirewall}.
|
||||
* {@link StrictHttpFirewall}.
|
||||
*
|
||||
* @param httpFirewall the custom {@link HttpFirewall}
|
||||
* @return the {@link WebSecurity} for further customizations
|
||||
@@ -382,5 +383,8 @@ public final class WebSecurity extends
|
||||
this.defaultWebSecurityExpressionHandler
|
||||
.setApplicationContext(applicationContext);
|
||||
this.ignoredRequestRegistry = new IgnoredRequestConfigurer(applicationContext);
|
||||
try {
|
||||
this.httpFirewall = applicationContext.getBean(HttpFirewall.class);
|
||||
} catch(NoSuchBeanDefinitionException e) {}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-3
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
@@ -98,7 +99,9 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
static class MvcCorsFilter {
|
||||
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
|
||||
/**
|
||||
* This needs to be isolated into a separate class as Spring MVC is an optional
|
||||
* dependency and will potentially cause ClassLoading issues
|
||||
@@ -106,9 +109,12 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* @return
|
||||
*/
|
||||
private static CorsFilter getMvcCorsFilter(ApplicationContext context) {
|
||||
HandlerMappingIntrospector mappingIntrospector = new HandlerMappingIntrospector(
|
||||
context);
|
||||
if(!context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
|
||||
throw new NoSuchBeanDefinitionException(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, "A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME +" of type " + HandlerMappingIntrospector.class.getName()
|
||||
+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
|
||||
}
|
||||
HandlerMappingIntrospector mappingIntrospector = context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, HandlerMappingIntrospector.class);
|
||||
return new CorsFilter(mappingIntrospector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -23,6 +23,7 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||
@@ -85,6 +86,12 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>> exte
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public H disable() {
|
||||
getBuilder().setSharedObject(RequestCache.class, new NullRequestCache());
|
||||
return super.disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(H http) throws Exception {
|
||||
http.setSharedObject(RequestCache.class, getRequestCache(http));
|
||||
|
||||
+3
-6
@@ -15,17 +15,16 @@
|
||||
*/
|
||||
package org.springframework.security.config.http;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Parser for the {@code CorsFilter}.
|
||||
@@ -71,8 +70,6 @@ public class CorsBeanDefinitionParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder configurationSourceBldr = BeanDefinitionBuilder.rootBeanDefinition(HANDLER_MAPPING_INTROSPECTOR);
|
||||
configurationSourceBldr.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
|
||||
return configurationSourceBldr.getBeanDefinition();
|
||||
return new RootBeanDefinition(HandlerMappingIntrospectorFactoryBean.class);
|
||||
}
|
||||
}
|
||||
|
||||
+19
-3
@@ -17,6 +17,8 @@
|
||||
package org.springframework.security.config.http;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||
@@ -28,12 +30,22 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||
* @author Rob Winch
|
||||
* @since 4.1.1
|
||||
*/
|
||||
class HandlerMappingIntrospectorFactoryBean implements ApplicationContextAware {
|
||||
class HandlerMappingIntrospectorFactoryBean implements FactoryBean<HandlerMappingIntrospector>, ApplicationContextAware {
|
||||
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
HandlerMappingIntrospector createHandlerMappingIntrospector() {
|
||||
return new HandlerMappingIntrospector(this.context);
|
||||
public HandlerMappingIntrospector getObject() {
|
||||
if(!this.context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
|
||||
throw new NoSuchBeanDefinitionException(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, "A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME +" of type " + HandlerMappingIntrospector.class.getName()
|
||||
+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
|
||||
}
|
||||
return this.context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, HandlerMappingIntrospector.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return HandlerMappingIntrospector.class;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -48,4 +60,8 @@ class HandlerMappingIntrospectorFactoryBean implements ApplicationContextAware {
|
||||
this.context = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,7 @@ public enum MatcherType {
|
||||
ant(AntPathRequestMatcher.class), regex(RegexRequestMatcher.class), ciRegex(
|
||||
RegexRequestMatcher.class), mvc(MvcRequestMatcher.class);
|
||||
|
||||
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
|
||||
private static final String HANDLER_MAPPING_INTROSPECTOR_FACTORY_BEAN_NAME = "org.springframework.security.config.http.HandlerMappingIntrospectorFactoryBean";
|
||||
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
|
||||
|
||||
private static final String ATT_MATCHER_TYPE = "request-matcher";
|
||||
|
||||
@@ -61,18 +60,7 @@ public enum MatcherType {
|
||||
.rootBeanDefinition(type);
|
||||
|
||||
if (this == mvc) {
|
||||
if (!pc.getRegistry().isBeanNameInUse(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
|
||||
BeanDefinitionBuilder hmifb = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(HandlerMappingIntrospectorFactoryBean.class);
|
||||
pc.getRegistry().registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_FACTORY_BEAN_NAME,
|
||||
hmifb.getBeanDefinition());
|
||||
|
||||
RootBeanDefinition hmi = new RootBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME);
|
||||
hmi.setFactoryBeanName(HANDLER_MAPPING_INTROSPECTOR_FACTORY_BEAN_NAME);
|
||||
hmi.setFactoryMethodName("createHandlerMappingIntrospector");
|
||||
pc.getRegistry().registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, hmi);
|
||||
}
|
||||
matcherBldr.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME);
|
||||
matcherBldr.addConstructorArgValue(new RootBeanDefinition(HandlerMappingIntrospectorFactoryBean.class));
|
||||
}
|
||||
|
||||
matcherBldr.addConstructorArgValue(path);
|
||||
|
||||
+1
-1
@@ -739,7 +739,7 @@ hsts-options.attlist &=
|
||||
## Specifies if subdomains should be included. Default true.
|
||||
attribute include-subdomains {xsd:boolean}?
|
||||
hsts-options.attlist &=
|
||||
## Specifies the maximum ammount of time the host should be considered a Known HSTS Host. Default one year.
|
||||
## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.
|
||||
attribute max-age-seconds {xsd:integer}?
|
||||
hsts-options.attlist &=
|
||||
## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.
|
||||
|
||||
+1
-1
@@ -2299,7 +2299,7 @@
|
||||
</xs:attribute>
|
||||
<xs:attribute name="max-age-seconds" type="xs:integer">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the maximum ammount of time the host should be considered a Known HSTS Host.
|
||||
<xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.
|
||||
Default one year.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
+1
-1
@@ -759,7 +759,7 @@ hsts-options.attlist &=
|
||||
## Specifies if subdomains should be included. Default true.
|
||||
attribute include-subdomains {xsd:boolean}?
|
||||
hsts-options.attlist &=
|
||||
## Specifies the maximum ammount of time the host should be considered a Known HSTS Host. Default one year.
|
||||
## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.
|
||||
attribute max-age-seconds {xsd:integer}?
|
||||
hsts-options.attlist &=
|
||||
## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.
|
||||
|
||||
+1
-1
@@ -2358,7 +2358,7 @@
|
||||
</xs:attribute>
|
||||
<xs:attribute name="max-age-seconds" type="xs:integer">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the maximum ammount of time the host should be considered a Known HSTS Host.
|
||||
<xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.
|
||||
Default one year.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
+1
-1
@@ -768,7 +768,7 @@ hsts-options.attlist &=
|
||||
## Specifies if subdomains should be included. Default true.
|
||||
attribute include-subdomains {xsd:boolean}?
|
||||
hsts-options.attlist &=
|
||||
## Specifies the maximum ammount of time the host should be considered a Known HSTS Host. Default one year.
|
||||
## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.
|
||||
attribute max-age-seconds {xsd:integer}?
|
||||
hsts-options.attlist &=
|
||||
## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.
|
||||
|
||||
+1
-1
@@ -2387,7 +2387,7 @@
|
||||
</xs:attribute>
|
||||
<xs:attribute name="max-age-seconds" type="xs:integer">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the maximum ammount of time the host should be considered a Known HSTS Host.
|
||||
<xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.
|
||||
Default one year.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
+2
-2
@@ -748,7 +748,7 @@ csrf-options.attlist &=
|
||||
## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except "GET", "TRACE", "HEAD", "OPTIONS"
|
||||
attribute request-matcher-ref { xsd:token }?
|
||||
csrf-options.attlist &=
|
||||
## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository
|
||||
## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.
|
||||
attribute token-repository-ref { xsd:token }?
|
||||
|
||||
headers =
|
||||
@@ -770,7 +770,7 @@ hsts-options.attlist &=
|
||||
## Specifies if subdomains should be included. Default true.
|
||||
attribute include-subdomains {xsd:boolean}?
|
||||
hsts-options.attlist &=
|
||||
## Specifies the maximum ammount of time the host should be considered a Known HSTS Host. Default one year.
|
||||
## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.
|
||||
attribute max-age-seconds {xsd:integer}?
|
||||
hsts-options.attlist &=
|
||||
## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.
|
||||
|
||||
+3
-2
@@ -2337,7 +2337,8 @@
|
||||
</xs:attribute>
|
||||
<xs:attribute name="token-repository-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository
|
||||
<xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by
|
||||
LazyCsrfTokenRepository.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
@@ -2401,7 +2402,7 @@
|
||||
</xs:attribute>
|
||||
<xs:attribute name="max-age-seconds" type="xs:integer">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the maximum ammount of time the host should be considered a Known HSTS Host.
|
||||
<xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.
|
||||
Default one year.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
+7
-11
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
@@ -36,19 +39,12 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc
|
||||
*/
|
||||
class CorsConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "HandlerMappingIntrospector default"() {
|
||||
setup:
|
||||
loadConfig(DefaultCorsConfig)
|
||||
def "No MVC throws meaningful error"() {
|
||||
when:
|
||||
addCors()
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
loadConfig(DefaultCorsConfig)
|
||||
then:
|
||||
responseHeaders == ['X-Content-Type-Options':'nosniff',
|
||||
'X-Frame-Options':'DENY',
|
||||
'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
|
||||
'Expires' : '0',
|
||||
'Pragma':'no-cache',
|
||||
'X-XSS-Protection' : '1; mode=block']
|
||||
BeanCreationException success = thrown()
|
||||
success.message.contains("Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext")
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
|
||||
+25
-1
@@ -13,7 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
@@ -89,6 +91,28 @@ public class NamespaceHttpFirewallTests extends BaseSpringSpec {
|
||||
}
|
||||
}
|
||||
|
||||
def "http-firewall bean"() {
|
||||
setup:
|
||||
loadConfig(CustomHttpFirewallBeanConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
request.setParameter("deny", "true")
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "the custom firewall is used"
|
||||
thrown(RequestRejectedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomHttpFirewallBeanConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) { }
|
||||
|
||||
@Bean
|
||||
CustomHttpFirewall firewall() {
|
||||
return new CustomHttpFirewall();
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomHttpFirewall extends DefaultHttpFirewall {
|
||||
|
||||
@Override
|
||||
|
||||
+6
-11
@@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.springframework.security.config.http
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
import org.springframework.http.*
|
||||
@@ -38,24 +40,17 @@ class HttpCorsConfigTests extends AbstractHttpConfigTests {
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
def "HandlerMappingIntrospector default"() {
|
||||
setup:
|
||||
def "No MVC throws meaningful error"() {
|
||||
when:
|
||||
xml.http('entry-point-ref' : 'ep') {
|
||||
'cors'()
|
||||
'intercept-url'(pattern:'/**', access: 'authenticated')
|
||||
}
|
||||
bean('ep', Http403ForbiddenEntryPoint)
|
||||
createAppContext()
|
||||
when:
|
||||
addCors()
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
responseHeaders == ['X-Content-Type-Options':'nosniff',
|
||||
'X-Frame-Options':'DENY',
|
||||
'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
|
||||
'Expires' : '0',
|
||||
'Pragma':'no-cache',
|
||||
'X-XSS-Protection' : '1; mode=block']
|
||||
BeanCreationException success = thrown()
|
||||
success.message.contains("Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext")
|
||||
}
|
||||
|
||||
def "HandlerMappingIntrospector explicit"() {
|
||||
|
||||
+1
@@ -252,6 +252,7 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
|
||||
'http-basic'()
|
||||
'intercept-url'(pattern: '/user/{un}/**', access: "#un == 'user'")
|
||||
}
|
||||
xml.'mvc:annotation-driven'()
|
||||
createWebAppContext(servletContext)
|
||||
when: 'user can access'
|
||||
request.servletPath = '/user/user/abc'
|
||||
|
||||
+4
@@ -38,6 +38,7 @@ import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||
import org.springframework.security.web.firewall.DefaultHttpFirewall;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
@@ -80,6 +81,7 @@ public class FilterChainProxyConfigTests {
|
||||
public void normalOperationWithNewConfig() throws Exception {
|
||||
FilterChainProxy filterChainProxy = appCtx.getBean("newFilterChainProxy",
|
||||
FilterChainProxy.class);
|
||||
filterChainProxy.setFirewall(new DefaultHttpFirewall());
|
||||
checkPathAndFilterOrder(filterChainProxy);
|
||||
doNormalOperation(filterChainProxy);
|
||||
}
|
||||
@@ -88,6 +90,7 @@ public class FilterChainProxyConfigTests {
|
||||
public void normalOperationWithNewConfigRegex() throws Exception {
|
||||
FilterChainProxy filterChainProxy = appCtx.getBean("newFilterChainProxyRegex",
|
||||
FilterChainProxy.class);
|
||||
filterChainProxy.setFirewall(new DefaultHttpFirewall());
|
||||
checkPathAndFilterOrder(filterChainProxy);
|
||||
doNormalOperation(filterChainProxy);
|
||||
}
|
||||
@@ -96,6 +99,7 @@ public class FilterChainProxyConfigTests {
|
||||
public void normalOperationWithNewConfigNonNamespace() throws Exception {
|
||||
FilterChainProxy filterChainProxy = appCtx.getBean(
|
||||
"newFilterChainProxyNonNamespace", FilterChainProxy.class);
|
||||
filterChainProxy.setFirewall(new DefaultHttpFirewall());
|
||||
checkPathAndFilterOrder(filterChainProxy);
|
||||
doNormalOperation(filterChainProxy);
|
||||
}
|
||||
|
||||
+2
-2
@@ -71,8 +71,8 @@ public class HttpSecurityHeadersTests {
|
||||
mockMvc.perform(get("/resources/file.js"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().string(HttpHeaders.CACHE_CONTROL,"max-age=12345"))
|
||||
.andExpect(header().string(HttpHeaders.PRAGMA, ""))
|
||||
.andExpect(header().string(HttpHeaders.EXPIRES, ""));
|
||||
.andExpect(header().doesNotExist(HttpHeaders.PRAGMA))
|
||||
.andExpect(header().doesNotExist(HttpHeaders.EXPIRES));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link RequestCacheConfigurer#disable()}
|
||||
*
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class RequestCacheConfigurerDisabledTests {
|
||||
AnnotationConfigWebApplicationContext context;
|
||||
|
||||
MockHttpServletRequest request;
|
||||
MockHttpServletResponse response;
|
||||
MockFilterChain chain;
|
||||
|
||||
@Autowired
|
||||
FilterChainProxy springSecurityFilterChain;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.request.setMethod("GET");
|
||||
this.response = new MockHttpServletResponse();
|
||||
this.chain = new MockFilterChain();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
// gh-6102
|
||||
@Test
|
||||
public void getWhenRequestCacheIsDisabledThenExceptionTranslationFilterDoesNotStoreRequest() throws Exception {
|
||||
loadConfig(RequestCacheDisabledConfig.class);
|
||||
|
||||
this.request.setServletPath("/path");
|
||||
this.request.setRequestURI("/path");
|
||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||
|
||||
HttpSession session = this.request.getSession();
|
||||
|
||||
setup();
|
||||
|
||||
this.request.setServletPath("/login");
|
||||
this.request.setMethod("POST");
|
||||
this.request.setParameter("username", "user");
|
||||
this.request.setParameter("password", "password");
|
||||
this.request.setSession(session);
|
||||
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||
|
||||
assertThat(this.response.getRedirectedUrl()).isEqualTo("/");
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RequestCacheDisabledConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
super.configure(http);
|
||||
http
|
||||
.requestCache().disable()
|
||||
.csrf().disable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UserDetailsService userDetailsService() {
|
||||
return new InMemoryUserDetailsManager(
|
||||
User.withUsername("user")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
public void loadConfig(Class<?>... configs) {
|
||||
this.context = new AnnotationConfigWebApplicationContext();
|
||||
this.context.register(configs);
|
||||
this.context.setServletContext(new MockServletContext());
|
||||
this.context.refresh();
|
||||
|
||||
this.context.getAutowireCapableBeanFactory().autowireBean(this);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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.
|
||||
@@ -299,7 +299,7 @@ public class MessageSecurityMetadataSourceRegistryTests {
|
||||
if (attrs == null) {
|
||||
return null;
|
||||
}
|
||||
assertThat(attrs.size()).isEqualTo(1);
|
||||
assertThat(attrs).hasSize(1);
|
||||
return attrs.iterator().next().toString();
|
||||
}
|
||||
}
|
||||
+4
-7
@@ -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.
|
||||
@@ -65,8 +65,7 @@ public class FilterSecurityMetadataSourceBeanDefinitionParserTests {
|
||||
.getBean("fids");
|
||||
Collection<ConfigAttribute> cad = fids
|
||||
.getAttributes(createFilterInvocation("/anything", "GET"));
|
||||
assertThat(cad).isNotNull();
|
||||
assertThat(cad.contains(new SecurityConfig("ROLE_A"))).isTrue();
|
||||
assertThat(cad).contains(new SecurityConfig("ROLE_A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,7 +79,7 @@ public class FilterSecurityMetadataSourceBeanDefinitionParserTests {
|
||||
ConfigAttribute[] cad = fids
|
||||
.getAttributes(createFilterInvocation("/anything", "GET"))
|
||||
.toArray(new ConfigAttribute[0]);
|
||||
assertThat(cad.length).isEqualTo(1);
|
||||
assertThat(cad).hasSize(1);
|
||||
assertThat(cad[0].toString()).isEqualTo("hasRole('ROLE_A')");
|
||||
}
|
||||
|
||||
@@ -98,9 +97,7 @@ public class FilterSecurityMetadataSourceBeanDefinitionParserTests {
|
||||
.getBean("fids");
|
||||
Collection<ConfigAttribute> cad = fids
|
||||
.getAttributes(createFilterInvocation("/secure", "GET"));
|
||||
assertThat(cad).isNotNull();
|
||||
assertThat(cad).hasSize(1);
|
||||
assertThat(cad.contains(new SecurityConfig("ROLE_A"))).isTrue();
|
||||
assertThat(cad).containsExactly(new SecurityConfig("ROLE_A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-3
@@ -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.
|
||||
@@ -108,7 +108,7 @@ public class GlobalMethodSecurityBeanDefinitionParserTests {
|
||||
|
||||
// SEC-1213. Check the order
|
||||
Advisor[] advisors = ((Advised) target).getAdvisors();
|
||||
assertThat(advisors.length).isEqualTo(1);
|
||||
assertThat(advisors).hasSize(1);
|
||||
assertThat(((MethodSecurityMetadataSourceAdvisor) advisors[0]).getOrder()).isEqualTo(1001);
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ public class GlobalMethodSecurityBeanDefinitionParserTests {
|
||||
target = (BusinessService) appContext.getBean("target");
|
||||
Object[] arg = new String[] { "joe", "bob", "sam" };
|
||||
Object[] result = target.methodReturningAnArray(arg);
|
||||
assertThat(result.length).isEqualTo(1);
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result[0]).isEqualTo("bob");
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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.
|
||||
@@ -66,11 +66,11 @@ public class InterceptMethodsBeanDefinitionDecoratorTests implements
|
||||
@Test
|
||||
public void targetDoesntLoseApplicationListenerInterface() {
|
||||
assertThat(appContext.getBeansOfType(ApplicationListener.class)).hasSize(1);
|
||||
assertThat(appContext.getBeanNamesForType(ApplicationListener.class).length).isEqualTo(1);
|
||||
assertThat(appContext.getBeanNamesForType(ApplicationListener.class)).hasSize(1);
|
||||
appContext.publishEvent(new AuthenticationSuccessEvent(
|
||||
new TestingAuthenticationToken("user", "")));
|
||||
|
||||
assertThat(target instanceof ApplicationListener<?>).isTrue();
|
||||
assertThat(target).isInstanceOf(ApplicationListener.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+15
-21
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>4.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<name>spring-security-core</name>
|
||||
<description>spring-security-core</description>
|
||||
<url>http://spring.io/spring-security</url>
|
||||
@@ -35,7 +35,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>4.3.5.RELEASE</version>
|
||||
<version>4.3.22.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -82,7 +82,7 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.8.4</version>
|
||||
<version>2.8.11.3</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
@@ -103,14 +103,14 @@
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
<artifactId>ehcache</artifactId>
|
||||
<version>2.9.0</version>
|
||||
<version>2.10.6</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
<version>1.8.4</version>
|
||||
<version>1.8.13</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
@@ -129,7 +129,7 @@
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<version>1.1.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -171,7 +171,7 @@
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<version>2.3.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -189,7 +189,7 @@
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-mockito</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.6.5</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -201,7 +201,7 @@
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-support</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.6.5</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -213,7 +213,7 @@
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-core</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.6.5</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -225,7 +225,7 @@
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.6.5</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -237,7 +237,7 @@
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4-common</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.6.5</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -249,7 +249,7 @@
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-reflect</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.6.5</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -261,7 +261,7 @@
|
||||
<dependency>
|
||||
<groupId>org.skyscreamer</groupId>
|
||||
<artifactId>jsonassert</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.4.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -273,7 +273,7 @@
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.7</version>
|
||||
<version>1.7.25</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -294,12 +294,6 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
@@ -55,5 +55,5 @@ public @interface Secured {
|
||||
*
|
||||
* @return String[] The secure method attributes
|
||||
*/
|
||||
public String[]value();
|
||||
public String[] value();
|
||||
}
|
||||
|
||||
+3
-1
@@ -112,7 +112,9 @@ class MethodSecurityEvaluationContext extends StandardEvaluationContext {
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
super.setVariable(paramNames[i], args[i]);
|
||||
if (paramNames[i] != null) {
|
||||
setVariable(paramNames[i], args[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -16,11 +16,11 @@
|
||||
|
||||
package org.springframework.security.authentication;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An {@link org.springframework.security.core.Authentication} implementation that is
|
||||
* designed for use whilst unit testing.
|
||||
@@ -49,7 +49,6 @@ public class TestingAuthenticationToken extends AbstractAuthenticationToken {
|
||||
public TestingAuthenticationToken(Object principal, Object credentials,
|
||||
String... authorities) {
|
||||
this(principal, credentials, AuthorityUtils.createAuthorityList(authorities));
|
||||
setAuthenticated(true);
|
||||
}
|
||||
|
||||
public TestingAuthenticationToken(Object principal, Object credentials,
|
||||
@@ -57,6 +56,7 @@ public class TestingAuthenticationToken extends AbstractAuthenticationToken {
|
||||
super(authorities);
|
||||
this.principal = principal;
|
||||
this.credentials = credentials;
|
||||
setAuthenticated(true);
|
||||
}
|
||||
|
||||
// ~ Methods
|
||||
|
||||
+3
@@ -22,7 +22,10 @@ package org.springframework.security.authentication.encoding;
|
||||
* </p>
|
||||
*
|
||||
* @author colin sampaleanu
|
||||
* @deprecated This is deprecated and marked for deletion. Replace with an implementation
|
||||
* of {@link org.springframework.security.crypto.password.PasswordEncoder}
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class BaseDigestPasswordEncoder extends BasePasswordEncoder {
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
+3
@@ -22,7 +22,10 @@ package org.springframework.security.authentication.encoding;
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated This is deprecated and marked for deletion. Replace with an implementation
|
||||
* of {@link org.springframework.security.crypto.password.PasswordEncoder}
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class BasePasswordEncoder implements PasswordEncoder {
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
+2
@@ -32,6 +32,8 @@ import org.springframework.util.Assert;
|
||||
* and non-encoded passwords are in use or when a null implementation is required.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @deprecated @deprecated This is deprecated and marked for deletion. Replace with
|
||||
* of {@link org.springframework.security.crypto.password.LdapShaPasswordEncoder}
|
||||
*/
|
||||
public class LdapShaPasswordEncoder implements PasswordEncoder {
|
||||
// ~ Static fields/initializers
|
||||
|
||||
+3
@@ -31,7 +31,10 @@ import org.springframework.security.crypto.codec.Utf8;
|
||||
* legacy applications, it's not secure, don't use it for anything new!
|
||||
*
|
||||
* @author Alan Stewart
|
||||
* @deprecated This is deprecated and marked for deletion. Replace with an implementation
|
||||
* of {@link org.springframework.security.crypto.password.Md4PasswordEncoder}
|
||||
*/
|
||||
@Deprecated
|
||||
public class Md4PasswordEncoder extends BaseDigestPasswordEncoder {
|
||||
|
||||
// ~ Methods
|
||||
|
||||
+4
@@ -33,7 +33,11 @@ package org.springframework.security.authentication.encoding;
|
||||
* @author Ray Krueger
|
||||
* @author colin sampaleanu
|
||||
* @author Ben Alex
|
||||
* @deprecated This is deprecated and marked for deletion. Replace with an implementation
|
||||
* of {@link org.springframework.security.crypto.password.MessageDigestPasswordEncoder}
|
||||
* with an algorithm of "MD5"
|
||||
*/
|
||||
@Deprecated
|
||||
public class Md5PasswordEncoder extends MessageDigestPasswordEncoder {
|
||||
|
||||
public Md5PasswordEncoder() {
|
||||
|
||||
+3
@@ -51,7 +51,10 @@ import org.springframework.util.Assert;
|
||||
* @author Ray Krueger
|
||||
* @author Luke Taylor
|
||||
* @since 1.0.1
|
||||
* @deprecated This is deprecated and marked for deletion. Replace with an implementation
|
||||
* of {@link org.springframework.security.crypto.password.MessageDigestPasswordEncoder}
|
||||
*/
|
||||
@Deprecated
|
||||
public class MessageDigestPasswordEncoder extends BaseDigestPasswordEncoder {
|
||||
|
||||
private final String algorithm;
|
||||
|
||||
+3
@@ -29,7 +29,10 @@ import java.util.Locale;
|
||||
*
|
||||
* @author colin sampaleanu
|
||||
* @author Ben Alex
|
||||
* @deprecated This class will be removed in Spring Security 5. For passivity switch to
|
||||
* {@link org.springframework.security.crypto.password.NoOpPasswordEncoder}.
|
||||
*/
|
||||
@Deprecated
|
||||
public class PlaintextPasswordEncoder extends BasePasswordEncoder {
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
+4
@@ -40,7 +40,11 @@ package org.springframework.security.authentication.encoding;
|
||||
* @author Ray Krueger
|
||||
* @author colin sampaleanu
|
||||
* @author Ben Alex
|
||||
* @deprecated This is deprecated and marked for deletion. Replace with an implementation
|
||||
* of {@link org.springframework.security.crypto.password.MessageDigestPasswordEncoder}
|
||||
* with an algorithm "SHA-$strength" (i.e. "SHA-1" or "SHA-256").
|
||||
*/
|
||||
@Deprecated
|
||||
public class ShaPasswordEncoder extends MessageDigestPasswordEncoder {
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@ import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.SmartApplicationListener;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Used for delegating to a number of SmartApplicationListener instances. This is useful
|
||||
@@ -32,7 +32,7 @@ import java.util.List;
|
||||
*/
|
||||
public final class DelegatingApplicationListener implements
|
||||
ApplicationListener<ApplicationEvent> {
|
||||
private List<SmartApplicationListener> listeners = new ArrayList<SmartApplicationListener>();
|
||||
private List<SmartApplicationListener> listeners = new CopyOnWriteArrayList<SmartApplicationListener>();
|
||||
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event == null) {
|
||||
|
||||
@@ -40,7 +40,7 @@ public class SpringSecurityCoreVersion {
|
||||
*/
|
||||
public static final long SERIAL_VERSION_UID = 420L;
|
||||
|
||||
static final String MIN_SPRING_VERSION = "4.3.5.RELEASE";
|
||||
static final String MIN_SPRING_VERSION = "4.3.22.RELEASE";
|
||||
|
||||
static {
|
||||
performVersionChecks();
|
||||
|
||||
+12
-2
@@ -48,13 +48,23 @@ public class SessionRegistryImpl implements SessionRegistry,
|
||||
protected final Log logger = LogFactory.getLog(SessionRegistryImpl.class);
|
||||
|
||||
/** <principal:Object,SessionIdSet> */
|
||||
private final ConcurrentMap<Object, Set<String>> principals = new ConcurrentHashMap<Object, Set<String>>();
|
||||
private final ConcurrentMap<Object, Set<String>> principals;
|
||||
/** <sessionId:Object,SessionInformation> */
|
||||
private final Map<String, SessionInformation> sessionIds = new ConcurrentHashMap<String, SessionInformation>();
|
||||
private final Map<String, SessionInformation> sessionIds;
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
public SessionRegistryImpl() {
|
||||
this.principals = new ConcurrentHashMap<Object, Set<String>>();
|
||||
this.sessionIds = new ConcurrentHashMap<String, SessionInformation>();
|
||||
}
|
||||
|
||||
public SessionRegistryImpl(ConcurrentMap<Object, Set<String>> principals,Map<String, SessionInformation> sessionIds) {
|
||||
this.principals=principals;
|
||||
this.sessionIds=sessionIds;
|
||||
}
|
||||
|
||||
public List<Object> getAllPrincipals() {
|
||||
return new ArrayList<Object>(principals.keySet());
|
||||
}
|
||||
|
||||
@@ -27,11 +27,16 @@ import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.CredentialsContainer;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -58,6 +63,8 @@ public class User implements UserDetails, CredentialsContainer {
|
||||
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private static final Log logger = LogFactory.getLog(User.class);
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
private String password;
|
||||
@@ -244,8 +251,99 @@ public class User implements UserDetails, CredentialsContainer {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a UserBuilder with a specified user name
|
||||
*
|
||||
* @param username the username to use
|
||||
* @return the UserBuilder
|
||||
*/
|
||||
public static UserBuilder withUsername(String username) {
|
||||
return new UserBuilder().username(username);
|
||||
return builder().username(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a UserBuilder
|
||||
*
|
||||
* @return the UserBuilder
|
||||
*/
|
||||
public static UserBuilder builder() {
|
||||
return new UserBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* <b>WARNING:</b> This method is considered unsafe for production and is only intended
|
||||
* for sample applications.
|
||||
* </p>
|
||||
* <p>
|
||||
* Creates a user and automatically encodes the provided password using
|
||||
* {@code PasswordEncoderFactories.createDelegatingPasswordEncoder()}. For example:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||
* .username("user")
|
||||
* .password("password")
|
||||
* .roles("USER")
|
||||
* .build();
|
||||
* // outputs {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
|
||||
* System.out.println(user.getPassword());
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* This is not safe for production (it is intended for getting started experience)
|
||||
* because the password "password" is compiled into the source code and then is
|
||||
* included in memory at the time of creation. This means there are still ways to
|
||||
* recover the plain text password making it unsafe. It does provide a slight
|
||||
* improvement to using plain text passwords since the UserDetails password is
|
||||
* securely hashed. This means if the UserDetails password is accidentally exposed,
|
||||
* the password is securely stored.
|
||||
*
|
||||
* In a production setting, it is recommended to hash the password ahead of time.
|
||||
* For example:
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
* // outputs {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
|
||||
* // remember the password that is printed out and use in the next step
|
||||
* System.out.println(encoder.encode("password"));
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* UserDetails user = User.withUsername("user")
|
||||
* .password("{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG")
|
||||
* .roles("USER")
|
||||
* .build();
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @return a UserBuilder that automatically encodes the password with the default
|
||||
* PasswordEncoder
|
||||
* @deprecated Using this method is not considered safe for production, but is
|
||||
* acceptable for demos and getting started. For production purposes, ensure the
|
||||
* password is encoded externally. See the method Javadoc for additional details.
|
||||
* There are no plans to remove this support. It is deprecated to indicate
|
||||
* that this is considered insecure for production purposes.
|
||||
*/
|
||||
@Deprecated
|
||||
public static UserBuilder withDefaultPasswordEncoder() {
|
||||
logger.warn("User.withDefaultPasswordEncoder() is considered unsafe for production and is only intended for sample applications.");
|
||||
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
return builder().passwordEncoder(encoder);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,6 +358,7 @@ public class User implements UserDetails, CredentialsContainer {
|
||||
private boolean accountLocked;
|
||||
private boolean credentialsExpired;
|
||||
private boolean disabled;
|
||||
private PasswordEncoder passwordEncoder = NoOpPasswordEncoder.getInstance();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
@@ -274,7 +373,7 @@ public class User implements UserDetails, CredentialsContainer {
|
||||
* @return the {@link UserBuilder} for method chaining (i.e. to populate
|
||||
* additional attributes for this user)
|
||||
*/
|
||||
private UserBuilder username(String username) {
|
||||
public UserBuilder username(String username) {
|
||||
Assert.notNull(username, "username cannot be null");
|
||||
this.username = username;
|
||||
return this;
|
||||
@@ -293,6 +392,20 @@ public class User implements UserDetails, CredentialsContainer {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the current password (if non-null) and any future passwords supplied
|
||||
* to {@link #password(String)}.
|
||||
*
|
||||
* @param encoder the encoder to use
|
||||
* @return the {@link UserBuilder} for method chaining (i.e. to populate
|
||||
* additional attributes for this user)
|
||||
*/
|
||||
private UserBuilder passwordEncoder(PasswordEncoder encoder) {
|
||||
Assert.notNull(encoder, "encoder cannot be null");
|
||||
this.passwordEncoder = encoder;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the roles. This method is a shortcut for calling
|
||||
* {@link #authorities(String...)}, but automatically prefixes each entry with
|
||||
@@ -351,7 +464,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;
|
||||
}
|
||||
@@ -418,7 +531,8 @@ public class User implements UserDetails, CredentialsContainer {
|
||||
}
|
||||
|
||||
public UserDetails build() {
|
||||
return new User(username, password, !disabled, !accountExpired,
|
||||
String encodedPassword = this.passwordEncoder.encode(password);
|
||||
return new User(username, encodedPassword, !disabled, !accountExpired,
|
||||
!credentialsExpired, !accountLocked, authorities);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -302,7 +302,7 @@ public class JdbcDaoImpl extends JdbcDaoSupport
|
||||
* Allows the default query string used to retrieve authorities based on username to
|
||||
* be overridden, if default table or column names need to be changed. The default
|
||||
* query is {@link #DEF_AUTHORITIES_BY_USERNAME_QUERY}; when modifying this query,
|
||||
* ensure that all returned columns are mapped back to the same column names as in the
|
||||
* ensure that all returned columns are mapped back to the same column positions as in the
|
||||
* default query.
|
||||
*
|
||||
* @param queryString The SQL query string to set
|
||||
@@ -320,7 +320,7 @@ public class JdbcDaoImpl extends JdbcDaoSupport
|
||||
* username to be overridden, if default table or column names need to be changed. The
|
||||
* default query is {@link #DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY}; when modifying
|
||||
* this query, ensure that all returned columns are mapped back to the same column
|
||||
* names as in the default query.
|
||||
* positions as in the default query.
|
||||
*
|
||||
* @param queryString The SQL query string to set
|
||||
*/
|
||||
@@ -370,7 +370,7 @@ public class JdbcDaoImpl extends JdbcDaoSupport
|
||||
* Allows the default query string used to retrieve users based on username to be
|
||||
* overridden, if default table or column names need to be changed. The default query
|
||||
* is {@link #DEF_USERS_BY_USERNAME_QUERY}; when modifying this query, ensure that all
|
||||
* returned columns are mapped back to the same column names as in the default query.
|
||||
* returned columns are mapped back to the same column positions as in the default query.
|
||||
* If the 'enabled' column does not exist in the source database, a permanent true
|
||||
* value for this column may be returned by using a query similar to
|
||||
*
|
||||
|
||||
@@ -58,6 +58,7 @@ public class CoreJackson2Module extends SimpleModule {
|
||||
context.setMixInAnnotations(RememberMeAuthenticationToken.class, RememberMeAuthenticationTokenMixin.class);
|
||||
context.setMixInAnnotations(SimpleGrantedAuthority.class, SimpleGrantedAuthorityMixin.class);
|
||||
context.setMixInAnnotations(Collections.<Object>unmodifiableSet(Collections.emptySet()).getClass(), UnmodifiableSetMixin.class);
|
||||
context.setMixInAnnotations(Collections.<Object>unmodifiableList(Collections.emptyList()).getClass(), UnmodifiableListMixin.class);
|
||||
context.setMixInAnnotations(User.class, UserMixin.class);
|
||||
context.setMixInAnnotations(UsernamePasswordAuthenticationToken.class, UsernamePasswordAuthenticationTokenMixin.class);
|
||||
}
|
||||
|
||||
+118
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
* 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.
|
||||
@@ -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,113 @@ 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.Collections$UnmodifiableRandomAccessList",
|
||||
"java.util.Date",
|
||||
"java.util.TreeMap",
|
||||
"java.util.HashMap",
|
||||
"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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Custom deserializer for {@link UnmodifiableListMixin}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @see UnmodifiableSetMixin
|
||||
* @since 4.2
|
||||
*/
|
||||
class UnmodifiableListDeserializer extends JsonDeserializer<List> {
|
||||
|
||||
@Override
|
||||
public List deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
|
||||
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
|
||||
JsonNode node = mapper.readTree(jp);
|
||||
List<Object> result = new ArrayList<Object>();
|
||||
if (node != null) {
|
||||
if (node instanceof ArrayNode) {
|
||||
ArrayNode arrayNode = (ArrayNode) node;
|
||||
Iterator<JsonNode> nodeIterator = arrayNode.iterator();
|
||||
while (nodeIterator.hasNext()) {
|
||||
JsonNode elementNode = nodeIterator.next();
|
||||
result.add(mapper.readValue(elementNode.traverse(mapper), Object.class));
|
||||
}
|
||||
} else {
|
||||
result.add(mapper.readValue(node.traverse(mapper), Object.class));
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This mixin class used to deserialize java.util.Collections$UnmodifiableRandomAccessList
|
||||
* and used with various AuthenticationToken implementation's mixin classes.
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new CoreJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @see UnmodifiableListDeserializer
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonDeserialize(using = UnmodifiableListDeserializer.class)
|
||||
class UnmodifiableListMixin {
|
||||
|
||||
/**
|
||||
* Mixin Constructor
|
||||
* @param s the Set
|
||||
*/
|
||||
@JsonCreator
|
||||
UnmodifiableListMixin(Set<?> s) {}
|
||||
}
|
||||
+2
-2
@@ -50,10 +50,10 @@ class UnmodifiableSetDeserializer extends JsonDeserializer<Set> {
|
||||
Iterator<JsonNode> nodeIterator = arrayNode.iterator();
|
||||
while (nodeIterator.hasNext()) {
|
||||
JsonNode elementNode = nodeIterator.next();
|
||||
resultSet.add(mapper.readValue(elementNode.toString(), Object.class));
|
||||
resultSet.add(mapper.readValue(elementNode.traverse(mapper), Object.class));
|
||||
}
|
||||
} else {
|
||||
resultSet.add(mapper.readValue(node.toString(), Object.class));
|
||||
resultSet.add(mapper.readValue(node.traverse(mapper), Object.class));
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableSet(resultSet);
|
||||
|
||||
+20
-8
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
@@ -24,12 +27,9 @@ import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.MissingNode;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Custom deserializer for {@link UsernamePasswordAuthenticationToken}. At the time of deserialization
|
||||
@@ -40,6 +40,7 @@ import java.util.List;
|
||||
* you can also registered it with your own mixin class.
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @author Greg Turnquist
|
||||
* @see UsernamePasswordAuthenticationTokenMixin
|
||||
* @since 4.2
|
||||
*/
|
||||
@@ -62,20 +63,31 @@ class UsernamePasswordAuthenticationTokenDeserializer extends JsonDeserializer<U
|
||||
JsonNode principalNode = readJsonNode(jsonNode, "principal");
|
||||
Object principal = null;
|
||||
if(principalNode.isObject()) {
|
||||
principal = mapper.readValue(principalNode.toString(), new TypeReference<User>() {});
|
||||
principal = mapper.readValue(principalNode.traverse(mapper), Object.class);
|
||||
} else {
|
||||
principal = principalNode.asText();
|
||||
}
|
||||
Object credentials = readJsonNode(jsonNode, "credentials").asText();
|
||||
JsonNode credentialsNode = readJsonNode(jsonNode, "credentials");
|
||||
Object credentials;
|
||||
if (credentialsNode.isNull()) {
|
||||
credentials = null;
|
||||
} else {
|
||||
credentials = credentialsNode.asText();
|
||||
}
|
||||
List<GrantedAuthority> authorities = mapper.readValue(
|
||||
readJsonNode(jsonNode, "authorities").toString(), new TypeReference<List<GrantedAuthority>>() {
|
||||
readJsonNode(jsonNode, "authorities").traverse(mapper), new TypeReference<List<GrantedAuthority>>() {
|
||||
});
|
||||
if (authenticated) {
|
||||
token = new UsernamePasswordAuthenticationToken(principal, credentials, authorities);
|
||||
} else {
|
||||
token = new UsernamePasswordAuthenticationToken(principal, credentials);
|
||||
}
|
||||
token.setDetails(readJsonNode(jsonNode, "details"));
|
||||
JsonNode detailsNode = readJsonNode(jsonNode, "details");
|
||||
if (detailsNode.isNull()) {
|
||||
token.setDetails(null);
|
||||
} else {
|
||||
token.setDetails(detailsNode);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -61,6 +61,12 @@ public class InMemoryUserDetailsManager implements UserDetailsManager {
|
||||
}
|
||||
}
|
||||
|
||||
public InMemoryUserDetailsManager(UserDetails... users) {
|
||||
for (UserDetails user : users) {
|
||||
createUser(user);
|
||||
}
|
||||
}
|
||||
|
||||
public InMemoryUserDetailsManager(Properties users) {
|
||||
Enumeration<?> names = users.propertyNames();
|
||||
UserAttributeEditor editor = new UserAttributeEditor();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
AbstractAccessDecisionManager.accessDenied=\u4E0D\u5141\u8BB8\u8BBF\u95EE
|
||||
AbstractLdapAuthenticationProvider.emptyPassword=\u574F\u7684\u51ED\u8BC1
|
||||
AbstractLdapAuthenticationProvider.emptyPassword=\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef
|
||||
AbstractSecurityInterceptor.authenticationNotFound=\u672A\u5728SecurityContext\u4E2D\u67E5\u627E\u5230\u8BA4\u8BC1\u5BF9\u8C61
|
||||
AbstractUserDetailsAuthenticationProvider.badCredentials=\u574F\u7684\u51ED\u8BC1
|
||||
AbstractUserDetailsAuthenticationProvider.badCredentials=\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef
|
||||
AbstractUserDetailsAuthenticationProvider.credentialsExpired=\u7528\u6237\u51ED\u8BC1\u5DF2\u8FC7\u671F
|
||||
AbstractUserDetailsAuthenticationProvider.disabled=\u7528\u6237\u5DF2\u5931\u6548
|
||||
AbstractUserDetailsAuthenticationProvider.expired=\u7528\u6237\u5E10\u53F7\u5DF2\u8FC7\u671F
|
||||
@@ -13,8 +13,8 @@ AccountStatusUserDetailsChecker.expired=\u7528\u6237\u5E10\u53F7\u5DF2\u8FC7\u67
|
||||
AccountStatusUserDetailsChecker.locked=\u7528\u6237\u5E10\u53F7\u5DF2\u88AB\u9501\u5B9A
|
||||
AclEntryAfterInvocationProvider.noPermission=\u7ED9\u5B9A\u7684Authentication\u5BF9\u8C61({0})\u6839\u672C\u65E0\u6743\u64CD\u63A7\u9886\u57DF\u5BF9\u8C61({1})
|
||||
AnonymousAuthenticationProvider.incorrectKey=\u5C55\u793A\u7684AnonymousAuthenticationToken\u4E0D\u542B\u6709\u9884\u671F\u7684key
|
||||
BindAuthenticator.badCredentials=\u574F\u7684\u51ED\u8BC1
|
||||
BindAuthenticator.emptyPassword=\u574F\u7684\u51ED\u8BC1
|
||||
BindAuthenticator.badCredentials=\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef
|
||||
BindAuthenticator.emptyPassword=\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef
|
||||
CasAuthenticationProvider.incorrectKey=\u5C55\u793A\u7684CasAuthenticationToken\u4E0D\u542B\u6709\u9884\u671F\u7684key
|
||||
CasAuthenticationProvider.noServiceTicket=\u672A\u80FD\u591F\u6B63\u786E\u63D0\u4F9B\u5F85\u9A8C\u8BC1\u7684CAS\u670D\u52A1\u7968\u6839
|
||||
ConcurrentSessionControlAuthenticationStrategy.exceededAllowed=\u5DF2\u7ECF\u8D85\u8FC7\u4E86\u5F53\u524D\u4E3B\u4F53({0})\u88AB\u5141\u8BB8\u7684\u6700\u5927\u4F1A\u8BDD\u6570\u91CF
|
||||
@@ -30,14 +30,14 @@ DigestAuthenticationFilter.nonceNotTwoTokens=Nonce\u5E94\u8BE5\u7531\u4E24\u90E8
|
||||
DigestAuthenticationFilter.usernameNotFound=\u7528\u6237\u540D{0}\u672A\u627E\u5230
|
||||
JdbcDaoImpl.noAuthority=\u6CA1\u6709\u4E3A\u7528\u6237{0}\u6307\u5B9A\u89D2\u8272
|
||||
JdbcDaoImpl.notFound=\u672A\u627E\u5230\u7528\u6237{0}
|
||||
LdapAuthenticationProvider.badCredentials=\u574F\u7684\u51ED\u8BC1
|
||||
LdapAuthenticationProvider.badCredentials=\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef
|
||||
LdapAuthenticationProvider.credentialsExpired=\u7528\u6237\u51ED\u8BC1\u5DF2\u8FC7\u671F
|
||||
LdapAuthenticationProvider.disabled=\u7528\u6237\u5DF2\u5931\u6548
|
||||
LdapAuthenticationProvider.expired=\u7528\u6237\u5E10\u53F7\u5DF2\u8FC7\u671F
|
||||
LdapAuthenticationProvider.locked=\u7528\u6237\u5E10\u53F7\u5DF2\u88AB\u9501\u5B9A
|
||||
LdapAuthenticationProvider.emptyUsername=\u7528\u6237\u540D\u4E0D\u5141\u8BB8\u4E3A\u7A7A
|
||||
LdapAuthenticationProvider.onlySupports=\u4EC5\u4EC5\u652F\u6301UsernamePasswordAuthenticationToken
|
||||
PasswordComparisonAuthenticator.badCredentials=\u574F\u7684\u51ED\u8BC1
|
||||
PasswordComparisonAuthenticator.badCredentials=\u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef
|
||||
#PersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.
|
||||
ProviderManager.providerNotFound=\u672A\u67E5\u627E\u5230\u9488\u5BF9{0}\u7684AuthenticationProvider
|
||||
RememberMeAuthenticationProvider.incorrectKey=\u5C55\u793ARememberMeAuthenticationToken\u4E0D\u542B\u6709\u9884\u671F\u7684key
|
||||
|
||||
+6
-6
@@ -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.
|
||||
@@ -56,7 +56,7 @@ public class Jsr250MethodSecurityMetadataSourceTests {
|
||||
@Test
|
||||
public void methodWithRolesAllowedHasCorrectAttribute() throws Exception {
|
||||
ConfigAttribute[] accessAttributes = findAttributes("adminMethod");
|
||||
assertThat(accessAttributes.length).isEqualTo(1);
|
||||
assertThat(accessAttributes).hasSize(1);
|
||||
assertThat(accessAttributes[0].toString()).isEqualTo("ROLE_ADMIN");
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public class Jsr250MethodSecurityMetadataSourceTests {
|
||||
this.mds.setDefaultRolePrefix("CUSTOMPREFIX_");
|
||||
|
||||
ConfigAttribute[] accessAttributes = findAttributes("adminMethod");
|
||||
assertThat(accessAttributes.length).isEqualTo(1);
|
||||
assertThat(accessAttributes).hasSize(1);
|
||||
assertThat(accessAttributes[0].toString()).isEqualTo("CUSTOMPREFIX_ADMIN");
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public class Jsr250MethodSecurityMetadataSourceTests {
|
||||
this.mds.setDefaultRolePrefix("");
|
||||
|
||||
ConfigAttribute[] accessAttributes = findAttributes("adminMethod");
|
||||
assertThat(accessAttributes.length).isEqualTo(1);
|
||||
assertThat(accessAttributes).hasSize(1);
|
||||
assertThat(accessAttributes[0].toString()).isEqualTo("ADMIN");
|
||||
}
|
||||
|
||||
@@ -113,14 +113,14 @@ public class Jsr250MethodSecurityMetadataSourceTests {
|
||||
this.mds.setDefaultRolePrefix(null);
|
||||
|
||||
ConfigAttribute[] accessAttributes = findAttributes("adminMethod");
|
||||
assertThat(accessAttributes.length).isEqualTo(1);
|
||||
assertThat(accessAttributes).hasSize(1);
|
||||
assertThat(accessAttributes[0].toString()).isEqualTo("ADMIN");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void alreadyHasDefaultPrefix() throws Exception {
|
||||
ConfigAttribute[] accessAttributes = findAttributes("roleAdminMethod");
|
||||
assertThat(accessAttributes.length).isEqualTo(1);
|
||||
assertThat(accessAttributes).hasSize(1);
|
||||
assertThat(accessAttributes[0].toString()).isEqualTo("ROLE_ADMIN");
|
||||
}
|
||||
|
||||
|
||||
+10
-13
@@ -137,8 +137,6 @@ public class SecuredAnnotationSecurityMetadataSourceTests {
|
||||
Collection<ConfigAttribute> attrs = this.mds.findAttributes(method,
|
||||
BusinessService.class);
|
||||
|
||||
assertThat(attrs).isNotNull();
|
||||
|
||||
// expect 2 attributes
|
||||
assertThat(attrs).hasSize(2);
|
||||
|
||||
@@ -147,7 +145,7 @@ public class SecuredAnnotationSecurityMetadataSourceTests {
|
||||
|
||||
// should have 2 SecurityConfigs
|
||||
for (ConfigAttribute sc : attrs) {
|
||||
assertThat(sc instanceof SecurityConfig).isTrue();
|
||||
assertThat(sc).isInstanceOf(SecurityConfig.class);
|
||||
|
||||
if (sc.getAttribute().equals("ROLE_USER")) {
|
||||
user = true;
|
||||
@@ -158,7 +156,7 @@ public class SecuredAnnotationSecurityMetadataSourceTests {
|
||||
}
|
||||
|
||||
// expect to have ROLE_USER and ROLE_ADMIN
|
||||
assertThat(user && admin).isTrue();
|
||||
assertThat(user).isEqualTo(admin).isTrue();
|
||||
}
|
||||
|
||||
// SEC-1491
|
||||
@@ -168,8 +166,7 @@ public class SecuredAnnotationSecurityMetadataSourceTests {
|
||||
new CustomSecurityAnnotationMetadataExtractor());
|
||||
Collection<ConfigAttribute> attrs = mds.findAttributes(
|
||||
CustomAnnotatedService.class);
|
||||
assertThat(attrs).hasSize(1);
|
||||
assertThat(attrs.toArray()[0]).isEqualTo(SecurityEnum.ADMIN);
|
||||
assertThat(attrs).containsOnly(SecurityEnum.ADMIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -181,8 +178,8 @@ public class SecuredAnnotationSecurityMetadataSourceTests {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(annotatedAtClassLevel).toArray(
|
||||
new ConfigAttribute[0]);
|
||||
|
||||
assertThat(attrs.length).isEqualTo(1);
|
||||
assertThat(attrs[0].getAttribute()).isEqualTo("CUSTOM");
|
||||
assertThat(attrs).hasSize(1);
|
||||
assertThat(attrs).extracting("attribute").containsOnly("CUSTOM");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -194,8 +191,8 @@ public class SecuredAnnotationSecurityMetadataSourceTests {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(annotatedAtInterfaceLevel).toArray(
|
||||
new ConfigAttribute[0]);
|
||||
|
||||
assertThat(attrs.length).isEqualTo(1);
|
||||
assertThat(attrs[0].getAttribute()).isEqualTo("CUSTOM");
|
||||
assertThat(attrs).hasSize(1);
|
||||
assertThat(attrs).extracting("attribute").containsOnly("CUSTOM");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -206,15 +203,15 @@ public class SecuredAnnotationSecurityMetadataSourceTests {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(annotatedAtMethodLevel).toArray(
|
||||
new ConfigAttribute[0]);
|
||||
|
||||
assertThat(attrs.length).isEqualTo(1);
|
||||
assertThat(attrs[0].getAttribute()).isEqualTo("CUSTOM");
|
||||
assertThat(attrs).hasSize(1);
|
||||
assertThat(attrs).extracting("attribute").containsOnly("CUSTOM");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyFactoryInterfaceAttributesFound() throws Exception {
|
||||
MockMethodInvocation mi = MethodInvocationFactory.createSec2150MethodInvocation();
|
||||
Collection<ConfigAttribute> attributes = mds.getAttributes(mi);
|
||||
assertThat(attributes.size()).isEqualTo(1);
|
||||
assertThat(attributes).hasSize(1);
|
||||
assertThat(attributes).extracting("attribute").containsOnly("ROLE_PERSON");
|
||||
}
|
||||
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.access.expression.method;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
/**
|
||||
* @author shabarijonnalagadda
|
||||
*
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class MethodSecurityEvaluationContextTests {
|
||||
@Mock
|
||||
private ParameterNameDiscoverer paramNameDiscoverer;
|
||||
@Mock
|
||||
private Authentication authentication;
|
||||
@Mock
|
||||
private MethodInvocation methodInvocation;
|
||||
|
||||
@Test
|
||||
public void lookupVariableWhenParameterNameNullThenNotSet() {
|
||||
Class<String> type = String.class;
|
||||
Method method = ReflectionUtils.findMethod(String.class, "contains", CharSequence.class);
|
||||
doReturn(new String[] {null}).when(paramNameDiscoverer).getParameterNames(method);
|
||||
doReturn(new Object[]{null}).when(methodInvocation).getArguments();
|
||||
doReturn(type).when(methodInvocation).getThis();
|
||||
doReturn(method).when(methodInvocation).getMethod();
|
||||
NotNullVariableMethodSecurityEvaluationContext context= new NotNullVariableMethodSecurityEvaluationContext(authentication, methodInvocation, paramNameDiscoverer);
|
||||
context.lookupVariable("testVariable");
|
||||
}
|
||||
|
||||
private static class NotNullVariableMethodSecurityEvaluationContext
|
||||
extends MethodSecurityEvaluationContext {
|
||||
|
||||
public NotNullVariableMethodSecurityEvaluationContext(Authentication auth, MethodInvocation mi,
|
||||
ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
super(auth, mi, parameterNameDiscoverer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVariable(String name, Object value) {
|
||||
if ( name == null ) {
|
||||
throw new IllegalArgumentException("name should not be null");
|
||||
}
|
||||
else {
|
||||
super.setVariable(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-11
@@ -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.
|
||||
@@ -89,7 +89,7 @@ public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(voidImpl1).toArray(
|
||||
new ConfigAttribute[0]);
|
||||
|
||||
assertThat(attrs.length).isEqualTo(1);
|
||||
assertThat(attrs).hasSize(1);
|
||||
assertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue();
|
||||
PreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0];
|
||||
assertThat(pre.getAuthorizeExpression()).isNotNull();
|
||||
@@ -102,7 +102,7 @@ public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(voidImpl2).toArray(
|
||||
new ConfigAttribute[0]);
|
||||
|
||||
assertThat(attrs.length).isEqualTo(1);
|
||||
assertThat(attrs).hasSize(1);
|
||||
assertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue();
|
||||
PreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0];
|
||||
assertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo("someExpression");
|
||||
@@ -115,7 +115,7 @@ public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(voidImpl3).toArray(
|
||||
new ConfigAttribute[0]);
|
||||
|
||||
assertThat(attrs.length).isEqualTo(1);
|
||||
assertThat(attrs).hasSize(1);
|
||||
assertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue();
|
||||
PreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0];
|
||||
assertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo("permitAll");
|
||||
@@ -128,7 +128,7 @@ public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(listImpl1).toArray(
|
||||
new ConfigAttribute[0]);
|
||||
|
||||
assertThat(attrs.length).isEqualTo(2);
|
||||
assertThat(attrs).hasSize(2);
|
||||
assertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue();
|
||||
assertThat(attrs[1] instanceof PostInvocationExpressionAttribute).isTrue();
|
||||
PreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0];
|
||||
@@ -143,7 +143,7 @@ public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(notherListImpl1).toArray(
|
||||
new ConfigAttribute[0]);
|
||||
|
||||
assertThat(attrs.length).isEqualTo(1);
|
||||
assertThat(attrs).hasSize(1);
|
||||
assertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue();
|
||||
PreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0];
|
||||
assertThat(pre.getFilterExpression()).isNotNull();
|
||||
@@ -157,7 +157,7 @@ public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(notherListImpl2).toArray(
|
||||
new ConfigAttribute[0]);
|
||||
|
||||
assertThat(attrs.length).isEqualTo(1);
|
||||
assertThat(attrs).hasSize(1);
|
||||
assertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue();
|
||||
PreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0];
|
||||
assertThat(pre.getFilterExpression()).isNotNull();
|
||||
@@ -171,7 +171,7 @@ public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(annotatedAtClassLevel).toArray(
|
||||
new ConfigAttribute[0]);
|
||||
|
||||
assertThat(attrs.length).isEqualTo(1);
|
||||
assertThat(attrs).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -179,7 +179,7 @@ public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(annotatedAtInterfaceLevel).toArray(
|
||||
new ConfigAttribute[0]);
|
||||
|
||||
assertThat(attrs.length).isEqualTo(1);
|
||||
assertThat(attrs).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -187,14 +187,14 @@ public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(annotatedAtMethodLevel).toArray(
|
||||
new ConfigAttribute[0]);
|
||||
|
||||
assertThat(attrs.length).isEqualTo(1);
|
||||
assertThat(attrs).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void proxyFactoryInterfaceAttributesFound() throws Exception {
|
||||
MockMethodInvocation mi = MethodInvocationFactory.createSec2150MethodInvocation();
|
||||
Collection<ConfigAttribute> attributes = mds.getAttributes(mi);
|
||||
assertThat(attributes.size()).isEqualTo(1);
|
||||
assertThat(attributes).hasSize(1);
|
||||
Expression expression = (Expression) ReflectionTestUtils.getField(attributes
|
||||
.iterator().next(), "authorizeExpression");
|
||||
assertThat(expression.getExpressionString()).isEqualTo("hasRole('ROLE_PERSON')");
|
||||
|
||||
+3
-3
@@ -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.
|
||||
@@ -181,12 +181,12 @@ public class TestHelperTests {
|
||||
public void testCreateAuthorityList() {
|
||||
List<GrantedAuthority> authorities1 = HierarchicalRolesTestHelper
|
||||
.createAuthorityList("ROLE_A");
|
||||
assertThat(1).isEqualTo(authorities1.size());
|
||||
assertThat(authorities1).hasSize(1);
|
||||
assertThat(authorities1.get(0).getAuthority()).isEqualTo("ROLE_A");
|
||||
|
||||
List<GrantedAuthority> authorities2 = HierarchicalRolesTestHelper
|
||||
.createAuthorityList("ROLE_A", "ROLE_C");
|
||||
assertThat(2).isEqualTo(authorities2.size());
|
||||
assertThat(authorities2).hasSize(2);
|
||||
assertThat(authorities2.get(0).getAuthority()).isEqualTo("ROLE_A");
|
||||
assertThat(authorities2.get(1).getAuthority()).isEqualTo("ROLE_C");
|
||||
}
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ public class AbstractAccessDecisionManagerTests {
|
||||
list.add(voter);
|
||||
list.add(denyVoter);
|
||||
MockDecisionManagerImpl mock = new MockDecisionManagerImpl(list);
|
||||
assertThat(mock.getDecisionVoters().size()).isEqualTo(list.size());
|
||||
assertThat(mock.getDecisionVoters()).hasSize(list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.Test;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class TestingAuthenticationTokenTests {
|
||||
@Test
|
||||
public void constructorWhenNoAuthoritiesThenUnauthenticated() {
|
||||
TestingAuthenticationToken unauthenticated =
|
||||
new TestingAuthenticationToken("principal", "credentials");
|
||||
|
||||
assertThat(unauthenticated.isAuthenticated()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenArityAuthoritiesThenAuthenticated() {
|
||||
TestingAuthenticationToken authenticated =
|
||||
new TestingAuthenticationToken("principal", "credentials", "authority");
|
||||
|
||||
assertThat(authenticated.isAuthenticated()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenCollectionAuthoritiesThenAuthenticated() {
|
||||
TestingAuthenticationToken authenticated =
|
||||
new TestingAuthenticationToken("principal", "credentials",
|
||||
Arrays.<GrantedAuthority>asList(new SimpleGrantedAuthority("authority")));
|
||||
|
||||
assertThat(authenticated.isAuthenticated()).isTrue();
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -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.
|
||||
@@ -48,7 +48,7 @@ public class DefaultSecurityParameterNameDiscovererTests {
|
||||
List<ParameterNameDiscoverer> discoverers = (List<ParameterNameDiscoverer>) ReflectionTestUtils
|
||||
.getField(discoverer, "parameterNameDiscoverers");
|
||||
|
||||
assertThat(discoverers.size()).isEqualTo(2);
|
||||
assertThat(discoverers).hasSize(2);
|
||||
|
||||
ParameterNameDiscoverer annotationDisc = discoverers.get(0);
|
||||
assertThat(annotationDisc).isInstanceOf(AnnotationParameterNameDiscoverer.class);
|
||||
@@ -68,7 +68,7 @@ public class DefaultSecurityParameterNameDiscovererTests {
|
||||
List<ParameterNameDiscoverer> discoverers = (List<ParameterNameDiscoverer>) ReflectionTestUtils
|
||||
.getField(discoverer, "parameterNameDiscoverers");
|
||||
|
||||
assertThat(discoverers.size()).isEqualTo(3);
|
||||
assertThat(discoverers).hasSize(3);
|
||||
assertThat(discoverers.get(0)).isInstanceOf(
|
||||
LocalVariableTableParameterNameDiscoverer.class);
|
||||
|
||||
@@ -78,7 +78,7 @@ public class DefaultSecurityParameterNameDiscovererTests {
|
||||
annotationDisc, "annotationClassesToUse");
|
||||
assertThat(annotationsToUse).containsOnly(P.class.getName());
|
||||
|
||||
assertThat(discoverers.get(2).getClass()).isEqualTo(
|
||||
assertThat(discoverers.get(2)).isInstanceOf(
|
||||
DefaultParameterNameDiscoverer.class);
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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 java.util.HashMap;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* @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\"}";
|
||||
try {
|
||||
mapper.readValue(content, Object.class);
|
||||
fail("Expected Exception");
|
||||
} catch(RuntimeException e) {
|
||||
assertThat(e).hasMessageContaining("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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readValueWhenHashMapThenReadsAsSpecificType() throws Exception {
|
||||
mapper.addMixIn(NotWhitelisted.class, NotWhitelistedMixin.class);
|
||||
String content = "{\"@class\":\"java.util.HashMap\"}";
|
||||
|
||||
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(HashMap.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 {
|
||||
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -36,11 +36,13 @@ public class SimpleGrantedAuthorityMixinTests extends AbstractMixinTests {
|
||||
// @formatter:off
|
||||
public static final String AUTHORITY_JSON = "{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\", \"authority\": \"ROLE_USER\"}";
|
||||
|
||||
public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.ArrayList\", [" + AUTHORITY_JSON + "]]";
|
||||
public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", [" + AUTHORITY_JSON + "]]";
|
||||
|
||||
public static final String AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", [" + AUTHORITY_JSON + "]]";
|
||||
|
||||
public static final String NO_AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.ArrayList\", []]";
|
||||
public static final String NO_AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", []]";
|
||||
|
||||
public static final String EMPTY_AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.ArrayList\", []]";
|
||||
|
||||
public static final String NO_AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", []]";
|
||||
// @formatter:on
|
||||
|
||||
+65
-2
@@ -17,20 +17,24 @@
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonClassDescription;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.json.JSONException;
|
||||
import org.junit.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @author Greg Turnquist
|
||||
* @since 4.2
|
||||
*/
|
||||
public class UsernamePasswordAuthenticationTokenMixinTests extends AbstractMixinTests {
|
||||
@@ -49,10 +53,24 @@ public class UsernamePasswordAuthenticationTokenMixinTests extends AbstractMixin
|
||||
public static final String AUTHENTICATED_STRINGPRINCIPAL_JSON = AUTHENTICATED_JSON.replace( UserDeserializerTests.USER_JSON, "\"admin\"");
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
private static final String NON_USER_PRINCIPAL_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.jackson2.UsernamePasswordAuthenticationTokenMixinTests$NonUserPrincipal\", "
|
||||
+ "\"username\": \"admin\""
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
private static final String AUTHENTICATED_NON_USER_PRINCIPAL_JSON = AUTHENTICATED_JSON
|
||||
.replace(UserDeserializerTests.USER_JSON, NON_USER_PRINCIPAL_JSON)
|
||||
.replaceAll(UserDeserializerTests.USER_PASSWORD, "null")
|
||||
.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON, SimpleGrantedAuthorityMixinTests.NO_AUTHORITIES_ARRAYLIST_JSON);
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
private static final String UNAUTHENTICATED_STRINGPRINCIPAL_JSON = AUTHENTICATED_STRINGPRINCIPAL_JSON
|
||||
.replace("\"authenticated\": true, ", "\"authenticated\": false, ")
|
||||
.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON, SimpleGrantedAuthorityMixinTests.NO_AUTHORITIES_ARRAYLIST_JSON);
|
||||
.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON, SimpleGrantedAuthorityMixinTests.EMPTY_AUTHORITIES_ARRAYLIST_JSON);
|
||||
// @formatter:on
|
||||
|
||||
@Test
|
||||
@@ -115,9 +133,54 @@ public class UsernamePasswordAuthenticationTokenMixinTests extends AbstractMixin
|
||||
JSONAssert.assertEquals(AUTHENTICATED_JSON.replaceAll(UserDeserializerTests.USER_PASSWORD, "null"), actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinWithNonUserPrincipalTest() throws JsonProcessingException, JSONException {
|
||||
NonUserPrincipal principal = new NonUserPrincipal();
|
||||
principal.setUsername("admin");
|
||||
UsernamePasswordAuthenticationToken token =
|
||||
new UsernamePasswordAuthenticationToken(principal, null, new ArrayList<GrantedAuthority>());
|
||||
String actualJson = mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(AUTHENTICATED_NON_USER_PRINCIPAL_JSON, actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithNonUserPrincipalTest() throws IOException {
|
||||
UsernamePasswordAuthenticationToken token = mapper
|
||||
.readValue(AUTHENTICATED_NON_USER_PRINCIPAL_JSON, UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getPrincipal()).isNotNull().isInstanceOf(NonUserPrincipal.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializingThenDeserializingWithNoCredentialsOrDetailsShouldWork() throws IOException {
|
||||
// given
|
||||
UsernamePasswordAuthenticationToken original = new UsernamePasswordAuthenticationToken("Frodo", null);
|
||||
|
||||
// when
|
||||
String serialized = this.mapper.writeValueAsString(original);
|
||||
UsernamePasswordAuthenticationToken deserialized = this.mapper.readValue(serialized, UsernamePasswordAuthenticationToken.class);
|
||||
|
||||
// then
|
||||
assertThat(deserialized).isEqualTo(original);
|
||||
}
|
||||
|
||||
|
||||
private UsernamePasswordAuthenticationToken createToken() {
|
||||
User user = createDefaultUser();
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
|
||||
return token;
|
||||
}
|
||||
|
||||
@JsonClassDescription
|
||||
public static class NonUserPrincipal {
|
||||
private String username;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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.
|
||||
@@ -262,7 +262,7 @@ public class JdbcUserDetailsManagerTests {
|
||||
manager.renameGroup("GROUP_0", "GROUP_X");
|
||||
|
||||
assertThat(template.queryForObject("select id from groups where group_name = 'GROUP_X'",
|
||||
Integer.class)).isEqualTo(0);
|
||||
Integer.class)).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
dependencies {
|
||||
optional 'org.bouncycastle:bcpkix-jdk15on:1.54'
|
||||
}
|
||||
optional 'org.bouncycastle:bcpkix-jdk15on:1.60'
|
||||
}
|
||||
|
||||
+5
-11
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
<version>4.2.3.BUILD-SNAPSHOT</version>
|
||||
<version>4.2.11.RELEASE</version>
|
||||
<name>spring-security-crypto</name>
|
||||
<description>spring-security-crypto</description>
|
||||
<url>http://spring.io/spring-security</url>
|
||||
@@ -35,7 +35,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>4.3.5.RELEASE</version>
|
||||
<version>4.3.22.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -52,14 +52,14 @@
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
<version>1.54</version>
|
||||
<version>1.60</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<version>1.1.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -83,7 +83,7 @@
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.7</version>
|
||||
<version>1.7.25</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -92,12 +92,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
+79
@@ -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.crypto.factory;
|
||||
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.LdapShaPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.Md4PasswordEncoder;
|
||||
import org.springframework.security.crypto.password.MessageDigestPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
|
||||
import org.springframework.security.crypto.password.StandardPasswordEncoder;
|
||||
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Used for creating {@link PasswordEncoder} instances
|
||||
* @author Rob Winch
|
||||
* @since 4.2.6
|
||||
*/
|
||||
public class PasswordEncoderFactories {
|
||||
|
||||
/**
|
||||
* Creates a {@link DelegatingPasswordEncoder} with default mappings. Additional
|
||||
* mappings may be added and the encoding will be updated to conform with best
|
||||
* practices. However, due to the nature of {@link DelegatingPasswordEncoder} the
|
||||
* updates should not impact users. The mappings current are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>bcrypt - {@link BCryptPasswordEncoder} (Also used for encoding)</li>
|
||||
* <li>ldap - {@link LdapShaPasswordEncoder}</li>
|
||||
* <li>MD4 - {@link Md4PasswordEncoder}</li>
|
||||
* <li>MD5 - {@code new MessageDigestPasswordEncoder("MD5")}</li>
|
||||
* <li>noop - {@link NoOpPasswordEncoder}</li>
|
||||
* <li>pbkdf2 - {@link Pbkdf2PasswordEncoder}</li>
|
||||
* <li>scrypt - {@link SCryptPasswordEncoder}</li>
|
||||
* <li>SHA-1 - {@code new MessageDigestPasswordEncoder("SHA-1")}</li>
|
||||
* <li>SHA-256 - {@code new MessageDigestPasswordEncoder("SHA-256")}</li>
|
||||
* <li>sha256 - {@link StandardPasswordEncoder}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the {@link PasswordEncoder} to use
|
||||
*/
|
||||
public static PasswordEncoder createDelegatingPasswordEncoder() {
|
||||
String encodingId = "bcrypt";
|
||||
Map<String, PasswordEncoder> encoders = new HashMap<String, PasswordEncoder>();
|
||||
encoders.put(encodingId, new BCryptPasswordEncoder());
|
||||
encoders.put("ldap", new LdapShaPasswordEncoder());
|
||||
encoders.put("MD4", new Md4PasswordEncoder());
|
||||
encoders.put("MD5", new MessageDigestPasswordEncoder("MD5"));
|
||||
encoders.put("noop", NoOpPasswordEncoder.getInstance());
|
||||
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
|
||||
encoders.put("scrypt", new SCryptPasswordEncoder());
|
||||
encoders.put("SHA-1", new MessageDigestPasswordEncoder("SHA-1"));
|
||||
encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256"));
|
||||
encoders.put("sha256", new StandardPasswordEncoder());
|
||||
|
||||
return new DelegatingPasswordEncoder(encodingId, encoders);
|
||||
}
|
||||
|
||||
private PasswordEncoderFactories() {}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.keygen;
|
||||
|
||||
import org.springframework.security.crypto.codec.Base64;
|
||||
|
||||
/**
|
||||
* A StringKeyGenerator that generates base64-encoded String keys. Delegates to a
|
||||
* {@link BytesKeyGenerator} for the actual key generation.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @author Rob Winch
|
||||
* @since 4.2.6
|
||||
*/
|
||||
public class Base64StringKeyGenerator implements StringKeyGenerator {
|
||||
private static final int DEFAULT_KEY_LENGTH = 32;
|
||||
private final BytesKeyGenerator keyGenerator;
|
||||
|
||||
/**
|
||||
* Creates an instance with keyLength of 32 bytes and standard Base64 encoding.
|
||||
*/
|
||||
public Base64StringKeyGenerator() {
|
||||
this(DEFAULT_KEY_LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with the provided key length in bytes and standard Base64
|
||||
* encoding.
|
||||
* @param keyLength the key length in bytes
|
||||
*/
|
||||
public Base64StringKeyGenerator(int keyLength) {
|
||||
if(keyLength < DEFAULT_KEY_LENGTH) {
|
||||
throw new IllegalArgumentException("keyLength must be greater than or equal to" + DEFAULT_KEY_LENGTH);
|
||||
}
|
||||
this.keyGenerator = KeyGenerators.secureRandom(keyLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateKey() {
|
||||
byte[] key = this.keyGenerator.generateKey();
|
||||
byte[] base64EncodedKey = Base64.encode(key);
|
||||
return new String(base64EncodedKey);
|
||||
}
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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.password;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A password encoder that delegates to another PasswordEncoder based upon a prefixed
|
||||
* identifier.
|
||||
*
|
||||
* <h2>Constructing an instance</h2>
|
||||
*
|
||||
* You can easily construct an instance using
|
||||
* {@link org.springframework.security.crypto.factory.PasswordEncoderFactories}.
|
||||
* Alternatively, you may create your own custom instance. For example:
|
||||
*
|
||||
* <pre>
|
||||
* String idForEncode = "bcrypt";
|
||||
* Map<String,PasswordEncoder> encoders = new HashMap<>();
|
||||
* encoders.put(idForEncode, new BCryptPasswordEncoder());
|
||||
* encoders.put("noop", NoOpPasswordEncoder.getInstance());
|
||||
* encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
|
||||
* encoders.put("scrypt", new SCryptPasswordEncoder());
|
||||
* encoders.put("sha256", new StandardPasswordEncoder());
|
||||
*
|
||||
* PasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(idForEncode, encoders);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <h2>Password Storage Format</h2>
|
||||
*
|
||||
* The general format for a password is:
|
||||
*
|
||||
* <pre>
|
||||
* {id}encodedPassword
|
||||
* </pre>
|
||||
*
|
||||
* Such that "id" is an identifier used to look up which {@link PasswordEncoder} should
|
||||
* be used and "encodedPassword" is the original encoded password for the selected
|
||||
* {@link PasswordEncoder}. The "id" must be at the beginning of the password, start with
|
||||
* "{" and end with "}". If the "id" cannot be found, the "id" will be null.
|
||||
*
|
||||
* For example, the following might be a list of passwords encoded using different "id".
|
||||
* All of the original passwords are "password".
|
||||
*
|
||||
* <pre>
|
||||
* {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
|
||||
* {noop}password
|
||||
* {pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
|
||||
* {scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=
|
||||
* {sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
|
||||
* </pre>
|
||||
*
|
||||
* For the DelegatingPasswordEncoder that we constructed above:
|
||||
*
|
||||
* <ol>
|
||||
* <li>The first password would have a {@code PasswordEncoder} id of "bcrypt" and
|
||||
* encodedPassword of "$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG".
|
||||
* When matching it would delegate to
|
||||
* {@link org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder}</li>
|
||||
* <li>The second password would have a {@code PasswordEncoder} id of "noop" and
|
||||
* encodedPassword of "password". When matching it would delegate to
|
||||
* {@link NoOpPasswordEncoder}</li>
|
||||
* <li>The third password would have a {@code PasswordEncoder} id of "pbkdf2" and
|
||||
* encodedPassword of
|
||||
* "5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc".
|
||||
* When matching it would delegate to {@link Pbkdf2PasswordEncoder}</li>
|
||||
* <li>The fourth password would have a {@code PasswordEncoder} id of "scrypt" and
|
||||
* encodedPassword of
|
||||
* "$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc="
|
||||
* When matching it would delegate to
|
||||
* {@link org.springframework.security.crypto.scrypt.SCryptPasswordEncoder}</li>
|
||||
* <li>The final password would have a {@code PasswordEncoder} id of "sha256" and
|
||||
* encodedPassword of
|
||||
* "97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0".
|
||||
* When matching it would delegate to {@link StandardPasswordEncoder}</li>
|
||||
* </ol>
|
||||
*
|
||||
* <h2>Password Encoding</h2>
|
||||
*
|
||||
* The {@code idForEncode} passed into the constructor determines which
|
||||
* {@link PasswordEncoder} will be used for encoding passwords. In the
|
||||
* {@code DelegatingPasswordEncoder} we constructed above, that means that the result of
|
||||
* encoding "password" would be delegated to {@code BCryptPasswordEncoder} and be prefixed
|
||||
* with "{bcrypt}". The end result would look like:
|
||||
*
|
||||
* <pre>
|
||||
* {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
|
||||
* </pre>
|
||||
*
|
||||
* <h2>Password Matching</h2>
|
||||
*
|
||||
* Matching is done based upon the "id" and the mapping of the "id" to the
|
||||
* {@link PasswordEncoder} provided in the constructor. Our example in "Password Storage
|
||||
* Format" provides a working example of how this is done.
|
||||
*
|
||||
* By default the result of invoking {@link #matches(CharSequence, String)} with a
|
||||
* password with an "id" that is not mapped (including a null id) will result in an
|
||||
* {@link IllegalArgumentException}. This behavior can be customized using
|
||||
* {@link #setDefaultPasswordEncoderForMatches(PasswordEncoder)}.
|
||||
*
|
||||
* @see org.springframework.security.crypto.factory.PasswordEncoderFactories
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Michael Simons
|
||||
* @since 4.2.6
|
||||
*/
|
||||
public class DelegatingPasswordEncoder implements PasswordEncoder {
|
||||
private static final String PREFIX = "{";
|
||||
private static final String SUFFIX = "}";
|
||||
private final String idForEncode;
|
||||
private final PasswordEncoder passwordEncoderForEncode;
|
||||
private final Map<String, PasswordEncoder> idToPasswordEncoder;
|
||||
private PasswordEncoder defaultPasswordEncoderForMatches = new UnmappedIdPasswordEncoder();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param idForEncode the id used to lookup which {@link PasswordEncoder} should be
|
||||
* used for {@link #encode(CharSequence)}
|
||||
* @param idToPasswordEncoder a Map of id to {@link PasswordEncoder} used to determine
|
||||
* which {@link PasswordEncoder} should be used for {@link #matches(CharSequence, String)}
|
||||
*/
|
||||
public DelegatingPasswordEncoder(String idForEncode,
|
||||
Map<String, PasswordEncoder> idToPasswordEncoder) {
|
||||
if(idForEncode == null) {
|
||||
throw new IllegalArgumentException("idForEncode cannot be null");
|
||||
}
|
||||
if(!idToPasswordEncoder.containsKey(idForEncode)) {
|
||||
throw new IllegalArgumentException("idForEncode " + idForEncode + "is not found in idToPasswordEncoder " + idToPasswordEncoder);
|
||||
}
|
||||
for(String id : idToPasswordEncoder.keySet()) {
|
||||
if(id == null) {
|
||||
continue;
|
||||
}
|
||||
if(id.contains(PREFIX)) {
|
||||
throw new IllegalArgumentException("id " + id + " cannot contain " + PREFIX);
|
||||
}
|
||||
if(id.contains(SUFFIX)) {
|
||||
throw new IllegalArgumentException("id " + id + " cannot contain " + SUFFIX);
|
||||
}
|
||||
}
|
||||
this.idForEncode = idForEncode;
|
||||
this.passwordEncoderForEncode = idToPasswordEncoder.get(idForEncode);
|
||||
this.idToPasswordEncoder = new HashMap<String, PasswordEncoder>(idToPasswordEncoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link PasswordEncoder} to delegate to for
|
||||
* {@link #matches(CharSequence, String)} if the id is not mapped to a
|
||||
* {@link PasswordEncoder}.
|
||||
*
|
||||
* <p>
|
||||
The encodedPassword provided will be the full password
|
||||
* passed in including the {"id"} portion.* For example, if the password of
|
||||
* "{notmapped}foobar" was used, the "id" would be "notmapped" and the encodedPassword
|
||||
* passed into the {@link PasswordEncoder} would be "{notmapped}foobar".
|
||||
* </p>
|
||||
* @param defaultPasswordEncoderForMatches the encoder to use. The default is to
|
||||
* throw an {@link IllegalArgumentException}
|
||||
*/
|
||||
public void setDefaultPasswordEncoderForMatches(
|
||||
PasswordEncoder defaultPasswordEncoderForMatches) {
|
||||
if(defaultPasswordEncoderForMatches == null) {
|
||||
throw new IllegalArgumentException("defaultPasswordEncoderForMatches cannot be null");
|
||||
}
|
||||
this.defaultPasswordEncoderForMatches = defaultPasswordEncoderForMatches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
return PREFIX + this.idForEncode + SUFFIX + this.passwordEncoderForEncode.encode(rawPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
|
||||
if(rawPassword == null && prefixEncodedPassword == null) {
|
||||
return true;
|
||||
}
|
||||
String id = extractId(prefixEncodedPassword);
|
||||
PasswordEncoder delegate = this.idToPasswordEncoder.get(id);
|
||||
if(delegate == null) {
|
||||
return this.defaultPasswordEncoderForMatches
|
||||
.matches(rawPassword, prefixEncodedPassword);
|
||||
}
|
||||
String encodedPassword = extractEncodedPassword(prefixEncodedPassword);
|
||||
return delegate.matches(rawPassword, encodedPassword);
|
||||
}
|
||||
|
||||
private String extractId(String prefixEncodedPassword) {
|
||||
if (prefixEncodedPassword == null) {
|
||||
return null;
|
||||
}
|
||||
int start = prefixEncodedPassword.indexOf(PREFIX);
|
||||
if(start != 0) {
|
||||
return null;
|
||||
}
|
||||
int end = prefixEncodedPassword.indexOf(SUFFIX, start);
|
||||
if(end < 0) {
|
||||
return null;
|
||||
}
|
||||
return prefixEncodedPassword.substring(start + 1, end);
|
||||
}
|
||||
|
||||
private String extractEncodedPassword(String prefixEncodedPassword) {
|
||||
int start = prefixEncodedPassword.indexOf(SUFFIX);
|
||||
return prefixEncodedPassword.substring(start + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default {@link PasswordEncoder} that throws an exception that a id could
|
||||
*/
|
||||
private class UnmappedIdPasswordEncoder implements PasswordEncoder {
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
throw new UnsupportedOperationException("encode is not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword,
|
||||
String prefixEncodedPassword) {
|
||||
String id = extractId(prefixEncodedPassword);
|
||||
throw new IllegalArgumentException("There is no PasswordEncoder mapped for the id \"" + id + "\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ final class Digester {
|
||||
|
||||
private final String algorithm;
|
||||
|
||||
private final int iterations;
|
||||
private int iterations;
|
||||
|
||||
/**
|
||||
* Create a new Digester.
|
||||
@@ -42,7 +42,7 @@ final class Digester {
|
||||
// eagerly validate the algorithm
|
||||
createDigest(algorithm);
|
||||
this.algorithm = algorithm;
|
||||
this.iterations = iterations;
|
||||
setIterations(iterations);
|
||||
}
|
||||
|
||||
public byte[] digest(byte[] value) {
|
||||
@@ -53,6 +53,13 @@ final class Digester {
|
||||
return value;
|
||||
}
|
||||
|
||||
final void setIterations(int iterations) {
|
||||
if(iterations <= 0) {
|
||||
throw new IllegalArgumentException("Iterations value must be greater than zero");
|
||||
}
|
||||
this.iterations = iterations;
|
||||
}
|
||||
|
||||
private static MessageDigest createDigest(String algorithm) {
|
||||
try {
|
||||
return MessageDigest.getInstance(algorithm);
|
||||
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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.password;
|
||||
|
||||
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 java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* This {@link PasswordEncoder} is provided for legacy purposes only and is not considered
|
||||
* secure.
|
||||
*
|
||||
* A version of {@link PasswordEncoder} which supports Ldap SHA and SSHA (salted-SHA)
|
||||
* encodings. The values are base-64 encoded and have the label "{SHA}" (or "{SSHA}")
|
||||
* prepended to the encoded hash. These can be made lower-case in the encoded password, if
|
||||
* required, by setting the <tt>forceLowerCasePrefix</tt> property to true.
|
||||
*
|
||||
* Also supports plain text passwords, so can safely be used in cases when both encoded
|
||||
* and non-encoded passwords are in use or when a null implementation is required.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 4.2.6
|
||||
* @deprecated Digest based password encoding is not considered secure. Instead use an
|
||||
* adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or
|
||||
* SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports
|
||||
* password upgrades. There are no plans to remove this support. It is deprecated to indicate
|
||||
* that this is a legacy implementation and using it is considered insecure.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LdapShaPasswordEncoder implements PasswordEncoder {
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
/** The number of bytes in a SHA hash */
|
||||
private static final int SHA_LENGTH = 20;
|
||||
private static final String SSHA_PREFIX = "{SSHA}";
|
||||
private static final String SSHA_PREFIX_LC = SSHA_PREFIX.toLowerCase();
|
||||
private static final String SHA_PREFIX = "{SHA}";
|
||||
private static final String SHA_PREFIX_LC = SHA_PREFIX.toLowerCase();
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
private BytesKeyGenerator saltGenerator;
|
||||
|
||||
private boolean forceLowerCasePrefix;
|
||||
|
||||
// ~ Constructors
|
||||
// ===================================================================================================
|
||||
|
||||
public LdapShaPasswordEncoder() {
|
||||
this(KeyGenerators.secureRandom());
|
||||
}
|
||||
|
||||
public LdapShaPasswordEncoder(BytesKeyGenerator saltGenerator) {
|
||||
if(saltGenerator == null) {
|
||||
throw new IllegalArgumentException("saltGenerator cannot be null");
|
||||
}
|
||||
this.saltGenerator = saltGenerator;
|
||||
}
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
private byte[] combineHashAndSalt(byte[] hash, byte[] salt) {
|
||||
if (salt == null) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
byte[] hashAndSalt = new byte[hash.length + salt.length];
|
||||
System.arraycopy(hash, 0, hashAndSalt, 0, hash.length);
|
||||
System.arraycopy(salt, 0, hashAndSalt, hash.length, salt.length);
|
||||
|
||||
return hashAndSalt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the hash of password (and salt bytes, if supplied) and returns a base64
|
||||
* encoded concatenation of the hash and salt, prefixed with {SHA} (or {SSHA} if salt
|
||||
* was used).
|
||||
*
|
||||
* @param rawPass the password to be encoded.
|
||||
*
|
||||
* @return the encoded password in the specified format
|
||||
*
|
||||
*/
|
||||
public String encode(CharSequence rawPass) {
|
||||
byte[] salt = this.saltGenerator.generateKey();
|
||||
return encode(rawPass, salt);
|
||||
}
|
||||
|
||||
|
||||
private String encode(CharSequence rawPassword, byte[] salt) {
|
||||
MessageDigest sha;
|
||||
|
||||
try {
|
||||
sha = MessageDigest.getInstance("SHA");
|
||||
sha.update(Utf8.encode(rawPassword));
|
||||
}
|
||||
catch (java.security.NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException("No SHA implementation available!");
|
||||
}
|
||||
|
||||
if (salt != null) {
|
||||
sha.update(salt);
|
||||
}
|
||||
|
||||
byte[] hash = combineHashAndSalt(sha.digest(), (byte[]) salt);
|
||||
|
||||
String prefix;
|
||||
|
||||
if (salt == null || salt.length == 0) {
|
||||
prefix = forceLowerCasePrefix ? SHA_PREFIX_LC : SHA_PREFIX;
|
||||
}
|
||||
else {
|
||||
prefix = forceLowerCasePrefix ? SSHA_PREFIX_LC : SSHA_PREFIX;
|
||||
}
|
||||
|
||||
return prefix + Utf8.decode(Base64.encode(hash));
|
||||
}
|
||||
|
||||
private byte[] extractSalt(String encPass) {
|
||||
String encPassNoLabel = encPass.substring(6);
|
||||
|
||||
byte[] hashAndSalt = Base64.decode(encPassNoLabel.getBytes());
|
||||
int saltLength = hashAndSalt.length - SHA_LENGTH;
|
||||
byte[] salt = new byte[saltLength];
|
||||
System.arraycopy(hashAndSalt, SHA_LENGTH, salt, 0, saltLength);
|
||||
|
||||
return salt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the validity of an unencoded password against an encoded one in the form
|
||||
* "{SSHA}sQuQF8vj8Eg2Y1hPdh3bkQhCKQBgjhQI".
|
||||
*
|
||||
* @param rawPassword unencoded password to be verified.
|
||||
* @param encodedPassword the actual SSHA or SHA encoded password
|
||||
*
|
||||
* @return true if they match (independent of the case of the prefix).
|
||||
*/
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
return matches(rawPassword == null ? null : rawPassword.toString(), encodedPassword);
|
||||
}
|
||||
|
||||
private boolean matches(String rawPassword, String encodedPassword) {
|
||||
String prefix = extractPrefix(encodedPassword);
|
||||
|
||||
if (prefix == null) {
|
||||
return PasswordEncoderUtils.equals(encodedPassword, rawPassword);
|
||||
}
|
||||
|
||||
byte[] salt;
|
||||
if (prefix.equals(SSHA_PREFIX) || prefix.equals(SSHA_PREFIX_LC)) {
|
||||
salt = extractSalt(encodedPassword);
|
||||
}
|
||||
else if (!prefix.equals(SHA_PREFIX) && !prefix.equals(SHA_PREFIX_LC)) {
|
||||
throw new IllegalArgumentException("Unsupported password prefix '" + prefix
|
||||
+ "'");
|
||||
}
|
||||
else {
|
||||
// Standard SHA
|
||||
salt = null;
|
||||
}
|
||||
|
||||
int startOfHash = prefix.length();
|
||||
|
||||
String encodedRawPass = encode(rawPassword, salt).substring(startOfHash);
|
||||
|
||||
return PasswordEncoderUtils
|
||||
.equals(encodedRawPass, encodedPassword.substring(startOfHash));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash prefix or null if there isn't one.
|
||||
*/
|
||||
private String extractPrefix(String encPass) {
|
||||
if (!encPass.startsWith("{")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int secondBrace = encPass.lastIndexOf('}');
|
||||
|
||||
if (secondBrace < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Couldn't find closing brace for SHA prefix");
|
||||
}
|
||||
|
||||
return encPass.substring(0, secondBrace + 1);
|
||||
}
|
||||
|
||||
public void setForceLowerCasePrefix(boolean forceLowerCasePrefix) {
|
||||
this.forceLowerCasePrefix = forceLowerCasePrefix;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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.password;
|
||||
|
||||
/**
|
||||
* Implementation of the MD4 message digest derived from the RSA Data Security, Inc, MD4
|
||||
* Message-Digest Algorithm.
|
||||
*
|
||||
* @author Alan Stewart
|
||||
*/
|
||||
class Md4 {
|
||||
private static final int BLOCK_SIZE = 64;
|
||||
private static final int HASH_SIZE = 16;
|
||||
private final byte[] buffer = new byte[BLOCK_SIZE];
|
||||
private int bufferOffset;
|
||||
private long byteCount;
|
||||
private final int[] state = new int[4];
|
||||
private final int[] tmp = new int[16];
|
||||
|
||||
Md4() {
|
||||
reset();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
bufferOffset = 0;
|
||||
byteCount = 0;
|
||||
state[0] = 0x67452301;
|
||||
state[1] = 0xEFCDAB89;
|
||||
state[2] = 0x98BADCFE;
|
||||
state[3] = 0x10325476;
|
||||
}
|
||||
|
||||
public byte[] digest() {
|
||||
byte[] resBuf = new byte[HASH_SIZE];
|
||||
digest(resBuf, 0, HASH_SIZE);
|
||||
return resBuf;
|
||||
}
|
||||
|
||||
private void digest(byte[] buffer, int off) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
buffer[off + (i * 4 + j)] = (byte) (state[i] >>> (8 * j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void digest(byte[] buffer, int offset, int len) {
|
||||
this.buffer[this.bufferOffset++] = (byte) 0x80;
|
||||
int lenOfBitLen = 8;
|
||||
int C = BLOCK_SIZE - lenOfBitLen;
|
||||
if (this.bufferOffset > C) {
|
||||
while (this.bufferOffset < BLOCK_SIZE) {
|
||||
this.buffer[this.bufferOffset++] = (byte) 0x00;
|
||||
}
|
||||
update(this.buffer, 0);
|
||||
this.bufferOffset = 0;
|
||||
}
|
||||
|
||||
while (this.bufferOffset < C) {
|
||||
this.buffer[this.bufferOffset++] = (byte) 0x00;
|
||||
}
|
||||
|
||||
long bitCount = byteCount * 8;
|
||||
for (int i = 0; i < 64; i += 8) {
|
||||
this.buffer[this.bufferOffset++] = (byte) (bitCount >>> (i));
|
||||
}
|
||||
|
||||
update(this.buffer, 0);
|
||||
digest(buffer, offset);
|
||||
}
|
||||
|
||||
public void update(byte[] input, int offset, int length) {
|
||||
byteCount += length;
|
||||
int todo;
|
||||
while (length >= (todo = BLOCK_SIZE - this.bufferOffset)) {
|
||||
System.arraycopy(input, offset, this.buffer, this.bufferOffset, todo);
|
||||
update(this.buffer, 0);
|
||||
length -= todo;
|
||||
offset += todo;
|
||||
this.bufferOffset = 0;
|
||||
}
|
||||
|
||||
System.arraycopy(input, offset, this.buffer, this.bufferOffset, length);
|
||||
bufferOffset += length;
|
||||
}
|
||||
|
||||
private void update(byte[] block, int offset) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
tmp[i] = (block[offset++] & 0xFF) | (block[offset++] & 0xFF) << 8
|
||||
| (block[offset++] & 0xFF) << 16 | (block[offset++] & 0xFF) << 24;
|
||||
}
|
||||
|
||||
int A = state[0];
|
||||
int B = state[1];
|
||||
int C = state[2];
|
||||
int D = state[3];
|
||||
|
||||
A = FF(A, B, C, D, tmp[0], 3);
|
||||
D = FF(D, A, B, C, tmp[1], 7);
|
||||
C = FF(C, D, A, B, tmp[2], 11);
|
||||
B = FF(B, C, D, A, tmp[3], 19);
|
||||
A = FF(A, B, C, D, tmp[4], 3);
|
||||
D = FF(D, A, B, C, tmp[5], 7);
|
||||
C = FF(C, D, A, B, tmp[6], 11);
|
||||
B = FF(B, C, D, A, tmp[7], 19);
|
||||
A = FF(A, B, C, D, tmp[8], 3);
|
||||
D = FF(D, A, B, C, tmp[9], 7);
|
||||
C = FF(C, D, A, B, tmp[10], 11);
|
||||
B = FF(B, C, D, A, tmp[11], 19);
|
||||
A = FF(A, B, C, D, tmp[12], 3);
|
||||
D = FF(D, A, B, C, tmp[13], 7);
|
||||
C = FF(C, D, A, B, tmp[14], 11);
|
||||
B = FF(B, C, D, A, tmp[15], 19);
|
||||
|
||||
A = GG(A, B, C, D, tmp[0], 3);
|
||||
D = GG(D, A, B, C, tmp[4], 5);
|
||||
C = GG(C, D, A, B, tmp[8], 9);
|
||||
B = GG(B, C, D, A, tmp[12], 13);
|
||||
A = GG(A, B, C, D, tmp[1], 3);
|
||||
D = GG(D, A, B, C, tmp[5], 5);
|
||||
C = GG(C, D, A, B, tmp[9], 9);
|
||||
B = GG(B, C, D, A, tmp[13], 13);
|
||||
A = GG(A, B, C, D, tmp[2], 3);
|
||||
D = GG(D, A, B, C, tmp[6], 5);
|
||||
C = GG(C, D, A, B, tmp[10], 9);
|
||||
B = GG(B, C, D, A, tmp[14], 13);
|
||||
A = GG(A, B, C, D, tmp[3], 3);
|
||||
D = GG(D, A, B, C, tmp[7], 5);
|
||||
C = GG(C, D, A, B, tmp[11], 9);
|
||||
B = GG(B, C, D, A, tmp[15], 13);
|
||||
|
||||
A = HH(A, B, C, D, tmp[0], 3);
|
||||
D = HH(D, A, B, C, tmp[8], 9);
|
||||
C = HH(C, D, A, B, tmp[4], 11);
|
||||
B = HH(B, C, D, A, tmp[12], 15);
|
||||
A = HH(A, B, C, D, tmp[2], 3);
|
||||
D = HH(D, A, B, C, tmp[10], 9);
|
||||
C = HH(C, D, A, B, tmp[6], 11);
|
||||
B = HH(B, C, D, A, tmp[14], 15);
|
||||
A = HH(A, B, C, D, tmp[1], 3);
|
||||
D = HH(D, A, B, C, tmp[9], 9);
|
||||
C = HH(C, D, A, B, tmp[5], 11);
|
||||
B = HH(B, C, D, A, tmp[13], 15);
|
||||
A = HH(A, B, C, D, tmp[3], 3);
|
||||
D = HH(D, A, B, C, tmp[11], 9);
|
||||
C = HH(C, D, A, B, tmp[7], 11);
|
||||
B = HH(B, C, D, A, tmp[15], 15);
|
||||
|
||||
state[0] += A;
|
||||
state[1] += B;
|
||||
state[2] += C;
|
||||
state[3] += D;
|
||||
}
|
||||
|
||||
private int FF(int a, int b, int c, int d, int x, int s) {
|
||||
int t = a + ((b & c) | (~b & d)) + x;
|
||||
return t << s | t >>> (32 - s);
|
||||
}
|
||||
|
||||
private int GG(int a, int b, int c, int d, int x, int s) {
|
||||
int t = a + ((b & (c | d)) | (c & d)) + x + 0x5A827999;
|
||||
return t << s | t >>> (32 - s);
|
||||
}
|
||||
|
||||
private int HH(int a, int b, int c, int d, int x, int s) {
|
||||
int t = a + (b ^ c ^ d) + x + 0x6ED9EBA1;
|
||||
return t << s | t >>> (32 - s);
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.password;
|
||||
|
||||
import org.springframework.security.crypto.codec.Base64;
|
||||
import org.springframework.security.crypto.codec.Hex;
|
||||
import org.springframework.security.crypto.codec.Utf8;
|
||||
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
|
||||
import org.springframework.security.crypto.keygen.StringKeyGenerator;
|
||||
|
||||
/**
|
||||
* This {@link PasswordEncoder} is provided for legacy purposes only and is not considered secure.
|
||||
*
|
||||
* Encodes passwords using MD4. The general format of the password is:
|
||||
*
|
||||
* <pre>
|
||||
* s = salt == null ? "" : "{" + salt + "}"
|
||||
* s + md4(password + s)
|
||||
* </pre>
|
||||
*
|
||||
* Such that "salt" is the salt, md4 is the digest method, and password is the actual
|
||||
* password. For example with a password of "password", and a salt of
|
||||
* "thisissalt":
|
||||
*
|
||||
* <pre>
|
||||
* String s = salt == null ? "" : "{" + salt + "}";
|
||||
* s + md4(password + s)
|
||||
* "{thisissalt}" + md4(password + "{thisissalt}")
|
||||
* "{thisissalt}6cc7924dad12ade79dfb99e424f25260"
|
||||
* </pre>
|
||||
*
|
||||
* If the salt does not exist, then omit "{salt}" like this:
|
||||
*
|
||||
* <pre>
|
||||
* md4(password)
|
||||
* </pre>
|
||||
*
|
||||
* If the salt is an empty String, then only use "{}" like this:
|
||||
*
|
||||
* <pre>
|
||||
* "{}" + md4(password + "{}")
|
||||
* </pre>
|
||||
*
|
||||
* The format is intended to work with the Md4PasswordEncoder that was found in the
|
||||
* Spring Security core module. However, the passwords will need to be migrated to include
|
||||
* any salt with the password since this API provides Salt internally vs making it the
|
||||
* responsibility of the user. To migrate passwords from the SaltSource use the following:
|
||||
*
|
||||
* <pre>
|
||||
* String salt = saltSource.getSalt(user);
|
||||
* String s = salt == null ? null : "{" + salt + "}";
|
||||
* String migratedPassword = s + user.getPassword();
|
||||
* </pre>
|
||||
*
|
||||
* @author Ray Krueger
|
||||
* @author Luke Taylor
|
||||
* @author Rob winch
|
||||
* @since 5.1
|
||||
* @deprecated Digest based password encoding is not considered secure. Instead use an
|
||||
* adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or
|
||||
* SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports
|
||||
* password upgrades. There are no plans to remove this support. It is deprecated to indicate
|
||||
* that this is a legacy implementation and using it is considered insecure.
|
||||
*/
|
||||
@Deprecated
|
||||
public class Md4PasswordEncoder implements PasswordEncoder {
|
||||
private static final String PREFIX = "{";
|
||||
private static final String SUFFIX = "}";
|
||||
private StringKeyGenerator saltGenerator = new Base64StringKeyGenerator();
|
||||
private boolean encodeHashAsBase64;
|
||||
|
||||
private Digester digester;
|
||||
|
||||
|
||||
public void setEncodeHashAsBase64(boolean encodeHashAsBase64) {
|
||||
this.encodeHashAsBase64 = encodeHashAsBase64;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the rawPass using a MessageDigest. If a salt is specified it will be merged
|
||||
* with the password before encoding.
|
||||
*
|
||||
* @param rawPassword The plain text password
|
||||
* @return Hex string of password digest (or base64 encoded string if
|
||||
* encodeHashAsBase64 is enabled.
|
||||
*/
|
||||
public String encode(CharSequence rawPassword) {
|
||||
String salt = PREFIX + this.saltGenerator.generateKey() + SUFFIX;
|
||||
return digest(salt, rawPassword);
|
||||
}
|
||||
|
||||
private String digest(String salt, CharSequence rawPassword) {
|
||||
if(rawPassword == null) {
|
||||
rawPassword = "";
|
||||
}
|
||||
String saltedPassword = rawPassword + salt;
|
||||
byte[] saltedPasswordBytes = Utf8.encode(saltedPassword);
|
||||
|
||||
Md4 md4 = new Md4();
|
||||
md4.update(saltedPasswordBytes, 0, saltedPasswordBytes.length);
|
||||
|
||||
byte[] digest = md4.digest();
|
||||
String encoded = encode(digest);
|
||||
return salt + encoded;
|
||||
}
|
||||
|
||||
private String encode(byte[] digest) {
|
||||
if (this.encodeHashAsBase64) {
|
||||
return Utf8.decode(Base64.encode(digest));
|
||||
}
|
||||
else {
|
||||
return new String(Hex.encode(digest));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a previously encoded password and compares it with a rawpassword after mixing
|
||||
* in the salt and encoding that value
|
||||
*
|
||||
* @param rawPassword plain text password
|
||||
* @param encodedPassword previously encoded password
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
String salt = extractSalt(encodedPassword);
|
||||
String rawPasswordEncoded = digest(salt, rawPassword);
|
||||
return PasswordEncoderUtils.equals(encodedPassword.toString(), rawPasswordEncoded);
|
||||
}
|
||||
|
||||
private String extractSalt(String prefixEncodedPassword) {
|
||||
int start = prefixEncodedPassword.indexOf(PREFIX);
|
||||
if(start != 0) {
|
||||
return "";
|
||||
}
|
||||
int end = prefixEncodedPassword.indexOf(SUFFIX, start);
|
||||
if(end < 0) {
|
||||
return "";
|
||||
}
|
||||
return prefixEncodedPassword.substring(start, end + 1);
|
||||
}
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.password;
|
||||
|
||||
import org.springframework.security.crypto.codec.Base64;
|
||||
import org.springframework.security.crypto.codec.Hex;
|
||||
import org.springframework.security.crypto.codec.Utf8;
|
||||
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
|
||||
import org.springframework.security.crypto.keygen.StringKeyGenerator;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* This {@link PasswordEncoder} is provided for legacy purposes only and is not considered secure.
|
||||
*
|
||||
* Encodes passwords using the passed in {@link MessageDigest}.
|
||||
*
|
||||
* The general format of the password is:
|
||||
*
|
||||
* <pre>
|
||||
* s = salt == null ? "" : "{" + salt + "}"
|
||||
* s + digest(password + s)
|
||||
* </pre>
|
||||
*
|
||||
* Such that "salt" is the salt, digest is the digest method, and password is the actual
|
||||
* password. For example when using MD5, a password of "password", and a salt of
|
||||
* "thisissalt":
|
||||
*
|
||||
* <pre>
|
||||
* String s = salt == null ? "" : "{" + salt + "}";
|
||||
* s + md5(password + s)
|
||||
* "{thisissalt}" + md5(password + "{thisissalt}")
|
||||
* "{thisissalt}2a4e7104c2780098f50ed5a84bb2323d"
|
||||
* </pre>
|
||||
*
|
||||
* If the salt does not exist, then omit "{salt}" like this:
|
||||
*
|
||||
* <pre>
|
||||
* digest(password)
|
||||
* </pre>
|
||||
*
|
||||
* If the salt is an empty String, then only use "{}" like this:
|
||||
*
|
||||
* <pre>
|
||||
* "{}" + digest(password + "{}")
|
||||
* </pre>
|
||||
*
|
||||
* The format is intended to work with the DigestPasswordEncoder that was found in the
|
||||
* Spring Security core module. However, the passwords will need to be migrated to include
|
||||
* any salt with the password since this API provides Salt internally vs making it the
|
||||
* responsibility of the user. To migrate passwords from the SaltSource use the following:
|
||||
*
|
||||
* <pre>
|
||||
* String salt = saltSource.getSalt(user);
|
||||
* String s = salt == null ? null : "{" + salt + "}";
|
||||
* String migratedPassword = s + user.getPassword();
|
||||
* </pre>
|
||||
*
|
||||
* @author Ray Krueger
|
||||
* @author Luke Taylor
|
||||
* @author Rob Winch
|
||||
* @since 4.2.6
|
||||
* @deprecated Digest based password encoding is not considered secure. Instead use an
|
||||
* adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or
|
||||
* SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports
|
||||
* password upgrades. There are no plans to remove this support. It is deprecated to indicate
|
||||
* that this is a legacy implementation and using it is considered insecure.
|
||||
*/
|
||||
@Deprecated
|
||||
public class MessageDigestPasswordEncoder implements PasswordEncoder {
|
||||
private static final String PREFIX = "{";
|
||||
private static final String SUFFIX = "}";
|
||||
private StringKeyGenerator saltGenerator = new Base64StringKeyGenerator();
|
||||
private boolean encodeHashAsBase64;
|
||||
|
||||
private Digester digester;
|
||||
|
||||
/**
|
||||
* The digest algorithm to use Supports the named
|
||||
* <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/CryptoSpec.html#AppA">
|
||||
* Message Digest Algorithms</a> in the Java environment.
|
||||
*
|
||||
* @param algorithm
|
||||
*/
|
||||
public MessageDigestPasswordEncoder(String algorithm) {
|
||||
this.digester = new Digester(algorithm, 1);
|
||||
}
|
||||
|
||||
public void setEncodeHashAsBase64(boolean encodeHashAsBase64) {
|
||||
this.encodeHashAsBase64 = encodeHashAsBase64;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the rawPass using a MessageDigest. If a salt is specified it will be merged
|
||||
* with the password before encoding.
|
||||
*
|
||||
* @param rawPassword The plain text password
|
||||
* @return Hex string of password digest (or base64 encoded string if
|
||||
* encodeHashAsBase64 is enabled.
|
||||
*/
|
||||
public String encode(CharSequence rawPassword) {
|
||||
String salt = PREFIX + this.saltGenerator.generateKey() + SUFFIX;
|
||||
return digest(salt, rawPassword);
|
||||
}
|
||||
|
||||
private String digest(String salt, CharSequence rawPassword) {
|
||||
String saltedPassword = rawPassword + salt;
|
||||
|
||||
byte[] digest = this.digester.digest(Utf8.encode(saltedPassword));
|
||||
String encoded = encode(digest);
|
||||
return salt + encoded;
|
||||
}
|
||||
|
||||
private String encode(byte[] digest) {
|
||||
if (this.encodeHashAsBase64) {
|
||||
return Utf8.decode(Base64.encode(digest));
|
||||
}
|
||||
else {
|
||||
return new String(Hex.encode(digest));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a previously encoded password and compares it with a rawpassword after mixing
|
||||
* in the salt and encoding that value
|
||||
*
|
||||
* @param rawPassword plain text password
|
||||
* @param encodedPassword previously encoded password
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
String salt = extractSalt(encodedPassword);
|
||||
String rawPasswordEncoded = digest(salt, rawPassword);
|
||||
return PasswordEncoderUtils.equals(encodedPassword.toString(), rawPasswordEncoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of iterations for which the calculated hash value should be
|
||||
* "stretched". If this is greater than one, the initial digest is calculated, the
|
||||
* digest function will be called repeatedly on the result for the additional number
|
||||
* of iterations.
|
||||
*
|
||||
* @param iterations the number of iterations which will be executed on the hashed
|
||||
* password/salt value. Defaults to 1.
|
||||
*/
|
||||
public void setIterations(int iterations) {
|
||||
this.digester.setIterations(iterations);
|
||||
}
|
||||
|
||||
private String extractSalt(String prefixEncodedPassword) {
|
||||
int start = prefixEncodedPassword.indexOf(PREFIX);
|
||||
if(start != 0) {
|
||||
return "";
|
||||
}
|
||||
int end = prefixEncodedPassword.indexOf(SUFFIX, start);
|
||||
if(end < 0) {
|
||||
return "";
|
||||
}
|
||||
return prefixEncodedPassword.substring(start, end + 1);
|
||||
}
|
||||
}
|
||||
+8
@@ -16,11 +16,19 @@
|
||||
package org.springframework.security.crypto.password;
|
||||
|
||||
/**
|
||||
* This {@link PasswordEncoder} is provided for legacy and testing purposes only and is
|
||||
* not considered secure.
|
||||
*
|
||||
* A password encoder that does nothing. Useful for testing where working with plain text
|
||||
* passwords may be preferred.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @deprecated This PasswordEncoder is not secure. Instead use an
|
||||
* adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or
|
||||
* SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports
|
||||
* password upgrades.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class NoOpPasswordEncoder implements PasswordEncoder {
|
||||
|
||||
public String encode(CharSequence rawPassword) {
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.password;
|
||||
|
||||
import org.springframework.security.crypto.codec.Utf8;
|
||||
|
||||
/**
|
||||
* Utility for constant time comparison to prevent against timing attacks.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class PasswordEncoderUtils {
|
||||
|
||||
/**
|
||||
* Constant time comparison to prevent against timing attacks.
|
||||
* @param expected
|
||||
* @param actual
|
||||
* @return
|
||||
*/
|
||||
static boolean equals(String expected, String actual) {
|
||||
byte[] expectedBytes = bytesUtf8(expected);
|
||||
byte[] actualBytes = bytesUtf8(actual);
|
||||
int expectedLength = expectedBytes == null ? -1 : expectedBytes.length;
|
||||
int actualLength = actualBytes == null ? -1 : actualBytes.length;
|
||||
|
||||
int result = expectedLength == actualLength ? 0 : 1;
|
||||
for (int i = 0; i < actualLength; i++) {
|
||||
byte expectedByte = expectedLength <= 0 ? 0 : expectedBytes[i % expectedLength];
|
||||
byte actualByte = actualBytes[i % actualLength];
|
||||
result |= expectedByte ^ actualByte;
|
||||
}
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
private static byte[] bytesUtf8(String s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Utf8.encode(s); // need to check if Utf8.encode() runs in constant time (probably not). This may leak length of string.
|
||||
}
|
||||
|
||||
private PasswordEncoderUtils() {
|
||||
}
|
||||
}
|
||||
+70
-8
@@ -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,12 @@
|
||||
package org.springframework.security.crypto.password;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
|
||||
import org.springframework.security.crypto.codec.Base64;
|
||||
import org.springframework.security.crypto.codec.Hex;
|
||||
import org.springframework.security.crypto.codec.Utf8;
|
||||
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
|
||||
@@ -41,7 +43,7 @@ import static org.springframework.security.crypto.util.EncodingUtils.subArray;
|
||||
* @since 4.1
|
||||
*/
|
||||
public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
||||
private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";
|
||||
|
||||
private static final int DEFAULT_HASH_WIDTH = 256;
|
||||
private static final int DEFAULT_ITERATIONS = 185000;
|
||||
|
||||
@@ -50,10 +52,12 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
||||
private final byte[] secret;
|
||||
private final int hashWidth;
|
||||
private final int iterations;
|
||||
private String algorithm = SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name();
|
||||
private boolean encodeHashAsBase64;
|
||||
|
||||
/**
|
||||
* Constructs a PBKDF2 password encoder with no additional secret value. There will be
|
||||
* 360000 iterations and a hash width of 160. The default is based upon aiming for .5
|
||||
* {@value DEFAULT_ITERATIONS} iterations and a hash width of {@value DEFAULT_HASH_WIDTH}. The default is based upon aiming for .5
|
||||
* seconds to validate the password when this class was added.. Users should tune
|
||||
* password verification to their own systems.
|
||||
*/
|
||||
@@ -63,7 +67,7 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
||||
|
||||
/**
|
||||
* Constructs a standard password encoder with a secret value which is also included
|
||||
* in the password hash. There will be 1024 iterations and a hash width of 160.
|
||||
* in the password hash. There will be {@value DEFAULT_ITERATIONS} iterations and a hash width of {@value DEFAULT_HASH_WIDTH}.
|
||||
*
|
||||
* @param secret the secret key used in the encoding process (should not be shared)
|
||||
*/
|
||||
@@ -86,16 +90,56 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
||||
this.hashWidth = hashWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the algorithm to use. See
|
||||
* <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecretKeyFactory">SecretKeyFactory Algorithms</a>
|
||||
* @param secretKeyFactoryAlgorithm the algorithm to use (i.e.
|
||||
* {@code SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1},
|
||||
* {@code SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256},
|
||||
* {@code SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA512})
|
||||
* @since 5.0
|
||||
*/
|
||||
public void setAlgorithm(SecretKeyFactoryAlgorithm secretKeyFactoryAlgorithm) {
|
||||
if(secretKeyFactoryAlgorithm == null) {
|
||||
throw new IllegalArgumentException("secretKeyFactoryAlgorithm cannot be null");
|
||||
}
|
||||
String algorithmName = secretKeyFactoryAlgorithm.name();
|
||||
try {
|
||||
SecretKeyFactory.getInstance(algorithmName);
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalArgumentException("Invalid algorithm '" + algorithmName + "'.", e);
|
||||
}
|
||||
this.algorithm = algorithmName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the resulting hash should be encoded as Base64. The default is false which
|
||||
* means it will be encoded in Hex.
|
||||
* @param encodeHashAsBase64 true if encode as Base64, false if should use Hex
|
||||
* (default)
|
||||
*/
|
||||
public void setEncodeHashAsBase64(boolean encodeHashAsBase64) {
|
||||
this.encodeHashAsBase64 = encodeHashAsBase64;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
byte[] salt = this.saltGenerator.generateKey();
|
||||
byte[] encoded = encode(rawPassword, salt);
|
||||
return String.valueOf(Hex.encode(encoded));
|
||||
return encode(encoded);
|
||||
}
|
||||
|
||||
private String encode(byte[] bytes) {
|
||||
if(this.encodeHashAsBase64) {
|
||||
return Utf8.decode(Base64.encode(bytes));
|
||||
}
|
||||
return String.valueOf(Hex.encode(bytes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
byte[] digested = Hex.decode(encodedPassword);
|
||||
byte[] digested = decode(encodedPassword);
|
||||
byte[] salt = subArray(digested, 0, this.saltGenerator.getKeyLength());
|
||||
return matches(digested, encode(rawPassword, salt));
|
||||
}
|
||||
@@ -115,15 +159,33 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
private byte[] decode(String encodedBytes) {
|
||||
if(this.encodeHashAsBase64) {
|
||||
return Base64.decode(Utf8.encode(encodedBytes));
|
||||
}
|
||||
return Hex.decode(encodedBytes);
|
||||
}
|
||||
|
||||
private byte[] encode(CharSequence rawPassword, byte[] salt) {
|
||||
try {
|
||||
PBEKeySpec spec = new PBEKeySpec(rawPassword.toString().toCharArray(),
|
||||
concatenate(salt, this.secret), this.iterations, this.hashWidth);
|
||||
SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
|
||||
SecretKeyFactory skf = SecretKeyFactory.getInstance(this.algorithm);
|
||||
return concatenate(salt, skf.generateSecret(spec).getEncoded());
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw new IllegalStateException("Could not create hash", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Algorithm used for creating the {@link SecretKeyFactory}
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public enum SecretKeyFactoryAlgorithm {
|
||||
PBKDF2WithHmacSHA1,
|
||||
PBKDF2WithHmacSHA256,
|
||||
PBKDF2WithHmacSHA512
|
||||
}
|
||||
}
|
||||
|
||||
+9
@@ -24,6 +24,9 @@ import org.springframework.security.crypto.keygen.BytesKeyGenerator;
|
||||
import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||
|
||||
/**
|
||||
* This {@link PasswordEncoder} is provided for legacy purposes only and is not considered
|
||||
* secure.
|
||||
*
|
||||
* A standard {@code PasswordEncoder} implementation that uses SHA-256 hashing with 1024
|
||||
* iterations and a random 8-byte random salt value. It uses an additional system-wide
|
||||
* secret value to provide additional protection.
|
||||
@@ -37,7 +40,13 @@ import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Luke Taylor
|
||||
* @deprecated Digest based password encoding is not considered secure. Instead use an
|
||||
* adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or
|
||||
* SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports
|
||||
* password upgrades. There are no plans to remove this support. It is deprecated to indicate
|
||||
* that this is a legacy implementation and using it is considered insecure.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class StandardPasswordEncoder implements PasswordEncoder {
|
||||
|
||||
private final Digester digester;
|
||||
|
||||
@@ -218,21 +218,21 @@ public class BCryptTests {
|
||||
@Test
|
||||
public void decodingCharsOutsideAsciiGivesNoResults() {
|
||||
byte[] ba = BCrypt.decode_base64("ππππππππ", 1);
|
||||
assertThat(ba.length).isEqualTo(0);
|
||||
assertThat(ba).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodingStopsWithFirstInvalidCharacter() {
|
||||
assertThat(BCrypt.decode_base64("....", 1).length).isEqualTo(1);
|
||||
assertThat(BCrypt.decode_base64(" ....", 1).length).isEqualTo(0);
|
||||
assertThat(BCrypt.decode_base64("....", 1)).hasSize(1);
|
||||
assertThat(BCrypt.decode_base64(" ....", 1)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodingOnlyProvidesAvailableBytes() {
|
||||
assertThat(BCrypt.decode_base64("", 1).length).isEqualTo(0);
|
||||
assertThat(BCrypt.decode_base64("......", 3).length).isEqualTo(3);
|
||||
assertThat(BCrypt.decode_base64("......", 4).length).isEqualTo(4);
|
||||
assertThat(BCrypt.decode_base64("......", 5).length).isEqualTo(4);
|
||||
assertThat(BCrypt.decode_base64("", 1)).isEmpty();
|
||||
assertThat(BCrypt.decode_base64("......", 3)).hasSize(3);
|
||||
assertThat(BCrypt.decode_base64("......", 4)).hasSize(4);
|
||||
assertThat(BCrypt.decode_base64("......", 5)).hasSize(4);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,8 +268,8 @@ public class BCryptTests {
|
||||
|
||||
@Test
|
||||
public void genSaltGeneratesCorrectSaltPrefix() {
|
||||
assertThat(BCrypt.gensalt(4).startsWith("$2a$04$")).isTrue();
|
||||
assertThat(BCrypt.gensalt(31).startsWith("$2a$31$")).isTrue();
|
||||
assertThat(BCrypt.gensalt(4)).startsWith("$2a$04$");
|
||||
assertThat(BCrypt.gensalt(31)).startsWith("$2a$31$");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
|
||||
@@ -34,7 +34,7 @@ public class HexTests {
|
||||
@Test
|
||||
public void encode() {
|
||||
assertThat(Hex.encode(new byte[] { (byte) 'A', (byte) 'B', (byte) 'C',
|
||||
(byte) 'D' })).isEqualTo(new char[] {'4','1','4','2','4','3','4','4'});
|
||||
(byte) 'D' })).isEqualTo(new char[] {'4', '1', '4', '2', '4', '3', '4', '4'});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -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.
|
||||
@@ -30,7 +30,7 @@ public class Utf8Tests {
|
||||
@Test
|
||||
public void utf8EncodesAndDecodesCorrectly() throws Exception {
|
||||
byte[] bytes = Utf8.encode("6048b75ed560785c");
|
||||
assertThat(bytes.length).isEqualTo(16);
|
||||
assertThat(bytes).hasSize(16);
|
||||
assertThat(Arrays.equals("6048b75ed560785c".getBytes("UTF-8"), bytes)).isTrue();
|
||||
|
||||
String decoded = Utf8.decode(bytes);
|
||||
|
||||
+101
@@ -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.crypto.factory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
public class PasswordEncoderFactoriesTests {
|
||||
private PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
|
||||
private String rawPassword = "password";
|
||||
|
||||
@Test
|
||||
public void encodeWhenDefaultThenBCryptUsed() {
|
||||
String encodedPassword = this.encoder.encode(this.rawPassword);
|
||||
|
||||
assertThat(encodedPassword).startsWith("{bcrypt}");
|
||||
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenBCryptThenWorks() {
|
||||
String encodedPassword = "{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG";
|
||||
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenLdapThenWorks() {
|
||||
String encodedPassword = "{ldap}{SSHA}igvD9lOiTXm16dmOw0YWRb9OjK2ThZvdQku2EQ==";
|
||||
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenMd4ThenWorks() {
|
||||
String encodedPassword = "{MD4}{KYp8/QErWyQemYazZQ8UnWWfbGbkYkVC8qMi0duoA84=}152ce09d3261d2b53cac55b2ea4d1c7a";
|
||||
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenMd5ThenWorks() {
|
||||
String encodedPassword = "{MD5}{aRYR+Yp2xSqtgF+vtjH6jNda6M083iEbP+zCFjLt9IA=}905e382a25eed53e22224223b3581092";
|
||||
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenNoopThenWorks() {
|
||||
String encodedPassword = "{noop}password";
|
||||
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenPbkdf2ThenWorks() {
|
||||
String encodedPassword = "{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc";
|
||||
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenSCryptThenWorks() {
|
||||
String encodedPassword = "{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=";
|
||||
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenSHA1ThenWorks() {
|
||||
String encodedPassword = "{SHA-1}{6581QepZz2qd8jVrT2QYPVtK8DuM2n45dVslmc3UTWc=}4f31573948ddbfb8ac9dd80107dfad13fd8f2454";
|
||||
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenSHA256ThenWorks() {
|
||||
String encodedPassword = "{SHA-256}{UisHp3pFSMqcqrhQsrhR+hspIG0SyMDyDW/XtY+t6nA=}a98efbaf59277bfd1837c33fd4fde67de5bcfd2205bcba0992f6fc32b03a8f88";
|
||||
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenSha256ThenWorks() {
|
||||
String encodedPassword = "{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0";
|
||||
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.junit;
|
||||
|
||||
import org.junit.Assume;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 4.2.7
|
||||
*/
|
||||
public final class Assumptions {
|
||||
|
||||
public static void assumeMinimumJdk8() {
|
||||
int majorVersion = JdkVersion.getMajorJavaVersion();
|
||||
Assume.assumeTrue(majorVersion >= 8);
|
||||
}
|
||||
|
||||
private Assumptions() {}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.junit;
|
||||
|
||||
/**
|
||||
* Internal helper class used to find the Java/JVM version that Spring is
|
||||
* operating on, to allow for automatically adapting to the present platform's
|
||||
* capabilities.
|
||||
*
|
||||
* <p>Note that Spring requires JVM 1.6 or higher, as of Spring 4.0.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Rick Evans
|
||||
* @author Sam Brannen
|
||||
* @deprecated as of Spring 4.2.1, in favor of direct checks for the desired
|
||||
* JDK API variants via reflection
|
||||
*/
|
||||
public abstract class JdkVersion {
|
||||
|
||||
/**
|
||||
* Constant identifying the 1.3.x JVM (JDK 1.3).
|
||||
*/
|
||||
public static final int JAVA_13 = 0;
|
||||
|
||||
/**
|
||||
* Constant identifying the 1.4.x JVM (J2SE 1.4).
|
||||
*/
|
||||
public static final int JAVA_14 = 1;
|
||||
|
||||
/**
|
||||
* Constant identifying the 1.5 JVM (Java 5).
|
||||
*/
|
||||
public static final int JAVA_15 = 2;
|
||||
|
||||
/**
|
||||
* Constant identifying the 1.6 JVM (Java 6).
|
||||
*/
|
||||
public static final int JAVA_16 = 3;
|
||||
|
||||
/**
|
||||
* Constant identifying the 1.7 JVM (Java 7).
|
||||
*/
|
||||
public static final int JAVA_17 = 4;
|
||||
|
||||
/**
|
||||
* Constant identifying the 1.8 JVM (Java 8).
|
||||
*/
|
||||
public static final int JAVA_18 = 5;
|
||||
|
||||
/**
|
||||
* Constant identifying the 1.9 JVM (Java 9).
|
||||
*/
|
||||
public static final int JAVA_19 = 6;
|
||||
|
||||
|
||||
private static final String javaVersion;
|
||||
|
||||
private static final int majorJavaVersion;
|
||||
|
||||
static {
|
||||
javaVersion = System.getProperty("java.version");
|
||||
// version String should look like "1.4.2_10"
|
||||
if (javaVersion.contains("1.9.")) {
|
||||
majorJavaVersion = JAVA_19;
|
||||
}
|
||||
else if (javaVersion.contains("1.8.")) {
|
||||
majorJavaVersion = JAVA_18;
|
||||
}
|
||||
else if (javaVersion.contains("1.7.")) {
|
||||
majorJavaVersion = JAVA_17;
|
||||
}
|
||||
else {
|
||||
// else leave 1.6 as default (it's either 1.6 or unknown)
|
||||
majorJavaVersion = JAVA_16;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the full Java version string, as returned by
|
||||
* {@code System.getProperty("java.version")}.
|
||||
* @return the full Java version string
|
||||
* @see System#getProperty(String)
|
||||
*/
|
||||
public static String getJavaVersion() {
|
||||
return javaVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the major version code. This means we can do things like
|
||||
* {@code if (getMajorJavaVersion() >= JAVA_17)}.
|
||||
* @return a code comparable to the {@code JAVA_XX} codes in this class
|
||||
* @see #JAVA_16
|
||||
* @see #JAVA_17
|
||||
* @see #JAVA_18
|
||||
* @see #JAVA_19
|
||||
*/
|
||||
public static int getMajorJavaVersion() {
|
||||
return majorJavaVersion;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.keygen;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.crypto.codec.Base64;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 4.2.6
|
||||
*/
|
||||
public class Base64StringKeyGeneratorTests {
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorIntWhenLessThan32ThenIllegalArgumentException() {
|
||||
new Base64StringKeyGenerator(31);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateKeyWhenDefaultConstructorThen32Bytes() {
|
||||
String result = new Base64StringKeyGenerator().generateKey();
|
||||
assertThat(Base64.decode(result.getBytes())).hasSize(32);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateKeyWhenCustomKeySizeThen32Bytes() {
|
||||
int size = 40;
|
||||
String result = new Base64StringKeyGenerator(size).generateKey();
|
||||
assertThat(Base64.decode(result.getBytes())).hasSize(size);
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -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.
|
||||
@@ -29,7 +29,7 @@ public class KeyGeneratorsTests {
|
||||
BytesKeyGenerator keyGenerator = KeyGenerators.secureRandom();
|
||||
assertThat(keyGenerator.getKeyLength()).isEqualTo(8);
|
||||
byte[] key = keyGenerator.generateKey();
|
||||
assertThat(key.length).isEqualTo(8);
|
||||
assertThat(key).hasSize(8);
|
||||
byte[] key2 = keyGenerator.generateKey();
|
||||
assertThat(Arrays.equals(key, key2)).isFalse();
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public class KeyGeneratorsTests {
|
||||
BytesKeyGenerator keyGenerator = KeyGenerators.secureRandom(21);
|
||||
assertThat(keyGenerator.getKeyLength()).isEqualTo(21);
|
||||
byte[] key = keyGenerator.generateKey();
|
||||
assertThat(key.length).isEqualTo(21);
|
||||
assertThat(key).hasSize(21);
|
||||
byte[] key2 = keyGenerator.generateKey();
|
||||
assertThat(Arrays.equals(key, key2)).isFalse();
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public class KeyGeneratorsTests {
|
||||
BytesKeyGenerator keyGenerator = KeyGenerators.shared(21);
|
||||
assertThat(keyGenerator.getKeyLength()).isEqualTo(21);
|
||||
byte[] key = keyGenerator.generateKey();
|
||||
assertThat(key.length).isEqualTo(21);
|
||||
assertThat(key).hasSize(21);
|
||||
byte[] key2 = keyGenerator.generateKey();
|
||||
assertThat(Arrays.equals(key, key2)).isTrue();
|
||||
}
|
||||
@@ -59,7 +59,7 @@ public class KeyGeneratorsTests {
|
||||
StringKeyGenerator keyGenerator = KeyGenerators.string();
|
||||
String hexStringKey = keyGenerator.generateKey();
|
||||
assertThat(hexStringKey.length()).isEqualTo(16);
|
||||
assertThat(Hex.decode(hexStringKey).length).isEqualTo(8);
|
||||
assertThat(Hex.decode(hexStringKey)).hasSize(8);
|
||||
String hexStringKey2 = keyGenerator.generateKey();
|
||||
assertThat(hexStringKey.equals(hexStringKey2)).isFalse();
|
||||
}
|
||||
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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.password;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @author Michael Simons
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DelegatingPasswordEncoderTests {
|
||||
@Mock
|
||||
private PasswordEncoder bcrypt;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder noop;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder invalidId;
|
||||
|
||||
private String bcryptId = "bcrypt";
|
||||
|
||||
private String rawPassword = "password";
|
||||
|
||||
private String encodedPassword = "ENCODED-PASSWORD";
|
||||
|
||||
private String bcryptEncodedPassword = "{bcrypt}" + this.encodedPassword;
|
||||
|
||||
private String noopEncodedPassword = "{noop}" + this.encodedPassword;
|
||||
|
||||
private Map<String, PasswordEncoder> delegates;
|
||||
|
||||
private DelegatingPasswordEncoder passwordEncoder;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.delegates = new HashMap<String, PasswordEncoder>();
|
||||
this.delegates.put(this.bcryptId, this.bcrypt);
|
||||
this.delegates.put("noop", this.noop);
|
||||
|
||||
this.passwordEncoder = new DelegatingPasswordEncoder(this.bcryptId, this.delegates);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorWhenIdForEncodeNullThenIllegalArgumentException() {
|
||||
new DelegatingPasswordEncoder(null, this.delegates);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorWhenIdForEncodeDoesNotExistThenIllegalArgumentException() {
|
||||
new DelegatingPasswordEncoder(this.bcryptId + "INVALID", this.delegates);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setDefaultPasswordEncoderForMatchesWhenNullThenIllegalArgumentException() {
|
||||
this.passwordEncoder.setDefaultPasswordEncoderForMatches(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenCustomDefaultPasswordEncoderForMatchesThenDelegates() {
|
||||
String encodedPassword = "{unmapped}" + this.rawPassword;
|
||||
this.passwordEncoder.setDefaultPasswordEncoderForMatches(this.invalidId);
|
||||
|
||||
assertThat(this.passwordEncoder.matches(this.rawPassword, encodedPassword)).isFalse();
|
||||
|
||||
verify(this.invalidId).matches(this.rawPassword, encodedPassword);
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenValidThenUsesIdForEncode() {
|
||||
when(this.bcrypt.encode(this.rawPassword)).thenReturn(this.encodedPassword);
|
||||
|
||||
assertThat(this.passwordEncoder.encode(this.rawPassword)).isEqualTo(this.bcryptEncodedPassword);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenBCryptThenDelegatesToBCrypt() {
|
||||
when(this.bcrypt.matches(this.rawPassword, this.encodedPassword)).thenReturn(true);
|
||||
|
||||
assertThat(this.passwordEncoder.matches(this.rawPassword, this.bcryptEncodedPassword)).isTrue();
|
||||
|
||||
verify(this.bcrypt).matches(this.rawPassword, this.encodedPassword);
|
||||
verifyZeroInteractions(this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenNoopThenDelegatesToNoop() {
|
||||
when(this.noop.matches(this.rawPassword, this.encodedPassword)).thenReturn(true);
|
||||
|
||||
assertThat(this.passwordEncoder.matches(this.rawPassword, this.noopEncodedPassword)).isTrue();
|
||||
|
||||
verify(this.noop).matches(this.rawPassword, this.encodedPassword);
|
||||
verifyZeroInteractions(this.bcrypt);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenUnMappedThenIllegalArgumentException() {
|
||||
try {
|
||||
this.passwordEncoder.matches(this.rawPassword, "{unmapped}" + this.rawPassword);
|
||||
fail("Expected Exception");
|
||||
} catch(IllegalArgumentException e) {
|
||||
assertThat(e).hasMessage("There is no PasswordEncoder mapped for the id \"unmapped\"");
|
||||
}
|
||||
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenNoClosingPrefixStringThenIllegalArgumentExcetion() {
|
||||
try {
|
||||
this.passwordEncoder.matches(this.rawPassword, "{bcrypt" + this.rawPassword);
|
||||
fail("Expected Exception");
|
||||
} catch(IllegalArgumentException e) {
|
||||
assertThat(e).hasMessage("There is no PasswordEncoder mapped for the id \"null\"");
|
||||
}
|
||||
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenNoStartingPrefixStringThenFalse() {
|
||||
try {
|
||||
this.passwordEncoder.matches(this.rawPassword, "bcrypt}" + this.rawPassword);
|
||||
fail("Expected Exception");
|
||||
} catch(IllegalArgumentException e) {
|
||||
assertThat(e).hasMessage("There is no PasswordEncoder mapped for the id \"null\"");
|
||||
}
|
||||
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenNoIdStringThenFalse() {
|
||||
try {
|
||||
this.passwordEncoder.matches(this.rawPassword, "{}" + this.rawPassword);
|
||||
fail("Expected Exception");
|
||||
} catch(IllegalArgumentException e) {
|
||||
assertThat(e).hasMessage("There is no PasswordEncoder mapped for the id \"\"");
|
||||
}
|
||||
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenPrefixInMiddleThenFalse() {
|
||||
try {
|
||||
this.passwordEncoder.matches(this.rawPassword, "invalid" + this.bcryptEncodedPassword);
|
||||
fail("Expected Exception");
|
||||
} catch(IllegalArgumentException e) {
|
||||
assertThat(e).hasMessage("There is no PasswordEncoder mapped for the id \"null\"");
|
||||
}
|
||||
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenIdIsNullThenFalse() {
|
||||
this.delegates = new Hashtable<String, PasswordEncoder>(this.delegates);
|
||||
|
||||
DelegatingPasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(this.bcryptId, this.delegates);
|
||||
|
||||
try {
|
||||
passwordEncoder.matches(this.rawPassword, this.rawPassword);
|
||||
fail("Expected Exception");
|
||||
} catch(IllegalArgumentException e) {
|
||||
assertThat(e).hasMessage("There is no PasswordEncoder mapped for the id \"null\"");
|
||||
}
|
||||
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenNullIdThenDelegatesToInvalidId() {
|
||||
this.delegates.put(null, this.invalidId);
|
||||
this.passwordEncoder = new DelegatingPasswordEncoder(this.bcryptId, this.delegates);
|
||||
when(this.invalidId.matches(this.rawPassword, this.encodedPassword)).thenReturn(true);
|
||||
|
||||
assertThat(this.passwordEncoder.matches(this.rawPassword, this.encodedPassword)).isTrue();
|
||||
|
||||
verify(this.invalidId).matches(this.rawPassword, this.encodedPassword);
|
||||
verifyZeroInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void matchesWhenRawPasswordNotNullAndEncodedPasswordNullThenThrowsIllegalArgumentException() {
|
||||
this.passwordEncoder.matches(this.rawPassword, null);
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.password;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests {@link LdapShaPasswordEncoder}.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class LdapShaPasswordEncoderTests {
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
LdapShaPasswordEncoder sha = new LdapShaPasswordEncoder();
|
||||
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
@Test
|
||||
public void invalidPasswordFails() {
|
||||
assertThat(this.sha.matches("wrongpassword", "{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidSaltedPasswordFails() {
|
||||
assertThat(this.sha.matches("wrongpassword", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isFalse();
|
||||
assertThat(this.sha.matches("wrongpassword", "{SSHA}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd")).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test values generated by 'slappasswd -h {SHA} -s boabspasswurd'
|
||||
*/
|
||||
@Test
|
||||
public void validPasswordSucceeds() {
|
||||
this.sha.setForceLowerCasePrefix(false);
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isTrue();
|
||||
assertThat(this.sha.matches("boabspasswurd", "{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isTrue();
|
||||
this.sha.setForceLowerCasePrefix(true);
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isTrue();
|
||||
assertThat(this.sha.matches("boabspasswurd", "{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test values generated by 'slappasswd -s boabspasswurd'
|
||||
*/
|
||||
@Test
|
||||
public void validSaltedPasswordSucceeds() {
|
||||
this.sha.setForceLowerCasePrefix(false);
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isTrue();
|
||||
assertThat(this.sha.matches("boabspasswurd", "{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd")).isTrue();
|
||||
this.sha.setForceLowerCasePrefix(true);
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isTrue();
|
||||
assertThat(this.sha.matches("boabspasswurd", "{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
// SEC-1031
|
||||
public void fullLengthOfHashIsUsedInComparison() throws Exception {
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isTrue();
|
||||
// Change the first hash character from '2' to '3'
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SSHA}35ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isFalse();
|
||||
// Change the last hash character from 'X' to 'Y'
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgY")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void correctPrefixCaseIsUsed() {
|
||||
this.sha.setForceLowerCasePrefix(false);
|
||||
assertThat(this.sha.encode("somepassword").startsWith("{SSHA}"));
|
||||
|
||||
this.sha.setForceLowerCasePrefix(true);
|
||||
assertThat(this.sha.encode("somepassword").startsWith("{ssha}"));
|
||||
|
||||
this.sha = new LdapShaPasswordEncoder(KeyGenerators.shared(0));
|
||||
this.sha.setForceLowerCasePrefix(false);
|
||||
assertThat(this.sha.encode("somepassword").startsWith("{SHA}"));
|
||||
|
||||
this.sha.setForceLowerCasePrefix(true);
|
||||
assertThat(this.sha.encode("somepassword").startsWith("{SSHA}"));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void invalidPrefixIsRejected() {
|
||||
this.sha.matches("somepassword", "{MD9}xxxxxxxxxx");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void malformedPrefixIsRejected() {
|
||||
// No right brace
|
||||
this.sha.matches("somepassword", "{SSHA25ro4PKC8jhQZ26jVsozhX/xaP0suHgX");
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.password;
|
||||
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
public class Md4PasswordEncoderTests {
|
||||
|
||||
@Test
|
||||
public void testEncodeUnsaltedPassword() {
|
||||
Md4PasswordEncoder md4 = new Md4PasswordEncoder();
|
||||
md4.setEncodeHashAsBase64(true);
|
||||
assertThat(md4.matches("ww_uni123", "8zobtq72iAt0W6KNqavGwg==")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeSaltedPassword() {
|
||||
Md4PasswordEncoder md4 = new Md4PasswordEncoder();
|
||||
md4.setEncodeHashAsBase64(true);
|
||||
assertThat(md4.matches("ww_uni123", "{Alan K Stewart}ZplT6P5Kv6Rlu6W4FIoYNA==")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeNullPassword() {
|
||||
Md4PasswordEncoder md4 = new Md4PasswordEncoder();
|
||||
md4.setEncodeHashAsBase64(true);
|
||||
assertThat(md4.matches(null, "MdbP4NFq6TG3PFnX4MCJwA==")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeEmptyPassword() {
|
||||
Md4PasswordEncoder md4 = new Md4PasswordEncoder();
|
||||
md4.setEncodeHashAsBase64(true);
|
||||
assertThat(md4.matches(null, "MdbP4NFq6TG3PFnX4MCJwA==")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonAsciiPasswordHasCorrectHash() {
|
||||
Md4PasswordEncoder md4 = new Md4PasswordEncoder();
|
||||
assertThat(md4.matches("\u4F60\u597d", "a7f1196539fd1f85f754ffd185b16e6e")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodedMatches() {
|
||||
String rawPassword = "password";
|
||||
Md4PasswordEncoder md4 = new Md4PasswordEncoder();
|
||||
String encodedPassword = md4.encode(rawPassword);
|
||||
|
||||
assertThat(md4.matches(rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void javadocWhenHasSaltThenMatches() {
|
||||
Md4PasswordEncoder encoder = new Md4PasswordEncoder();
|
||||
assertThat(encoder.matches("password", "{thisissalt}6cc7924dad12ade79dfb99e424f25260"));
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user