Compare commits
183 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a1c93b1cce | |||
| 32e368d9b7 | |||
| f29e4cf91f | |||
| bc3467c118 | |||
| 7013c6fd76 | |||
| 23f4b9d3d1 | |||
| de959dbff6 | |||
| c1e9785a48 | |||
| 7401cb2b51 | |||
| 3cd2ddf793 | |||
| 3220e9560a | |||
| b613b2d253 | |||
| 5e9c714ff0 | |||
| a02b0c17f8 | |||
| ca9cd20832 | |||
| d874c4954e | |||
| c696640276 | |||
| 8fa6dd0f5b | |||
| e78457d3a1 | |||
| 26f53a20b3 | |||
| 21750242cf | |||
| bc9f8ec430 | |||
| 53850978b2 | |||
| a7a8ac756a | |||
| cf4e139aa0 | |||
| 902fc0f657 | |||
| b91ebf7090 | |||
| 26bc6be850 | |||
| 6adbe8dae0 | |||
| 4bfce2a591 | |||
| fddc28ba3b | |||
| fed15f2b01 | |||
| b3c5bfe4db | |||
| 9b42831c70 | |||
| 2a0f529ee4 | |||
| 3ba15a16bf | |||
| 37b1136c0c | |||
| 1eaecc12ec | |||
| 0570cebbce | |||
| 3740d33e64 | |||
| 23dd136efb | |||
| 44b22e7208 | |||
| 8d716f75a4 | |||
| fff64db0e2 | |||
| 2356749cc3 | |||
| b8f225c49e | |||
| 0a5da93640 | |||
| 948e650a0e | |||
| bc0c3a5b69 | |||
| 618e0ab98d | |||
| bc017a62ca | |||
| e971490605 | |||
| 58b02364c2 | |||
| ef9279dd7c | |||
| ba5986f9b1 | |||
| 332aee8dd5 | |||
| c0813b7448 | |||
| dc75c3e011 | |||
| 4cc5705ae5 | |||
| 49b63e260d | |||
| 2273839aad | |||
| 9bb841ac67 | |||
| eb067bc3a1 | |||
| 359a73eff2 | |||
| 52d95770ec | |||
| 3c1231efd3 | |||
| 65326b1178 | |||
| f9eea1a58d | |||
| 9c0f2cc281 | |||
| 7daa27874e | |||
| f8247fa346 | |||
| afdefe7b13 | |||
| 6095340e93 | |||
| d8f91e4261 | |||
| 2bd31c96ed | |||
| d4e459874a | |||
| 526e0fdd4f | |||
| 982fc360b2 | |||
| 8fbec3f0f1 | |||
| d83b67e4cb | |||
| 234c20eb30 | |||
| 0c0abea3ad | |||
| fb7394c1de | |||
| 6e1e977778 | |||
| 9b692b9616 | |||
| ffbc1f1e93 | |||
| 6d9e9007f6 | |||
| a006ac3bb0 | |||
| 692f2d343b | |||
| b48972cf79 | |||
| 2ad90b95e1 | |||
| 453fb24ef1 | |||
| 2ab18baa09 | |||
| c0f52f73bb | |||
| 512fcf6094 | |||
| 7a204a5f58 | |||
| ce2f669245 | |||
| ec46b7dbe1 | |||
| 3c07d99b0a | |||
| d20ed9f5c9 | |||
| d07cfe655d | |||
| 7d4e7bf42d | |||
| b1d013e8f0 | |||
| 6f6aadbcff | |||
| 0e37c0912e | |||
| bb15213091 | |||
| 90f9d728cd | |||
| e4255c9793 | |||
| 332c395875 | |||
| bf41d48718 | |||
| 04e2e86e6e | |||
| 59cef7d339 | |||
| 7e6ed52603 | |||
| 018ab7d92c | |||
| 01152ede41 | |||
| 1851aaa66d | |||
| 76e36bd06e | |||
| b640d84b12 | |||
| 3a740ad988 | |||
| 67d793ae5f | |||
| efaf2b080f | |||
| e86becc151 | |||
| 4f709d47b9 | |||
| 452d855396 | |||
| 6e5105f899 | |||
| 40bb73124c | |||
| 780e6aefd2 | |||
| 35345fac70 | |||
| 8cf51032e0 | |||
| ae9075c023 | |||
| cf4272ff64 | |||
| 4152530e69 | |||
| 73cec43842 | |||
| 86465026a1 | |||
| e799f13ae2 | |||
| 744bb1b1be | |||
| 776b378a1d | |||
| d21338d212 | |||
| a2073b2b91 | |||
| d816af2337 | |||
| 7fafd899ee | |||
| bc21f80ebe | |||
| 65193963ad | |||
| 2228485a40 | |||
| 949c7d68b8 | |||
| 1d0e97880d | |||
| 3121f9c000 | |||
| c91ca0584c | |||
| 350fcd4277 | |||
| 505aa8dd02 | |||
| abae2f3e87 | |||
| 055a2ca917 | |||
| 9f23212e43 | |||
| ca93b34f56 | |||
| a5bd76b6ed | |||
| c922fe3be1 | |||
| b1f3d495d9 | |||
| 0aa87e8501 | |||
| 5af1d1d936 | |||
| 2a678ebc6e | |||
| 5b023d0abc | |||
| b8dfcd0d35 | |||
| eae7afd9aa | |||
| d316803596 | |||
| 1ed51033cc | |||
| b8ae110b7b | |||
| 458b571d02 | |||
| ce8bea69ae | |||
| ab5a760380 | |||
| ec3534ac8d | |||
| 68afb2475a | |||
| 0e1060e736 | |||
| c9ef2549b2 | |||
| 51ddd2b36d | |||
| 822129cebd | |||
| 3205f68f29 | |||
| 1b69c62d20 | |||
| e08d4cc90c | |||
| 8d75554b6b | |||
| bb59733736 | |||
| dc9248e73c | |||
| c24490cbb8 | |||
| cc2f676c03 |
+14
-9
@@ -1,17 +1,22 @@
|
||||
# EditorConfig for Spring Security
|
||||
# see https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.md#mind-the-whitespace
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = latin1
|
||||
|
||||
# 4 tabs indentation
|
||||
indent_style = tab
|
||||
tab_width = 4
|
||||
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 120
|
||||
|
||||
[*.java]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
charset = latin1
|
||||
continuation_indent_size = 8
|
||||
|
||||
[*.xml]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
charset = latin1
|
||||
continuation_indent_size = 8
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
<!--
|
||||
For Security Vulnerabilities, please use https://pivotal.io/security#reporting
|
||||
-->
|
||||
|
||||
### Summary
|
||||
|
||||
<!--
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
<!--
|
||||
For Security Vulnerabilities, please use https://pivotal.io/security#reporting
|
||||
-->
|
||||
|
||||
<!--
|
||||
Thanks for contributing to Spring Security. Please provide a brief description of your pull-request and reference any related issue numbers (prefix references with #).
|
||||
-->
|
||||
|
||||
+2
-1
@@ -8,7 +8,7 @@ By participating, you are expected to uphold this code. Please report unaccepta
|
||||
|
||||
# Similar but different
|
||||
|
||||
Each Spring module is slightly different than another in terms of team size, number of issues, etc. Therefore each project is managed slightly different. You will notice that this document is very similar to the [Spring Framework Contributor guidelines](https://github.com/SpringSource/spring-framework/wiki/Contributor-guidelines). However, there are some subtle differences between the two documents, so please be sure to read this document thoroughly.
|
||||
Each Spring module is slightly different than another in terms of team size, number of issues, etc. Therefore each project is managed slightly different. You will notice that this document is very similar to the [Spring Framework Contributor guidelines](https://github.com/spring-projects/spring-framework/wiki/Contributor-guidelines). However, there are some subtle differences between the two documents, so please be sure to read this document thoroughly.
|
||||
|
||||
# Importing into IDE
|
||||
|
||||
@@ -180,6 +180,7 @@ Fixes gh-123
|
||||
2. Do not end the subject line with a period
|
||||
3. In the body of the commit message, explain how things worked before this commit, what has changed, and how things work now
|
||||
3. Include Fixes gh-<issue-number> at the end if this fixes a GitHub issue
|
||||
5. Avoid markdown, including back-ticks identifying code
|
||||
|
||||
# Run all tests prior to submission
|
||||
|
||||
|
||||
Vendored
+58
@@ -53,6 +53,64 @@ try {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
snapshots: {
|
||||
stage('Snapshot Tests') {
|
||||
node {
|
||||
checkout scm
|
||||
try {
|
||||
sh "./gradlew clean test -PspringVersion='5.+' -PreactorVersion=Bismuth-BUILD-SNAPSHOT -PspringDataVersion=Kay-BUILD-SNAPSHOT --refresh-dependencies --no-daemon --stacktrace"
|
||||
} catch(Exception e) {
|
||||
currentBuild.result = 'FAILED: snapshots'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk9: {
|
||||
stage('JDK 9') {
|
||||
node {
|
||||
checkout scm
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${ tool 'jdk9' }"]) {
|
||||
sh "./gradlew clean test --no-daemon --stacktrace"
|
||||
}
|
||||
} catch(Exception e) {
|
||||
currentBuild.result = 'FAILED: jdk9'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk10: {
|
||||
stage('JDK 10') {
|
||||
node {
|
||||
checkout scm
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${ tool 'jdk10' }"]) {
|
||||
sh "./gradlew clean test --no-daemon --stacktrace"
|
||||
}
|
||||
} catch(Exception e) {
|
||||
currentBuild.result = 'FAILED: jdk10'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk11: {
|
||||
stage('JDK 11') {
|
||||
node {
|
||||
checkout scm
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${ tool 'jdk11' }"]) {
|
||||
sh "./gradlew clean test --no-daemon --stacktrace"
|
||||
}
|
||||
} catch(Exception e) {
|
||||
currentBuild.result = 'FAILED: jdk11'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(currentBuild.result == 'SUCCESS') {
|
||||
|
||||
@@ -92,11 +92,11 @@ public abstract class AclFormattingUtils {
|
||||
*/
|
||||
public static String printBinary(int mask, char code) {
|
||||
Assert.doesNotContain(Character.toString(code),
|
||||
Character.toString(Permission.RESERVED_ON), Permission.RESERVED_ON
|
||||
+ " is a reserved character code");
|
||||
Character.toString(Permission.RESERVED_ON),
|
||||
() -> Permission.RESERVED_ON + " is a reserved character code");
|
||||
Assert.doesNotContain(Character.toString(code),
|
||||
Character.toString(Permission.RESERVED_OFF), Permission.RESERVED_OFF
|
||||
+ " is a reserved character code");
|
||||
Character.toString(Permission.RESERVED_OFF),
|
||||
() -> Permission.RESERVED_OFF + " is a reserved character code");
|
||||
|
||||
return printBinary(mask, Permission.RESERVED_ON, Permission.RESERVED_OFF)
|
||||
.replace(Permission.RESERVED_ON, code);
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -104,9 +104,9 @@ public class DefaultPermissionFactory implements PermissionFactory {
|
||||
|
||||
// Ensure no existing Permission uses this integer or code
|
||||
Assert.isTrue(!registeredPermissionsByInteger.containsKey(mask),
|
||||
"An existing Permission already provides mask " + mask);
|
||||
() -> "An existing Permission already provides mask " + mask);
|
||||
Assert.isTrue(!registeredPermissionsByName.containsKey(permissionName),
|
||||
"An existing Permission already provides name '" + permissionName + "'");
|
||||
() -> "An existing Permission already provides name '" + permissionName + "'");
|
||||
|
||||
// Register the new Permission
|
||||
registeredPermissionsByInteger.put(mask, perm);
|
||||
|
||||
@@ -111,7 +111,7 @@ public class JdbcAclService implements AclService {
|
||||
throws NotFoundException {
|
||||
Map<ObjectIdentity, Acl> map = readAclsById(Arrays.asList(object), sids);
|
||||
Assert.isTrue(map.containsKey(object),
|
||||
"There should have been an Acl entry for ObjectIdentity " + object);
|
||||
() -> "There should have been an Acl entry for ObjectIdentity " + object);
|
||||
|
||||
return (Acl) map.get(object);
|
||||
}
|
||||
|
||||
+9
-1
@@ -1,6 +1,6 @@
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.12.RELEASE'
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.15.RELEASE'
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
|
||||
}
|
||||
repositories {
|
||||
@@ -30,3 +30,11 @@ gradle.taskGraph.whenReady { graph ->
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
subprojects {
|
||||
plugins.withType(JavaPlugin) {
|
||||
project.sourceCompatibility='1.8'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 versions
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class VersionsResourceTasks extends DefaultTask {
|
||||
@OutputFile
|
||||
File versionsFile;
|
||||
|
||||
@Input
|
||||
Closure<Map<String,String>> versions;
|
||||
|
||||
void setVersions(Map<String,String> versions) {
|
||||
this.versions = { versions };
|
||||
}
|
||||
|
||||
void setVersions(Closure<Map<String,String>> versions) {
|
||||
this.versions = versions
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void generateVersions() {
|
||||
versionsFile.parentFile.mkdirs()
|
||||
versionsFile.createNewFile()
|
||||
Properties versionsProperties = new Properties()
|
||||
versionsProperties.putAll(versions.call())
|
||||
versionsProperties.store(versionsFile.newWriter(), null)
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -137,7 +137,9 @@ public class CasAuthenticationTokenMixinTests {
|
||||
assertThat(token.getUserDetails()).isNotNull().isInstanceOf(User.class);
|
||||
assertThat(token.getAssertion()).isNotNull().isInstanceOf(AssertionImpl.class);
|
||||
assertThat(token.getKeyHash()).isEqualTo(KEY.hashCode());
|
||||
assertThat(token.getUserDetails().getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
assertThat(token.getUserDetails().getAuthorities())
|
||||
.extracting(GrantedAuthority::getAuthority)
|
||||
.containsOnly("ROLE_USER");
|
||||
assertThat(token.getAssertion().getAuthenticationDate()).isEqualTo(START_DATE);
|
||||
assertThat(token.getAssertion().getValidFromDate()).isEqualTo(START_DATE);
|
||||
assertThat(token.getAssertion().getValidUntilDate()).isEqualTo(END_DATE);
|
||||
|
||||
+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"));
|
||||
|
||||
@@ -35,7 +35,9 @@ dependencies {
|
||||
testCompile powerMock2Dependencies
|
||||
testCompile spockDependencies
|
||||
testCompile 'ch.qos.logback:logback-classic'
|
||||
testCompile 'io.projectreactor.ipc:reactor-netty'
|
||||
testCompile 'javax.annotation:jsr250-api:1.0'
|
||||
testCompile 'javax.xml.bind:jaxb-api'
|
||||
testCompile 'ldapsdk:ldapsdk:4.1'
|
||||
testCompile('net.sourceforge.htmlunit:htmlunit') {
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -152,7 +152,7 @@ public class AuthenticationConfiguration {
|
||||
return null;
|
||||
}
|
||||
Assert.isTrue(beanNamesForType.length == 1,
|
||||
"Expecting to only find a single bean for type " + interfaceName
|
||||
() -> "Expecting to only find a single bean for type " + interfaceName
|
||||
+ ", but found " + Arrays.asList(beanNamesForType));
|
||||
lazyTargetSource.setTargetBeanName(beanNamesForType[0]);
|
||||
lazyTargetSource.setBeanFactory(applicationContext);
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ import org.springframework.security.config.annotation.authentication.configurati
|
||||
* {@link AuthenticationConfiguration} to configure the global
|
||||
* {@link AuthenticationManagerBuilder}.
|
||||
*
|
||||
* @since 3.2.1
|
||||
* @since 5.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Order(100)
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -457,7 +457,7 @@ public class GlobalMethodSecurityConfiguration
|
||||
EnableGlobalMethodSecurity methodSecurityAnnotation = AnnotationUtils
|
||||
.findAnnotation(getClass(), EnableGlobalMethodSecurity.class);
|
||||
Assert.notNull(methodSecurityAnnotation,
|
||||
EnableGlobalMethodSecurity.class.getName() + " is required");
|
||||
() -> EnableGlobalMethodSecurity.class.getName() + " is required");
|
||||
Map<String, Object> methodSecurityAttrs = AnnotationUtils
|
||||
.getAnnotationAttributes(methodSecurityAnnotation);
|
||||
this.enableMethodSecurity = AnnotationAttributes.fromMap(methodSecurityAttrs);
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -42,7 +42,7 @@ final class GlobalMethodSecuritySelector implements ImportSelector {
|
||||
.getAnnotationAttributes(annoType.getName(), false);
|
||||
AnnotationAttributes attributes = AnnotationAttributes
|
||||
.fromMap(annotationAttributes);
|
||||
Assert.notNull(attributes, String.format(
|
||||
Assert.notNull(attributes, () -> String.format(
|
||||
"@%s is not present on importing class '%s' as expected",
|
||||
annoType.getSimpleName(), importingClassMetadata.getClassName()));
|
||||
|
||||
|
||||
+4
@@ -117,6 +117,10 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
|
||||
order += STEP;
|
||||
put(AnonymousAuthenticationFilter.class, order);
|
||||
order += STEP;
|
||||
filterToOrder.put(
|
||||
"org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
|
||||
order);
|
||||
order += STEP;
|
||||
put(SessionManagementFilter.class, order);
|
||||
order += STEP;
|
||||
put(ExceptionTranslationFilter.class, order);
|
||||
|
||||
+27
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,14 +15,6 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.builders;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
@@ -56,12 +48,13 @@ import org.springframework.security.config.annotation.web.configurers.SecurityCo
|
||||
import org.springframework.security.config.annotation.web.configurers.ServletApiConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.X509Configurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.OAuth2Configurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.openid.OpenIDLoginConfigurer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.PortMapper;
|
||||
import org.springframework.security.web.PortMapperImpl;
|
||||
@@ -79,6 +72,13 @@ import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A {@link HttpSecurity} is similar to Spring Security's XML <http> element in the
|
||||
* namespace configuration. It allows configuring web based security for specific http
|
||||
@@ -991,6 +991,18 @@ public final class HttpSecurity extends
|
||||
return getOrApply(new OAuth2LoginConfigurer<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures support for the <a target="_blank" href="https://tools.ietf.org/html/rfc6749">OAuth 2.0 Authorization Framework</a>.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.1
|
||||
* @return the {@link OAuth2Configurer} for further customizations
|
||||
* @throws Exception
|
||||
*/
|
||||
public OAuth2Configurer oauth2() throws Exception {
|
||||
return getOrApply(new OAuth2Configurer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures channel security. In order for this configuration to be useful at least
|
||||
* one mapping to a required channel must be provided.
|
||||
@@ -1163,6 +1175,11 @@ public final class HttpSecurity extends
|
||||
* addFilterAt(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
|
||||
* </pre>
|
||||
*
|
||||
* Registration of multiple Filters in the same location means their ordering is not
|
||||
* deterministic. More concretely, registering multiple Filters in the same location
|
||||
* does not override existing Filters. Instead, do not register Filters you do not
|
||||
* want to use.
|
||||
*
|
||||
* @param filter the Filter to register
|
||||
* @param atFilter the location of another {@link Filter} that is already registered
|
||||
* (i.e. known) with Spring Security.
|
||||
|
||||
+10
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,6 +29,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
@@ -278,7 +279,9 @@ public final class WebSecurity extends
|
||||
protected Filter performBuild() throws Exception {
|
||||
Assert.state(
|
||||
!securityFilterChainBuilders.isEmpty(),
|
||||
"At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. More advanced users can invoke "
|
||||
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
|
||||
+ "Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
|
||||
+ "More advanced users can invoke "
|
||||
+ WebSecurity.class.getSimpleName()
|
||||
+ ".addSecurityFilterChainBuilder directly");
|
||||
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
|
||||
@@ -382,6 +385,11 @@ public final class WebSecurity extends
|
||||
throws BeansException {
|
||||
this.defaultWebSecurityExpressionHandler
|
||||
.setApplicationContext(applicationContext);
|
||||
try {
|
||||
this.defaultWebSecurityExpressionHandler.setPermissionEvaluator(applicationContext.getBean(
|
||||
PermissionEvaluator.class));
|
||||
} catch(NoSuchBeanDefinitionException e) {}
|
||||
|
||||
this.ignoredRequestRegistry = new IgnoredRequestConfigurer(applicationContext);
|
||||
try {
|
||||
this.httpFirewall = applicationContext.getBean(HttpFirewall.class);
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -73,7 +73,8 @@ import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
@Target(value = { java.lang.annotation.ElementType.TYPE })
|
||||
@Documented
|
||||
@Import({ WebSecurityConfiguration.class,
|
||||
SpringWebMvcImportSelector.class })
|
||||
SpringWebMvcImportSelector.class,
|
||||
OAuth2ImportSelector.class })
|
||||
@EnableGlobalAuthentication
|
||||
@Configuration
|
||||
public @interface EnableWebSecurity {
|
||||
@@ -83,4 +84,4 @@ public @interface EnableWebSecurity {
|
||||
* @return if true, enables debug support with Spring Security
|
||||
*/
|
||||
boolean debug() default false;
|
||||
}
|
||||
}
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.configuration;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.method.annotation.OAuth2ClientArgumentResolver;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link Configuration} for OAuth 2.0 Client support.
|
||||
*
|
||||
* <p>
|
||||
* This {@code Configuration} is conditionally imported by {@link OAuth2ImportSelector}
|
||||
* when the {@code spring-security-oauth2-client} module is present on the classpath.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.1
|
||||
* @see OAuth2ImportSelector
|
||||
*/
|
||||
@Import(OAuth2ClientConfiguration.OAuth2ClientWebMvcImportSelector.class)
|
||||
final class OAuth2ClientConfiguration {
|
||||
|
||||
static class OAuth2ClientWebMvcImportSelector implements ImportSelector {
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
boolean webmvcPresent = ClassUtils.isPresent(
|
||||
"org.springframework.web.servlet.DispatcherServlet", getClass().getClassLoader());
|
||||
|
||||
return webmvcPresent ?
|
||||
new String[] { "org.springframework.security.config.annotation.web.configuration.OAuth2ClientConfiguration.OAuth2ClientWebMvcSecurityConfiguration" } :
|
||||
new String[] {};
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OAuth2ClientWebMvcSecurityConfiguration implements WebMvcConfigurer {
|
||||
@Autowired(required = false)
|
||||
private ClientRegistrationRepository clientRegistrationRepository;
|
||||
|
||||
@Autowired(required = false)
|
||||
private OAuth2AuthorizedClientService authorizedClientService;
|
||||
|
||||
@Override
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
if (this.clientRegistrationRepository != null && this.authorizedClientService != null) {
|
||||
OAuth2ClientArgumentResolver oauth2ClientArgumentResolver = new OAuth2ClientArgumentResolver(
|
||||
this.clientRegistrationRepository, this.authorizedClientService);
|
||||
argumentResolvers.add(oauth2ClientArgumentResolver);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.configuration;
|
||||
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Used by {@link EnableWebSecurity} to conditionally import {@link OAuth2ClientConfiguration}
|
||||
* when the {@code spring-security-oauth2-client} module is present on the classpath.
|
||||
|
||||
* @author Joe Grandja
|
||||
* @since 5.1
|
||||
* @see OAuth2ClientConfiguration
|
||||
*/
|
||||
final class OAuth2ImportSelector implements ImportSelector {
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
boolean oauth2ClientPresent = ClassUtils.isPresent(
|
||||
"org.springframework.security.oauth2.client.registration.ClientRegistration", getClass().getClassLoader());
|
||||
|
||||
return oauth2ClientPresent ?
|
||||
new String[] { "org.springframework.security.config.annotation.web.configuration.OAuth2ClientConfiguration" } :
|
||||
new String[] {};
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -220,7 +220,7 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
private static String hasRole(String role) {
|
||||
Assert.isTrue(
|
||||
!role.startsWith("ROLE_"),
|
||||
role
|
||||
() -> role
|
||||
+ " should not start with ROLE_ since ROLE_ is automatically prepended when using hasRole. Consider using hasAuthority or access instead.");
|
||||
return "ROLE_" + role;
|
||||
}
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.oauth2;
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2ClientConfigurer;
|
||||
|
||||
/**
|
||||
* An {@link AbstractHttpConfigurer} that provides support for the
|
||||
* <a target="_blank" href="https://tools.ietf.org/html/rfc6749">OAuth 2.0 Authorization Framework</a>.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.1
|
||||
* @see HttpSecurity#oauth2()
|
||||
* @see OAuth2ClientConfigurer
|
||||
* @see AbstractHttpConfigurer
|
||||
*/
|
||||
public final class OAuth2Configurer extends AbstractHttpConfigurer<OAuth2Configurer, HttpSecurity> {
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2ClientConfigurer} for configuring OAuth 2.0 Client support.
|
||||
*
|
||||
* @return the {@link OAuth2ClientConfigurer}
|
||||
* @throws Exception
|
||||
*/
|
||||
public OAuth2ClientConfigurer<HttpSecurity> client() throws Exception {
|
||||
return this.getOrApply(new OAuth2ClientConfigurer<>());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <C extends AbstractHttpConfigurer<C, HttpSecurity>> C getOrApply(C configurer) throws Exception {
|
||||
C existingConfigurer = (C) this.getBuilder().getConfigurer(configurer.getClass());
|
||||
if (existingConfigurer != null) {
|
||||
return existingConfigurer;
|
||||
}
|
||||
return this.getBuilder().apply(configurer);
|
||||
}
|
||||
}
|
||||
+1
-15
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
@@ -86,7 +85,7 @@ public final class ImplicitGrantConfigurer<B extends HttpSecurityBuilder<B>> ext
|
||||
@Override
|
||||
public void configure(B http) throws Exception {
|
||||
OAuth2AuthorizationRequestRedirectFilter authorizationRequestFilter = new OAuth2AuthorizationRequestRedirectFilter(
|
||||
this.getClientRegistrationRepository(), this.getAuthorizationRequestBaseUri());
|
||||
OAuth2ClientConfigurerUtils.getClientRegistrationRepository(this.getBuilder()), this.getAuthorizationRequestBaseUri());
|
||||
http.addFilter(this.postProcess(authorizationRequestFilter));
|
||||
}
|
||||
|
||||
@@ -95,17 +94,4 @@ public final class ImplicitGrantConfigurer<B extends HttpSecurityBuilder<B>> ext
|
||||
this.authorizationRequestBaseUri :
|
||||
OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
|
||||
}
|
||||
|
||||
private ClientRegistrationRepository getClientRegistrationRepository() {
|
||||
ClientRegistrationRepository clientRegistrationRepository = this.getBuilder().getSharedObject(ClientRegistrationRepository.class);
|
||||
if (clientRegistrationRepository == null) {
|
||||
clientRegistrationRepository = this.getClientRegistrationRepositoryBean();
|
||||
this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
|
||||
}
|
||||
return clientRegistrationRepository;
|
||||
}
|
||||
|
||||
private ClientRegistrationRepository getClientRegistrationRepositoryBean() {
|
||||
return this.getBuilder().getSharedObject(ApplicationContext.class).getBean(ClientRegistrationRepository.class);
|
||||
}
|
||||
}
|
||||
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* 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.oauth2.client;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.client.endpoint.NimbusAuthorizationCodeTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link AbstractHttpConfigurer} for OAuth 2.0 Client support.
|
||||
*
|
||||
* <p>
|
||||
* The following configuration options are available:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #authorizationCodeGrant()} - enables the OAuth 2.0 Authorization Code Grant</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Defaults are provided for all configuration options with the only required configuration
|
||||
* being {@link #clientRegistrationRepository(ClientRegistrationRepository)}.
|
||||
* Alternatively, a {@link ClientRegistrationRepository} {@code @Bean} may be registered instead.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following {@code Filter}'s are populated when {@link #authorizationCodeGrant()} is configured:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link OAuth2AuthorizationRequestRedirectFilter}</li>
|
||||
* <li>{@link OAuth2AuthorizationCodeGrantFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* The following shared objects are populated:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link ClientRegistrationRepository} (required)</li>
|
||||
* <li>{@link OAuth2AuthorizedClientService} (optional)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link ClientRegistrationRepository}</li>
|
||||
* <li>{@link OAuth2AuthorizedClientService}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.1
|
||||
* @see OAuth2AuthorizationRequestRedirectFilter
|
||||
* @see OAuth2AuthorizationCodeGrantFilter
|
||||
* @see ClientRegistrationRepository
|
||||
* @see OAuth2AuthorizedClientService
|
||||
* @see AbstractHttpConfigurer
|
||||
*/
|
||||
public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>> extends
|
||||
AbstractHttpConfigurer<OAuth2ClientConfigurer<B>, B> {
|
||||
|
||||
private AuthorizationCodeGrantConfigurer authorizationCodeGrantConfigurer;
|
||||
|
||||
/**
|
||||
* Sets the repository of client registrations.
|
||||
*
|
||||
* @param clientRegistrationRepository the repository of client registrations
|
||||
* @return the {@link OAuth2ClientConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2ClientConfigurer<B> clientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) {
|
||||
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
|
||||
this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the service for authorized client(s).
|
||||
*
|
||||
* @param authorizedClientService the authorized client service
|
||||
* @return the {@link OAuth2ClientConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2ClientConfigurer<B> authorizedClientService(OAuth2AuthorizedClientService authorizedClientService) {
|
||||
Assert.notNull(authorizedClientService, "authorizedClientService cannot be null");
|
||||
this.getBuilder().setSharedObject(OAuth2AuthorizedClientService.class, authorizedClientService);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link AuthorizationCodeGrantConfigurer} for configuring the OAuth 2.0 Authorization Code Grant.
|
||||
*
|
||||
* @return the {@link AuthorizationCodeGrantConfigurer}
|
||||
*/
|
||||
public AuthorizationCodeGrantConfigurer authorizationCodeGrant() {
|
||||
if (this.authorizationCodeGrantConfigurer == null) {
|
||||
this.authorizationCodeGrantConfigurer = new AuthorizationCodeGrantConfigurer();
|
||||
}
|
||||
return this.authorizationCodeGrantConfigurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for the OAuth 2.0 Authorization Code Grant.
|
||||
*/
|
||||
public class AuthorizationCodeGrantConfigurer {
|
||||
private final AuthorizationEndpointConfig authorizationEndpointConfig = new AuthorizationEndpointConfig();
|
||||
private final TokenEndpointConfig tokenEndpointConfig = new TokenEndpointConfig();
|
||||
|
||||
private AuthorizationCodeGrantConfigurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link AuthorizationEndpointConfig} for configuring the Authorization Server's Authorization Endpoint.
|
||||
*
|
||||
* @return the {@link AuthorizationEndpointConfig}
|
||||
*/
|
||||
public AuthorizationEndpointConfig authorizationEndpoint() {
|
||||
return this.authorizationEndpointConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for the Authorization Server's Authorization Endpoint.
|
||||
*/
|
||||
public class AuthorizationEndpointConfig {
|
||||
private String authorizationRequestBaseUri;
|
||||
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
|
||||
|
||||
private AuthorizationEndpointConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base {@code URI} used for authorization requests.
|
||||
*
|
||||
* @param authorizationRequestBaseUri the base {@code URI} used for authorization requests
|
||||
* @return the {@link AuthorizationEndpointConfig} for further configuration
|
||||
*/
|
||||
public AuthorizationEndpointConfig baseUri(String authorizationRequestBaseUri) {
|
||||
Assert.hasText(authorizationRequestBaseUri, "authorizationRequestBaseUri cannot be empty");
|
||||
this.authorizationRequestBaseUri = authorizationRequestBaseUri;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the repository used for storing {@link OAuth2AuthorizationRequest}'s.
|
||||
*
|
||||
* @param authorizationRequestRepository the repository used for storing {@link OAuth2AuthorizationRequest}'s
|
||||
* @return the {@link AuthorizationEndpointConfig} for further configuration
|
||||
*/
|
||||
public AuthorizationEndpointConfig authorizationRequestRepository(
|
||||
AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {
|
||||
|
||||
Assert.notNull(authorizationRequestRepository, "authorizationRequestRepository cannot be null");
|
||||
this.authorizationRequestRepository = authorizationRequestRepository;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link AuthorizationCodeGrantConfigurer} for further configuration.
|
||||
*
|
||||
* @return the {@link AuthorizationCodeGrantConfigurer}
|
||||
*/
|
||||
public AuthorizationCodeGrantConfigurer and() {
|
||||
return AuthorizationCodeGrantConfigurer.this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link TokenEndpointConfig} for configuring the Authorization Server's Token Endpoint.
|
||||
*
|
||||
* @return the {@link TokenEndpointConfig}
|
||||
*/
|
||||
public TokenEndpointConfig tokenEndpoint() {
|
||||
return this.tokenEndpointConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for the Authorization Server's Token Endpoint.
|
||||
*/
|
||||
public class TokenEndpointConfig {
|
||||
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
|
||||
|
||||
private TokenEndpointConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client used for requesting the access token credential from the Token Endpoint.
|
||||
*
|
||||
* @param accessTokenResponseClient the client used for requesting the access token credential from the Token Endpoint
|
||||
* @return the {@link TokenEndpointConfig} for further configuration
|
||||
*/
|
||||
public TokenEndpointConfig accessTokenResponseClient(
|
||||
OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient) {
|
||||
|
||||
Assert.notNull(accessTokenResponseClient, "accessTokenResponseClient cannot be null");
|
||||
this.accessTokenResponseClient = accessTokenResponseClient;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link AuthorizationCodeGrantConfigurer} for further configuration.
|
||||
*
|
||||
* @return the {@link AuthorizationCodeGrantConfigurer}
|
||||
*/
|
||||
public AuthorizationCodeGrantConfigurer and() {
|
||||
return AuthorizationCodeGrantConfigurer.this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2ClientConfigurer} for further configuration.
|
||||
*
|
||||
* @return the {@link OAuth2ClientConfigurer}
|
||||
*/
|
||||
public OAuth2ClientConfigurer<B> and() {
|
||||
return OAuth2ClientConfigurer.this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(B builder) throws Exception {
|
||||
if (this.authorizationCodeGrantConfigurer != null) {
|
||||
this.init(builder, this.authorizationCodeGrantConfigurer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(B builder) throws Exception {
|
||||
if (this.authorizationCodeGrantConfigurer != null) {
|
||||
this.configure(builder, this.authorizationCodeGrantConfigurer);
|
||||
}
|
||||
}
|
||||
|
||||
private void init(B builder, AuthorizationCodeGrantConfigurer authorizationCodeGrantConfigurer) throws Exception {
|
||||
OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient =
|
||||
authorizationCodeGrantConfigurer.tokenEndpointConfig.accessTokenResponseClient;
|
||||
if (accessTokenResponseClient == null) {
|
||||
accessTokenResponseClient = new NimbusAuthorizationCodeTokenResponseClient();
|
||||
}
|
||||
|
||||
OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider =
|
||||
new OAuth2AuthorizationCodeAuthenticationProvider(accessTokenResponseClient);
|
||||
builder.authenticationProvider(this.postProcess(authorizationCodeAuthenticationProvider));
|
||||
}
|
||||
|
||||
private void configure(B builder, AuthorizationCodeGrantConfigurer authorizationCodeGrantConfigurer) throws Exception {
|
||||
String authorizationRequestBaseUri = authorizationCodeGrantConfigurer.authorizationEndpointConfig.authorizationRequestBaseUri;
|
||||
if (authorizationRequestBaseUri == null) {
|
||||
authorizationRequestBaseUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
|
||||
}
|
||||
|
||||
OAuth2AuthorizationRequestRedirectFilter authorizationRequestFilter = new OAuth2AuthorizationRequestRedirectFilter(
|
||||
OAuth2ClientConfigurerUtils.getClientRegistrationRepository(builder), authorizationRequestBaseUri);
|
||||
|
||||
if (authorizationCodeGrantConfigurer.authorizationEndpointConfig.authorizationRequestRepository != null) {
|
||||
authorizationRequestFilter.setAuthorizationRequestRepository(
|
||||
authorizationCodeGrantConfigurer.authorizationEndpointConfig.authorizationRequestRepository);
|
||||
}
|
||||
RequestCache requestCache = builder.getSharedObject(RequestCache.class);
|
||||
if (requestCache != null) {
|
||||
authorizationRequestFilter.setRequestCache(requestCache);
|
||||
}
|
||||
builder.addFilter(this.postProcess(authorizationRequestFilter));
|
||||
|
||||
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
|
||||
|
||||
OAuth2AuthorizationCodeGrantFilter authorizationCodeGrantFilter = new OAuth2AuthorizationCodeGrantFilter(
|
||||
OAuth2ClientConfigurerUtils.getClientRegistrationRepository(builder),
|
||||
OAuth2ClientConfigurerUtils.getAuthorizedClientService(builder),
|
||||
authenticationManager);
|
||||
|
||||
if (authorizationCodeGrantConfigurer.authorizationEndpointConfig.authorizationRequestRepository != null) {
|
||||
authorizationCodeGrantFilter.setAuthorizationRequestRepository(
|
||||
authorizationCodeGrantConfigurer.authorizationEndpointConfig.authorizationRequestRepository);
|
||||
}
|
||||
builder.addFilter(this.postProcess(authorizationCodeGrantFilter));
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.oauth2.client;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Utility methods for the OAuth 2.0 Client {@link AbstractHttpConfigurer}'s.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.1
|
||||
*/
|
||||
final class OAuth2ClientConfigurerUtils {
|
||||
|
||||
private OAuth2ClientConfigurerUtils() {
|
||||
}
|
||||
|
||||
static <B extends HttpSecurityBuilder<B>> ClientRegistrationRepository getClientRegistrationRepository(B builder) {
|
||||
ClientRegistrationRepository clientRegistrationRepository = builder.getSharedObject(ClientRegistrationRepository.class);
|
||||
if (clientRegistrationRepository == null) {
|
||||
clientRegistrationRepository = getClientRegistrationRepositoryBean(builder);
|
||||
builder.setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
|
||||
}
|
||||
return clientRegistrationRepository;
|
||||
}
|
||||
|
||||
private static <B extends HttpSecurityBuilder<B>> ClientRegistrationRepository getClientRegistrationRepositoryBean(B builder) {
|
||||
return builder.getSharedObject(ApplicationContext.class).getBean(ClientRegistrationRepository.class);
|
||||
}
|
||||
|
||||
static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientService getAuthorizedClientService(B builder) {
|
||||
OAuth2AuthorizedClientService authorizedClientService = builder.getSharedObject(OAuth2AuthorizedClientService.class);
|
||||
if (authorizedClientService == null) {
|
||||
authorizedClientService = getAuthorizedClientServiceBean(builder);
|
||||
if (authorizedClientService == null) {
|
||||
authorizedClientService = new InMemoryOAuth2AuthorizedClientService(getClientRegistrationRepository(builder));
|
||||
}
|
||||
builder.setSharedObject(OAuth2AuthorizedClientService.class, authorizedClientService);
|
||||
}
|
||||
return authorizedClientService;
|
||||
}
|
||||
|
||||
private static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientService getAuthorizedClientServiceBean(B builder) {
|
||||
Map<String, OAuth2AuthorizedClientService> authorizedClientServiceMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(
|
||||
builder.getSharedObject(ApplicationContext.class), OAuth2AuthorizedClientService.class);
|
||||
if (authorizedClientServiceMap.size() > 1) {
|
||||
throw new NoUniqueBeanDefinitionException(OAuth2AuthorizedClientService.class, authorizedClientServiceMap.size(),
|
||||
"Only one matching @Bean of type " + OAuth2AuthorizedClientService.class.getName() + " should be registered.");
|
||||
}
|
||||
return (!authorizedClientServiceMap.isEmpty() ? authorizedClientServiceMap.values().iterator().next() : null);
|
||||
}
|
||||
}
|
||||
+10
-41
@@ -26,7 +26,6 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;
|
||||
@@ -53,6 +52,7 @@ import org.springframework.security.oauth2.core.oidc.OidcScopes;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -376,8 +376,8 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
|
||||
public void init(B http) throws Exception {
|
||||
OAuth2LoginAuthenticationFilter authenticationFilter =
|
||||
new OAuth2LoginAuthenticationFilter(
|
||||
this.getClientRegistrationRepository(),
|
||||
this.getAuthorizedClientService(),
|
||||
OAuth2ClientConfigurerUtils.getClientRegistrationRepository(this.getBuilder()),
|
||||
OAuth2ClientConfigurerUtils.getAuthorizedClientService(this.getBuilder()),
|
||||
OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI);
|
||||
this.setAuthenticationFilter(authenticationFilter);
|
||||
this.loginProcessingUrl(OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI);
|
||||
@@ -442,12 +442,16 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
|
||||
}
|
||||
|
||||
OAuth2AuthorizationRequestRedirectFilter authorizationRequestFilter = new OAuth2AuthorizationRequestRedirectFilter(
|
||||
this.getClientRegistrationRepository(), authorizationRequestBaseUri);
|
||||
OAuth2ClientConfigurerUtils.getClientRegistrationRepository(this.getBuilder()), authorizationRequestBaseUri);
|
||||
|
||||
if (this.authorizationEndpointConfig.authorizationRequestRepository != null) {
|
||||
authorizationRequestFilter.setAuthorizationRequestRepository(
|
||||
this.authorizationEndpointConfig.authorizationRequestRepository);
|
||||
}
|
||||
RequestCache requestCache = http.getSharedObject(RequestCache.class);
|
||||
if (requestCache != null) {
|
||||
authorizationRequestFilter.setRequestCache(requestCache);
|
||||
}
|
||||
http.addFilter(this.postProcess(authorizationRequestFilter));
|
||||
|
||||
OAuth2LoginAuthenticationFilter authenticationFilter = this.getAuthenticationFilter();
|
||||
@@ -466,41 +470,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
|
||||
return new AntPathRequestMatcher(loginProcessingUrl);
|
||||
}
|
||||
|
||||
private ClientRegistrationRepository getClientRegistrationRepository() {
|
||||
ClientRegistrationRepository clientRegistrationRepository =
|
||||
this.getBuilder().getSharedObject(ClientRegistrationRepository.class);
|
||||
if (clientRegistrationRepository == null) {
|
||||
clientRegistrationRepository = this.getClientRegistrationRepositoryBean();
|
||||
this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
|
||||
}
|
||||
return clientRegistrationRepository;
|
||||
}
|
||||
|
||||
private ClientRegistrationRepository getClientRegistrationRepositoryBean() {
|
||||
return this.getBuilder().getSharedObject(ApplicationContext.class).getBean(ClientRegistrationRepository.class);
|
||||
}
|
||||
|
||||
private OAuth2AuthorizedClientService getAuthorizedClientService() {
|
||||
OAuth2AuthorizedClientService authorizedClientService =
|
||||
this.getBuilder().getSharedObject(OAuth2AuthorizedClientService.class);
|
||||
if (authorizedClientService == null) {
|
||||
authorizedClientService = this.getAuthorizedClientServiceBean();
|
||||
if (authorizedClientService == null) {
|
||||
authorizedClientService = new InMemoryOAuth2AuthorizedClientService(this.getClientRegistrationRepository());
|
||||
}
|
||||
this.getBuilder().setSharedObject(OAuth2AuthorizedClientService.class, authorizedClientService);
|
||||
}
|
||||
return authorizedClientService;
|
||||
}
|
||||
|
||||
private OAuth2AuthorizedClientService getAuthorizedClientServiceBean() {
|
||||
Map<String, OAuth2AuthorizedClientService> authorizedClientServiceMap =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(
|
||||
this.getBuilder().getSharedObject(ApplicationContext.class),
|
||||
OAuth2AuthorizedClientService.class);
|
||||
return (!authorizedClientServiceMap.isEmpty() ? authorizedClientServiceMap.values().iterator().next() : null);
|
||||
}
|
||||
|
||||
private GrantedAuthoritiesMapper getGrantedAuthoritiesMapper() {
|
||||
GrantedAuthoritiesMapper grantedAuthoritiesMapper =
|
||||
this.getBuilder().getSharedObject(GrantedAuthoritiesMapper.class);
|
||||
@@ -528,7 +497,8 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
|
||||
}
|
||||
|
||||
Iterable<ClientRegistration> clientRegistrations = null;
|
||||
ClientRegistrationRepository clientRegistrationRepository = this.getClientRegistrationRepository();
|
||||
ClientRegistrationRepository clientRegistrationRepository =
|
||||
OAuth2ClientConfigurerUtils.getClientRegistrationRepository(this.getBuilder());
|
||||
ResolvableType type = ResolvableType.forInstance(clientRegistrationRepository).as(Iterable.class);
|
||||
if (type != ResolvableType.NONE && ClientRegistration.class.isAssignableFrom(type.resolveGenerics()[0])) {
|
||||
clientRegistrations = (Iterable<ClientRegistration>) clientRegistrationRepository;
|
||||
@@ -580,5 +550,4 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
|
||||
return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-2
@@ -55,7 +55,7 @@ import java.lang.annotation.Target;
|
||||
* @EnableWebFluxSecurity
|
||||
* public class MyExplicitSecurityConfiguration {
|
||||
* @Bean
|
||||
* SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
* public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
* http
|
||||
* .authorizeExchange()
|
||||
* .anyExchange().authenticated()
|
||||
@@ -82,7 +82,8 @@ import java.lang.annotation.Target;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import({ServerHttpSecurityConfiguration.class, WebFluxSecurityConfiguration.class})
|
||||
@Import({ServerHttpSecurityConfiguration.class, WebFluxSecurityConfiguration.class,
|
||||
ReactiveOAuth2ClientImportSelector.class})
|
||||
@Configuration
|
||||
public @interface EnableWebFluxSecurity {
|
||||
}
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.reactive;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.reactive.result.method.annotation.OAuth2ClientArgumentResolver;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
|
||||
|
||||
/**
|
||||
* {@link Configuration} for OAuth 2.0 Client support.
|
||||
*
|
||||
* <p>
|
||||
* This {@code Configuration} is imported by {@link EnableWebFluxSecurity}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 5.1
|
||||
*/
|
||||
final class ReactiveOAuth2ClientImportSelector implements ImportSelector {
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
boolean oauth2ClientPresent = ClassUtils.isPresent(
|
||||
"org.springframework.security.oauth2.client.registration.ClientRegistration", getClass().getClassLoader());
|
||||
|
||||
return oauth2ClientPresent ?
|
||||
new String[] { "org.springframework.security.config.annotation.web.reactive.ReactiveOAuth2ClientImportSelector$OAuth2ClientWebFluxSecurityConfiguration" } :
|
||||
new String[] {};
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OAuth2ClientWebFluxSecurityConfiguration implements WebFluxConfigurer {
|
||||
private ReactiveClientRegistrationRepository clientRegistrationRepository;
|
||||
|
||||
private ReactiveOAuth2AuthorizedClientService authorizedClientService;
|
||||
|
||||
@Override
|
||||
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
|
||||
if (this.clientRegistrationRepository != null && this.authorizedClientService != null) {
|
||||
configurer.addCustomResolver(new OAuth2ClientArgumentResolver(this.clientRegistrationRepository, this.authorizedClientService));
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setClientRegistrationRepository(List<ReactiveClientRegistrationRepository> clientRegistrationRepository) {
|
||||
if (clientRegistrationRepository.size() == 1) {
|
||||
this.clientRegistrationRepository = clientRegistrationRepository.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setAuthorizedClientService(List<ReactiveOAuth2AuthorizedClientService> authorizedClientService) {
|
||||
if (authorizedClientService.size() == 1) {
|
||||
this.authorizedClientService = authorizedClientService.get(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,9 +16,14 @@
|
||||
|
||||
package org.springframework.security.config.annotation.web.reactive;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
|
||||
@@ -29,8 +34,6 @@ import org.springframework.security.web.reactive.result.method.annotation.Authen
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
|
||||
|
||||
import static org.springframework.security.config.web.server.ServerHttpSecurity.http;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
@@ -51,6 +54,9 @@ class ServerHttpSecurityConfiguration implements WebFluxConfigurer {
|
||||
@Autowired(required = false)
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Autowired(required = false)
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
|
||||
configurer.addCustomResolver(authenticationPrincipalArgumentResolver());
|
||||
@@ -58,13 +64,19 @@ class ServerHttpSecurityConfiguration implements WebFluxConfigurer {
|
||||
|
||||
@Bean
|
||||
public AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver() {
|
||||
return new AuthenticationPrincipalArgumentResolver(this.adapterRegistry);
|
||||
AuthenticationPrincipalArgumentResolver resolver = new AuthenticationPrincipalArgumentResolver(
|
||||
this.adapterRegistry);
|
||||
if(this.beanFactory != null) {
|
||||
resolver.setBeanResolver(new BeanFactoryResolver(this.beanFactory));
|
||||
}
|
||||
return resolver;
|
||||
}
|
||||
|
||||
@Bean(HTTPSECURITY_BEAN_NAME)
|
||||
@Scope("prototype")
|
||||
public ServerHttpSecurity httpSecurity() {
|
||||
return http()
|
||||
ContextAwareServerHttpSecurity http = new ContextAwareServerHttpSecurity();
|
||||
return http
|
||||
.authenticationManager(authenticationManager())
|
||||
.headers().and()
|
||||
.logout().and();
|
||||
@@ -84,4 +96,13 @@ class ServerHttpSecurityConfiguration implements WebFluxConfigurer {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ContextAwareServerHttpSecurity extends ServerHttpSecurity implements
|
||||
ApplicationContextAware {
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
super.setApplicationContext(applicationContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-8
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.security.config.annotation.web.reactive;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -25,12 +28,10 @@ import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.WebFilterChainProxy;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.reactive.result.view.AbstractView;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
@@ -43,6 +44,11 @@ class WebFluxSecurityConfiguration {
|
||||
|
||||
private static final String SPRING_SECURITY_WEBFILTERCHAINFILTER_BEAN_NAME = BEAN_NAME_PREFIX + "WebFilterChainFilter";
|
||||
|
||||
public static final String REACTIVE_CLIENT_REGISTRATION_REPOSITORY_CLASSNAME = "org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository";
|
||||
|
||||
private static final boolean isOAuth2Present = ClassUtils.isPresent(
|
||||
REACTIVE_CLIENT_REGISTRATION_REPOSITORY_CLASSNAME, WebFluxSecurityConfiguration.class.getClassLoader());
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<SecurityWebFilterChain> securityWebFilterChains;
|
||||
|
||||
@@ -81,10 +87,29 @@ class WebFluxSecurityConfiguration {
|
||||
private SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http
|
||||
.authorizeExchange()
|
||||
.anyExchange().authenticated()
|
||||
.and()
|
||||
.httpBasic().and()
|
||||
.formLogin();
|
||||
return http.build();
|
||||
.anyExchange().authenticated();
|
||||
|
||||
if (isOAuth2Present && OAuth2ClasspathGuard.shouldConfigure(this.context)) {
|
||||
OAuth2ClasspathGuard.configure(this.context, http);
|
||||
} else {
|
||||
http
|
||||
.httpBasic().and()
|
||||
.formLogin();
|
||||
}
|
||||
|
||||
SecurityWebFilterChain result = http.build();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class OAuth2ClasspathGuard {
|
||||
static void configure(ApplicationContext context, ServerHttpSecurity http) {
|
||||
http.oauth2Login();
|
||||
}
|
||||
|
||||
static boolean shouldConfigure(ApplicationContext context) {
|
||||
ClassLoader loader = context.getClassLoader();
|
||||
Class<?> reactiveClientRegistrationRepositoryClass = ClassUtils.resolveClassName(REACTIVE_CLIENT_REGISTRATION_REPOSITORY_CLASSNAME, loader);
|
||||
return context.getBeanNamesForType(reactiveClientRegistrationRepositoryClass).length == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -103,7 +103,7 @@ public class AuthenticationManagerBeanDefinitionParser implements BeanDefinition
|
||||
else {
|
||||
BeanDefinition provider = resolver.resolve(
|
||||
providerElt.getNamespaceURI()).parse(providerElt, pc);
|
||||
Assert.notNull(provider, "Parser for " + providerElt.getNodeName()
|
||||
Assert.notNull(provider, () -> "Parser for " + providerElt.getNodeName()
|
||||
+ " returned a null bean definition");
|
||||
String providerId = pc.getReaderContext().generateBeanName(provider);
|
||||
pc.registerBeanComponent(new BeanComponentDefinition(provider,
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -49,7 +49,7 @@ public class CachingUserDetailsService implements UserDetailsService {
|
||||
user = delegate.loadUserByUsername(username);
|
||||
}
|
||||
|
||||
Assert.notNull(user, "UserDetailsService " + delegate
|
||||
Assert.notNull(user, () -> "UserDetailsService " + delegate
|
||||
+ " returned null for username " + username + ". "
|
||||
+ "This is an interface contract violation");
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -512,7 +512,7 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP
|
||||
synchronized (delegateMonitor) {
|
||||
if (delegate == null) {
|
||||
Assert.state(beanFactory != null,
|
||||
"BeanFactory must be set to resolve " + authMgrBean);
|
||||
() -> "BeanFactory must be set to resolve " + authMgrBean);
|
||||
try {
|
||||
delegate = beanFactory.getBean(authMgrBean,
|
||||
AuthenticationManager.class);
|
||||
|
||||
+5
-5
@@ -36,7 +36,7 @@ public enum CommonOAuth2Provider {
|
||||
@Override
|
||||
public Builder getBuilder(String registrationId) {
|
||||
ClientRegistration.Builder builder = getBuilder(registrationId,
|
||||
ClientAuthenticationMethod.BASIC, DEFAULT_LOGIN_REDIRECT_URL);
|
||||
ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
|
||||
builder.scope("openid", "profile", "email");
|
||||
builder.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth");
|
||||
builder.tokenUri("https://www.googleapis.com/oauth2/v4/token");
|
||||
@@ -53,7 +53,7 @@ public enum CommonOAuth2Provider {
|
||||
@Override
|
||||
public Builder getBuilder(String registrationId) {
|
||||
ClientRegistration.Builder builder = getBuilder(registrationId,
|
||||
ClientAuthenticationMethod.BASIC, DEFAULT_LOGIN_REDIRECT_URL);
|
||||
ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
|
||||
builder.scope("read:user");
|
||||
builder.authorizationUri("https://github.com/login/oauth/authorize");
|
||||
builder.tokenUri("https://github.com/login/oauth/access_token");
|
||||
@@ -69,7 +69,7 @@ public enum CommonOAuth2Provider {
|
||||
@Override
|
||||
public Builder getBuilder(String registrationId) {
|
||||
ClientRegistration.Builder builder = getBuilder(registrationId,
|
||||
ClientAuthenticationMethod.POST, DEFAULT_LOGIN_REDIRECT_URL);
|
||||
ClientAuthenticationMethod.POST, DEFAULT_REDIRECT_URL);
|
||||
builder.scope("public_profile", "email");
|
||||
builder.authorizationUri("https://www.facebook.com/v2.8/dialog/oauth");
|
||||
builder.tokenUri("https://graph.facebook.com/v2.8/oauth/access_token");
|
||||
@@ -85,7 +85,7 @@ public enum CommonOAuth2Provider {
|
||||
@Override
|
||||
public Builder getBuilder(String registrationId) {
|
||||
ClientRegistration.Builder builder = getBuilder(registrationId,
|
||||
ClientAuthenticationMethod.BASIC, DEFAULT_LOGIN_REDIRECT_URL);
|
||||
ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL);
|
||||
builder.scope("openid", "profile", "email", "address", "phone");
|
||||
builder.userNameAttributeName(IdTokenClaimNames.SUB);
|
||||
builder.clientName("Okta");
|
||||
@@ -93,7 +93,7 @@ public enum CommonOAuth2Provider {
|
||||
}
|
||||
};
|
||||
|
||||
private static final String DEFAULT_LOGIN_REDIRECT_URL = "{baseUrl}/login/oauth2/code/{registrationId}";
|
||||
private static final String DEFAULT_REDIRECT_URL = "{baseUrl}/{action}/oauth2/code/{registrationId}";
|
||||
|
||||
protected final ClientRegistration.Builder getBuilder(String registrationId,
|
||||
ClientAuthenticationMethod method, String redirectUri) {
|
||||
|
||||
+828
-26
File diff suppressed because it is too large
Load Diff
-92
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.method.configuration
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.method.configuration.NamespaceGlobalMethodSecurityTests.BaseMethodConfig;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class NamespaceGlobalMethodSecurityExpressionHandlerTests extends BaseSpringSpec {
|
||||
def setup() {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken("user", "password","ROLE_USER"))
|
||||
}
|
||||
|
||||
def "global-method-security/expression-handler @PreAuthorize"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomAccessDecisionManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.hasPermission("granted")
|
||||
then:
|
||||
noExceptionThrown()
|
||||
when:
|
||||
service.hasPermission("denied")
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
def "global-method-security/expression-handler @PostAuthorize"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomAccessDecisionManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.postHasPermission("granted")
|
||||
then:
|
||||
noExceptionThrown()
|
||||
when:
|
||||
service.postHasPermission("denied")
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class CustomAccessDecisionManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected MethodSecurityExpressionHandler createExpressionHandler() {
|
||||
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler()
|
||||
expressionHandler.permissionEvaluator = new PermissionEvaluator() {
|
||||
boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
|
||||
"granted" == targetDomainObject
|
||||
}
|
||||
boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
|
||||
throw new UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
return expressionHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
-444
@@ -1,444 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.method.configuration
|
||||
|
||||
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource
|
||||
import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
import java.lang.reflect.Method
|
||||
|
||||
import org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect
|
||||
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import org.springframework.context.annotation.AdviceMode
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.core.Ordered
|
||||
import org.springframework.security.access.AccessDecisionManager
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.access.ConfigAttribute
|
||||
import org.springframework.security.access.SecurityConfig
|
||||
import org.springframework.security.access.intercept.AfterInvocationManager
|
||||
import org.springframework.security.access.intercept.RunAsManager
|
||||
import org.springframework.security.access.intercept.RunAsManagerImpl
|
||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor
|
||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor
|
||||
import org.springframework.security.access.method.AbstractMethodSecurityMetadataSource
|
||||
import org.springframework.security.access.method.MethodSecurityMetadataSource
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.BaseAuthenticationConfig;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class NamespaceGlobalMethodSecurityTests extends BaseSpringSpec {
|
||||
def setup() {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken("user", "password","ROLE_USER"))
|
||||
}
|
||||
|
||||
// --- access-decision-manager-ref ---
|
||||
|
||||
def "custom AccessDecisionManager can be used"() {
|
||||
setup: "Create an instance with an AccessDecisionManager that always denies access"
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomAccessDecisionManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
when:
|
||||
service.secured()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||
public static class CustomAccessDecisionManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected AccessDecisionManager accessDecisionManager() {
|
||||
return new DenyAllAccessDecisionManager()
|
||||
}
|
||||
|
||||
public static class DenyAllAccessDecisionManager implements AccessDecisionManager {
|
||||
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) {
|
||||
throw new AccessDeniedException("Always Denied")
|
||||
}
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return true
|
||||
}
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- authentication-manager-ref ---
|
||||
|
||||
def "custom AuthenticationManager can be used"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(CustomAuthenticationConfig)
|
||||
MethodSecurityInterceptor interceptor = context.getBean(MethodSecurityInterceptor)
|
||||
interceptor.authenticationManager.authenticate(SecurityContextHolder.context.authentication)
|
||||
then:
|
||||
thrown(UnsupportedOperationException)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity
|
||||
public static class CustomAuthenticationConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected AuthenticationManager authenticationManager() {
|
||||
return new AuthenticationManager() {
|
||||
Authentication authenticate(Authentication authentication) {
|
||||
throw new UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- jsr250-annotations ---
|
||||
|
||||
def "enable jsr250"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(Jsr250Config)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then: "@Secured and @PreAuthorize are ignored"
|
||||
service.secured() == null
|
||||
service.preAuthorize() == null
|
||||
|
||||
when: "@DenyAll method invoked"
|
||||
service.jsr250()
|
||||
then: "access is denied"
|
||||
thrown(AccessDeniedException)
|
||||
when: "@PermitAll method invoked"
|
||||
String jsr250PermitAll = service.jsr250PermitAll()
|
||||
then: "access is allowed"
|
||||
jsr250PermitAll == null
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(jsr250Enabled = true)
|
||||
@Configuration
|
||||
public static class Jsr250Config extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
// --- metadata-source-ref ---
|
||||
|
||||
def "custom MethodSecurityMetadataSource can be used with higher priority than other sources"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomMethodSecurityMetadataSourceConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
when:
|
||||
service.secured()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
when:
|
||||
service.jsr250()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity
|
||||
public static class CustomMethodSecurityMetadataSourceConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
|
||||
return new AbstractMethodSecurityMetadataSource() {
|
||||
public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
|
||||
// require ROLE_NOBODY for any method on MethodSecurityService class
|
||||
return MethodSecurityService.isAssignableFrom(targetClass) ? [new SecurityConfig("ROLE_NOBODY")] : []
|
||||
}
|
||||
public Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- mode ---
|
||||
|
||||
def "aspectj mode works"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(AspectJModeConfig)
|
||||
then:
|
||||
context.getBean(AnnotationSecurityAspect)
|
||||
context.getBean(AspectJMethodSecurityInterceptor)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(mode = AdviceMode.ASPECTJ, proxyTargetClass = true)
|
||||
public static class AspectJModeConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "aspectj mode works extending GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,AspectJModeExtendsGMSCConfig)
|
||||
then:
|
||||
context.getBean(AnnotationSecurityAspect)
|
||||
context.getBean(AspectJMethodSecurityInterceptor)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(mode = AdviceMode.ASPECTJ)
|
||||
public static class AspectJModeExtendsGMSCConfig extends GlobalMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
// --- order ---
|
||||
|
||||
def order() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(CustomOrderConfig)
|
||||
MethodSecurityMetadataSourceAdvisor advisor = context.getBean(MethodSecurityMetadataSourceAdvisor)
|
||||
then:
|
||||
advisor.order == 135
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(order = 135)
|
||||
public static class CustomOrderConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "order is defaulted to Ordered.LOWEST_PRECEDENCE when using @EnableGlobalMethodSecurity"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(DefaultOrderConfig)
|
||||
MethodSecurityMetadataSourceAdvisor advisor = context.getBean(MethodSecurityMetadataSourceAdvisor)
|
||||
then:
|
||||
advisor.order == Ordered.LOWEST_PRECEDENCE
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity
|
||||
public static class DefaultOrderConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "order is defaulted to Ordered.LOWEST_PRECEDENCE when extending GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,DefaultOrderExtendsMethodSecurityConfig)
|
||||
MethodSecurityMetadataSourceAdvisor advisor = context.getBean(MethodSecurityMetadataSourceAdvisor)
|
||||
then:
|
||||
advisor.order == Ordered.LOWEST_PRECEDENCE
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity
|
||||
public static class DefaultOrderExtendsMethodSecurityConfig extends GlobalMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
// --- pre-post-annotations ---
|
||||
|
||||
def preAuthorize() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(PreAuthorizeConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.secured() == null
|
||||
service.jsr250() == null
|
||||
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@Configuration
|
||||
public static class PreAuthorizeConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "prePostEnabled extends GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,PreAuthorizeExtendsGMSCConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.secured() == null
|
||||
service.jsr250() == null
|
||||
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@Configuration
|
||||
public static class PreAuthorizeExtendsGMSCConfig extends GlobalMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
// --- proxy-target-class ---
|
||||
|
||||
def "proxying classes works"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(ProxyTargetClass)
|
||||
MethodSecurityServiceImpl service = context.getBean(MethodSecurityServiceImpl)
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(proxyTargetClass = true)
|
||||
@Configuration
|
||||
public static class ProxyTargetClass extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "proxying interfaces works"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(PreAuthorizeConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then: "we get an instance of the interface"
|
||||
noExceptionThrown()
|
||||
when: "try to cast to the class"
|
||||
MethodSecurityServiceImpl serviceImpl = service
|
||||
then: "we get a class cast exception"
|
||||
thrown(ClassCastException)
|
||||
}
|
||||
|
||||
// --- run-as-manager-ref ---
|
||||
|
||||
def "custom RunAsManager"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomRunAsManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.runAs().authorities.find { it.authority == "ROLE_RUN_AS_SUPER"}
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true)
|
||||
public static class CustomRunAsManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected RunAsManager runAsManager() {
|
||||
RunAsManagerImpl runAsManager = new RunAsManagerImpl()
|
||||
runAsManager.setKey("some key")
|
||||
return runAsManager
|
||||
}
|
||||
}
|
||||
|
||||
// --- secured-annotation ---
|
||||
|
||||
def "secured enabled"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(SecuredConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.secured()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
and: "service with ROLE_USER allowed"
|
||||
service.securedUser() == null
|
||||
and:
|
||||
service.preAuthorize() == null
|
||||
service.jsr250() == null
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true)
|
||||
@Configuration
|
||||
public static class SecuredConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
// --- after-invocation-provider
|
||||
|
||||
def "custom AfterInvocationManager"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomAfterInvocationManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.preAuthorizePermitAll()
|
||||
then:
|
||||
AccessDeniedException e = thrown()
|
||||
e.message == "custom AfterInvocationManager"
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class CustomAfterInvocationManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected AfterInvocationManager afterInvocationManager() {
|
||||
return new AfterInvocationManagerStub()
|
||||
}
|
||||
|
||||
public static class AfterInvocationManagerStub implements AfterInvocationManager {
|
||||
Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes,
|
||||
Object returnedObject) throws AccessDeniedException {
|
||||
throw new AccessDeniedException("custom AfterInvocationManager")
|
||||
}
|
||||
|
||||
boolean supports(ConfigAttribute attribute) {
|
||||
return true
|
||||
}
|
||||
boolean supports(Class<?> clazz) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- misc ---
|
||||
|
||||
def "good error message when no Enable annotation"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(ExtendsNoEnableAnntotationConfig)
|
||||
MethodSecurityInterceptor interceptor = context.getBean(MethodSecurityInterceptor)
|
||||
interceptor.authenticationManager.authenticate(SecurityContextHolder.context.authentication)
|
||||
then:
|
||||
BeanCreationException e = thrown()
|
||||
e.message.contains(EnableGlobalMethodSecurity.class.getName() + " is required")
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class ExtendsNoEnableAnntotationConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected AuthenticationManager authenticationManager() {
|
||||
return new AuthenticationManager() {
|
||||
Authentication authenticate(Authentication authentication) {
|
||||
throw new UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "import subclass of GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(ImportSubclassGMSCConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.secured() == null
|
||||
service.jsr250() == null
|
||||
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(PreAuthorizeExtendsGMSCConfig)
|
||||
public static class ImportSubclassGMSCConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class BaseMethodConfig extends BaseAuthenticationConfig {
|
||||
@Bean
|
||||
public MethodSecurityService methodSecurityService() {
|
||||
return new MethodSecurityServiceImpl()
|
||||
}
|
||||
}
|
||||
}
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.sec2758;
|
||||
|
||||
import javax.annotation.security.RolesAllowed;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configuration.sec2377.a.*
|
||||
import org.springframework.security.config.annotation.web.configuration.sec2377.b.*
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext
|
||||
|
||||
public class Sec2758Tests extends BaseSpringSpec {
|
||||
|
||||
def cleanup() {
|
||||
SecurityContextHolder.clearContext()
|
||||
}
|
||||
|
||||
def "SEC-2758: Verify Passivity Restored with Advice from JIRA"() {
|
||||
setup:
|
||||
SecurityContextHolder.context.authentication = new TestingAuthenticationToken("user", "pass", "USER")
|
||||
loadConfig(SecurityConfig)
|
||||
Service service = context.getBean(Service)
|
||||
|
||||
when:
|
||||
findFilter(FilterSecurityInterceptor).doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), new MockFilterChain())
|
||||
then:
|
||||
noExceptionThrown()
|
||||
|
||||
when:
|
||||
service.doPreAuthorize()
|
||||
then:
|
||||
noExceptionThrown()
|
||||
|
||||
when:
|
||||
service.doJsr250()
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled=true)
|
||||
static class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasAnyAuthority("USER");
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").authorities("USER")
|
||||
}
|
||||
|
||||
@Bean
|
||||
Service service() {
|
||||
return new ServiceImpl()
|
||||
}
|
||||
|
||||
@Bean
|
||||
static DefaultRolesPrefixPostProcessor defaultRolesPrefixPostProcessor() {
|
||||
new DefaultRolesPrefixPostProcessor()
|
||||
}
|
||||
}
|
||||
|
||||
interface Service {
|
||||
void doPreAuthorize()
|
||||
void doJsr250()
|
||||
}
|
||||
|
||||
static class ServiceImpl implements Service {
|
||||
@PreAuthorize("hasRole('USER')")
|
||||
void doPreAuthorize() {}
|
||||
|
||||
@RolesAllowed("USER")
|
||||
void doJsr250() {}
|
||||
}
|
||||
|
||||
static class DefaultRolesPrefixPostProcessor implements BeanPostProcessor, PriorityOrdered {
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if(bean instanceof Jsr250MethodSecurityMetadataSource) {
|
||||
((Jsr250MethodSecurityMetadataSource) bean).setDefaultRolePrefix(null);
|
||||
}
|
||||
if(bean instanceof DefaultMethodSecurityExpressionHandler) {
|
||||
((DefaultMethodSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
|
||||
}
|
||||
if(bean instanceof DefaultWebSecurityExpressionHandler) {
|
||||
((DefaultWebSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return PriorityOrdered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.builders;
|
||||
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer;
|
||||
|
||||
@EnableWebSecurity
|
||||
public class DisableUseExpressionsConfig extends BaseWebConfig {
|
||||
// @formatter:off
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// This config is also on UrlAuthorizationConfigurer javadoc
|
||||
http
|
||||
.apply(new UrlAuthorizationConfigurer<>(getApplicationContext())).getRegistry()
|
||||
.antMatchers("/users**", "/sessions/**").hasRole("USER")
|
||||
.antMatchers("/signup").hasRole("ANONYMOUS")
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
-499
@@ -1,499 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.builders
|
||||
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.access.AccessDecisionManager
|
||||
import org.springframework.security.access.ConfigAttribute
|
||||
import org.springframework.security.access.vote.AuthenticatedVoter
|
||||
import org.springframework.security.access.vote.RoleVoter
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.BadCredentialsException
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.NamespaceHttpTests.AuthenticationManagerRefConfig.CustomAuthenticationManager
|
||||
import org.springframework.security.config.annotation.web.builders.NamespaceHttpTests.RequestMatcherRefConfig.MyRequestMatcher
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.security.web.FilterInvocation
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource
|
||||
import org.springframework.security.web.access.expression.WebExpressionVoter
|
||||
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
|
||||
import org.springframework.security.web.context.NullSecurityContextRepository
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache
|
||||
import org.springframework.security.web.savedrequest.NullRequestCache
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
|
||||
import org.springframework.security.web.session.SessionManagementFilter
|
||||
import org.springframework.security.web.util.matcher.RegexRequestMatcher
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <http> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpTests extends BaseSpringSpec {
|
||||
def "http@access-decision-manager-ref"() {
|
||||
setup:
|
||||
AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR = Mock(AccessDecisionManager)
|
||||
AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR.supports(FilterInvocation) >> true
|
||||
AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR.supports(_ as ConfigAttribute) >> true
|
||||
when:
|
||||
loadConfig(AccessDecisionManagerRefConfig)
|
||||
then:
|
||||
findFilter(FilterSecurityInterceptor).accessDecisionManager == AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AccessDecisionManagerRefConfig extends BaseWebConfig {
|
||||
static AccessDecisionManager ACCESS_DECISION_MGR
|
||||
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().permitAll()
|
||||
.accessDecisionManager(ACCESS_DECISION_MGR)
|
||||
}
|
||||
}
|
||||
|
||||
def "http@access-denied-page"() {
|
||||
when:
|
||||
loadConfig(AccessDeniedPageConfig)
|
||||
then:
|
||||
findFilter(ExceptionTranslationFilter).accessDeniedHandler.errorPage == "/AccessDeniedPageConfig"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AccessDeniedPageConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.exceptionHandling()
|
||||
.accessDeniedPage("/AccessDeniedPageConfig")
|
||||
}
|
||||
}
|
||||
|
||||
def "http@authentication-manager-ref"() {
|
||||
when: "Specify AuthenticationManager"
|
||||
loadConfig(AuthenticationManagerRefConfig)
|
||||
then: "Populates the AuthenticationManager"
|
||||
findFilter(FilterSecurityInterceptor).authenticationManager.parent.class == CustomAuthenticationManager
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AuthenticationManagerRefConfig extends BaseWebConfig {
|
||||
// demo authentication-manager-ref (could be any value)
|
||||
|
||||
@Override
|
||||
protected AuthenticationManager authenticationManager() throws Exception {
|
||||
return new CustomAuthenticationManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
|
||||
static class CustomAuthenticationManager implements AuthenticationManager {
|
||||
public Authentication authenticate(Authentication authentication)
|
||||
throws AuthenticationException {
|
||||
throw new BadCredentialsException("This always fails");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: There is no http@auto-config equivalent in Java Config
|
||||
|
||||
def "http@create-session=always"() {
|
||||
when:
|
||||
loadConfig(IfRequiredConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == true
|
||||
findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == true
|
||||
findFilter(ExceptionTranslationFilter).requestCache.class == HttpSessionRequestCache
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CreateSessionAlwaysConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
|
||||
}
|
||||
}
|
||||
|
||||
def "http@create-session=stateless"() {
|
||||
when:
|
||||
loadConfig(CreateSessionStatelessConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.class == NullSecurityContextRepository
|
||||
findFilter(SessionManagementFilter).securityContextRepository.class == NullSecurityContextRepository
|
||||
findFilter(ExceptionTranslationFilter).requestCache.class == NullRequestCache
|
||||
findFilter(RequestCacheAwareFilter).requestCache.class == NullRequestCache
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CreateSessionStatelessConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||
}
|
||||
}
|
||||
|
||||
def "http@create-session=ifRequired"() {
|
||||
when:
|
||||
loadConfig(IfRequiredConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == true
|
||||
findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == true
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class IfRequiredConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
||||
def "http@create-session defaults to ifRequired"() {
|
||||
when:
|
||||
loadConfig(IfRequiredConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == true
|
||||
findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == true
|
||||
}
|
||||
|
||||
def "http@create-session=never"() {
|
||||
when:
|
||||
loadConfig(CreateSessionNeverConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == false
|
||||
findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == false
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CreateSessionNeverConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultCreateSessionConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
def "http@disable-url-rewriting = true (default for Java Config)"() {
|
||||
when:
|
||||
loadConfig(DefaultUrlRewritingConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).repo.disableUrlRewriting
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultUrlRewritingConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
// http@disable-url-rewriting is on by default to disable it create a custom HttpSecurityContextRepository and use security-context-repository-ref
|
||||
|
||||
def "http@disable-url-rewriting = false"() {
|
||||
when:
|
||||
loadConfig(EnableUrlRewritingConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).repo.disableUrlRewriting == false
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class EnableUrlRewritingConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
HttpSessionSecurityContextRepository repository = new HttpSessionSecurityContextRepository()
|
||||
repository.disableUrlRewriting = false // explicitly configured
|
||||
|
||||
http.
|
||||
securityContext()
|
||||
.securityContextRepository(repository)
|
||||
}
|
||||
}
|
||||
|
||||
def "http@entry-point-ref"() {
|
||||
when:
|
||||
loadConfig(EntryPointRefConfig)
|
||||
then:
|
||||
findFilter(ExceptionTranslationFilter).authenticationEntryPoint.loginFormUrl == "/EntryPointRefConfig"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class EntryPointRefConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/EntryPointRefConfig"))
|
||||
}
|
||||
}
|
||||
|
||||
def "http@jaas-api-provision"() {
|
||||
when:
|
||||
loadConfig(JaasApiProvisionConfig)
|
||||
then:
|
||||
findFilter(JaasApiIntegrationFilter)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class JaasApiProvisionConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.addFilter(new JaasApiIntegrationFilter())
|
||||
}
|
||||
}
|
||||
|
||||
// http@name is not available since it can be done w/ standard bean configuration easily
|
||||
|
||||
def "http@once-per-request=true"() {
|
||||
when:
|
||||
loadConfig(OncePerRequestConfig)
|
||||
then:
|
||||
findFilter(FilterSecurityInterceptor).observeOncePerRequest
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OncePerRequestConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
}
|
||||
|
||||
def "http@once-per-request=false"() {
|
||||
when:
|
||||
loadConfig(OncePerRequestFalseConfig)
|
||||
then:
|
||||
!findFilter(FilterSecurityInterceptor).observeOncePerRequest
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OncePerRequestFalseConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.
|
||||
authorizeRequests()
|
||||
.filterSecurityInterceptorOncePerRequest(false)
|
||||
.antMatchers("/users**","/sessions/**").hasRole("ADMIN")
|
||||
.antMatchers("/signup").permitAll()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
}
|
||||
|
||||
def "http@realm"() {
|
||||
setup:
|
||||
loadConfig(RealmConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getHeader("WWW-Authenticate") == 'Basic realm="RealmConfig"'
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RealmConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.httpBasic().realmName("RealmConfig")
|
||||
}
|
||||
}
|
||||
|
||||
// http@request-matcher is not available (instead request securityMatcher instances are used)
|
||||
|
||||
def "http@request-matcher-ref ant"() {
|
||||
when:
|
||||
loadConfig(RequestMatcherAntConfig)
|
||||
then:
|
||||
filterChain(0).requestMatcher.pattern == "/api/**"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RequestMatcherAntConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/api/**")
|
||||
}
|
||||
}
|
||||
|
||||
def "http@request-matcher-ref regex"() {
|
||||
when:
|
||||
loadConfig(RequestMatcherRegexConfig)
|
||||
then:
|
||||
filterChain(0).requestMatcher.class == RegexRequestMatcher
|
||||
filterChain(0).requestMatcher.pattern.matcher("/regex/a")
|
||||
filterChain(0).requestMatcher.pattern.matcher("/regex/b")
|
||||
!filterChain(0).requestMatcher.pattern.matcher("/regex1/b")
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RequestMatcherRegexConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.regexMatcher("/regex/.*")
|
||||
}
|
||||
}
|
||||
|
||||
def "http@request-matcher-ref"() {
|
||||
when:
|
||||
loadConfig(RequestMatcherRefConfig)
|
||||
then:
|
||||
filterChain(0).requestMatcher.class == MyRequestMatcher
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RequestMatcherRefConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.requestMatcher(new MyRequestMatcher());
|
||||
}
|
||||
static class MyRequestMatcher implements RequestMatcher {
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "http@security=none"() {
|
||||
when:
|
||||
loadConfig(SecurityNoneConfig)
|
||||
then:
|
||||
filterChain(0).requestMatcher.pattern == "/resources/**"
|
||||
filterChain(0).filters.empty
|
||||
filterChain(1).requestMatcher.pattern == "/public/**"
|
||||
filterChain(1).filters.empty
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SecurityNoneConfig extends BaseWebConfig {
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web)
|
||||
throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**","/public/**")
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {}
|
||||
|
||||
}
|
||||
|
||||
def "http@security-context-repository-ref"() {
|
||||
when:
|
||||
loadConfig(SecurityContextRepoConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).repo.class == NullSecurityContextRepository
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SecurityContextRepoConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.securityContext()
|
||||
.securityContextRepository(new NullSecurityContextRepository()) // security-context-repository-ref
|
||||
}
|
||||
}
|
||||
|
||||
def "http@servlet-api-provision=false"() {
|
||||
when:
|
||||
loadConfig(ServletApiProvisionConfig)
|
||||
then:
|
||||
findFilter(SecurityContextHolderAwareRequestFilter) == null
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ServletApiProvisionConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.servletApi().disable()
|
||||
}
|
||||
}
|
||||
|
||||
def "http@servlet-api-provision defaults to true"() {
|
||||
when:
|
||||
loadConfig(ServletApiProvisionDefaultsConfig)
|
||||
then:
|
||||
findFilter(SecurityContextHolderAwareRequestFilter) != null
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ServletApiProvisionDefaultsConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
def "http@use-expressions=true"() {
|
||||
when:
|
||||
loadConfig(UseExpressionsConfig)
|
||||
then:
|
||||
findFilter(FilterSecurityInterceptor).securityMetadataSource.class == ExpressionBasedFilterInvocationSecurityMetadataSource
|
||||
findFilter(FilterSecurityInterceptor).accessDecisionManager.decisionVoters.collect { it.class } == [WebExpressionVoter]
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class UseExpressionsConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.antMatchers("/users**","/sessions/**").hasRole("USER")
|
||||
.antMatchers("/signup").permitAll()
|
||||
.anyRequest().hasRole("USER")
|
||||
}
|
||||
}
|
||||
|
||||
def "http@use-expressions=false"() {
|
||||
when:
|
||||
loadConfig(DisableUseExpressionsConfig)
|
||||
then:
|
||||
findFilter(FilterSecurityInterceptor).securityMetadataSource.class == DefaultFilterInvocationSecurityMetadataSource
|
||||
findFilter(FilterSecurityInterceptor).accessDecisionManager.decisionVoters.collect { it.class } == [RoleVoter, AuthenticatedVoter]
|
||||
}
|
||||
}
|
||||
-144
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.configuration
|
||||
|
||||
import org.springframework.mock.web.MockServletContext
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||
import org.springframework.security.core.context.SecurityContext
|
||||
import org.springframework.security.core.context.SecurityContextImpl
|
||||
import org.springframework.security.core.userdetails.PasswordEncodedUser
|
||||
import org.springframework.security.core.userdetails.User
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
|
||||
import org.springframework.test.context.web.WebAppConfiguration
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter
|
||||
import org.springframework.security.web.debug.DebugFilter
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
class EnableWebSecurityTests extends BaseSpringSpec {
|
||||
|
||||
def "@Bean(BeanIds.AUTHENTICATION_MANAGER) includes HttpSecurity's AuthenticationManagerBuilder"() {
|
||||
when:
|
||||
loadConfig(SecurityConfig)
|
||||
AuthenticationManager authenticationManager = context.getBean(AuthenticationManager)
|
||||
AnonymousAuthenticationToken anonymousAuthToken = findFilter(AnonymousAuthenticationFilter).createAuthentication(new MockHttpServletRequest())
|
||||
then:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
authenticationManager.authenticate(anonymousAuthToken)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@EnableWebSecurity
|
||||
static class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser(PasswordEncodedUser.user());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.antMatchers("/*").hasRole("USER")
|
||||
.and()
|
||||
.formLogin();
|
||||
}
|
||||
}
|
||||
|
||||
def "@EnableWebSecurity on superclass"() {
|
||||
when:
|
||||
loadConfig(ChildSecurityConfig)
|
||||
then:
|
||||
context.getBean("springSecurityFilterChain", DebugFilter)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ChildSecurityConfig extends DebugSecurityConfig {
|
||||
}
|
||||
|
||||
@EnableWebSecurity(debug=true)
|
||||
static class DebugSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
}
|
||||
|
||||
def "SEC-2942: EnableWebSecurity adds AuthenticationPrincipalArgumentResolver"() {
|
||||
setup:
|
||||
def username = "test"
|
||||
context = new AnnotationConfigWebApplicationContext()
|
||||
context.servletContext = new MockServletContext()
|
||||
context.register(AuthenticationPrincipalConfig)
|
||||
context.refresh()
|
||||
SecurityContext securityContext = new SecurityContextImpl(authentication: new TestingAuthenticationToken(username, "pass", "ROLE_USER"))
|
||||
MockMvc mockMvc = MockMvcBuilders
|
||||
.webAppContextSetup(context)
|
||||
.addFilters(springSecurityFilterChain)
|
||||
.build()
|
||||
when:
|
||||
String body = mockMvc
|
||||
.perform(get("/").sessionAttr(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext))
|
||||
.andReturn().response.contentAsString
|
||||
then:
|
||||
body == username
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@EnableWebMvc
|
||||
@Configuration
|
||||
static class AuthenticationPrincipalConfig {
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) {
|
||||
auth.inMemoryAuthentication()
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class AuthController {
|
||||
|
||||
@RequestMapping("/")
|
||||
String principal(@AuthenticationPrincipal String principal) {
|
||||
principal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.configuration;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
|
||||
public class Sec2515Tests extends BaseSpringSpec {
|
||||
|
||||
def "SEC-2515: Prevent StackOverflow with bean graph cycle"() {
|
||||
when:
|
||||
loadConfig(StackOverflowSecurityConfig)
|
||||
then:
|
||||
thrown(FatalBeanException)
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class StackOverflowSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
|
||||
def "Custom Name Prevent StackOverflow with bean graph cycle"() {
|
||||
when:
|
||||
loadConfig(StackOverflowSecurityConfig)
|
||||
then:
|
||||
thrown(FatalBeanException)
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomBeanNameStackOverflowSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
@Bean(name="custom")
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
|
||||
def "SEC-2549: Can load with child classloader"() {
|
||||
setup:
|
||||
CanLoadWithChildConfig.AM = Mock(AuthenticationManager)
|
||||
context = new AnnotationConfigApplicationContext()
|
||||
context.classLoader = new URLClassLoader(new URL[0], context.classLoader)
|
||||
context.register(CanLoadWithChildConfig)
|
||||
context.refresh()
|
||||
when:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
then:
|
||||
noExceptionThrown()
|
||||
1 * CanLoadWithChildConfig.AM.authenticate(_) >> new TestingAuthenticationToken("user","password","ROLE_USER")
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CanLoadWithChildConfig extends WebSecurityConfigurerAdapter {
|
||||
static AuthenticationManager AM
|
||||
@Bean
|
||||
public AuthenticationManager am() {
|
||||
AM
|
||||
}
|
||||
}
|
||||
|
||||
def "SEC-2515: @Bean still works when configure(AuthenticationManagerBuilder) used"() {
|
||||
when:
|
||||
loadConfig(SecurityConfig)
|
||||
then:
|
||||
noExceptionThrown();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth.inMemoryAuthentication()
|
||||
}
|
||||
}
|
||||
}
|
||||
-344
@@ -1,344 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.configuration
|
||||
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.core.annotation.Order
|
||||
import org.springframework.expression.ExpressionParser
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurationTests.DuplicateOrderConfig;
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator
|
||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher
|
||||
import org.springframework.test.util.ReflectionTestUtils
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class WebSecurityConfigurationTests extends BaseSpringSpec {
|
||||
|
||||
def "WebSecurityConfigurers are sorted"() {
|
||||
when:
|
||||
loadConfig(SortedWebSecurityConfigurerAdaptersConfig);
|
||||
List<SecurityFilterChain> filterChains = context.getBean(FilterChainProxy).filterChains
|
||||
then:
|
||||
filterChains[0].requestMatcher.pattern == "/ignore1"
|
||||
filterChains[0].filters.empty
|
||||
filterChains[1].requestMatcher.pattern == "/ignore2"
|
||||
filterChains[1].filters.empty
|
||||
|
||||
filterChains[2].requestMatcher.pattern == "/role1/**"
|
||||
filterChains[3].requestMatcher.pattern == "/role2/**"
|
||||
filterChains[4].requestMatcher.pattern == "/role3/**"
|
||||
filterChains[5].requestMatcher.class == AnyRequestMatcher
|
||||
}
|
||||
|
||||
|
||||
@EnableWebSecurity
|
||||
static class SortedWebSecurityConfigurerAdaptersConfig {
|
||||
public AuthenticationManager authenticationManager() throws Exception {
|
||||
return new AuthenticationManagerBuilder()
|
||||
.inMemoryAuthentication()
|
||||
.withUser("marissa").password("koala").roles("USER").and()
|
||||
.withUser("paul").password("emu").roles("USER").and()
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(1)
|
||||
public static class WebConfigurer1 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/ignore1","/ignore2");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role1/**")
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("1");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(2)
|
||||
public static class WebConfigurer2 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role2/**")
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("2");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(3)
|
||||
public static class WebConfigurer3 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role3/**")
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("3");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class WebConfigurer4 extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("4");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "WebSecurityConfigurers fails with duplicate order"() {
|
||||
when:
|
||||
loadConfig(DuplicateOrderConfig);
|
||||
then:
|
||||
BeanCreationException e = thrown()
|
||||
e.message.contains "@Order on WebSecurityConfigurers must be unique"
|
||||
e.message.contains DuplicateOrderConfig.WebConfigurer1.class.name
|
||||
e.message.contains DuplicateOrderConfig.WebConfigurer2.class.name
|
||||
}
|
||||
|
||||
|
||||
@EnableWebSecurity
|
||||
static class DuplicateOrderConfig {
|
||||
public AuthenticationManager authenticationManager() throws Exception {
|
||||
return new AuthenticationManagerBuilder()
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class WebConfigurer1 extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role1/**")
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("1");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class WebConfigurer2 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role2/**")
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("2");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "Override privilegeEvaluator"() {
|
||||
setup:
|
||||
WebInvocationPrivilegeEvaluator privilegeEvaluator = Mock()
|
||||
PrivilegeEvaluatorConfigurerAdapterConfig.PE = privilegeEvaluator
|
||||
when:
|
||||
loadConfig(PrivilegeEvaluatorConfigurerAdapterConfig)
|
||||
then:
|
||||
context.getBean(WebInvocationPrivilegeEvaluator) == privilegeEvaluator
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class PrivilegeEvaluatorConfigurerAdapterConfig extends WebSecurityConfigurerAdapter {
|
||||
static WebInvocationPrivilegeEvaluator PE
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.privilegeEvaluator(PE)
|
||||
}
|
||||
}
|
||||
|
||||
def "Override webSecurityExpressionHandler"() {
|
||||
setup:
|
||||
SecurityExpressionHandler expressionHandler = Mock()
|
||||
ExpressionParser parser = Mock()
|
||||
WebSecurityExpressionHandlerConfig.EH = expressionHandler
|
||||
when:
|
||||
loadConfig(WebSecurityExpressionHandlerConfig)
|
||||
then:
|
||||
context.getBean(SecurityExpressionHandler) == expressionHandler
|
||||
1 * expressionHandler.getExpressionParser() >> parser
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class WebSecurityExpressionHandlerConfig extends WebSecurityConfigurerAdapter {
|
||||
static SecurityExpressionHandler EH
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.expressionHandler(EH)
|
||||
}
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.expressionHandler(EH)
|
||||
.anyRequest().authenticated()
|
||||
}
|
||||
}
|
||||
|
||||
def "#138 webSecurityExpressionHandler defaults"() {
|
||||
when:
|
||||
loadConfig(WebSecurityExpressionHandlerDefaultsConfig)
|
||||
then:
|
||||
SecurityExpressionHandler wseh = context.getBean(SecurityExpressionHandler)
|
||||
wseh instanceof DefaultWebSecurityExpressionHandler
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class WebSecurityExpressionHandlerDefaultsConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
}
|
||||
}
|
||||
|
||||
def "#138 WebInvocationPrivilegeEvaluator defaults"() {
|
||||
when:
|
||||
loadConfig(WebInvocationPrivilegeEvaluatorDefaultsConfig)
|
||||
then:
|
||||
WebInvocationPrivilegeEvaluator wipe = context.getBean(WebInvocationPrivilegeEvaluator)
|
||||
wipe instanceof DefaultWebInvocationPrivilegeEvaluator
|
||||
wipe.securityInterceptor != null
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class WebInvocationPrivilegeEvaluatorDefaultsConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
}
|
||||
}
|
||||
|
||||
def "SEC-2303: DefaultExpressionHandler has bean resolver set"() {
|
||||
when:
|
||||
loadConfig(DefaultExpressionHandlerSetsBeanResolverConfig)
|
||||
then: "the exposed bean has a BeanResolver set"
|
||||
ReflectionTestUtils.getField(context.getBean(SecurityExpressionHandler),"br")
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request, response, chain)
|
||||
then: "we can use the BeanResolver with a grant"
|
||||
noExceptionThrown()
|
||||
when: "we can use the Beanresolver with a deny"
|
||||
springSecurityFilterChain.doFilter(new MockHttpServletRequest(method:'POST'), response, chain)
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class DefaultExpressionHandlerSetsBeanResolverConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().access("request.method == 'GET' ? @b.grant() : @b.deny()")
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MyBean b() {
|
||||
new MyBean()
|
||||
}
|
||||
|
||||
static class MyBean {
|
||||
boolean deny() {
|
||||
false
|
||||
}
|
||||
|
||||
boolean grant() {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "SEC-2461: Multiple WebSecurityConfiguration instances cause null springSecurityFilterChain"() {
|
||||
setup:
|
||||
def parent = loadConfig(ParentConfig)
|
||||
def child = new AnnotationConfigApplicationContext()
|
||||
child.register(ChildConfig)
|
||||
child.parent = parent
|
||||
when:
|
||||
child.refresh()
|
||||
then: "springSecurityFilterChain can be found in parent and child"
|
||||
parent.getBean("springSecurityFilterChain")
|
||||
child.getBean("springSecurityFilterChain")
|
||||
and: "springSecurityFilterChain is defined in both parent and child (don't search parent)"
|
||||
parent.containsBeanDefinition("springSecurityFilterChain")
|
||||
child.containsBeanDefinition("springSecurityFilterChain")
|
||||
cleanup:
|
||||
child?.close()
|
||||
// parent.close() is in superclass
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class ParentConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) {
|
||||
auth.inMemoryAuthentication()
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class ChildConfig extends WebSecurityConfigurerAdapter { }
|
||||
|
||||
def "SEC-2773: delegatingApplicationListener is static method"() {
|
||||
expect: 'delegatingApplicationListener to prevent premature instantiation of WebSecurityConfiguration'
|
||||
Modifier.isStatic(WebSecurityConfiguration.metaClass.methods.find { it.name == 'delegatingApplicationListener'}.modifiers)
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.configuration.sec2377;
|
||||
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.configuration.sec2377.a.*
|
||||
import org.springframework.security.config.annotation.web.configuration.sec2377.b.*
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext
|
||||
|
||||
public class Sec2377Tests extends BaseSpringSpec {
|
||||
|
||||
def "SEC-2377: Error reporting with multiple EnableWebSecurity from other packages"() {
|
||||
when:
|
||||
AnnotationConfigWebApplicationContext parent = new AnnotationConfigWebApplicationContext()
|
||||
parent.register(Sec2377AConfig)
|
||||
parent.refresh()
|
||||
|
||||
AnnotationConfigWebApplicationContext child = new AnnotationConfigWebApplicationContext()
|
||||
child.register(Sec2377BConfig)
|
||||
child.parent = parent
|
||||
child.refresh()
|
||||
then:
|
||||
noExceptionThrown();
|
||||
}
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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 org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class AnonymousConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "invoke logout twice does not override"() {
|
||||
when:
|
||||
loadConfig(InvokeTwiceDoesNotOverride)
|
||||
then:
|
||||
findFilter(AnonymousAuthenticationFilter).key == "custom"
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class InvokeTwiceDoesNotOverride extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.anonymous()
|
||||
.key("custom")
|
||||
.and()
|
||||
.anonymous()
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -186,13 +186,14 @@ class LogoutConfigurerTests extends BaseSpringSpec {
|
||||
}
|
||||
}
|
||||
|
||||
def "LogoutConfigurer content negotiation default redirects"() {
|
||||
def "LogoutConfigurer content negotiation text/html redirects"() {
|
||||
setup:
|
||||
loadConfig(LogoutHandlerContentNegotiation)
|
||||
when:
|
||||
login()
|
||||
request.method = 'POST'
|
||||
request.servletPath = '/logout'
|
||||
request.addHeader('Accept', 'text/html')
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 302
|
||||
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author 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.debug
|
||||
|
||||
import org.springframework.security.config.BeanIds
|
||||
import org.springframework.security.config.http.AbstractHttpConfigTests
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.debug.DebugFilter;
|
||||
|
||||
class SecurityDebugBeanFactoryPostProcessorTest extends AbstractHttpConfigTests {
|
||||
|
||||
// SEC-1885
|
||||
def 'SEC-1885 - SecurityDebugBeanFactoryPostProcessor works when dependencies have Autowired constructor'() {
|
||||
when: 'debug used and FilterChainProxy has dependency with @Autowired constructor'
|
||||
xml.debug()
|
||||
httpAutoConfig {}
|
||||
xml.'authentication-manager'() {
|
||||
'authentication-provider'('ref': 'authProvider')
|
||||
}
|
||||
xml.'context:component-scan'('base-package':'org.springframework.security.config.debug')
|
||||
createAppContext('')
|
||||
then: 'TestAuthenticationProvider.<init>() is not thrown'
|
||||
appContext.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN) instanceof DebugFilter
|
||||
appContext.getBean(BeanIds.FILTER_CHAIN_PROXY) instanceof FilterChainProxy
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author 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.doc
|
||||
|
||||
/**
|
||||
* Represents a Spring Security XSD Attribute. It is created when parsing the current xsd to compare to the documented appendix.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @see SpringSecurityXsdParser
|
||||
* @see XsdDocumentedSpec
|
||||
*/
|
||||
class Attribute {
|
||||
def name
|
||||
def desc
|
||||
def elmt
|
||||
|
||||
def getId() {
|
||||
return "${elmt.id}-${name}".toString()
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author 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.doc
|
||||
|
||||
/**
|
||||
* Represents a Spring Security XSD Element. It is created when parsing the current xsd to compare to the documented appendix.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @see SpringSecurityXsdParser
|
||||
* @see XsdDocumentedSpec
|
||||
*/
|
||||
class Element {
|
||||
def name
|
||||
def desc
|
||||
def attrs
|
||||
/**
|
||||
* Contains the elements that extend this element (i.e. any-user-service contains ldap-user-service)
|
||||
*/
|
||||
def subGrps = []
|
||||
def childElmts = [:]
|
||||
def parentElmts = [:]
|
||||
|
||||
def getId() {
|
||||
return "nsa-${name}".toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the ids related to this Element including attributes, parent elements, and child elements.
|
||||
*
|
||||
* <p>
|
||||
* The expected ids to be found are documented below.
|
||||
* <ul>
|
||||
* <li>Elements - any xml element will have the nsa-<element>. For example the http element will have the id
|
||||
* nsa-http</li>
|
||||
* <li>Parent Section - Any element with a parent other than beans will have a section named
|
||||
* nsa-<element>-parents. For example, authentication-provider would have a section id of
|
||||
* nsa-authentication-provider-parents. The section would then contain a list of links pointing to the
|
||||
* documentation for each parent element.</li>
|
||||
* <li>Attributes Section - Any element with attributes will have a section with the id
|
||||
* nsa-<element>-attributes. For example the http element would require a section with the id
|
||||
* http-attributes.</li>
|
||||
* <li>Attribute - Each attribute of an element would have an id of nsa-<element>-<attributeName>. For
|
||||
* example the attribute create-session for the http attribute would have the id http-create-session.</li>
|
||||
* <li>Child Section - Any element with a child element will have a section named nsa-<element>-children.
|
||||
* For example, authentication-provider would have a section id of nsa-authentication-provider-children. The
|
||||
* section would then contain a list of links pointing to the documentation for each child element.</li>
|
||||
* </ul>
|
||||
* @return
|
||||
*/
|
||||
def getIds() {
|
||||
def ids = [id]
|
||||
childElmts.values()*.ids.each { ids.addAll it }
|
||||
attrs*.id.each { ids.add it }
|
||||
if(childElmts) {
|
||||
ids.add id+'-children'
|
||||
}
|
||||
if(attrs) {
|
||||
ids.add id+'-attributes'
|
||||
}
|
||||
if(parentElmts) {
|
||||
ids.add id+'-parents'
|
||||
}
|
||||
ids
|
||||
}
|
||||
|
||||
def getAllChildElmts() {
|
||||
def result = [:]
|
||||
childElmts.values()*.subGrps*.each { elmt -> result.put(elmt.name,elmt) }
|
||||
result + childElmts
|
||||
}
|
||||
|
||||
def getAllParentElmts() {
|
||||
def result = [:]
|
||||
parentElmts.values()*.subGrps*.each { elmt -> result.put(elmt.name,elmt) }
|
||||
result + parentElmts
|
||||
}
|
||||
}
|
||||
-177
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author 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.doc
|
||||
|
||||
import groovy.xml.Namespace
|
||||
|
||||
/**
|
||||
* Parses the Spring Security Xsd Document
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class SpringSecurityXsdParser {
|
||||
private def rootElement
|
||||
|
||||
private def xs = new Namespace("http://www.w3.org/2001/XMLSchema", 'xs')
|
||||
private def attrElmts = [] as Set
|
||||
private def elementNameToElement = [:] as Map
|
||||
|
||||
/**
|
||||
* Returns a map of the element name to the {@link Element}.
|
||||
* @return
|
||||
*/
|
||||
Map<String,Element> parse() {
|
||||
elements(rootElement)
|
||||
elementNameToElement
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Map of the name to an Element object of all the children of element.
|
||||
*
|
||||
* @param element
|
||||
* @return
|
||||
*/
|
||||
private def elements(element) {
|
||||
def elementNameToElement = [:] as Map
|
||||
element.children().each { c->
|
||||
if(c.name() == 'element') {
|
||||
def e = elmt(c)
|
||||
elementNameToElement.put(e.name,e)
|
||||
} else {
|
||||
elementNameToElement.putAll(elements(c))
|
||||
}
|
||||
}
|
||||
elementNameToElement
|
||||
}
|
||||
|
||||
/**
|
||||
* Any children that are attribute will be returned as an Attribute object.
|
||||
* @param element
|
||||
* @return a collection of Attribute objects that are children of element.
|
||||
*/
|
||||
private def attrs(element) {
|
||||
def r = []
|
||||
element.children().each { c->
|
||||
if(c.name() == 'attribute') {
|
||||
r.add(attr(c))
|
||||
}else if(c.name() == 'element') {
|
||||
}else {
|
||||
r.addAll(attrs(c))
|
||||
}
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
/**
|
||||
* Any children will be searched for an attributeGroup, each of it's children will be returned as an Attribute
|
||||
* @param element
|
||||
* @return
|
||||
*/
|
||||
private def attrgrps(element) {
|
||||
def r = []
|
||||
element.children().each { c->
|
||||
if(c.name() == 'element') {
|
||||
}else if (c.name() == 'attributeGroup') {
|
||||
if(c.attributes().get('name')) {
|
||||
r.addAll(attrgrp(c))
|
||||
} else {
|
||||
def n = c.attributes().get('ref').split(':')[1]
|
||||
def attrGrp = findNode(element,n)
|
||||
r.addAll(attrgrp(attrGrp))
|
||||
}
|
||||
} else {
|
||||
r.addAll(attrgrps(c))
|
||||
}
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
private def findNode(c,name) {
|
||||
def root = c
|
||||
while(root.name() != 'schema') {
|
||||
root = root.parent()
|
||||
}
|
||||
def result = root.breadthFirst().find { child-> name == child.@name?.text() }
|
||||
assert result?.@name?.text() == name
|
||||
result
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an individual attributeGroup by obtaining all the attributes and then looking for more attributeGroup elements and prcessing them.
|
||||
* @param e
|
||||
* @return all the attributes for a specific attributeGroup and any child attributeGroups
|
||||
*/
|
||||
private def attrgrp(e) {
|
||||
def attrs = attrs(e)
|
||||
attrs.addAll(attrgrps(e))
|
||||
attrs
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the description for a specific element
|
||||
* @param element
|
||||
* @return
|
||||
*/
|
||||
private def desc(element) {
|
||||
return element['annotation']['documentation']
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an element creates an attribute from it.
|
||||
* @param n
|
||||
* @return
|
||||
*/
|
||||
private def attr(n) {
|
||||
new Attribute(desc: desc(n), name: n.@name.text())
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an element creates an Element out of it by collecting all its attributes and child elements.
|
||||
*
|
||||
* @param n
|
||||
* @return
|
||||
*/
|
||||
private def elmt(n) {
|
||||
def name = n.@ref.text()
|
||||
if(name) {
|
||||
name = name.split(':')[1]
|
||||
n = findNode(n,name)
|
||||
} else {
|
||||
name = n.@name.text()
|
||||
}
|
||||
if(elementNameToElement.containsKey(name)) {
|
||||
return elementNameToElement.get(name)
|
||||
}
|
||||
attrElmts.add(name)
|
||||
def e = new Element()
|
||||
e.name = n.@name.text()
|
||||
e.desc = desc(n)
|
||||
e.childElmts = elements(n)
|
||||
e.attrs = attrs(n)
|
||||
e.attrs.addAll(attrgrps(n))
|
||||
e.attrs*.elmt = e
|
||||
e.childElmts.values()*.each { it.parentElmts.put(e.name,e) }
|
||||
|
||||
def subGrpName = n.@substitutionGroup.text()
|
||||
if(subGrpName) {
|
||||
def subGrp = elmt(findNode(n,subGrpName.split(":")[1]))
|
||||
subGrp.subGrps.add(e)
|
||||
}
|
||||
|
||||
elementNameToElement.put(name,e)
|
||||
e
|
||||
}
|
||||
}
|
||||
-222
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.springframework.security.config.doc
|
||||
|
||||
import groovy.util.slurpersupport.GPathResult
|
||||
import spock.lang.*
|
||||
|
||||
import org.springframework.security.config.http.SecurityFilters
|
||||
|
||||
/**
|
||||
* Tests to ensure that the xsd is properly documented.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class XsdDocumentedTests extends Specification {
|
||||
|
||||
def ignoredIds = [
|
||||
'nsa-any-user-service',
|
||||
'nsa-any-user-service-parents',
|
||||
'nsa-authentication',
|
||||
'nsa-websocket-security',
|
||||
'nsa-ldap',
|
||||
'nsa-method-security',
|
||||
'nsa-web'
|
||||
]
|
||||
@Shared def reference = new File('../docs/manual/src/docs/asciidoc/index.adoc')
|
||||
|
||||
@Shared File schema31xDocument = new File('src/main/resources/org/springframework/security/config/spring-security-3.1.xsd')
|
||||
@Shared File schemaDocument = new File('src/main/resources/org/springframework/security/config/spring-security-5.0.xsd')
|
||||
@Shared Map<String,Element> elementNameToElement
|
||||
@Shared GPathResult schemaRootElement
|
||||
|
||||
def setupSpec() {
|
||||
schemaRootElement = new XmlSlurper().parse(schemaDocument)
|
||||
elementNameToElement = new SpringSecurityXsdParser(rootElement: schemaRootElement).parse()
|
||||
}
|
||||
|
||||
def cleanupSpec() {
|
||||
reference = null
|
||||
schema31xDocument = null
|
||||
schemaDocument = null
|
||||
elementNameToElement = null
|
||||
schemaRootElement = null
|
||||
}
|
||||
|
||||
def 'SEC-2139: named-security-filter are all defined and ordered properly'() {
|
||||
setup:
|
||||
def expectedFilters = (EnumSet.allOf(SecurityFilters) as List).sort { it.order }
|
||||
when:
|
||||
def nsf = schemaRootElement.simpleType.find { it.@name == 'named-security-filter' }
|
||||
def nsfValues = nsf.children().children().collect { c ->
|
||||
Enum.valueOf(SecurityFilters, c.@value.toString())
|
||||
}
|
||||
then:
|
||||
expectedFilters == nsfValues
|
||||
}
|
||||
|
||||
def 'SEC-2139: 3.1.x named-security-filter are all defined and ordered properly'() {
|
||||
setup:
|
||||
def expectedFilters = [
|
||||
"FIRST",
|
||||
"CHANNEL_FILTER",
|
||||
"SECURITY_CONTEXT_FILTER",
|
||||
"CONCURRENT_SESSION_FILTER",
|
||||
"LOGOUT_FILTER",
|
||||
"X509_FILTER",
|
||||
"PRE_AUTH_FILTER",
|
||||
"CAS_FILTER",
|
||||
"FORM_LOGIN_FILTER",
|
||||
"OPENID_FILTER",
|
||||
"LOGIN_PAGE_FILTER",
|
||||
"DIGEST_AUTH_FILTER",
|
||||
"BASIC_AUTH_FILTER",
|
||||
"REQUEST_CACHE_FILTER",
|
||||
"SERVLET_API_SUPPORT_FILTER",
|
||||
"JAAS_API_SUPPORT_FILTER",
|
||||
"REMEMBER_ME_FILTER",
|
||||
"ANONYMOUS_FILTER",
|
||||
"SESSION_MANAGEMENT_FILTER",
|
||||
"EXCEPTION_TRANSLATION_FILTER",
|
||||
"FILTER_SECURITY_INTERCEPTOR",
|
||||
"SWITCH_USER_FILTER",
|
||||
"LAST"
|
||||
].collect {
|
||||
Enum.valueOf(SecurityFilters, it)
|
||||
}
|
||||
def schema31xRootElement = new XmlSlurper().parse(schema31xDocument)
|
||||
when:
|
||||
def nsf = schema31xRootElement.simpleType.find { it.@name == 'named-security-filter' }
|
||||
def nsfValues = nsf.children().children().collect { c ->
|
||||
Enum.valueOf(SecurityFilters, c.@value.toString())
|
||||
}
|
||||
then:
|
||||
expectedFilters == nsfValues
|
||||
}
|
||||
|
||||
/**
|
||||
* This will check to ensure that the expected number of xsd documents are found to ensure that we are validating
|
||||
* against the current xsd document. If this test fails, all that is needed is to update the schemaDocument
|
||||
* and the expected size for this test.
|
||||
* @return
|
||||
*/
|
||||
def 'the latest schema is being validated'() {
|
||||
when: 'all the schemas are found'
|
||||
def schemas = schemaDocument.getParentFile().list().findAll { it.endsWith('.xsd') }
|
||||
then: 'the count is equal to 12, if not then schemaDocument needs updated'
|
||||
schemas.size() == 12
|
||||
}
|
||||
|
||||
/**
|
||||
* This uses a naming convention for the ids of the appendix to ensure that the entire appendix is documented.
|
||||
* The naming convention for the ids is documented in {@link Element#getIds()}.
|
||||
* @return
|
||||
*/
|
||||
def 'the entire schema is included in the appendix documentation'() {
|
||||
setup: 'get all the documented ids and the expected ids'
|
||||
def documentedIds = []
|
||||
reference.eachLine { line ->
|
||||
if(line.matches("\\[\\[(nsa-.*)\\]\\]")) {
|
||||
documentedIds.add(line.substring(2,line.length() - 2))
|
||||
}
|
||||
}
|
||||
when: 'the schema is compared to the appendix documentation'
|
||||
def expectedIds = [] as Set
|
||||
elementNameToElement*.value*.ids*.each { expectedIds.addAll it }
|
||||
documentedIds.removeAll ignoredIds
|
||||
expectedIds.removeAll ignoredIds
|
||||
def undocumentedIds = (expectedIds - documentedIds)
|
||||
def shouldNotBeDocumented = (documentedIds - expectedIds)
|
||||
then: 'all the elements and attributes are documented'
|
||||
shouldNotBeDocumented.empty
|
||||
undocumentedIds.empty
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that any element that has children or parents contains a section that has links pointing to that
|
||||
* documentation.
|
||||
* @return
|
||||
*/
|
||||
def 'validate parents and children are linked in the appendix documentation'() {
|
||||
when: "get all the links for each element's children and parents"
|
||||
def docAttrNameToChildren = [:]
|
||||
def docAttrNameToParents = [:]
|
||||
|
||||
def currentDocAttrNameToElmt
|
||||
def docAttrName
|
||||
|
||||
reference.eachLine { line ->
|
||||
if(line.matches('^\\[\\[.*\\]\\]$')) {
|
||||
def id = line.substring(2,line.length() - 2)
|
||||
if(id.endsWith("-children")) {
|
||||
docAttrName = id.substring(0,id.length() - 9)
|
||||
currentDocAttrNameToElmt = docAttrNameToChildren
|
||||
} else if(id.endsWith("-parents")) {
|
||||
docAttrName = id.substring(0,id.length() - 8)
|
||||
currentDocAttrNameToElmt = docAttrNameToParents
|
||||
} else if(docAttrName && !id.startsWith(docAttrName)) {
|
||||
currentDocAttrNameToElmt = null
|
||||
docAttrName = null
|
||||
}
|
||||
}
|
||||
|
||||
if(docAttrName) {
|
||||
def expression = '^\\* <<(nsa-.*),.*>>$'
|
||||
if(line.matches(expression)) {
|
||||
String elmtId = line.replaceAll(expression, '$1')
|
||||
currentDocAttrNameToElmt.get(docAttrName, []).add(elmtId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def schemaAttrNameToParents = [:]
|
||||
def schemaAttrNameToChildren = [:]
|
||||
elementNameToElement.each { entry ->
|
||||
def key = 'nsa-'+entry.key
|
||||
if(ignoredIds.contains(key)) {
|
||||
return
|
||||
}
|
||||
def parentIds = entry.value.allParentElmts.values()*.id.findAll { !ignoredIds.contains(it) }.sort()
|
||||
if(parentIds) {
|
||||
schemaAttrNameToParents.put(key,parentIds)
|
||||
}
|
||||
def childIds = entry.value.allChildElmts.values()*.id.findAll { !ignoredIds.contains(it) }.sort()
|
||||
if(childIds) {
|
||||
schemaAttrNameToChildren.put(key,childIds)
|
||||
}
|
||||
}
|
||||
then: "the expected parents and children are all documented"
|
||||
schemaAttrNameToChildren.sort() == docAttrNameToChildren.sort()
|
||||
schemaAttrNameToParents.sort() == docAttrNameToParents.sort()
|
||||
}
|
||||
|
||||
/**
|
||||
* This test checks each xsd element and ensures there is documentation for it.
|
||||
* @return
|
||||
*/
|
||||
def 'entire xsd is documented'() {
|
||||
when: "validate that the entire xsd contains documentation"
|
||||
def notDocElmtIds = elementNameToElement.values().findAll {
|
||||
!it.desc.text() && !ignoredIds.contains(it.id)
|
||||
}*.id.sort().join("\n")
|
||||
def notDocAttrIds = elementNameToElement.values()*.attrs.flatten().findAll {
|
||||
!it.desc.text() && !ignoredIds.contains(it.id)
|
||||
}*.id.sort().join("\n")
|
||||
then: "all the elements and attributes have some documentation"
|
||||
!notDocElmtIds
|
||||
!notDocAttrIds
|
||||
}
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
package org.springframework.security.config.http
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException
|
||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
class AccessDeniedConfigTests extends AbstractHttpConfigTests {
|
||||
def invalidAccessDeniedUrlIsDetected() {
|
||||
when:
|
||||
httpAutoConfig() {
|
||||
'access-denied-handler'('error-page':'noLeadingSlash')
|
||||
}
|
||||
createAppContext();
|
||||
then:
|
||||
thrown(BeanCreationException)
|
||||
}
|
||||
|
||||
def accessDeniedHandlerIsSetCorectly() {
|
||||
httpAutoConfig() {
|
||||
'access-denied-handler'(ref: 'adh')
|
||||
}
|
||||
bean('adh', AccessDeniedHandlerImpl)
|
||||
createAppContext();
|
||||
|
||||
def filter = getFilter(ExceptionTranslationFilter.class);
|
||||
def adh = appContext.getBean("adh");
|
||||
|
||||
expect:
|
||||
filter.accessDeniedHandler == adh
|
||||
}
|
||||
|
||||
def void accessDeniedHandlerPageAndRefAreMutuallyExclusive() {
|
||||
when:
|
||||
httpAutoConfig {
|
||||
'access-denied-handler'('error-page': '/go-away', ref: 'adh')
|
||||
}
|
||||
createAppContext();
|
||||
bean('adh', AccessDeniedHandlerImpl)
|
||||
then:
|
||||
thrown(BeanDefinitionParsingException)
|
||||
}
|
||||
}
|
||||
-342
@@ -1,342 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* 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.http
|
||||
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
import spock.lang.Unroll
|
||||
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.authority.AuthorityUtils
|
||||
import org.springframework.security.core.context.SecurityContextImpl
|
||||
import org.springframework.security.web.access.AccessDeniedHandler
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
|
||||
import org.springframework.security.web.csrf.CsrfFilter
|
||||
import org.springframework.security.web.csrf.CsrfToken
|
||||
import org.springframework.security.web.csrf.CsrfTokenRepository
|
||||
import org.springframework.security.web.csrf.DefaultCsrfToken
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||
import org.springframework.web.servlet.support.RequestDataValueProcessor
|
||||
|
||||
import static org.mockito.Matchers.*
|
||||
import static org.mockito.Mockito.*
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class CsrfConfigTests extends AbstractHttpConfigTests {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest()
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
MockFilterChain chain = new MockFilterChain()
|
||||
|
||||
@Unroll
|
||||
def 'csrf is enabled by default'() {
|
||||
setup:
|
||||
httpAutoConfig {
|
||||
}
|
||||
createAppContext()
|
||||
when:
|
||||
request.method = httpMethod
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == httpStatus
|
||||
where:
|
||||
httpMethod | httpStatus
|
||||
'POST' | HttpServletResponse.SC_FORBIDDEN
|
||||
'PUT' | HttpServletResponse.SC_FORBIDDEN
|
||||
'PATCH' | HttpServletResponse.SC_FORBIDDEN
|
||||
'DELETE' | HttpServletResponse.SC_FORBIDDEN
|
||||
'INVALID' | HttpServletResponse.SC_FORBIDDEN
|
||||
'GET' | HttpServletResponse.SC_OK
|
||||
'HEAD' | HttpServletResponse.SC_OK
|
||||
'TRACE' | HttpServletResponse.SC_OK
|
||||
'OPTIONS' | HttpServletResponse.SC_OK
|
||||
}
|
||||
|
||||
def 'csrf disabled'() {
|
||||
when:
|
||||
httpAutoConfig { csrf(disabled:true) }
|
||||
createAppContext()
|
||||
then:
|
||||
!getFilter(CsrfFilter)
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def 'csrf defaults'() {
|
||||
setup:
|
||||
httpAutoConfig { 'csrf'() }
|
||||
createAppContext()
|
||||
when:
|
||||
request.method = httpMethod
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == httpStatus
|
||||
where:
|
||||
httpMethod | httpStatus
|
||||
'POST' | HttpServletResponse.SC_FORBIDDEN
|
||||
'PUT' | HttpServletResponse.SC_FORBIDDEN
|
||||
'PATCH' | HttpServletResponse.SC_FORBIDDEN
|
||||
'DELETE' | HttpServletResponse.SC_FORBIDDEN
|
||||
'INVALID' | HttpServletResponse.SC_FORBIDDEN
|
||||
'GET' | HttpServletResponse.SC_OK
|
||||
'HEAD' | HttpServletResponse.SC_OK
|
||||
'TRACE' | HttpServletResponse.SC_OK
|
||||
'OPTIONS' | HttpServletResponse.SC_OK
|
||||
}
|
||||
|
||||
def 'csrf default creates CsrfRequestDataValueProcessor'() {
|
||||
when:
|
||||
httpAutoConfig { 'csrf'() }
|
||||
createAppContext()
|
||||
then:
|
||||
appContext.getBean("requestDataValueProcessor",RequestDataValueProcessor)
|
||||
}
|
||||
|
||||
def 'csrf custom AccessDeniedHandler'() {
|
||||
setup:
|
||||
httpAutoConfig {
|
||||
'access-denied-handler'(ref:'adh')
|
||||
'csrf'()
|
||||
}
|
||||
mockBean(AccessDeniedHandler,'adh')
|
||||
createAppContext()
|
||||
AccessDeniedHandler adh = appContext.getBean(AccessDeniedHandler)
|
||||
request.method = "POST"
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
verify(adh).handle(any(HttpServletRequest),any(HttpServletResponse),any(AccessDeniedException))
|
||||
response.status == HttpServletResponse.SC_OK // our mock doesn't do anything
|
||||
}
|
||||
|
||||
def "csrf disables posts for RequestCache"() {
|
||||
setup:
|
||||
httpAutoConfig {
|
||||
'csrf'('token-repository-ref':'repo')
|
||||
'intercept-url'(pattern:"/**",access:'ROLE_USER')
|
||||
}
|
||||
mockBean(CsrfTokenRepository,'repo')
|
||||
createAppContext()
|
||||
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
|
||||
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
|
||||
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
|
||||
when(repo.generateToken(any(HttpServletRequest))).thenReturn(token)
|
||||
request.setParameter(token.parameterName,token.token)
|
||||
request.servletPath = "/some-url"
|
||||
request.requestURI = "/some-url"
|
||||
request.method = "POST"
|
||||
when: "CSRF passes and our session times out"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to the login page"
|
||||
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
|
||||
response.redirectedUrl == "http://localhost/login"
|
||||
when: "authenticate successfully"
|
||||
response = new MockHttpServletResponse()
|
||||
request = new MockHttpServletRequest(session: request.session)
|
||||
request.servletPath = "/login"
|
||||
request.setParameter(token.parameterName,token.token)
|
||||
request.setParameter("username","user")
|
||||
request.setParameter("password","password")
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default success because we don't want csrf attempts made prior to authentication to pass"
|
||||
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
|
||||
response.redirectedUrl == "/"
|
||||
}
|
||||
|
||||
def "csrf enables gets for RequestCache"() {
|
||||
setup:
|
||||
httpAutoConfig {
|
||||
'csrf'('token-repository-ref':'repo')
|
||||
'intercept-url'(pattern:"/**",access:'ROLE_USER')
|
||||
}
|
||||
mockBean(CsrfTokenRepository,'repo')
|
||||
createAppContext()
|
||||
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
|
||||
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
|
||||
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
|
||||
when(repo.generateToken(any(HttpServletRequest))).thenReturn(token)
|
||||
request.setParameter(token.parameterName,token.token)
|
||||
request.servletPath = "/some-url"
|
||||
request.requestURI = "/some-url"
|
||||
request.method = "GET"
|
||||
when: "CSRF passes and our session times out"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to the login page"
|
||||
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
|
||||
response.redirectedUrl == "http://localhost/login"
|
||||
when: "authenticate successfully"
|
||||
response = new MockHttpServletResponse()
|
||||
request = new MockHttpServletRequest(session: request.session)
|
||||
request.servletPath = "/login"
|
||||
request.setParameter(token.parameterName,token.token)
|
||||
request.setParameter("username","user")
|
||||
request.setParameter("password","password")
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to original URL since it was a GET"
|
||||
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
|
||||
response.redirectedUrl == "http://localhost/some-url"
|
||||
}
|
||||
|
||||
def "SEC-2422: csrf expire CSRF token and session-management invalid-session-url"() {
|
||||
setup:
|
||||
httpAutoConfig {
|
||||
'csrf'()
|
||||
'session-management'('invalid-session-url': '/error/sessionError')
|
||||
}
|
||||
createAppContext()
|
||||
request.setParameter("_csrf","abc")
|
||||
request.method = "POST"
|
||||
when: "No existing expected CsrfToken (session times out) and a POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to the session timeout page page"
|
||||
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
|
||||
response.redirectedUrl == "/error/sessionError"
|
||||
when: "Existing expected CsrfToken and a POST (invalid token provided)"
|
||||
response = new MockHttpServletResponse()
|
||||
request = new MockHttpServletRequest(session: request.session, method:'POST')
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "Access Denied occurs"
|
||||
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||
}
|
||||
|
||||
def "csrf requireCsrfProtectionMatcher"() {
|
||||
setup:
|
||||
httpAutoConfig { 'csrf'('request-matcher-ref':'matcher') }
|
||||
mockBean(RequestMatcher,'matcher')
|
||||
createAppContext()
|
||||
request.method = 'POST'
|
||||
RequestMatcher matcher = appContext.getBean("matcher",RequestMatcher)
|
||||
when:
|
||||
when(matcher.matches(any(HttpServletRequest))).thenReturn(false)
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == HttpServletResponse.SC_OK
|
||||
when:
|
||||
when(matcher.matches(any(HttpServletRequest))).thenReturn(true)
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||
}
|
||||
|
||||
def "csrf csrfTokenRepository default delays save"() {
|
||||
setup:
|
||||
httpAutoConfig {
|
||||
}
|
||||
createAppContext()
|
||||
request.method = "GET"
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == HttpServletResponse.SC_OK
|
||||
request.getSession(false) == null
|
||||
}
|
||||
|
||||
def "csrf csrfTokenRepository"() {
|
||||
setup:
|
||||
httpAutoConfig { 'csrf'('token-repository-ref':'repo') }
|
||||
mockBean(CsrfTokenRepository,'repo')
|
||||
createAppContext()
|
||||
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
|
||||
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
|
||||
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
|
||||
request.setParameter(token.parameterName,token.token)
|
||||
request.method = "POST"
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == HttpServletResponse.SC_OK
|
||||
when:
|
||||
request.setParameter(token.parameterName,token.token+"INVALID")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == HttpServletResponse.SC_FORBIDDEN
|
||||
}
|
||||
|
||||
def "csrf clears on login"() {
|
||||
setup:
|
||||
httpAutoConfig { 'csrf'('token-repository-ref':'repo') }
|
||||
mockBean(CsrfTokenRepository,'repo')
|
||||
createAppContext()
|
||||
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
|
||||
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
|
||||
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
|
||||
when(repo.generateToken(any(HttpServletRequest))).thenReturn(token)
|
||||
request.setParameter(token.parameterName,token.token)
|
||||
request.method = "POST"
|
||||
request.setParameter("username","user")
|
||||
request.setParameter("password","password")
|
||||
request.servletPath = "/login"
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
verify(repo, atLeastOnce()).saveToken(eq(null),any(HttpServletRequest), any(HttpServletResponse))
|
||||
}
|
||||
|
||||
def "csrf clears on logout"() {
|
||||
setup:
|
||||
httpAutoConfig { 'csrf'('token-repository-ref':'repo') }
|
||||
mockBean(CsrfTokenRepository,'repo')
|
||||
createAppContext()
|
||||
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
|
||||
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
|
||||
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
|
||||
request.setParameter(token.parameterName,token.token)
|
||||
request.method = "POST"
|
||||
request.servletPath = "/logout"
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
verify(repo).saveToken(eq(null),any(HttpServletRequest), any(HttpServletResponse))
|
||||
}
|
||||
|
||||
def "SEC-2495: csrf disables logout on GET"() {
|
||||
setup:
|
||||
httpAutoConfig { 'csrf'() }
|
||||
createAppContext()
|
||||
login()
|
||||
request.method = "GET"
|
||||
request.requestURI = "/logout"
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
getAuthentication(request) != null
|
||||
}
|
||||
|
||||
|
||||
def login(String username="user", String role="ROLE_USER") {
|
||||
login(new UsernamePasswordAuthenticationToken(username, null, AuthorityUtils.createAuthorityList(role)))
|
||||
}
|
||||
|
||||
def login(Authentication auth) {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository()
|
||||
HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response)
|
||||
repo.loadContext(requestResponseHolder)
|
||||
repo.saveContext(new SecurityContextImpl(authentication:auth), requestResponseHolder.request, requestResponseHolder.response)
|
||||
}
|
||||
|
||||
def getAuthentication(HttpServletRequest request) {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository()
|
||||
HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response)
|
||||
repo.loadContext(requestResponseHolder)?.authentication
|
||||
}
|
||||
}
|
||||
-153
@@ -1,153 +0,0 @@
|
||||
package org.springframework.security.config.http
|
||||
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.web.WebAttributes
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
class FormLoginBeanDefinitionParserTests extends AbstractHttpConfigTests {
|
||||
|
||||
def 'form-login default login page'() {
|
||||
setup:
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET',requestURI:'/login')
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
MockFilterChain chain = new MockFilterChain()
|
||||
httpAutoConfig {
|
||||
csrf(disabled:true)
|
||||
}
|
||||
createAppContext()
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
|
||||
<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
}
|
||||
|
||||
def 'form-login default login page custom attributes'() {
|
||||
setup:
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET',requestURI:'/login')
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
MockFilterChain chain = new MockFilterChain()
|
||||
httpAutoConfig {
|
||||
'form-login'('login-processing-url':'/login_custom','username-parameter':'custom_user','password-parameter':'custom_password')
|
||||
csrf(disabled:true)
|
||||
}
|
||||
createAppContext()
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.custom_user.focus();'>
|
||||
<h3>Login with Username and Password</h3><form name='f' action='/login_custom' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='custom_user' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='custom_password'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
}
|
||||
|
||||
def 'openid-login default login page'() {
|
||||
setup:
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET',requestURI:'/login')
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
MockFilterChain chain = new MockFilterChain()
|
||||
httpAutoConfig {
|
||||
'openid-login'()
|
||||
csrf(disabled:true)
|
||||
}
|
||||
createAppContext()
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
|
||||
<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form><h3>Login with OpenID Identity</h3><form name='oidf' action='/login/openid' method='POST'>
|
||||
<table>
|
||||
<tr><td>Identity:</td><td><input type='text' size='30' name='openid_identifier'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
}
|
||||
|
||||
def 'openid-login default login page custom attributes'() {
|
||||
setup:
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET',requestURI:'/login')
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
MockFilterChain chain = new MockFilterChain()
|
||||
httpAutoConfig {
|
||||
'openid-login'('login-processing-url':'/login_custom')
|
||||
csrf(disabled:true)
|
||||
}
|
||||
createAppContext()
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
|
||||
<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form><h3>Login with OpenID Identity</h3><form name='oidf' action='/login_custom' method='POST'>
|
||||
<table>
|
||||
<tr><td>Identity:</td><td><input type='text' size='30' name='openid_identifier'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
}
|
||||
|
||||
def 'form-login forward authentication failure handler'() {
|
||||
setup:
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(method:'POST',servletPath:'/login')
|
||||
request.setParameter("username", "bob")
|
||||
request.setParameter("password", "invalidpassword")
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
MockFilterChain chain = new MockFilterChain()
|
||||
httpAutoConfig {
|
||||
'form-login'('authentication-failure-forward-url':'/failure_forward_url')
|
||||
csrf(disabled:true)
|
||||
}
|
||||
createAppContext()
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getStatus() == 200
|
||||
response.forwardedUrl == "/failure_forward_url"
|
||||
request.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) != null;
|
||||
}
|
||||
|
||||
def 'form-login forward authentication success handler'() {
|
||||
setup:
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(method:'POST',servletPath:'/login')
|
||||
request.setParameter("username", "bob")
|
||||
request.setParameter("password", "bobspassword")
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
MockFilterChain chain = new MockFilterChain()
|
||||
httpAutoConfig {
|
||||
'form-login'('authentication-success-forward-url':'/success_forward_url')
|
||||
csrf(disabled:true)
|
||||
}
|
||||
createAppContext()
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getStatus() == 200
|
||||
response.forwardedUrl == "/success_forward_url"
|
||||
}
|
||||
}
|
||||
-161
@@ -1,161 +0,0 @@
|
||||
package org.springframework.security.config.http
|
||||
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.util.FieldUtils
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
|
||||
|
||||
import spock.lang.Unroll;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
class FormLoginConfigTests extends AbstractHttpConfigTests {
|
||||
|
||||
def formLoginWithNoLoginPageAddsDefaultLoginPageFilter() {
|
||||
httpAutoConfig('ant') {
|
||||
form-login()
|
||||
}
|
||||
createAppContext()
|
||||
filtersMatchExpectedAutoConfigList();
|
||||
}
|
||||
|
||||
def 'Form login alwaysUseDefaultTarget sets correct property'() {
|
||||
xml.http {
|
||||
'form-login'('default-target-url':'/default', 'always-use-default-target': 'true')
|
||||
}
|
||||
createAppContext()
|
||||
def filter = getFilter(UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
expect:
|
||||
FieldUtils.getFieldValue(filter, 'successHandler.defaultTargetUrl') == '/default';
|
||||
FieldUtils.getFieldValue(filter, 'successHandler.alwaysUseDefaultTargetUrl');
|
||||
}
|
||||
|
||||
def 'form-login attributes support SpEL'() {
|
||||
setup:
|
||||
def spelUrl = '#{T(org.springframework.security.config.http.WebConfigUtilsTest).URL}'
|
||||
def expectedUrl = WebConfigUtilsTest.URL
|
||||
when:
|
||||
xml.http {
|
||||
'form-login'('default-target-url': spelUrl , 'authentication-failure-url': spelUrl, 'login-page': spelUrl)
|
||||
}
|
||||
createAppContext()
|
||||
def unPwdFilter = getFilter(UsernamePasswordAuthenticationFilter)
|
||||
def exTransFilter = getFilter(ExceptionTranslationFilter)
|
||||
|
||||
then:
|
||||
unPwdFilter.successHandler.defaultTargetUrl == expectedUrl
|
||||
unPwdFilter
|
||||
FieldUtils.getFieldValue(unPwdFilter, 'successHandler.defaultTargetUrl') == expectedUrl
|
||||
FieldUtils.getFieldValue(unPwdFilter, 'failureHandler.defaultFailureUrl') == expectedUrl
|
||||
FieldUtils.getFieldValue(exTransFilter, 'authenticationEntryPoint.loginFormUrl') == expectedUrl
|
||||
}
|
||||
|
||||
def invalidLoginPageIsDetected() {
|
||||
when:
|
||||
xml.http {
|
||||
'form-login'('login-page': 'noLeadingSlash')
|
||||
}
|
||||
createAppContext()
|
||||
|
||||
then:
|
||||
BeanCreationException e = thrown();
|
||||
}
|
||||
|
||||
def invalidDefaultTargetUrlIsDetected() {
|
||||
when:
|
||||
xml.http {
|
||||
'form-login'('default-target-url': 'noLeadingSlash')
|
||||
}
|
||||
createAppContext()
|
||||
|
||||
then:
|
||||
BeanCreationException e = thrown();
|
||||
}
|
||||
|
||||
def customSuccessAndFailureHandlersCanBeSetThroughTheNamespace() {
|
||||
xml.http {
|
||||
'form-login'('authentication-success-handler-ref': 'sh', 'authentication-failure-handler-ref':'fh')
|
||||
}
|
||||
bean('sh', SavedRequestAwareAuthenticationSuccessHandler.class.name)
|
||||
bean('fh', SimpleUrlAuthenticationFailureHandler.class.name)
|
||||
createAppContext()
|
||||
|
||||
def apf = getFilter(UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
expect:
|
||||
FieldUtils.getFieldValue(apf, "successHandler") == appContext.getBean("sh");
|
||||
FieldUtils.getFieldValue(apf, "failureHandler") == appContext.getBean("fh")
|
||||
}
|
||||
|
||||
def usernameAndPasswordParametersCanBeSetThroughNamespace() {
|
||||
xml.http {
|
||||
'form-login'('username-parameter': 'xname', 'password-parameter':'xpass')
|
||||
}
|
||||
createAppContext()
|
||||
|
||||
def apf = getFilter(UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
expect:
|
||||
apf.usernameParameter == 'xname';
|
||||
apf.passwordParameter == 'xpass'
|
||||
}
|
||||
|
||||
def 'SEC-2919: DefaultLoginGeneratingFilter should not be present if login-page="/login"'() {
|
||||
when:
|
||||
xml.http() {
|
||||
'form-login'('login-page':'/login')
|
||||
}
|
||||
createAppContext()
|
||||
|
||||
then:
|
||||
getFilter(DefaultLoginPageGeneratingFilter) == null
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def 'Form Login requires CSRF Token #csrfDisabled'(int status, boolean csrfDisabled) {
|
||||
setup:
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(method:'POST',servletPath:'/login')
|
||||
request.setParameter('username','user')
|
||||
request.setParameter('password','password')
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
MockFilterChain chain = new MockFilterChain()
|
||||
httpAutoConfig {
|
||||
'form-login'()
|
||||
csrf(disabled:csrfDisabled) {}
|
||||
}
|
||||
createAppContext()
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == status
|
||||
where:
|
||||
status | csrfDisabled
|
||||
HttpServletResponse.SC_FORBIDDEN | false
|
||||
HttpServletResponse.SC_MOVED_TEMPORARILY | true
|
||||
}
|
||||
|
||||
def 'SEC-3147: authentication-failure-url should be contained "error" parameter if login-page="/login"'() {
|
||||
xml.http {
|
||||
'form-login'('login-page':'/login')
|
||||
}
|
||||
createAppContext()
|
||||
|
||||
def apf = getFilter(UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
expect:
|
||||
apf.failureHandler.defaultFailureUrl == '/login?error'
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+4
@@ -131,6 +131,7 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
|
||||
when: 'user cannot access otheruser'
|
||||
request = new MockHttpServletRequest(method:'GET', servletPath : '/user/otheruser/abc')
|
||||
login(request, 'user', 'password')
|
||||
response = new MockHttpServletResponse()
|
||||
chain.reset()
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: 'The response is OK'
|
||||
@@ -138,6 +139,7 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
|
||||
when: 'user can access case insensitive URL'
|
||||
request = new MockHttpServletRequest(method:'GET', servletPath : '/USER/user/abc')
|
||||
login(request, 'user', 'password')
|
||||
response = new MockHttpServletResponse()
|
||||
chain.reset()
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: 'The response is OK'
|
||||
@@ -164,6 +166,7 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
|
||||
when: 'user cannot access otheruser'
|
||||
request = new MockHttpServletRequest(method:'GET', servletPath : '/user/otheruser/abc')
|
||||
login(request, 'user', 'password')
|
||||
response = new MockHttpServletResponse()
|
||||
chain.reset()
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: 'The response is OK'
|
||||
@@ -171,6 +174,7 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
|
||||
when: 'user can access case insensitive URL'
|
||||
request = new MockHttpServletRequest(method:'GET', servletPath : '/USER/user/abc')
|
||||
login(request, 'user', 'password')
|
||||
response = new MockHttpServletResponse()
|
||||
chain.reset()
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: 'The response is OK'
|
||||
|
||||
-560
@@ -1,560 +0,0 @@
|
||||
package org.springframework.security.config.websocket
|
||||
|
||||
import static org.mockito.Mockito.*
|
||||
|
||||
import org.springframework.beans.BeansException
|
||||
import org.springframework.beans.factory.config.BeanDefinition
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition
|
||||
import org.springframework.core.MethodParameter
|
||||
import org.springframework.core.task.SyncTaskExecutor
|
||||
import org.springframework.http.server.ServerHttpRequest
|
||||
import org.springframework.http.server.ServerHttpResponse
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.messaging.MessageDeliveryException
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping
|
||||
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor
|
||||
import org.springframework.messaging.simp.SimpMessageType
|
||||
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler
|
||||
import org.springframework.messaging.support.ChannelInterceptor
|
||||
import org.springframework.messaging.support.GenericMessage
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.access.expression.SecurityExpressionOperations;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||
import org.springframework.security.config.AbstractXmlConfigTests
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.messaging.access.expression.DefaultMessageSecurityExpressionHandler;
|
||||
import org.springframework.security.messaging.access.expression.MessageSecurityExpressionRoot;
|
||||
import org.springframework.security.web.csrf.CsrfToken
|
||||
import org.springframework.security.web.csrf.DefaultCsrfToken
|
||||
import org.springframework.security.web.csrf.InvalidCsrfTokenException
|
||||
import org.springframework.stereotype.Controller
|
||||
import org.springframework.util.AntPathMatcher
|
||||
import org.springframework.web.servlet.HandlerMapping
|
||||
import org.springframework.web.socket.WebSocketHandler
|
||||
import org.springframework.web.socket.server.HandshakeFailureException
|
||||
import org.springframework.web.socket.server.HandshakeHandler
|
||||
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor
|
||||
import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler
|
||||
import org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler
|
||||
import org.springframework.web.socket.sockjs.transport.handler.SockJsWebSocketHandler
|
||||
|
||||
import spock.lang.Unroll
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class WebSocketMessageBrokerConfigTests extends AbstractXmlConfigTests {
|
||||
Authentication messageUser = new TestingAuthenticationToken('user','pass','ROLE_USER')
|
||||
boolean useSockJS = false
|
||||
CsrfToken csrfToken = new DefaultCsrfToken('headerName', 'paramName', 'token')
|
||||
|
||||
def cleanup() {
|
||||
SecurityContextHolder.clearContext()
|
||||
}
|
||||
|
||||
def 'websocket with no id automatically integrates with clientInboundChannel'() {
|
||||
setup:
|
||||
websocket {
|
||||
'intercept-message'(pattern:'/permitAll',access:'permitAll')
|
||||
'intercept-message'(pattern:'/denyAll',access:'denyAll')
|
||||
}
|
||||
|
||||
|
||||
when: 'message is sent to the denyAll endpoint'
|
||||
clientInboundChannel.send(message('/denyAll'))
|
||||
|
||||
then: 'access is denied to the denyAll endpoint'
|
||||
def e = thrown(MessageDeliveryException)
|
||||
e.cause instanceof AccessDeniedException
|
||||
|
||||
and: 'access is granted to the permitAll endpoint'
|
||||
clientInboundChannel.send(message('/permitAll'))
|
||||
}
|
||||
|
||||
def 'anonymous authentication supported'() {
|
||||
setup:
|
||||
websocket {
|
||||
'intercept-message'(pattern:'/permitAll',access:'permitAll')
|
||||
'intercept-message'(pattern:'/denyAll',access:'denyAll')
|
||||
}
|
||||
messageUser = null
|
||||
|
||||
when: 'message is sent to the permitAll endpoint with no user'
|
||||
clientInboundChannel.send(message('/permitAll'))
|
||||
|
||||
then: 'access is granted'
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "message type - #type"(SimpMessageType type) {
|
||||
setup:
|
||||
websocket {
|
||||
'intercept-message'('type': type.toString(), access:'permitAll')
|
||||
'intercept-message'(pattern:'/**', access:'denyAll')
|
||||
}
|
||||
messageUser = null
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(type)
|
||||
if(SimpMessageType.CONNECT == type) {
|
||||
headers.setNativeHeader(csrfToken.headerName, csrfToken.token)
|
||||
}
|
||||
Message message = message(headers, '/permitAll')
|
||||
|
||||
when: 'message is sent to the permitAll endpoint with no user'
|
||||
clientInboundChannel.send(message)
|
||||
|
||||
then: 'access is granted'
|
||||
noExceptionThrown()
|
||||
|
||||
where:
|
||||
type << SimpMessageType.values()
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "pattern and message type - #type"(SimpMessageType type) {
|
||||
setup:
|
||||
websocket {
|
||||
'intercept-message'(pattern: '/permitAll', 'type': type.toString(), access:'permitAll')
|
||||
'intercept-message'(pattern:'/**', access:'denyAll')
|
||||
}
|
||||
|
||||
when: 'message is sent to the permitAll endpoint with no user'
|
||||
clientInboundChannel.send(message('/permitAll', type))
|
||||
|
||||
then: 'access is granted'
|
||||
noExceptionThrown()
|
||||
|
||||
when: 'message sent to other message type'
|
||||
clientInboundChannel.send(message('/permitAll', SimpMessageType.UNSUBSCRIBE))
|
||||
|
||||
then: 'does not match'
|
||||
MessageDeliveryException e = thrown()
|
||||
e.cause instanceof AccessDeniedException
|
||||
|
||||
when: 'message is sent to other pattern'
|
||||
clientInboundChannel.send(message('/other', type))
|
||||
|
||||
then: 'does not match'
|
||||
MessageDeliveryException eOther = thrown()
|
||||
eOther.cause instanceof AccessDeniedException
|
||||
|
||||
where:
|
||||
type << [SimpMessageType.MESSAGE, SimpMessageType.SUBSCRIBE]
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "intercept-message with invalid type and pattern - #type"(SimpMessageType type) {
|
||||
when:
|
||||
websocket {
|
||||
'intercept-message'(pattern : '/**', 'type': type.toString(), access:'permitAll')
|
||||
}
|
||||
then:
|
||||
thrown(BeanDefinitionParsingException)
|
||||
|
||||
where:
|
||||
type << [SimpMessageType.CONNECT, SimpMessageType.CONNECT_ACK, SimpMessageType.DISCONNECT, SimpMessageType.DISCONNECT_ACK, SimpMessageType.HEARTBEAT, SimpMessageType.OTHER, SimpMessageType.UNSUBSCRIBE ]
|
||||
}
|
||||
|
||||
def 'messages with no id automatically adds Authentication argument resolver'() {
|
||||
setup:
|
||||
def id = 'authenticationController'
|
||||
bean(id,MyController)
|
||||
bean('inPostProcessor',InboundExecutorPostProcessor)
|
||||
websocket {
|
||||
'intercept-message'(pattern:'/**',access:'permitAll')
|
||||
}
|
||||
|
||||
when: 'message is sent to the authentication endpoint'
|
||||
clientInboundChannel.send(message('/authentication'))
|
||||
|
||||
then: 'the AuthenticationPrincipal is resolved'
|
||||
def controller = appContext.getBean(id)
|
||||
controller.authenticationPrincipal == messageUser.name
|
||||
}
|
||||
|
||||
def 'messages of type CONNECT use CsrfTokenHandshakeInterceptor'() {
|
||||
setup:
|
||||
def id = 'authenticationController'
|
||||
bean(id,MyController)
|
||||
bean('inPostProcessor',InboundExecutorPostProcessor)
|
||||
websocket {
|
||||
'intercept-message'(pattern:'/**',access:'permitAll')
|
||||
}
|
||||
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT)
|
||||
Message<?> message = message(headers,'/authentication')
|
||||
WebSocketHttpRequestHandler handler = appContext.getBean(WebSocketHttpRequestHandler)
|
||||
MockHttpServletRequest request = new MockHttpServletRequest()
|
||||
String sessionAttr = "sessionAttr"
|
||||
request.getSession().setAttribute(sessionAttr,"sessionValue")
|
||||
|
||||
CsrfToken token = new DefaultCsrfToken("header", "param", "token")
|
||||
request.setAttribute(CsrfToken.name, token)
|
||||
|
||||
when:
|
||||
handler.handleRequest(request , new MockHttpServletResponse())
|
||||
TestHandshakeHandler handshakeHandler = appContext.getBean(TestHandshakeHandler)
|
||||
|
||||
then: 'CsrfToken is populated'
|
||||
handshakeHandler.attributes.get(CsrfToken.name) == token
|
||||
|
||||
and: 'Explicitly listed HandshakeInterceptor are not overridden'
|
||||
handshakeHandler.attributes.get(sessionAttr) == request.getSession().getAttribute(sessionAttr)
|
||||
}
|
||||
|
||||
def 'messages of type CONNECT use CsrfTokenHandshakeInterceptor with SockJS'() {
|
||||
setup:
|
||||
useSockJS = true
|
||||
def id = 'authenticationController'
|
||||
bean(id,MyController)
|
||||
bean('inPostProcessor',InboundExecutorPostProcessor)
|
||||
websocket {
|
||||
'intercept-message'(pattern:'/**',access:'permitAll')
|
||||
}
|
||||
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT)
|
||||
Message<?> message = message(headers,'/authentication')
|
||||
SockJsHttpRequestHandler handler = appContext.getBean(SockJsHttpRequestHandler)
|
||||
MockHttpServletRequest request = new MockHttpServletRequest()
|
||||
String sessionAttr = "sessionAttr"
|
||||
request.getSession().setAttribute(sessionAttr,"sessionValue")
|
||||
|
||||
CsrfToken token = new DefaultCsrfToken("header", "param", "token")
|
||||
request.setAttribute(CsrfToken.name, token)
|
||||
|
||||
request.setMethod("GET")
|
||||
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "/289/tpyx6mde/websocket")
|
||||
|
||||
when:
|
||||
handler.handleRequest(request , new MockHttpServletResponse())
|
||||
TestHandshakeHandler handshakeHandler = appContext.getBean(TestHandshakeHandler)
|
||||
|
||||
then: 'CsrfToken is populated'
|
||||
handshakeHandler.attributes?.get(CsrfToken.name) == token
|
||||
|
||||
and: 'Explicitly listed HandshakeInterceptor are not overridden'
|
||||
handshakeHandler.attributes?.get(sessionAttr) == request.getSession().getAttribute(sessionAttr)
|
||||
}
|
||||
|
||||
def 'messages of type CONNECT require valid CsrfToken'() {
|
||||
setup:
|
||||
def id = 'authenticationController'
|
||||
bean(id,MyController)
|
||||
bean('inPostProcessor',InboundExecutorPostProcessor)
|
||||
websocket {
|
||||
'intercept-message'(pattern:'/**',access:'permitAll')
|
||||
}
|
||||
|
||||
when: 'websocket of type CONNECTION is sent without CsrfTOken'
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT)
|
||||
Message<?> message = message(headers,'/authentication')
|
||||
clientInboundChannel.send(message)
|
||||
|
||||
then: 'CSRF Protection blocks the Message'
|
||||
MessageDeliveryException expected = thrown()
|
||||
expected.cause instanceof InvalidCsrfTokenException
|
||||
}
|
||||
|
||||
def 'messages of type CONNECT disabled valid CsrfToken'() {
|
||||
setup:
|
||||
def id = 'authenticationController'
|
||||
bean(id,MyController)
|
||||
bean('inPostProcessor',InboundExecutorPostProcessor)
|
||||
websocket('same-origin-disabled':true) {
|
||||
'intercept-message'(pattern:'/**',access:'permitAll')
|
||||
}
|
||||
|
||||
when: 'websocket of type CONNECTION is sent without CsrfTOken'
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT)
|
||||
Message<?> message = message(headers,'/authentication')
|
||||
clientInboundChannel.send(message)
|
||||
|
||||
then: 'CSRF Protection blocks the Message'
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
def 'websocket with no id does not override customArgumentResolvers'() {
|
||||
setup:
|
||||
def id = 'authenticationController'
|
||||
bean(id,MyController)
|
||||
bean('inPostProcessor',InboundExecutorPostProcessor)
|
||||
bean('mcar', MyCustomArgumentResolver)
|
||||
xml.'websocket:message-broker' {
|
||||
'websocket:transport' {}
|
||||
'websocket:stomp-endpoint'(path:'/app') {
|
||||
'websocket:handshake-handler'(ref:'testHandler') {}
|
||||
}
|
||||
'websocket:simple-broker'(prefix:"/queue, /topic"){}
|
||||
'websocket:argument-resolvers' {
|
||||
'b:ref'(bean:'mcar')
|
||||
}
|
||||
}
|
||||
websocket {
|
||||
'intercept-message'(pattern:'/**',access:'permitAll')
|
||||
}
|
||||
|
||||
when: 'websocket is sent to the myCustom endpoint'
|
||||
clientInboundChannel.send(message('/myCustom'))
|
||||
|
||||
then: 'myCustomArgument is resolved'
|
||||
def controller = appContext.getBean(id)
|
||||
controller.myCustomArgument!= null
|
||||
}
|
||||
|
||||
def 'websocket defaults pathMatcher'() {
|
||||
setup:
|
||||
bean('pathMatcher',AntPathMatcher.name,['.'])
|
||||
bean('testHandler', TestHandshakeHandler)
|
||||
xml.'websocket:message-broker'('path-matcher':'pathMatcher') {
|
||||
'websocket:transport' {}
|
||||
'websocket:stomp-endpoint'(path:'/app') {
|
||||
'websocket:handshake-handler'(ref:'testHandler') {}
|
||||
}
|
||||
'websocket:simple-broker'(prefix:"/queue, /topic"){}
|
||||
}
|
||||
xml.'websocket-message-broker' {
|
||||
'intercept-message'(pattern:'/denyAll.*',access:'denyAll')
|
||||
}
|
||||
createAppContext()
|
||||
|
||||
when: 'sent to denyAll.a'
|
||||
appContext.getBean(SimpAnnotationMethodMessageHandler)
|
||||
clientInboundChannel.send(message('/denyAll.a'))
|
||||
|
||||
then: 'access is denied'
|
||||
MessageDeliveryException expected = thrown()
|
||||
expected.cause instanceof AccessDeniedException
|
||||
|
||||
when: 'sent to denyAll.a.b'
|
||||
clientInboundChannel.send(message('/denyAll.a.b'))
|
||||
|
||||
then: 'access is allowed'
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
def 'websocket with id does not integrate with clientInboundChannel'() {
|
||||
setup:
|
||||
websocket([id:'inCsi']) {
|
||||
'intercept-message'(pattern:'/**',access:'denyAll')
|
||||
}
|
||||
|
||||
when:
|
||||
def success = clientInboundChannel.send(message('/denyAll'))
|
||||
|
||||
then:
|
||||
success
|
||||
|
||||
}
|
||||
|
||||
def 'websocket with id can be explicitly integrated with clientInboundChannel'() {
|
||||
setup: 'websocket security explicitly setup'
|
||||
xml.'websocket:message-broker' {
|
||||
'websocket:transport' {}
|
||||
'websocket:stomp-endpoint'(path:'/app') {
|
||||
'websocket:sockjs' {}
|
||||
}
|
||||
'websocket:simple-broker'(prefix:"/queue, /topic"){}
|
||||
'websocket:client-inbound-channel' {
|
||||
'websocket:interceptors' {
|
||||
'b:bean'(class:'org.springframework.security.messaging.context.SecurityContextChannelInterceptor'){}
|
||||
'b:ref'(bean:'inCsi'){}
|
||||
}
|
||||
}
|
||||
}
|
||||
xml.'websocket-message-broker'(id:'inCsi') {
|
||||
'intercept-message'(pattern:'/**',access:'denyAll')
|
||||
}
|
||||
createAppContext()
|
||||
|
||||
when:
|
||||
clientInboundChannel.send(message('/denyAll'))
|
||||
|
||||
then:
|
||||
def e = thrown(MessageDeliveryException)
|
||||
e.cause instanceof AccessDeniedException
|
||||
|
||||
}
|
||||
|
||||
def 'automatic integration with clientInboundChannel does not override exisiting websocket:interceptors'() {
|
||||
setup:
|
||||
mockBean(ChannelInterceptor,'mci')
|
||||
xml.'websocket:message-broker'('application-destination-prefix':'/app',
|
||||
'user-destination-prefix':'/user') {
|
||||
'websocket:transport' {}
|
||||
'websocket:stomp-endpoint'(path:'/foo') {
|
||||
'websocket:sockjs' {}
|
||||
}
|
||||
'websocket:simple-broker'(prefix:"/queue, /topic"){}
|
||||
'websocket:client-inbound-channel' {
|
||||
'websocket:interceptors' {
|
||||
'b:ref'(bean:'mci'){}
|
||||
}
|
||||
}
|
||||
}
|
||||
xml.'websocket-message-broker' {
|
||||
'intercept-message'(pattern:'/denyAll',access:'denyAll')
|
||||
'intercept-message'(pattern:'/permitAll',access:'permitAll')
|
||||
}
|
||||
createAppContext()
|
||||
ChannelInterceptor mci = appContext.getBean('mci')
|
||||
when:
|
||||
Message<?> message = message('/permitAll')
|
||||
clientInboundChannel.send(message)
|
||||
|
||||
then:
|
||||
verify(mci).preSend(message, clientInboundChannel) || true
|
||||
|
||||
}
|
||||
|
||||
def websocket(Map<String,Object> attrs=[:], Closure c) {
|
||||
bean('testHandler', TestHandshakeHandler)
|
||||
xml.'websocket:message-broker' {
|
||||
'websocket:transport' {}
|
||||
'websocket:stomp-endpoint'(path:'/app') {
|
||||
'websocket:handshake-handler'(ref:'testHandler') {}
|
||||
'websocket:handshake-interceptors' {
|
||||
'b:bean'('class':HttpSessionHandshakeInterceptor.name) {}
|
||||
}
|
||||
if(useSockJS) {
|
||||
'websocket:sockjs' {}
|
||||
}
|
||||
}
|
||||
'websocket:simple-broker'(prefix:"/queue, /topic"){}
|
||||
}
|
||||
xml.'websocket-message-broker'(attrs, c)
|
||||
createAppContext()
|
||||
}
|
||||
|
||||
def 'custom expressions'() {
|
||||
setup:
|
||||
bean('expressionHandler', DenyRobMessageSecurityExpressionHandler)
|
||||
websocket {
|
||||
'expression-handler' (ref: 'expressionHandler') {}
|
||||
'intercept-message'(pattern:'/**',access:'denyRob()')
|
||||
}
|
||||
|
||||
when: 'message is sent with user'
|
||||
clientInboundChannel.send(message('/message'))
|
||||
|
||||
then: 'access is allowed to custom expression'
|
||||
noExceptionThrown()
|
||||
|
||||
when:
|
||||
messageUser = new TestingAuthenticationToken('rob', 'pass', 'ROLE_USER')
|
||||
clientInboundChannel.send(message('/message'))
|
||||
|
||||
then:
|
||||
def e = thrown(MessageDeliveryException)
|
||||
e.cause instanceof AccessDeniedException
|
||||
}
|
||||
|
||||
def getClientInboundChannel() {
|
||||
appContext.getBean("clientInboundChannel")
|
||||
}
|
||||
|
||||
def message(String destination, SimpMessageType type=SimpMessageType.MESSAGE) {
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(type)
|
||||
message(headers, destination)
|
||||
}
|
||||
|
||||
def message(SimpMessageHeaderAccessor headers, String destination) {
|
||||
headers.sessionId = '123'
|
||||
headers.sessionAttributes = [:]
|
||||
headers.destination = destination
|
||||
if(messageUser != null) {
|
||||
headers.user = messageUser
|
||||
}
|
||||
if(csrfToken != null) {
|
||||
headers.sessionAttributes[CsrfToken.name] = csrfToken
|
||||
}
|
||||
new GenericMessage<String>("hi",headers.messageHeaders)
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class MyController {
|
||||
String authenticationPrincipal
|
||||
MyCustomArgument myCustomArgument
|
||||
|
||||
@MessageMapping('/authentication')
|
||||
public void authentication(@AuthenticationPrincipal String un) {
|
||||
this.authenticationPrincipal = un
|
||||
}
|
||||
|
||||
@MessageMapping('/myCustom')
|
||||
public void myCustom(MyCustomArgument myCustomArgument) {
|
||||
this.myCustomArgument = myCustomArgument
|
||||
}
|
||||
}
|
||||
|
||||
static class MyCustomArgument {
|
||||
MyCustomArgument(String notDefaultConstr) {}
|
||||
}
|
||||
|
||||
static class MyCustomArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
boolean supportsParameter(MethodParameter parameter) {
|
||||
parameter.parameterType.isAssignableFrom(MyCustomArgument)
|
||||
}
|
||||
|
||||
@Override
|
||||
Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
|
||||
new MyCustomArgument("")
|
||||
}
|
||||
}
|
||||
|
||||
static class TestHandshakeHandler implements HandshakeHandler {
|
||||
Map<String, Object> attributes;
|
||||
|
||||
boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws HandshakeFailureException {
|
||||
this.attributes = attributes
|
||||
if(wsHandler instanceof SockJsWebSocketHandler) {
|
||||
// work around SPR-12716
|
||||
SockJsWebSocketHandler sockJs = (SockJsWebSocketHandler) wsHandler;
|
||||
this.attributes = sockJs.sockJsSession.attributes
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the clientInboundChannel Executor to be synchronous
|
||||
*/
|
||||
static class InboundExecutorPostProcessor implements BeanDefinitionRegistryPostProcessor {
|
||||
|
||||
@Override
|
||||
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
|
||||
BeanDefinition inbound = registry.getBeanDefinition("clientInboundChannel")
|
||||
inbound.getConstructorArgumentValues().addIndexedArgumentValue(0, new RootBeanDefinition(SyncTaskExecutor));
|
||||
}
|
||||
|
||||
@Override
|
||||
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static class DenyRobMessageSecurityExpressionHandler extends DefaultMessageSecurityExpressionHandler<Object> {
|
||||
@Override
|
||||
protected SecurityExpressionOperations createSecurityExpressionRoot(
|
||||
Authentication authentication,
|
||||
Message<Object> invocation) {
|
||||
return new MessageSecurityExpressionRoot(authentication, invocation) {
|
||||
public boolean denyRob() {
|
||||
Authentication auth = getAuthentication();
|
||||
return auth != null && !"rob".equals(auth.getName());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public class MockEventListener<T extends ApplicationEvent>
|
||||
implements ApplicationListener<T> {
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ public class AuthenticationManagerBuilderTests {
|
||||
verify(opp, never()).postProcess(provider);
|
||||
}
|
||||
|
||||
// https://github.com/SpringSource/spring-security-javaconfig/issues/132
|
||||
// https://github.com/spring-projects/spring-security-javaconfig/issues/132
|
||||
@Test
|
||||
public void customAuthenticationEventPublisherWithWeb() throws Exception {
|
||||
ObjectPostProcessor<Object> opp = mock(ObjectPostProcessor.class);
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ public class Issue50Tests {
|
||||
}
|
||||
|
||||
@Test
|
||||
// https://github.com/SpringSource/spring-security-javaconfig/issues/50
|
||||
// https://github.com/spring-projects/spring-security-javaconfig/issues/50
|
||||
public void loadWhenGlobalMethodSecurityConfigurationThenAuthenticationManagerLazy() {
|
||||
// no exception
|
||||
}
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.method.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class MethodSecurityServiceConfig {
|
||||
@Bean
|
||||
MethodSecurityService service() {
|
||||
return new MethodSecurityServiceImpl();
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.method.configuration;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SecurityTestExecutionListeners
|
||||
public class NamespaceGlobalMethodSecurityExpressionHandlerTests {
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired(required = false)
|
||||
private MethodSecurityService service;
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenUsingCustomPermissionEvaluatorThenPreAuthorizesAccordingly() {
|
||||
this.spring.register(CustomAccessDecisionManagerConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThatCode(() -> this.service.hasPermission("granted"))
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatThrownBy(() -> this.service.hasPermission("denied"))
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenUsingCustomPermissionEvaluatorThenPostAuthorizesAccordingly() {
|
||||
this.spring.register(CustomAccessDecisionManagerConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThatCode(() -> this.service.postHasPermission("granted"))
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatThrownBy(() -> this.service.postHasPermission("denied"))
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class CustomAccessDecisionManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected MethodSecurityExpressionHandler createExpressionHandler() {
|
||||
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||
|
||||
expressionHandler.setPermissionEvaluator(new PermissionEvaluator() {
|
||||
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
|
||||
return "granted".equals(targetDomainObject);
|
||||
}
|
||||
|
||||
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
});
|
||||
|
||||
return expressionHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
+514
@@ -0,0 +1,514 @@
|
||||
/*
|
||||
* 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.method.configuration;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.annotation.*;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.access.intercept.AfterInvocationManager;
|
||||
import org.springframework.security.access.intercept.RunAsManager;
|
||||
import org.springframework.security.access.intercept.RunAsManagerImpl;
|
||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;
|
||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor;
|
||||
import org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;
|
||||
import org.springframework.security.access.method.AbstractMethodSecurityMetadataSource;
|
||||
import org.springframework.security.access.method.MethodSecurityMetadataSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SecurityTestExecutionListeners
|
||||
public class NamespaceGlobalMethodSecurityTests {
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired(required = false)
|
||||
private MethodSecurityService service;
|
||||
|
||||
// --- access-decision-manager-ref ---
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenCustomAccessDecisionManagerThenAuthorizes() {
|
||||
this.spring.register(CustomAccessDecisionManagerConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThatThrownBy(() -> this.service.preAuthorize())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
|
||||
assertThatThrownBy(() -> this.service.secured())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||
public static class CustomAccessDecisionManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
|
||||
@Override
|
||||
protected AccessDecisionManager accessDecisionManager() {
|
||||
return new DenyAllAccessDecisionManager();
|
||||
}
|
||||
|
||||
public static class DenyAllAccessDecisionManager implements AccessDecisionManager {
|
||||
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) {
|
||||
throw new AccessDeniedException("Always Denied");
|
||||
}
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return true;
|
||||
}
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- after-invocation-provider
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenCustomAfterInvocationManagerThenAuthorizes() {
|
||||
this.spring.register(CustomAfterInvocationManagerConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThatThrownBy(() -> this.service.preAuthorizePermitAll())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class CustomAfterInvocationManagerConfig
|
||||
extends GlobalMethodSecurityConfiguration {
|
||||
|
||||
@Override
|
||||
protected AfterInvocationManager afterInvocationManager() {
|
||||
return new AfterInvocationManagerStub();
|
||||
}
|
||||
|
||||
public static class AfterInvocationManagerStub implements AfterInvocationManager {
|
||||
public Object decide(Authentication authentication,
|
||||
Object object,
|
||||
Collection<ConfigAttribute> attributes,
|
||||
Object returnedObject) throws AccessDeniedException {
|
||||
|
||||
throw new AccessDeniedException("custom AfterInvocationManager");
|
||||
}
|
||||
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return true;
|
||||
}
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- authentication-manager-ref ---
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenCustomAuthenticationManagerThenAuthorizes() {
|
||||
this.spring.register(CustomAuthenticationConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThatThrownBy(() -> this.service.preAuthorize())
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class CustomAuthenticationConfig extends GlobalMethodSecurityConfiguration {
|
||||
|
||||
@Override
|
||||
public MethodInterceptor methodSecurityInterceptor() throws Exception {
|
||||
MethodInterceptor interceptor = super.methodSecurityInterceptor();
|
||||
((MethodSecurityInterceptor) interceptor).setAlwaysReauthenticate(true);
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthenticationManager authenticationManager() {
|
||||
return (authentication) -> {
|
||||
throw new UnsupportedOperationException();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// --- jsr250-annotations ---
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenJsr250EnabledThenAuthorizes() {
|
||||
this.spring.register(Jsr250Config.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThatCode(() -> this.service.preAuthorize())
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatCode(() -> this.service.secured())
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatThrownBy(() -> this.service.jsr250())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
|
||||
assertThatCode(() -> this.service.jsr250PermitAll())
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(jsr250Enabled = true)
|
||||
@Configuration
|
||||
public static class Jsr250Config {
|
||||
|
||||
}
|
||||
|
||||
// --- metadata-source-ref ---
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenCustomMethodSecurityMetadataSourceThenAuthorizes() {
|
||||
this.spring.register(CustomMethodSecurityMetadataSourceConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThatThrownBy(() -> this.service.preAuthorize())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
|
||||
assertThatThrownBy(() -> this.service.secured())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
|
||||
assertThatThrownBy(() -> this.service.jsr250())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity
|
||||
public static class CustomMethodSecurityMetadataSourceConfig extends GlobalMethodSecurityConfiguration {
|
||||
|
||||
@Override
|
||||
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
|
||||
return new AbstractMethodSecurityMetadataSource() {
|
||||
public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
|
||||
// require ROLE_NOBODY for any method on MethodSecurityService interface
|
||||
return MethodSecurityService.class.isAssignableFrom(targetClass) ?
|
||||
Arrays.asList(new SecurityConfig("ROLE_NOBODY")) :
|
||||
Collections.emptyList();
|
||||
}
|
||||
public Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// --- mode ---
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void contextRefreshWhenUsingAspectJThenAutowire() throws Exception {
|
||||
this.spring.register(AspectJModeConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThat(this.spring.getContext().getBean(Class.forName("org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect"))).isNotNull();
|
||||
assertThat(this.spring.getContext().getBean(AspectJMethodSecurityInterceptor.class)).isNotNull();
|
||||
|
||||
//TODO diagnose why aspectj isn't weaving method security advice around MethodSecurityServiceImpl
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(mode = AdviceMode.ASPECTJ, securedEnabled = true)
|
||||
public static class AspectJModeConfig {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextRefreshWhenUsingAspectJAndCustomGlobalMethodSecurityConfigurationThenAutowire()
|
||||
throws Exception {
|
||||
|
||||
this.spring.register(AspectJModeExtendsGMSCConfig.class).autowire();
|
||||
|
||||
assertThat(this.spring.getContext().getBean(Class.forName("org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect"))).isNotNull();
|
||||
assertThat(this.spring.getContext().getBean(AspectJMethodSecurityInterceptor.class)).isNotNull();
|
||||
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(mode = AdviceMode.ASPECTJ)
|
||||
public static class AspectJModeExtendsGMSCConfig extends GlobalMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
// --- order ---
|
||||
|
||||
private static class AdvisorOrderConfig
|
||||
implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
private static class ExceptingInterceptor implements MethodInterceptor {
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) {
|
||||
throw new UnsupportedOperationException("Deny All");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
BeanDefinitionBuilder advice = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(ExceptingInterceptor.class);
|
||||
registry.registerBeanDefinition("exceptingInterceptor",
|
||||
advice.getBeanDefinition());
|
||||
|
||||
BeanDefinitionBuilder advisor = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(MethodSecurityMetadataSourceAdvisor.class);
|
||||
advisor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
advisor.addConstructorArgValue("exceptingInterceptor");
|
||||
advisor.addConstructorArgReference("methodSecurityMetadataSource");
|
||||
advisor.addConstructorArgValue("methodSecurityMetadataSource");
|
||||
advisor.addPropertyValue("order", 0);
|
||||
registry.registerBeanDefinition("exceptingAdvisor",
|
||||
advisor.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenOrderSpecifiedThenConfigured() {
|
||||
this.spring.register(CustomOrderConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThat(this.spring.getContext()
|
||||
.getBean("metaDataSourceAdvisor", MethodSecurityMetadataSourceAdvisor.class)
|
||||
.getOrder())
|
||||
.isEqualTo(-135);
|
||||
|
||||
assertThatThrownBy(() -> this.service.jsr250())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(order = -135, jsr250Enabled = true)
|
||||
@Import(AdvisorOrderConfig.class)
|
||||
public static class CustomOrderConfig {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenOrderUnspecifiedThenConfiguredToLowestPrecedence() {
|
||||
this.spring.register(DefaultOrderConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThat(this.spring.getContext()
|
||||
.getBean("metaDataSourceAdvisor", MethodSecurityMetadataSourceAdvisor.class)
|
||||
.getOrder())
|
||||
.isEqualTo(Ordered.LOWEST_PRECEDENCE);
|
||||
|
||||
assertThatThrownBy(() -> this.service.jsr250())
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(jsr250Enabled = true)
|
||||
@Import(AdvisorOrderConfig.class)
|
||||
public static class DefaultOrderConfig {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenOrderUnspecifiedAndCustomGlobalMethodSecurityConfigurationThenConfiguredToLowestPrecedence() {
|
||||
this.spring.register(DefaultOrderExtendsMethodSecurityConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThat(this.spring.getContext()
|
||||
.getBean("metaDataSourceAdvisor", MethodSecurityMetadataSourceAdvisor.class)
|
||||
.getOrder())
|
||||
.isEqualTo(Ordered.LOWEST_PRECEDENCE);
|
||||
|
||||
assertThatThrownBy(() -> this.service.jsr250())
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(jsr250Enabled = true)
|
||||
@Import(AdvisorOrderConfig.class)
|
||||
public static class DefaultOrderExtendsMethodSecurityConfig extends GlobalMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
// --- pre-post-annotations ---
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenPrePostEnabledThenPreAuthorizes() {
|
||||
this.spring.register(PreAuthorizeConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThatCode(() -> this.service.secured())
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatCode(() -> this.service.jsr250())
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatThrownBy(() -> this.service.preAuthorize())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class PreAuthorizeConfig {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenPrePostEnabledAndCustomGlobalMethodSecurityConfigurationThenPreAuthorizes() {
|
||||
this.spring.register(PreAuthorizeExtendsGMSCConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThatCode(() -> this.service.secured())
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatCode(() -> this.service.jsr250())
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatThrownBy(() -> this.service.preAuthorize())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class PreAuthorizeExtendsGMSCConfig extends GlobalMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
// --- proxy-target-class ---
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenProxyTargetClassThenDoesNotWireToInterface() {
|
||||
this.spring.register(ProxyTargetClassConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
// make sure service was actually proxied
|
||||
assertThat(this.service.getClass().getInterfaces())
|
||||
.doesNotContain(MethodSecurityService.class);
|
||||
|
||||
assertThatThrownBy(() -> this.service.preAuthorize())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(proxyTargetClass = true, prePostEnabled = true)
|
||||
public static class ProxyTargetClassConfig {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenDefaultProxyThenWiresToInterface() {
|
||||
this.spring.register(DefaultProxyConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThat(this.service.getClass().getInterfaces())
|
||||
.contains(MethodSecurityService.class);
|
||||
|
||||
assertThatThrownBy(() -> this.service.preAuthorize())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class DefaultProxyConfig {
|
||||
}
|
||||
|
||||
// --- run-as-manager-ref ---
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenCustomRunAsManagerThenRunAsWrapsAuthentication() {
|
||||
this.spring.register(CustomRunAsManagerConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThat(service.runAs().getAuthorities())
|
||||
.anyMatch(authority -> "ROLE_RUN_AS_SUPER".equals(authority.getAuthority()));
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true)
|
||||
public static class CustomRunAsManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
|
||||
@Override
|
||||
protected RunAsManager runAsManager() {
|
||||
RunAsManagerImpl runAsManager = new RunAsManagerImpl();
|
||||
runAsManager.setKey("some key");
|
||||
return runAsManager;
|
||||
}
|
||||
}
|
||||
|
||||
// --- secured-annotation ---
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenSecuredEnabledThenSecures() {
|
||||
this.spring.register(SecuredConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThatThrownBy(() -> this.service.secured())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
|
||||
assertThatCode(() -> this.service.securedUser())
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatCode(() -> this.service.preAuthorize())
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatCode(() -> this.service.jsr250())
|
||||
.doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true)
|
||||
public static class SecuredConfig {
|
||||
}
|
||||
|
||||
// --- unsorted ---
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenMissingEnableAnnotationThenShowsHelpfulError() {
|
||||
assertThatThrownBy(() ->
|
||||
this.spring.register(ExtendsNoEnableAnntotationConfig.class).autowire())
|
||||
.hasStackTraceContaining(EnableGlobalMethodSecurity.class.getName() + " is required");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class ExtendsNoEnableAnntotationConfig
|
||||
extends GlobalMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void methodSecurityWhenImportingGlobalMethodSecurityConfigurationSubclassThenAuthorizes() {
|
||||
this.spring.register(ImportSubclassGMSCConfig.class, MethodSecurityServiceConfig.class).autowire();
|
||||
|
||||
assertThatCode(() -> this.service.secured())
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatCode(() -> this.service.jsr250())
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatThrownBy(() -> this.service.preAuthorize())
|
||||
.isInstanceOf(AccessDeniedException.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(PreAuthorizeExtendsGMSCConfig.class)
|
||||
public static class ImportSubclassGMSCConfig {
|
||||
}
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.sec2758;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.security.RolesAllowed;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author Josh Cummings
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SecurityTestExecutionListeners
|
||||
public class Sec2758Tests {
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
MockMvc mvc;
|
||||
|
||||
@Autowired(required = false)
|
||||
Service service;
|
||||
|
||||
@WithMockUser(authorities = "CUSTOM")
|
||||
@Test
|
||||
public void requestWhenNullifyingRolePrefixThenPassivityRestored() throws Exception {
|
||||
|
||||
this.spring.register(SecurityConfig.class).autowire();
|
||||
|
||||
this.mvc.perform(get("/")).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@WithMockUser(authorities = "CUSTOM")
|
||||
@Test
|
||||
public void methodSecurityWhenNullifyingRolePrefixThenPassivityRestored() {
|
||||
|
||||
this.spring.register(SecurityConfig.class).autowire();
|
||||
|
||||
assertThatCode(() -> service.doJsr250())
|
||||
.doesNotThrowAnyException();
|
||||
|
||||
assertThatCode(() -> service.doPreAuthorize())
|
||||
.doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled=true, jsr250Enabled = true)
|
||||
static class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@RestController
|
||||
static class RootController {
|
||||
@GetMapping("/")
|
||||
public String ok() { return "ok"; }
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().access("hasAnyRole('CUSTOM')");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Service service() {
|
||||
return new Service();
|
||||
}
|
||||
|
||||
@Bean
|
||||
static DefaultRolesPrefixPostProcessor defaultRolesPrefixPostProcessor() {
|
||||
return new DefaultRolesPrefixPostProcessor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class Service {
|
||||
@PreAuthorize("hasRole('CUSTOM')")
|
||||
public void doPreAuthorize() {}
|
||||
|
||||
@RolesAllowed("CUSTOM")
|
||||
public void doJsr250() {}
|
||||
}
|
||||
|
||||
static class DefaultRolesPrefixPostProcessor implements BeanPostProcessor, PriorityOrdered {
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if(bean instanceof Jsr250MethodSecurityMetadataSource) {
|
||||
((Jsr250MethodSecurityMetadataSource) bean).setDefaultRolePrefix(null);
|
||||
}
|
||||
if(bean instanceof DefaultMethodSecurityExpressionHandler) {
|
||||
((DefaultMethodSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
|
||||
}
|
||||
if(bean instanceof DefaultWebSecurityExpressionHandler) {
|
||||
((DefaultWebSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return PriorityOrdered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
-27
@@ -17,6 +17,10 @@ package org.springframework.security.config.annotation.web;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
@@ -34,15 +38,16 @@ import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
||||
import org.springframework.security.web.context.request.async.SecurityContextCallableProcessingInterceptor;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.accept.ContentNegotiationStrategy;
|
||||
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
|
||||
import org.springframework.web.context.request.async.WebAsyncManager;
|
||||
import org.springframework.web.context.request.async.WebAsyncUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -53,7 +58,8 @@ import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.ThrowableAssert.catchThrowable;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
@@ -65,13 +71,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
* @author Rob Winch
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
@PrepareForTest({WebAsyncManager.class})
|
||||
@RunWith(PowerMockRunner.class)
|
||||
public class WebSecurityConfigurerAdapterTests {
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private FilterChainProxy springSecurityFilterChain;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@@ -108,7 +113,21 @@ public class WebSecurityConfigurerAdapterTests {
|
||||
public void loadConfigWhenDefaultConfigThenWebAsyncManagerIntegrationFilterAdded() throws Exception {
|
||||
this.spring.register(WebAsyncPopulatedByDefaultConfig.class).autowire();
|
||||
|
||||
assertThat(this.findFilter(WebAsyncManagerIntegrationFilter.class, this.springSecurityFilterChain)).isNotNull();
|
||||
WebAsyncManager webAsyncManager = mock(WebAsyncManager.class);
|
||||
|
||||
this.mockMvc.perform(get("/").requestAttr(WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE, webAsyncManager));
|
||||
|
||||
ArgumentCaptor<CallableProcessingInterceptor> callableProcessingInterceptorArgCaptor =
|
||||
ArgumentCaptor.forClass(CallableProcessingInterceptor.class);
|
||||
verify(webAsyncManager, atLeastOnce()).registerCallableInterceptor(any(), callableProcessingInterceptorArgCaptor.capture());
|
||||
|
||||
CallableProcessingInterceptor callableProcessingInterceptor =
|
||||
callableProcessingInterceptorArgCaptor.getAllValues().stream()
|
||||
.filter(e -> SecurityContextCallableProcessingInterceptor.class.isAssignableFrom(e.getClass()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
assertThat(callableProcessingInterceptor).isNotNull();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@@ -212,7 +231,7 @@ public class WebSecurityConfigurerAdapterTests {
|
||||
public void loadConfigWhenUserDetailsServiceHasCircularReferenceThenStillLoads() throws Exception {
|
||||
this.spring.register(RequiresUserDetailsServiceConfig.class, UserDetailsServiceConfig.class).autowire();
|
||||
|
||||
MyFilter myFilter = this.findFilter(MyFilter.class, this.springSecurityFilterChain);
|
||||
MyFilter myFilter = this.spring.getContext().getBean(MyFilter.class);
|
||||
|
||||
Throwable thrown = catchThrowable(() -> myFilter.userDetailsService.loadUserByUsername("user") );
|
||||
assertThat(thrown).isNull();
|
||||
@@ -335,22 +354,4 @@ public class WebSecurityConfigurerAdapterTests {
|
||||
@Order
|
||||
static class LowestPriorityWebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
}
|
||||
|
||||
private <T extends Filter> T findFilter(Class<T> filterType, FilterChainProxy filterChainProxy) {
|
||||
return this.findFilter(filterType, filterChainProxy, 0);
|
||||
}
|
||||
|
||||
private <T extends Filter> T findFilter(Class<T> filterType, FilterChainProxy filterChainProxy, int filterChainIndex) {
|
||||
if (filterChainIndex >= filterChainProxy.getFilterChains().size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Filter filter = filterChainProxy.getFilterChains().get(filterChainIndex).getFilters()
|
||||
.stream()
|
||||
.filter(f -> f.getClass().isAssignableFrom(filterType))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
return (T) filter;
|
||||
}
|
||||
}
|
||||
|
||||
+12
-26
@@ -25,19 +25,20 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.ThrowableAssert.catchThrowable;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@@ -51,9 +52,6 @@ public class HttpConfigurationTests {
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private FilterChainProxy springSecurityFilterChain;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@@ -90,19 +88,25 @@ public class HttpConfigurationTests {
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/SpringSource/spring-security-javaconfig/issues/104
|
||||
// https://github.com/spring-projects/spring-security-javaconfig/issues/104
|
||||
@Test
|
||||
public void configureWhenAddFilterCasAuthenticationFilterThenFilterAdded() throws Exception {
|
||||
CasAuthenticationFilterConfig.CAS_AUTHENTICATION_FILTER = spy(new CasAuthenticationFilter());
|
||||
this.spring.register(CasAuthenticationFilterConfig.class).autowire();
|
||||
|
||||
assertThat(this.findFilter(CasAuthenticationFilter.class, this.springSecurityFilterChain)).isNotNull();
|
||||
this.mockMvc.perform(get("/"));
|
||||
|
||||
verify(CasAuthenticationFilterConfig.CAS_AUTHENTICATION_FILTER).doFilter(
|
||||
any(ServletRequest.class), any(ServletResponse.class), any(FilterChain.class));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CasAuthenticationFilterConfig extends WebSecurityConfigurerAdapter {
|
||||
static CasAuthenticationFilter CAS_AUTHENTICATION_FILTER;
|
||||
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.addFilter(new CasAuthenticationFilter());
|
||||
.addFilter(CAS_AUTHENTICATION_FILTER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,22 +135,4 @@ public class HttpConfigurationTests {
|
||||
.httpBasic();
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Filter> T findFilter(Class<T> filterType, FilterChainProxy filterChainProxy) {
|
||||
return this.findFilter(filterType, filterChainProxy, 0);
|
||||
}
|
||||
|
||||
private <T extends Filter> T findFilter(Class<T> filterType, FilterChainProxy filterChainProxy, int filterChainIndex) {
|
||||
if (filterChainIndex >= filterChainProxy.getFilterChains().size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Filter filter = filterChainProxy.getFilterChains().get(filterChainIndex).getFilters()
|
||||
.stream()
|
||||
.filter(f -> f.getClass().isAssignableFrom(filterType))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
return (T) filter;
|
||||
}
|
||||
}
|
||||
|
||||
+582
@@ -0,0 +1,582 @@
|
||||
/*
|
||||
* 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.builders;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.jaas.JaasAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.context.NullSecurityContextRepository;
|
||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <http> attributes are present in Java Config.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class NamespaceHttpTests {
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test // http@access-decision-manager-ref
|
||||
public void configureWhenAccessDecisionManagerSetThenVerifyUse() throws Exception {
|
||||
AccessDecisionManagerRefConfig.ACCESS_DECISION_MANAGER = mock(AccessDecisionManager.class);
|
||||
when(AccessDecisionManagerRefConfig.ACCESS_DECISION_MANAGER.supports(FilterInvocation.class)).thenReturn(true);
|
||||
when(AccessDecisionManagerRefConfig.ACCESS_DECISION_MANAGER.supports(any(ConfigAttribute.class))).thenReturn(true);
|
||||
|
||||
this.spring.register(AccessDecisionManagerRefConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(get("/"));
|
||||
|
||||
verify(AccessDecisionManagerRefConfig.ACCESS_DECISION_MANAGER, times(1)).decide(any(Authentication.class), any(), anyCollection());
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class AccessDecisionManagerRefConfig extends WebSecurityConfigurerAdapter {
|
||||
static AccessDecisionManager ACCESS_DECISION_MANAGER;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().permitAll()
|
||||
.accessDecisionManager(ACCESS_DECISION_MANAGER);
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@access-denied-page
|
||||
public void configureWhenAccessDeniedPageSetAndRequestForbiddenThenForwardedToAccessDeniedPage() throws Exception {
|
||||
this.spring.register(AccessDeniedPageConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(get("/admin").with(user(PasswordEncodedUser.user())))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(forwardedUrl("/AccessDeniedPage"));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class AccessDeniedPageConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.antMatchers("/admin").hasRole("ADMIN")
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
.accessDeniedPage("/AccessDeniedPage");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@authentication-manager-ref
|
||||
public void configureWhenAuthenticationManagerProvidedThenVerifyUse() throws Exception {
|
||||
AuthenticationManagerRefConfig.AUTHENTICATION_MANAGER = mock(AuthenticationManager.class);
|
||||
this.spring.register(AuthenticationManagerRefConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin());
|
||||
|
||||
verify(AuthenticationManagerRefConfig.AUTHENTICATION_MANAGER, times(1)).authenticate(any(Authentication.class));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class AuthenticationManagerRefConfig extends WebSecurityConfigurerAdapter {
|
||||
static AuthenticationManager AUTHENTICATION_MANAGER;
|
||||
|
||||
@Override
|
||||
protected AuthenticationManager authenticationManager() throws Exception {
|
||||
return AUTHENTICATION_MANAGER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin();
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@create-session=always
|
||||
public void configureWhenSessionCreationPolicyAlwaysThenSessionCreatedOnRequest() throws Exception {
|
||||
this.spring.register(CreateSessionAlwaysConfig.class).autowire();
|
||||
|
||||
MvcResult mvcResult = this.mockMvc.perform(get("/")).andReturn();
|
||||
HttpSession session = mvcResult.getRequest().getSession(false);
|
||||
|
||||
assertThat(session).isNotNull();
|
||||
assertThat(session.isNew()).isTrue();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CreateSessionAlwaysConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().permitAll()
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@create-session=stateless
|
||||
public void configureWhenSessionCreationPolicyStatelessThenSessionNotCreatedOnRequest() throws Exception {
|
||||
this.spring.register(CreateSessionStatelessConfig.class).autowire();
|
||||
|
||||
MvcResult mvcResult = this.mockMvc.perform(get("/")).andReturn();
|
||||
HttpSession session = mvcResult.getRequest().getSession(false);
|
||||
|
||||
assertThat(session).isNull();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CreateSessionStatelessConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().permitAll()
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@create-session=ifRequired
|
||||
public void configureWhenSessionCreationPolicyIfRequiredThenSessionCreatedWhenRequiredOnRequest() throws Exception {
|
||||
this.spring.register(IfRequiredConfig.class).autowire();
|
||||
|
||||
MvcResult mvcResult = this.mockMvc.perform(get("/unsecure")).andReturn();
|
||||
HttpSession session = mvcResult.getRequest().getSession(false);
|
||||
|
||||
assertThat(session).isNull();
|
||||
|
||||
mvcResult = this.mockMvc.perform(formLogin()).andReturn();
|
||||
session = mvcResult.getRequest().getSession(false);
|
||||
|
||||
assertThat(session).isNotNull();
|
||||
assertThat(session.isNew()).isTrue();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class IfRequiredConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.antMatchers("/unsecure").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
|
||||
.and()
|
||||
.formLogin();
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@create-session=never
|
||||
public void configureWhenSessionCreationPolicyNeverThenSessionNotCreatedOnRequest() throws Exception {
|
||||
this.spring.register(CreateSessionNeverConfig.class).autowire();
|
||||
|
||||
MvcResult mvcResult = this.mockMvc.perform(get("/")).andReturn();
|
||||
HttpSession session = mvcResult.getRequest().getSession(false);
|
||||
|
||||
assertThat(session).isNull();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CreateSessionNeverConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().anonymous()
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@entry-point-ref
|
||||
public void configureWhenAuthenticationEntryPointSetAndRequestUnauthorizedThenRedirectedToAuthenticationEntryPoint() throws Exception {
|
||||
this.spring.register(EntryPointRefConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(get("/"))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrlPattern("**/entry-point"));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class EntryPointRefConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/entry-point"))
|
||||
.and()
|
||||
.formLogin();
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@jaas-api-provision
|
||||
public void configureWhenJaasApiIntegrationFilterAddedThenJaasSubjectObtained() throws Exception {
|
||||
LoginContext loginContext = mock(LoginContext.class);
|
||||
when(loginContext.getSubject()).thenReturn(new Subject());
|
||||
|
||||
JaasAuthenticationToken authenticationToken = mock(JaasAuthenticationToken.class);
|
||||
when(authenticationToken.isAuthenticated()).thenReturn(true);
|
||||
when(authenticationToken.getLoginContext()).thenReturn(loginContext);
|
||||
|
||||
this.spring.register(JaasApiProvisionConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(get("/").with(authentication(authenticationToken)));
|
||||
|
||||
verify(loginContext, times(1)).getSubject();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class JaasApiProvisionConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.addFilter(new JaasApiIntegrationFilter());
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@realm
|
||||
public void configureWhenHttpBasicAndRequestUnauthorizedThenReturnWWWAuthenticateWithRealm() throws Exception {
|
||||
this.spring.register(RealmConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(get("/"))
|
||||
.andExpect(status().isUnauthorized())
|
||||
.andExpect(header().string("WWW-Authenticate", "Basic realm=\"RealmConfig\""));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RealmConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.httpBasic()
|
||||
.realmName("RealmConfig");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@request-matcher-ref ant
|
||||
public void configureWhenAntPatternMatchingThenAntPathRequestMatcherUsed() throws Exception {
|
||||
this.spring.register(RequestMatcherAntConfig.class).autowire();
|
||||
|
||||
FilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);
|
||||
|
||||
assertThat(filterChainProxy.getFilterChains().get(0)).isInstanceOf(DefaultSecurityFilterChain.class);
|
||||
DefaultSecurityFilterChain securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains().get(0);
|
||||
assertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(AntPathRequestMatcher.class);
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RequestMatcherAntConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/api/**");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@request-matcher-ref regex
|
||||
public void configureWhenRegexPatternMatchingThenRegexRequestMatcherUsed() throws Exception {
|
||||
this.spring.register(RequestMatcherRegexConfig.class).autowire();
|
||||
|
||||
FilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);
|
||||
|
||||
assertThat(filterChainProxy.getFilterChains().get(0)).isInstanceOf(DefaultSecurityFilterChain.class);
|
||||
DefaultSecurityFilterChain securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains().get(0);
|
||||
assertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(RegexRequestMatcher.class);
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RequestMatcherRegexConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.regexMatcher("/regex/.*");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@request-matcher-ref
|
||||
public void configureWhenRequestMatcherProvidedThenRequestMatcherUsed() throws Exception {
|
||||
this.spring.register(RequestMatcherRefConfig.class).autowire();
|
||||
|
||||
FilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);
|
||||
|
||||
assertThat(filterChainProxy.getFilterChains().get(0)).isInstanceOf(DefaultSecurityFilterChain.class);
|
||||
DefaultSecurityFilterChain securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains().get(0);
|
||||
assertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(RequestMatcherRefConfig.MyRequestMatcher.class);
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RequestMatcherRefConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.requestMatcher(new MyRequestMatcher());
|
||||
}
|
||||
|
||||
static class MyRequestMatcher implements RequestMatcher {
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@security=none
|
||||
public void configureWhenIgnoredAntPatternsThenAntPathRequestMatcherUsedWithNoFilters() throws Exception {
|
||||
this.spring.register(SecurityNoneConfig.class).autowire();
|
||||
|
||||
FilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);
|
||||
|
||||
assertThat(filterChainProxy.getFilterChains().get(0)).isInstanceOf(DefaultSecurityFilterChain.class);
|
||||
DefaultSecurityFilterChain securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains().get(0);
|
||||
assertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(AntPathRequestMatcher.class);
|
||||
assertThat(((AntPathRequestMatcher) securityFilterChain.getRequestMatcher()).getPattern()).isEqualTo("/resources/**");
|
||||
assertThat(securityFilterChain.getFilters()).isEmpty();
|
||||
|
||||
assertThat(filterChainProxy.getFilterChains().get(1)).isInstanceOf(DefaultSecurityFilterChain.class);
|
||||
securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains().get(1);
|
||||
assertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(AntPathRequestMatcher.class);
|
||||
assertThat(((AntPathRequestMatcher) securityFilterChain.getRequestMatcher()).getPattern()).isEqualTo("/public/**");
|
||||
assertThat(securityFilterChain.getFilters()).isEmpty();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class SecurityNoneConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**", "/public/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@security-context-repository-ref
|
||||
public void configureWhenNullSecurityContextRepositoryThenSecurityContextNotSavedInSession() throws Exception {
|
||||
this.spring.register(SecurityContextRepoConfig.class).autowire();
|
||||
|
||||
MvcResult mvcResult = this.mockMvc.perform(formLogin()).andReturn();
|
||||
HttpSession session = mvcResult.getRequest().getSession(false);
|
||||
assertThat(session).isNull();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class SecurityContextRepoConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.securityContext()
|
||||
.securityContextRepository(new NullSecurityContextRepository())
|
||||
.and()
|
||||
.formLogin();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser(PasswordEncodedUser.user());
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@servlet-api-provision=false
|
||||
public void configureWhenServletApiDisabledThenRequestNotServletApiWrapper() throws Exception {
|
||||
this.spring.register(ServletApiProvisionConfig.class, MainController.class).autowire();
|
||||
|
||||
this.mockMvc.perform(get("/"));
|
||||
|
||||
assertThat(MainController.HTTP_SERVLET_REQUEST_TYPE).isNotInstanceOf(SecurityContextHolderAwareRequestWrapper.class);
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class ServletApiProvisionConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().permitAll()
|
||||
.and()
|
||||
.servletApi()
|
||||
.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@servlet-api-provision defaults to true
|
||||
public void configureWhenServletApiDefaultThenRequestIsServletApiWrapper() throws Exception {
|
||||
this.spring.register(ServletApiProvisionDefaultsConfig.class, MainController.class).autowire();
|
||||
|
||||
this.mockMvc.perform(get("/"));
|
||||
|
||||
assertThat(SecurityContextHolderAwareRequestWrapper.class).isAssignableFrom(MainController.HTTP_SERVLET_REQUEST_TYPE);
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class ServletApiProvisionDefaultsConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().permitAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class MainController {
|
||||
static Class<? extends HttpServletRequest> HTTP_SERVLET_REQUEST_TYPE;
|
||||
|
||||
@GetMapping("/")
|
||||
public String index(HttpServletRequest request) {
|
||||
HTTP_SERVLET_REQUEST_TYPE = request.getClass();
|
||||
return "index";
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@use-expressions=true
|
||||
public void configureWhenUseExpressionsEnabledThenExpressionBasedSecurityMetadataSource() throws Exception {
|
||||
this.spring.register(UseExpressionsConfig.class).autowire();
|
||||
|
||||
UseExpressionsConfig config = this.spring.getContext().getBean(UseExpressionsConfig.class);
|
||||
|
||||
assertThat(ExpressionBasedFilterInvocationSecurityMetadataSource.class)
|
||||
.isAssignableFrom(config.filterInvocationSecurityMetadataSourceType);
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class UseExpressionsConfig extends WebSecurityConfigurerAdapter {
|
||||
private Class<? extends FilterInvocationSecurityMetadataSource> filterInvocationSecurityMetadataSourceType;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.antMatchers("/users**", "/sessions/**").hasRole("USER")
|
||||
.antMatchers("/signup").permitAll()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(final WebSecurity web) throws Exception {
|
||||
super.init(web);
|
||||
final HttpSecurity http = this.getHttp();
|
||||
web.postBuildAction(() -> {
|
||||
FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
|
||||
UseExpressionsConfig.this.filterInvocationSecurityMetadataSourceType =
|
||||
securityInterceptor.getSecurityMetadataSource().getClass();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test // http@use-expressions=false
|
||||
public void configureWhenUseExpressionsDisabledThenDefaultSecurityMetadataSource() throws Exception {
|
||||
this.spring.register(DisableUseExpressionsConfig.class).autowire();
|
||||
|
||||
DisableUseExpressionsConfig config = this.spring.getContext().getBean(DisableUseExpressionsConfig.class);
|
||||
|
||||
assertThat(DefaultFilterInvocationSecurityMetadataSource.class)
|
||||
.isAssignableFrom(config.filterInvocationSecurityMetadataSourceType);
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class DisableUseExpressionsConfig extends WebSecurityConfigurerAdapter {
|
||||
private Class<? extends FilterInvocationSecurityMetadataSource> filterInvocationSecurityMetadataSourceType;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.apply(new UrlAuthorizationConfigurer<>(getApplicationContext())).getRegistry()
|
||||
.antMatchers("/users**", "/sessions/**").hasRole("USER")
|
||||
.antMatchers("/signup").hasRole("ANONYMOUS")
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(final WebSecurity web) throws Exception {
|
||||
super.init(web);
|
||||
final HttpSecurity http = this.getHttp();
|
||||
web.postBuildAction(() -> {
|
||||
FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
|
||||
DisableUseExpressionsConfig.this.filterInvocationSecurityMetadataSourceType =
|
||||
securityInterceptor.getSecurityMetadataSource().getClass();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.configuration;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
||||
import org.springframework.security.web.debug.DebugFilter;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
|
||||
/**
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class EnableWebSecurityTests {
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void configureWhenOverrideAuthenticationManagerBeanThenAuthenticationManagerBeanRegistered() throws Exception {
|
||||
this.spring.register(SecurityConfig.class).autowire();
|
||||
|
||||
AuthenticationManager authenticationManager = this.spring.getContext().getBean(AuthenticationManager.class);
|
||||
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"));
|
||||
assertThat(authentication.isAuthenticated()).isTrue();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser(PasswordEncodedUser.user());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.antMatchers("/*").hasRole("USER")
|
||||
.and()
|
||||
.formLogin();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadConfigWhenChildConfigExtendsSecurityConfigThenSecurityConfigInherited() throws Exception {
|
||||
this.spring.register(ChildSecurityConfig.class).autowire();
|
||||
this.spring.getContext().getBean("springSecurityFilterChain", DebugFilter.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ChildSecurityConfig extends DebugSecurityConfig {
|
||||
}
|
||||
|
||||
@EnableWebSecurity(debug=true)
|
||||
static class DebugSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenEnableWebMvcThenAuthenticationPrincipalResolvable() throws Exception {
|
||||
this.spring.register(AuthenticationPrincipalConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(get("/").with(authentication(new TestingAuthenticationToken("user1", "password"))))
|
||||
.andExpect(content().string("user1"));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@EnableWebMvc
|
||||
static class AuthenticationPrincipalConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class AuthController {
|
||||
|
||||
@GetMapping("/")
|
||||
String principal(@AuthenticationPrincipal String principal) {
|
||||
return principal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.configuration;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.annotation.OAuth2Client;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for {@link OAuth2ClientConfiguration}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class OAuth2ClientConfigurationTests {
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void requestWhenAuthorizedClientFoundThenOAuth2ClientArgumentsResolved() throws Exception {
|
||||
String clientRegistrationId = "client1";
|
||||
String principalName = "user1";
|
||||
|
||||
ClientRegistrationRepository clientRegistrationRepository = mock(ClientRegistrationRepository.class);
|
||||
ClientRegistration clientRegistration = ClientRegistration.withRegistrationId(clientRegistrationId)
|
||||
.clientId("client-id")
|
||||
.clientSecret("secret")
|
||||
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||
.redirectUriTemplate("{baseUrl}/client1")
|
||||
.scope("scope1", "scope2")
|
||||
.authorizationUri("https://provider.com/oauth2/auth")
|
||||
.tokenUri("https://provider.com/oauth2/token")
|
||||
.clientName("Client 1")
|
||||
.build();
|
||||
when(clientRegistrationRepository.findByRegistrationId(clientRegistrationId)).thenReturn(clientRegistration);
|
||||
|
||||
OAuth2AuthorizedClientService authorizedClientService = mock(OAuth2AuthorizedClientService.class);
|
||||
OAuth2AuthorizedClient authorizedClient = mock(OAuth2AuthorizedClient.class);
|
||||
when(authorizedClientService.loadAuthorizedClient(clientRegistrationId, principalName)).thenReturn(authorizedClient);
|
||||
|
||||
OAuth2AccessToken accessToken = mock(OAuth2AccessToken.class);
|
||||
when(authorizedClient.getAccessToken()).thenReturn(accessToken);
|
||||
|
||||
OAuth2ClientArgumentResolverConfig.CLIENT_REGISTRATION_REPOSITORY = clientRegistrationRepository;
|
||||
OAuth2ClientArgumentResolverConfig.AUTHORIZED_CLIENT_SERVICE = authorizedClientService;
|
||||
this.spring.register(OAuth2ClientArgumentResolverConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(get("/access-token").with(user(principalName)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("resolved"));
|
||||
this.mockMvc.perform(get("/authorized-client").with(user(principalName)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("resolved"));
|
||||
this.mockMvc.perform(get("/client-registration").with(user(principalName)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("resolved"));
|
||||
}
|
||||
|
||||
@EnableWebMvc
|
||||
@EnableWebSecurity
|
||||
static class OAuth2ClientArgumentResolverConfig extends WebSecurityConfigurerAdapter {
|
||||
static ClientRegistrationRepository CLIENT_REGISTRATION_REPOSITORY;
|
||||
static OAuth2AuthorizedClientService AUTHORIZED_CLIENT_SERVICE;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
|
||||
@RestController
|
||||
public class Controller {
|
||||
|
||||
@GetMapping("/access-token")
|
||||
public String accessToken(@OAuth2Client("client1") OAuth2AccessToken accessToken) {
|
||||
return accessToken != null ? "resolved" : "not-resolved";
|
||||
}
|
||||
|
||||
@GetMapping("/authorized-client")
|
||||
public String authorizedClient(@OAuth2Client("client1") OAuth2AuthorizedClient authorizedClient) {
|
||||
return authorizedClient != null ? "resolved" : "not-resolved";
|
||||
}
|
||||
|
||||
@GetMapping("/client-registration")
|
||||
public String clientRegistration(@OAuth2Client("client1") ClientRegistration clientRegistration) {
|
||||
return clientRegistration != null ? "resolved" : "not-resolved";
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ClientRegistrationRepository clientRegistrationRepository() {
|
||||
return CLIENT_REGISTRATION_REPOSITORY;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OAuth2AuthorizedClientService authorizedClientService() {
|
||||
return AUTHORIZED_CLIENT_SERVICE;
|
||||
}
|
||||
}
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.configuration;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class Sec2515Tests {
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
// SEC-2515
|
||||
@Test(expected = FatalBeanException.class)
|
||||
public void loadConfigWhenAuthenticationManagerNotConfiguredAndRegisterBeanThenThrowFatalBeanException() throws Exception {
|
||||
this.spring.register(StackOverflowSecurityConfig.class).autowire();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class StackOverflowSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = FatalBeanException.class)
|
||||
public void loadConfigWhenAuthenticationManagerNotConfiguredAndRegisterBeanCustomNameThenThrowFatalBeanException() throws Exception {
|
||||
this.spring.register(CustomBeanNameStackOverflowSecurityConfig.class).autowire();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomBeanNameStackOverflowSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
@Bean(name="custom")
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
|
||||
// SEC-2549
|
||||
@Test
|
||||
public void loadConfigWhenChildClassLoaderSetThenContextLoads() throws Exception {
|
||||
CanLoadWithChildConfig.AUTHENTICATION_MANAGER = mock(AuthenticationManager.class);
|
||||
this.spring.register(CanLoadWithChildConfig.class);
|
||||
AnnotationConfigWebApplicationContext context = (AnnotationConfigWebApplicationContext) this.spring.getContext();
|
||||
context.setClassLoader(new URLClassLoader(new URL[0], context.getClassLoader()));
|
||||
this.spring.autowire();
|
||||
|
||||
assertThat(this.spring.getContext().getBean(AuthenticationManager.class)).isNotNull();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CanLoadWithChildConfig extends WebSecurityConfigurerAdapter {
|
||||
static AuthenticationManager AUTHENTICATION_MANAGER;
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() {
|
||||
return AUTHENTICATION_MANAGER;
|
||||
}
|
||||
}
|
||||
|
||||
// SEC-2515
|
||||
@Test
|
||||
public void loadConfigWhenAuthenticationManagerConfiguredAndRegisterBeanThenContextLoads() throws Exception {
|
||||
this.spring.register(SecurityConfig.class).autowire();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication();
|
||||
}
|
||||
}
|
||||
}
|
||||
+406
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* 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.configuration;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.config.users.AuthenticationTestConfiguration;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.catchThrowable;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for {@link WebSecurityConfiguration}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class WebSecurityConfigurationTests {
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void loadConfigWhenWebSecurityConfigurersHaveOrderThenFilterChainsOrdered() throws Exception {
|
||||
this.spring.register(SortedWebSecurityConfigurerAdaptersConfig.class).autowire();
|
||||
|
||||
FilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);
|
||||
List<SecurityFilterChain> filterChains = filterChainProxy.getFilterChains();
|
||||
assertThat(filterChains).hasSize(6);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "");
|
||||
|
||||
request.setServletPath("/ignore1");
|
||||
assertThat(filterChains.get(0).matches(request)).isTrue();
|
||||
assertThat(filterChains.get(0).getFilters()).isEmpty();
|
||||
|
||||
request.setServletPath("/ignore2");
|
||||
assertThat(filterChains.get(1).matches(request)).isTrue();
|
||||
assertThat(filterChains.get(1).getFilters()).isEmpty();
|
||||
|
||||
request.setServletPath("/role1/**");
|
||||
assertThat(filterChains.get(2).matches(request)).isTrue();
|
||||
|
||||
request.setServletPath("/role2/**");
|
||||
assertThat(filterChains.get(3).matches(request)).isTrue();
|
||||
|
||||
request.setServletPath("/role3/**");
|
||||
assertThat(filterChains.get(4).matches(request)).isTrue();
|
||||
|
||||
request.setServletPath("/**");
|
||||
assertThat(filterChains.get(5).matches(request)).isTrue();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(AuthenticationTestConfiguration.class)
|
||||
static class SortedWebSecurityConfigurerAdaptersConfig {
|
||||
|
||||
@Configuration
|
||||
@Order(1)
|
||||
static class WebConfigurer1 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/ignore1", "/ignore2");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role1/**")
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("1");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(2)
|
||||
static class WebConfigurer2 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role2/**")
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("2");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(3)
|
||||
static class WebConfigurer3 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role3/**")
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("3");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class WebConfigurer4 extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("4");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadConfigWhenWebSecurityConfigurersHaveSameOrderThenThrowBeanCreationException() throws Exception {
|
||||
Throwable thrown = catchThrowable(() -> this.spring.register(DuplicateOrderConfig.class).autowire());
|
||||
|
||||
assertThat(thrown).isInstanceOf(BeanCreationException.class)
|
||||
.hasMessageContaining("@Order on WebSecurityConfigurers must be unique")
|
||||
.hasMessageContaining(DuplicateOrderConfig.WebConfigurer1.class.getName())
|
||||
.hasMessageContaining(DuplicateOrderConfig.WebConfigurer2.class.getName());
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(AuthenticationTestConfiguration.class)
|
||||
static class DuplicateOrderConfig {
|
||||
|
||||
@Configuration
|
||||
static class WebConfigurer1 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role1/**")
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("1");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class WebConfigurer2 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role2/**")
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasRole("2");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadConfigWhenWebInvocationPrivilegeEvaluatorSetThenIsRegistered() throws Exception {
|
||||
PrivilegeEvaluatorConfigurerAdapterConfig.PRIVILEGE_EVALUATOR = mock(WebInvocationPrivilegeEvaluator.class);
|
||||
|
||||
this.spring.register(PrivilegeEvaluatorConfigurerAdapterConfig.class).autowire();
|
||||
|
||||
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
|
||||
.isSameAs(PrivilegeEvaluatorConfigurerAdapterConfig.PRIVILEGE_EVALUATOR);
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class PrivilegeEvaluatorConfigurerAdapterConfig extends WebSecurityConfigurerAdapter {
|
||||
static WebInvocationPrivilegeEvaluator PRIVILEGE_EVALUATOR;
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web.privilegeEvaluator(PRIVILEGE_EVALUATOR);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadConfigWhenSecurityExpressionHandlerSetThenIsRegistered() throws Exception {
|
||||
WebSecurityExpressionHandlerConfig.EXPRESSION_HANDLER = mock(SecurityExpressionHandler.class);
|
||||
when(WebSecurityExpressionHandlerConfig.EXPRESSION_HANDLER.getExpressionParser()).thenReturn(mock(ExpressionParser.class));
|
||||
|
||||
this.spring.register(WebSecurityExpressionHandlerConfig.class).autowire();
|
||||
|
||||
assertThat(this.spring.getContext().getBean(SecurityExpressionHandler.class))
|
||||
.isSameAs(WebSecurityExpressionHandlerConfig.EXPRESSION_HANDLER);
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class WebSecurityExpressionHandlerConfig extends WebSecurityConfigurerAdapter {
|
||||
static SecurityExpressionHandler EXPRESSION_HANDLER;
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web.expressionHandler(EXPRESSION_HANDLER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
.expressionHandler(EXPRESSION_HANDLER);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadConfigWhenDefaultSecurityExpressionHandlerThenDefaultIsRegistered() throws Exception {
|
||||
this.spring.register(WebSecurityExpressionHandlerDefaultsConfig.class).autowire();
|
||||
|
||||
assertThat(this.spring.getContext().getBean(SecurityExpressionHandler.class))
|
||||
.isInstanceOf(DefaultWebSecurityExpressionHandler.class);
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class WebSecurityExpressionHandlerDefaultsConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securityExpressionHandlerWhenPermissionEvaluatorBeanThenPermissionEvaluatorUsed() throws Exception {
|
||||
this.spring.register(WebSecurityExpressionHandlerPermissionEvaluatorBeanConfig.class).autowire();
|
||||
TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "notused");
|
||||
FilterInvocation invocation = new FilterInvocation(new MockHttpServletRequest(), new MockHttpServletResponse(), new MockFilterChain());
|
||||
|
||||
AbstractSecurityExpressionHandler handler = this.spring.getContext().getBean(AbstractSecurityExpressionHandler.class);
|
||||
EvaluationContext evaluationContext = handler.createEvaluationContext(authentication, invocation);
|
||||
Expression expression = handler.getExpressionParser()
|
||||
.parseExpression("hasPermission(#study,'DELETE')");
|
||||
boolean granted = expression.getValue(evaluationContext, Boolean.class);
|
||||
assertThat(granted).isTrue();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class WebSecurityExpressionHandlerPermissionEvaluatorBeanConfig extends WebSecurityConfigurerAdapter {
|
||||
static final PermissionEvaluator PERMIT_ALL_PERMISSION_EVALUATOR = new PermissionEvaluator() {
|
||||
@Override
|
||||
public boolean hasPermission(Authentication authentication,
|
||||
Object targetDomainObject, Object permission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Authentication authentication,
|
||||
Serializable targetId, String targetType, Object permission) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@Bean
|
||||
public PermissionEvaluator permissionEvaluator() {
|
||||
return PERMIT_ALL_PERMISSION_EVALUATOR;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadConfigWhenDefaultWebInvocationPrivilegeEvaluatorThenDefaultIsRegistered() throws Exception {
|
||||
this.spring.register(WebInvocationPrivilegeEvaluatorDefaultsConfig.class).autowire();
|
||||
|
||||
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
|
||||
.isInstanceOf(DefaultWebInvocationPrivilegeEvaluator.class);
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class WebInvocationPrivilegeEvaluatorDefaultsConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated();
|
||||
}
|
||||
}
|
||||
|
||||
// SEC-2303
|
||||
@Test
|
||||
public void loadConfigWhenDefaultSecurityExpressionHandlerThenBeanResolverSet() throws Exception {
|
||||
this.spring.register(DefaultExpressionHandlerSetsBeanResolverConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(get("/")).andExpect(status().isOk());
|
||||
this.mockMvc.perform(post("/")).andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class DefaultExpressionHandlerSetsBeanResolverConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().access("request.method == 'GET' ? @b.grant() : @b.deny()");
|
||||
}
|
||||
|
||||
@RestController
|
||||
public class HomeController {
|
||||
@GetMapping("/")
|
||||
public String home() {
|
||||
return "home";
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MyBean b() {
|
||||
return new MyBean();
|
||||
}
|
||||
|
||||
static class MyBean {
|
||||
public boolean deny() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean grant() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Rule
|
||||
public SpringTestRule child = new SpringTestRule();
|
||||
|
||||
// SEC-2461
|
||||
@Test
|
||||
public void loadConfigWhenMultipleWebSecurityConfigurationThenContextLoads() throws Exception {
|
||||
this.spring.register(ParentConfig.class).autowire();
|
||||
|
||||
this.child.register(ChildConfig.class);
|
||||
this.child.getContext().setParent(this.spring.getContext());
|
||||
this.child.autowire();
|
||||
|
||||
assertThat(this.spring.getContext().getBean("springSecurityFilterChain")).isNotNull();
|
||||
assertThat(this.child.getContext().getBean("springSecurityFilterChain")).isNotNull();
|
||||
|
||||
assertThat(this.spring.getContext().containsBean("springSecurityFilterChain")).isTrue();
|
||||
assertThat(this.child.getContext().containsBean("springSecurityFilterChain")).isTrue();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class ParentConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class ChildConfig extends WebSecurityConfigurerAdapter {
|
||||
}
|
||||
|
||||
// SEC-2773
|
||||
@Test
|
||||
public void getMethodDelegatingApplicationListenerWhenWebSecurityConfigurationThenIsStatic() throws Exception {
|
||||
Method method = ClassUtils.getMethod(WebSecurityConfiguration.class, "delegatingApplicationListener", null);
|
||||
assertThat(Modifier.isStatic(method.getModifiers())).isTrue();
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.configuration.sec2377;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.security.config.annotation.web.configuration.sec2377.a.Sec2377AConfig;
|
||||
import org.springframework.security.config.annotation.web.configuration.sec2377.b.Sec2377BConfig;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class Sec2377Tests {
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule parent = new SpringTestRule();
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule child = new SpringTestRule();
|
||||
|
||||
@Test
|
||||
public void refreshContextWhenParentAndChildRegisteredThenNoException() {
|
||||
this.parent.register(Sec2377AConfig.class).autowire();
|
||||
|
||||
ConfigurableApplicationContext context =
|
||||
this.child.register(Sec2377BConfig.class).getContext();
|
||||
context.setParent(this.parent.getContext());
|
||||
|
||||
this.child.autowire();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,4 +21,4 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
|
||||
@EnableWebSecurity
|
||||
public class Sec2377BConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class AnonymousConfigurerTests {
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void requestWhenAnonymousTwiceInvokedThenDoesNotOverride() throws Exception {
|
||||
this.spring.register(InvokeTwiceDoesNotOverride.class).autowire();
|
||||
|
||||
this.mockMvc.perform(get("/"))
|
||||
.andExpect(content().string("principal"));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@EnableWebMvc
|
||||
static class InvokeTwiceDoesNotOverride extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.anonymous()
|
||||
.key("key")
|
||||
.principal("principal")
|
||||
.and()
|
||||
.anonymous();
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class PrincipalController {
|
||||
@GetMapping("/")
|
||||
String principal(@AuthenticationPrincipal String principal) {
|
||||
return principal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
-24
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,41 +13,42 @@
|
||||
* 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.Configuration
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.context.NullSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.context.SecurityContextRepository
|
||||
import org.springframework.security.web.savedrequest.RequestCache
|
||||
import org.springframework.security.web.session.ConcurrentSessionFilter
|
||||
import org.springframework.security.web.session.SessionManagementFilter
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
class PortMapperConfigurerTests extends BaseSpringSpec {
|
||||
public class PortMapperConfigurerTests {
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
def "invoke portMapper twice does not override"() {
|
||||
setup:
|
||||
loadConfig(InvokeTwiceDoesNotOverride)
|
||||
request.setServerPort(543)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.redirectedUrl == "https://localhost:123"
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void requestWhenPortMapperTwiceInvokedThenDoesNotOverride() throws Exception {
|
||||
this.spring.register(InvokeTwiceDoesNotOverride.class).autowire();
|
||||
|
||||
this.mockMvc.perform(get("http://localhost:543"))
|
||||
.andExpect(redirectedUrl("https://localhost:123"));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class InvokeTwiceDoesNotOverride extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
@@ -57,7 +58,7 @@ class PortMapperConfigurerTests extends BaseSpringSpec {
|
||||
.portMapper()
|
||||
.http(543).mapsTo(123)
|
||||
.and()
|
||||
.portMapper()
|
||||
.portMapper();
|
||||
}
|
||||
}
|
||||
}
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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.oauth2.client;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.annotation.OAuth2Client;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
|
||||
import org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for {@link OAuth2ClientConfigurer}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class OAuth2ClientConfigurerTests {
|
||||
private static ClientRegistrationRepository clientRegistrationRepository;
|
||||
|
||||
private static OAuth2AuthorizedClientService authorizedClientService;
|
||||
|
||||
private static OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
|
||||
|
||||
private static RequestCache requestCache;
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private ClientRegistration registration1;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.registration1 = ClientRegistration.withRegistrationId("registration-1")
|
||||
.clientId("client-1")
|
||||
.clientSecret("secret")
|
||||
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||
.redirectUriTemplate("{baseUrl}/client-1")
|
||||
.scope("user")
|
||||
.authorizationUri("https://provider.com/oauth2/authorize")
|
||||
.tokenUri("https://provider.com/oauth2/token")
|
||||
.userInfoUri("https://provider.com/oauth2/user")
|
||||
.userNameAttributeName("id")
|
||||
.clientName("client-1")
|
||||
.build();
|
||||
clientRegistrationRepository = new InMemoryClientRegistrationRepository(this.registration1);
|
||||
authorizedClientService = new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);
|
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken("access-token-1234")
|
||||
.tokenType(OAuth2AccessToken.TokenType.BEARER)
|
||||
.expiresIn(300)
|
||||
.build();
|
||||
accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);
|
||||
when(accessTokenResponseClient.getTokenResponse(any(OAuth2AuthorizationCodeGrantRequest.class))).thenReturn(accessTokenResponse);
|
||||
requestCache = mock(RequestCache.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenAuthorizationCodeRequestThenRedirectForAuthorization() throws Exception {
|
||||
this.spring.register(OAuth2ClientConfig.class).autowire();
|
||||
|
||||
MvcResult mvcResult = this.mockMvc.perform(get("/oauth2/authorization/registration-1"))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andReturn();
|
||||
assertThat(mvcResult.getResponse().getRedirectedUrl()).matches("https://provider.com/oauth2/authorize\\?response_type=code&client_id=client-1&scope=user&state=.{15,}&redirect_uri=http://localhost/client-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenAuthorizationCodeResponseSuccessThenAuthorizedClientSaved() throws Exception {
|
||||
this.spring.register(OAuth2ClientConfig.class).autowire();
|
||||
|
||||
// Setup the Authorization Request in the session
|
||||
Map<String, Object> additionalParameters = new HashMap<>();
|
||||
additionalParameters.put(OAuth2ParameterNames.REGISTRATION_ID, this.registration1.getRegistrationId());
|
||||
OAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()
|
||||
.authorizationUri(this.registration1.getProviderDetails().getAuthorizationUri())
|
||||
.clientId(this.registration1.getClientId())
|
||||
.redirectUri("http://localhost/client-1")
|
||||
.state("state")
|
||||
.additionalParameters(additionalParameters)
|
||||
.build();
|
||||
|
||||
AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository =
|
||||
new HttpSessionOAuth2AuthorizationRequestRepository();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);
|
||||
|
||||
MockHttpSession session = (MockHttpSession) request.getSession();
|
||||
|
||||
String principalName = "user1";
|
||||
|
||||
this.mockMvc.perform(get("/client-1")
|
||||
.param(OAuth2ParameterNames.CODE, "code")
|
||||
.param(OAuth2ParameterNames.STATE, "state")
|
||||
.with(user(principalName))
|
||||
.session(session))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("http://localhost/client-1"));
|
||||
|
||||
OAuth2AuthorizedClient authorizedClient = authorizedClientService.loadAuthorizedClient(
|
||||
this.registration1.getRegistrationId(), principalName);
|
||||
assertThat(authorizedClient).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenRequestCacheProvidedAndClientAuthorizationRequiredExceptionThrownThenRequestCacheUsed() throws Exception {
|
||||
this.spring.register(OAuth2ClientConfig.class).autowire();
|
||||
|
||||
MvcResult mvcResult = this.mockMvc.perform(get("/resource1").with(user("user1")))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andReturn();
|
||||
assertThat(mvcResult.getResponse().getRedirectedUrl()).matches("https://provider.com/oauth2/authorize\\?response_type=code&client_id=client-1&scope=user&state=.{15,}&redirect_uri=http://localhost/client-1");
|
||||
|
||||
verify(requestCache).saveRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@EnableWebMvc
|
||||
static class OAuth2ClientConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.requestCache()
|
||||
.requestCache(requestCache)
|
||||
.and()
|
||||
.oauth2()
|
||||
.client()
|
||||
.authorizationCodeGrant()
|
||||
.tokenEndpoint()
|
||||
.accessTokenResponseClient(accessTokenResponseClient);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ClientRegistrationRepository clientRegistrationRepository() {
|
||||
return clientRegistrationRepository;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OAuth2AuthorizedClientService authorizedClientService() {
|
||||
return authorizedClientService;
|
||||
}
|
||||
|
||||
@RestController
|
||||
public class ResourceController {
|
||||
@GetMapping("/resource1")
|
||||
public String resource1(@OAuth2Client("registration-1") OAuth2AuthorizedClient authorizedClient) {
|
||||
return "resource1";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+61
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,8 +16,17 @@
|
||||
|
||||
package org.springframework.security.config.annotation.web.reactive;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf;
|
||||
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
|
||||
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.Credentials.basicAuthenticationCredentials;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Principal;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -32,40 +41,43 @@ import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
|
||||
import org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.WebFilterChainProxy;
|
||||
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
|
||||
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.reactive.server.FluxExchangeResult;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.result.view.AbstractView;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Principal;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf;
|
||||
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.Credentials.basicAuthenticationCredentials;
|
||||
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SecurityTestExecutionListeners
|
||||
public class EnableWebFluxSecurityTests {
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
@@ -288,6 +300,46 @@ public class EnableWebFluxSecurityTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void authenticationPrincipalArgumentResolverWhenSpelThenWorks() {
|
||||
this.spring.register(AuthenticationPrincipalConfig.class).autowire();
|
||||
|
||||
WebTestClient client = WebTestClient.bindToApplicationContext(this.spring.getContext()).build();
|
||||
|
||||
client.get()
|
||||
.uri("/spel")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody(String.class).isEqualTo("user");
|
||||
}
|
||||
|
||||
|
||||
@EnableWebFluxSecurity
|
||||
@EnableWebFlux
|
||||
@Import(ReactiveAuthenticationTestConfiguration.class)
|
||||
static class AuthenticationPrincipalConfig {
|
||||
|
||||
@Bean
|
||||
public PrincipalBean principalBean() {
|
||||
return new PrincipalBean();
|
||||
}
|
||||
|
||||
static class PrincipalBean {
|
||||
public String username(UserDetails user) {
|
||||
return user.getUsername();
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
public static class AuthenticationPrincipalResolver {
|
||||
@GetMapping("/spel")
|
||||
String username(@AuthenticationPrincipal(expression = "@principalBean.username(#this)") String username) {
|
||||
return username;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static DataBuffer toDataBuffer(String body) {
|
||||
DataBuffer buffer = new DefaultDataBufferFactory().allocateBuffer();
|
||||
buffer.write(body.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
+4
-4
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* 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,
|
||||
@@ -13,9 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.debug
|
||||
package org.springframework.security.config.debug;
|
||||
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Fake depenency for {@link TestAuthenticationProvider}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.debug;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.debug.DebugFilter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.security.config.BeanIds.FILTER_CHAIN_PROXY;
|
||||
import static org.springframework.security.config.BeanIds.SPRING_SECURITY_FILTER_CHAIN;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class SecurityDebugBeanFactoryPostProcessorTests {
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Test
|
||||
public void contextRefreshWhenInDebugModeAndDependencyHasAutowiredConstructorThenDebugModeStillWorks() {
|
||||
// SEC-1885
|
||||
this.spring.configLocations("classpath:org/springframework/security/config/debug/SecurityDebugBeanFactoryPostProcessorTests-context.xml")
|
||||
.autowire();
|
||||
|
||||
assertThat(this.spring.getContext().getBean(SPRING_SECURITY_FILTER_CHAIN)).isInstanceOf(DebugFilter.class);
|
||||
assertThat(this.spring.getContext().getBean(FILTER_CHAIN_PROXY)).isInstanceOf(FilterChainProxy.class);
|
||||
}
|
||||
}
|
||||
+8
-7
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* 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,
|
||||
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package org.springframework.security.config.debug;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} that has an {@link Autowired} constructor which is necessary to recreate SEC-1885.
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.doc;
|
||||
|
||||
/**
|
||||
* Represents a Spring Security XSD Attribute. It is created when parsing the current xsd to compare to the documented appendix.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Josh Cummings
|
||||
*
|
||||
* @see SpringSecurityXsdParser
|
||||
* @see XsdDocumentedTests
|
||||
*/
|
||||
public class Attribute {
|
||||
private String name;
|
||||
|
||||
private String desc;
|
||||
|
||||
private Element elmt;
|
||||
|
||||
public Attribute(String desc, String name) {
|
||||
this.desc = desc;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return this.desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public Element getElmt() {
|
||||
return this.elmt;
|
||||
}
|
||||
|
||||
public void setElmt(Element elmt) {
|
||||
this.elmt = elmt;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return String.format("%s-%s", this.elmt.getId(), this.name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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.doc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a Spring Security XSD Element. It is created when parsing
|
||||
* the current xsd to compare to the documented appendix.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Josh Cummings
|
||||
*
|
||||
* @see SpringSecurityXsdParser
|
||||
* @see XsdDocumentedTests
|
||||
*/
|
||||
public class Element {
|
||||
private String name;
|
||||
private String desc;
|
||||
private Collection<Attribute> attrs = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Contains the elements that extend this element (i.e. any-user-service contains ldap-user-service)
|
||||
*/
|
||||
private Collection<Element> subGrps = new ArrayList<>();
|
||||
private Map<String, Element> childElmts = new HashMap<>();
|
||||
private Map<String, Element> parentElmts = new HashMap<>();
|
||||
|
||||
public String getId() {
|
||||
return String.format("nsa-%s", this.name);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return this.desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public Collection<Attribute> getAttrs() {
|
||||
return this.attrs;
|
||||
}
|
||||
|
||||
public void setAttrs(Collection<Attribute> attrs) {
|
||||
this.attrs = attrs;
|
||||
}
|
||||
|
||||
public Collection<Element> getSubGrps() {
|
||||
return this.subGrps;
|
||||
}
|
||||
|
||||
public void setSubGrps(Collection<Element> subGrps) {
|
||||
this.subGrps = subGrps;
|
||||
}
|
||||
|
||||
public Map<String, Element> getChildElmts() {
|
||||
return this.childElmts;
|
||||
}
|
||||
|
||||
public void setChildElmts(Map<String, Element> childElmts) {
|
||||
this.childElmts = childElmts;
|
||||
}
|
||||
|
||||
public Map<String, Element> getParentElmts() {
|
||||
return this.parentElmts;
|
||||
}
|
||||
|
||||
public void setParentElmts(Map<String, Element> parentElmts) {
|
||||
this.parentElmts = parentElmts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the ids related to this Element including attributes, parent elements, and child elements.
|
||||
*
|
||||
* <p>
|
||||
* The expected ids to be found are documented below.
|
||||
* <ul>
|
||||
* <li>Elements - any xml element will have the nsa-<element>. For example the http element will have the id
|
||||
* nsa-http</li>
|
||||
* <li>Parent Section - Any element with a parent other than beans will have a section named
|
||||
* nsa-<element>-parents. For example, authentication-provider would have a section id of
|
||||
* nsa-authentication-provider-parents. The section would then contain a list of links pointing to the
|
||||
* documentation for each parent element.</li>
|
||||
* <li>Attributes Section - Any element with attributes will have a section with the id
|
||||
* nsa-<element>-attributes. For example the http element would require a section with the id
|
||||
* http-attributes.</li>
|
||||
* <li>Attribute - Each attribute of an element would have an id of nsa-<element>-<attributeName>. For
|
||||
* example the attribute create-session for the http attribute would have the id http-create-session.</li>
|
||||
* <li>Child Section - Any element with a child element will have a section named nsa-<element>-children.
|
||||
* For example, authentication-provider would have a section id of nsa-authentication-provider-children. The
|
||||
* section would then contain a list of links pointing to the documentation for each child element.</li>
|
||||
* </ul>
|
||||
* @return
|
||||
*/
|
||||
public Collection<String> getIds() {
|
||||
Collection<String> ids = new ArrayList<>();
|
||||
ids.add(getId());
|
||||
|
||||
this.childElmts.values()
|
||||
.forEach(elmt -> ids.add(elmt.getId()));
|
||||
|
||||
this.attrs.forEach(attr -> ids.add(attr.getId()));
|
||||
|
||||
if ( !this.childElmts.isEmpty() ) {
|
||||
ids.add(getId() + "-children");
|
||||
}
|
||||
|
||||
if ( !this.attrs.isEmpty() ) {
|
||||
ids.add(getId() + "-attributes");
|
||||
}
|
||||
|
||||
if ( !this.parentElmts.isEmpty() ) {
|
||||
ids.add(getId() + "-parents");
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
public Map<String, Element> getAllChildElmts() {
|
||||
Map<String, Element> result = new HashMap<>();
|
||||
|
||||
this.childElmts.values()
|
||||
.forEach(elmt ->
|
||||
elmt.subGrps.forEach(
|
||||
subElmt -> result.put(subElmt.name, subElmt)));
|
||||
|
||||
result.putAll(this.childElmts);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<String, Element> getAllParentElmts() {
|
||||
Map<String, Element> result = new HashMap<>();
|
||||
|
||||
this.parentElmts.values()
|
||||
.forEach(elmt ->
|
||||
elmt.subGrps.forEach(
|
||||
subElmt -> result.put(subElmt.name, subElmt)));
|
||||
|
||||
result.putAll(this.parentElmts);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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.doc;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Parses the Spring Security Xsd Document
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class SpringSecurityXsdParser {
|
||||
private XmlNode rootElement;
|
||||
|
||||
private Set<String> attrElmts = new LinkedHashSet<>();
|
||||
private Map<String, Element> elementNameToElement = new HashMap<>();
|
||||
|
||||
public SpringSecurityXsdParser(XmlNode rootElement) {
|
||||
this.rootElement = rootElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of the element name to the {@link Element}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Map<String, Element> parse() {
|
||||
elements(this.rootElement);
|
||||
return this.elementNameToElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Map of the name to an Element object of all the children of element.
|
||||
*
|
||||
* @param node
|
||||
* @return
|
||||
*/
|
||||
private Map<String, Element> elements(XmlNode node) {
|
||||
Map<String, Element> elementNameToElement = new HashMap<>();
|
||||
|
||||
node.children().forEach(child -> {
|
||||
if ("element".equals(child.simpleName())) {
|
||||
Element e = elmt(child);
|
||||
elementNameToElement.put(e.getName(), e);
|
||||
} else {
|
||||
elementNameToElement.putAll(elements(child));
|
||||
}
|
||||
});
|
||||
|
||||
return elementNameToElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Any children that are attribute will be returned as an Attribute object.
|
||||
*
|
||||
* @param element
|
||||
* @return a collection of Attribute objects that are children of element.
|
||||
*/
|
||||
private Collection<Attribute> attrs(XmlNode element) {
|
||||
Collection<Attribute> attrs = new ArrayList<>();
|
||||
element.children().forEach(c -> {
|
||||
String name = c.simpleName();
|
||||
if ("attribute".equals(name)) {
|
||||
attrs.add(attr(c));
|
||||
} else if ("element".equals(name)) {
|
||||
} else {
|
||||
attrs.addAll(attrs(c));
|
||||
}
|
||||
});
|
||||
|
||||
return attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Any children will be searched for an attributeGroup, each of its children will be returned as an Attribute
|
||||
*
|
||||
* @param element
|
||||
* @return
|
||||
*/
|
||||
private Collection<Attribute> attrgrps(XmlNode element) {
|
||||
Collection<Attribute> attrgrp = new ArrayList<>();
|
||||
|
||||
element.children().forEach(c -> {
|
||||
if ("element".equals(c.simpleName())) {
|
||||
|
||||
} else if ("attributeGroup".equals(c.simpleName())) {
|
||||
if (c.attribute("name") != null) {
|
||||
attrgrp.addAll(attrgrp(c));
|
||||
} else {
|
||||
String name = c.attribute("ref").split(":")[1];
|
||||
XmlNode attrGrp = findNode(element, name);
|
||||
attrgrp.addAll(attrgrp(attrGrp));
|
||||
}
|
||||
} else {
|
||||
attrgrp.addAll(attrgrps(c));
|
||||
}
|
||||
});
|
||||
|
||||
return attrgrp;
|
||||
}
|
||||
|
||||
private XmlNode findNode(XmlNode c, String name) {
|
||||
XmlNode root = c;
|
||||
while (!"schema".equals(root.simpleName())) {
|
||||
root = root.parent().get();
|
||||
}
|
||||
|
||||
return expand(root)
|
||||
.filter(node -> name.equals(node.attribute("name")))
|
||||
.findFirst().orElseThrow(IllegalArgumentException::new);
|
||||
}
|
||||
|
||||
private Stream<XmlNode> expand(XmlNode root) {
|
||||
return Stream.concat(
|
||||
Stream.of(root),
|
||||
root.children().flatMap(this::expand));
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an individual attributeGroup by obtaining all the attributes and then looking for more attributeGroup elements and prcessing them.
|
||||
*
|
||||
* @param e
|
||||
* @return all the attributes for a specific attributeGroup and any child attributeGroups
|
||||
*/
|
||||
private Collection<Attribute> attrgrp(XmlNode e) {
|
||||
Collection<Attribute> attrs = attrs(e);
|
||||
attrs.addAll(attrgrps(e));
|
||||
return attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the description for a specific element
|
||||
*
|
||||
* @param element
|
||||
* @return
|
||||
*/
|
||||
private String desc(XmlNode element) {
|
||||
return element.child("annotation")
|
||||
.flatMap(annotation -> annotation.child("documentation"))
|
||||
.map(documentation -> documentation.text())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an element creates an attribute from it.
|
||||
*
|
||||
* @param n
|
||||
* @return
|
||||
*/
|
||||
private Attribute attr(XmlNode n) {
|
||||
return new Attribute(desc(n), n.attribute("name"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an element creates an Element out of it by collecting all its attributes and child elements.
|
||||
*
|
||||
* @param n
|
||||
* @return
|
||||
*/
|
||||
private Element elmt(XmlNode n) {
|
||||
String name = n.attribute("ref");
|
||||
if (StringUtils.isEmpty(name)) {
|
||||
name = n.attribute("name");
|
||||
} else {
|
||||
name = name.split(":")[1];
|
||||
n = findNode(n, name);
|
||||
}
|
||||
|
||||
if (this.elementNameToElement.containsKey(name)) {
|
||||
return this.elementNameToElement.get(name);
|
||||
}
|
||||
this.attrElmts.add(name);
|
||||
|
||||
Element e = new Element();
|
||||
e.setName(n.attribute("name"));
|
||||
e.setDesc(desc(n));
|
||||
e.setChildElmts(elements(n));
|
||||
e.setAttrs(attrs(n));
|
||||
e.getAttrs().addAll(attrgrps(n));
|
||||
e.getAttrs().forEach(attr -> attr.setElmt(e));
|
||||
e.getChildElmts().values().forEach(element ->
|
||||
element.getParentElmts().put(e.getName(), e));
|
||||
|
||||
String subGrpName = n.attribute("substitutionGroup");
|
||||
if (!StringUtils.isEmpty(subGrpName)) {
|
||||
Element subGrp = elmt(findNode(n, subGrpName.split(":")[1]));
|
||||
subGrp.getSubGrps().add(e);
|
||||
}
|
||||
|
||||
this.elementNameToElement.put(name, e);
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.doc;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class XmlNode {
|
||||
private final Node node;
|
||||
|
||||
public XmlNode(Node node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public String simpleName() {
|
||||
String[] parts = this.node.getNodeName().split(":");
|
||||
return parts[parts.length-1];
|
||||
}
|
||||
|
||||
public String text() {
|
||||
return this.node.getTextContent();
|
||||
}
|
||||
|
||||
public Stream<XmlNode> children() {
|
||||
NodeList children = this.node.getChildNodes();
|
||||
|
||||
return IntStream.range(0, children.getLength())
|
||||
.mapToObj(children::item)
|
||||
.map(XmlNode::new);
|
||||
}
|
||||
|
||||
public Optional<XmlNode> child(String name) {
|
||||
return this.children()
|
||||
.filter(child -> name.equals(child.simpleName()))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public Optional<XmlNode> parent() {
|
||||
return Optional.ofNullable(this.node.getParentNode())
|
||||
.map(parent -> new XmlNode(parent));
|
||||
}
|
||||
|
||||
public String attribute(String name) {
|
||||
return Optional.ofNullable(this.node.getAttributes())
|
||||
.map(attrs -> attrs.getNamedItem(name))
|
||||
.map(attr -> attr.getTextContent())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public Node node() {
|
||||
return this.node;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.doc;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class XmlParser implements AutoCloseable {
|
||||
private InputStream xml;
|
||||
|
||||
public XmlParser(InputStream xml) {
|
||||
this.xml = xml;
|
||||
}
|
||||
|
||||
public XmlNode parse() {
|
||||
try {
|
||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
|
||||
|
||||
return new XmlNode(dBuilder.parse(this.xml));
|
||||
} catch ( IOException | ParserConfigurationException | SAXException e ) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.xml.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.doc;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Support for ensuring preparing the givens in {@link XsdDocumentedTests}
|
||||
*
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class XmlSupport {
|
||||
private XmlParser parser;
|
||||
|
||||
public XmlNode parse(String location) throws IOException {
|
||||
ClassPathResource resource = new ClassPathResource(location);
|
||||
this.parser = new XmlParser(resource.getInputStream());
|
||||
|
||||
return this.parser.parse();
|
||||
}
|
||||
|
||||
public Map<String, Element> elementsByElementName(String location) throws IOException {
|
||||
XmlNode node = parse(location);
|
||||
return new SpringSecurityXsdParser(node).parse();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if ( this.parser != null ) {
|
||||
this.parser.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* 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.doc;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.security.config.http.SecurityFiltersAssertions;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests to ensure that the xsd is properly documented.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class XsdDocumentedTests {
|
||||
|
||||
Collection<String> ignoredIds = Arrays.asList(
|
||||
"nsa-any-user-service",
|
||||
"nsa-any-user-service-parents",
|
||||
"nsa-authentication",
|
||||
"nsa-websocket-security",
|
||||
"nsa-ldap",
|
||||
"nsa-method-security",
|
||||
"nsa-web");
|
||||
|
||||
String referenceLocation = "../docs/manual/src/docs/asciidoc/_includes/appendix/namespace.adoc";
|
||||
|
||||
String schema31xDocumentLocation = "org/springframework/security/config/spring-security-3.1.xsd";
|
||||
String schemaDocumentLocation = "org/springframework/security/config/spring-security-5.0.xsd";
|
||||
|
||||
XmlSupport xml = new XmlSupport();
|
||||
|
||||
@After
|
||||
public void close() throws IOException {
|
||||
this.xml.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseWhenLatestXsdThenAllNamedSecurityFiltersAreDefinedAndOrderedProperly()
|
||||
throws IOException {
|
||||
XmlNode root = this.xml.parse(this.schemaDocumentLocation);
|
||||
|
||||
List<String> nodes =
|
||||
root.child("schema")
|
||||
.map(XmlNode::children)
|
||||
.orElse(Stream.empty())
|
||||
.filter(node ->
|
||||
"simpleType".equals(node.simpleName()) &&
|
||||
"named-security-filter".equals(node.attribute("name")))
|
||||
.flatMap(XmlNode::children)
|
||||
.flatMap(XmlNode::children)
|
||||
.map(node -> node.attribute("value"))
|
||||
.filter(StringUtils::isNotEmpty)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
SecurityFiltersAssertions.assertEquals(nodes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseWhen31XsdThenAllNamedSecurityFiltersAreDefinedAndOrderedProperly()
|
||||
throws IOException {
|
||||
|
||||
List<String> expected = Arrays.asList(
|
||||
"FIRST",
|
||||
"CHANNEL_FILTER",
|
||||
"SECURITY_CONTEXT_FILTER",
|
||||
"CONCURRENT_SESSION_FILTER",
|
||||
"LOGOUT_FILTER",
|
||||
"X509_FILTER",
|
||||
"PRE_AUTH_FILTER",
|
||||
"CAS_FILTER",
|
||||
"FORM_LOGIN_FILTER",
|
||||
"OPENID_FILTER",
|
||||
"LOGIN_PAGE_FILTER",
|
||||
"DIGEST_AUTH_FILTER",
|
||||
"BASIC_AUTH_FILTER",
|
||||
"REQUEST_CACHE_FILTER",
|
||||
"SERVLET_API_SUPPORT_FILTER",
|
||||
"JAAS_API_SUPPORT_FILTER",
|
||||
"REMEMBER_ME_FILTER",
|
||||
"ANONYMOUS_FILTER",
|
||||
"SESSION_MANAGEMENT_FILTER",
|
||||
"EXCEPTION_TRANSLATION_FILTER",
|
||||
"FILTER_SECURITY_INTERCEPTOR",
|
||||
"SWITCH_USER_FILTER",
|
||||
"LAST"
|
||||
);
|
||||
|
||||
XmlNode root = this.xml.parse(this.schema31xDocumentLocation);
|
||||
|
||||
List<String> nodes =
|
||||
root.child("schema")
|
||||
.map(XmlNode::children)
|
||||
.orElse(Stream.empty())
|
||||
.filter(node ->
|
||||
"simpleType".equals(node.simpleName()) &&
|
||||
"named-security-filter".equals(node.attribute("name")))
|
||||
.flatMap(XmlNode::children)
|
||||
.flatMap(XmlNode::children)
|
||||
.map(node -> node.attribute("value"))
|
||||
.filter(StringUtils::isNotEmpty)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertThat(nodes).isEqualTo(expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will check to ensure that the expected number of xsd documents are found to ensure that we are validating
|
||||
* against the current xsd document. If this test fails, all that is needed is to update the schemaDocument
|
||||
* and the expected size for this test.
|
||||
* @return
|
||||
*/
|
||||
@Test
|
||||
public void sizeWhenReadingFilesystemThenIsCorrectNumberOfSchemaFiles()
|
||||
throws IOException {
|
||||
|
||||
ClassPathResource resource = new ClassPathResource(this.schemaDocumentLocation);
|
||||
|
||||
String[] schemas = resource.getFile().getParentFile().list((dir, name) -> name.endsWith(".xsd"));
|
||||
|
||||
assertThat(schemas.length).isEqualTo(12)
|
||||
.withFailMessage("the count is equal to 12, if not then schemaDocument needs updating");
|
||||
}
|
||||
|
||||
/**
|
||||
* This uses a naming convention for the ids of the appendix to ensure that the entire appendix is documented.
|
||||
* The naming convention for the ids is documented in {@link Element#getIds()}.
|
||||
* @return
|
||||
*/
|
||||
@Test
|
||||
public void countReferencesWhenReviewingDocumentationThenEntireSchemaIsIncluded()
|
||||
throws IOException {
|
||||
|
||||
Map<String, Element> elementsByElementName =
|
||||
this.xml.elementsByElementName(this.schemaDocumentLocation);
|
||||
|
||||
List<String> documentIds =
|
||||
Files.lines(Paths.get(this.referenceLocation))
|
||||
.filter(line -> line.matches("\\[\\[(nsa-.*)\\]\\]"))
|
||||
.map(line -> line.substring(2, line.length() - 2))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Set<String> expectedIds =
|
||||
elementsByElementName.values().stream()
|
||||
.flatMap(element -> element.getIds().stream())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
documentIds.removeAll(this.ignoredIds);
|
||||
expectedIds.removeAll(this.ignoredIds);
|
||||
|
||||
assertThat(documentIds).containsAll(expectedIds);
|
||||
assertThat(expectedIds).containsAll(documentIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that any element that has children or parents contains a section that has links pointing to that
|
||||
* documentation.
|
||||
* @return
|
||||
*/
|
||||
@Test
|
||||
public void countLinksWhenReviewingDocumentationThenParentsAndChildrenAreCorrectlyLinked()
|
||||
throws IOException {
|
||||
|
||||
Map<String, List<String>> docAttrNameToChildren = new HashMap<>();
|
||||
Map<String, List<String>> docAttrNameToParents = new HashMap<>();
|
||||
|
||||
String docAttrName = null;
|
||||
Map<String, List<String>> currentDocAttrNameToElmt = null;
|
||||
|
||||
List<String> lines = Files.readAllLines(Paths.get(this.referenceLocation));
|
||||
for ( String line : lines ) {
|
||||
if(line.matches("^\\[\\[.*\\]\\]$")) {
|
||||
String id = line.substring(2, line.length() - 2);
|
||||
|
||||
if(id.endsWith("-children")) {
|
||||
docAttrName = id.substring(0, id.length() - 9);
|
||||
currentDocAttrNameToElmt = docAttrNameToChildren;
|
||||
} else if(id.endsWith("-parents")) {
|
||||
docAttrName = id.substring(0, id.length() - 8);
|
||||
currentDocAttrNameToElmt = docAttrNameToParents;
|
||||
} else if(docAttrName != null && !id.startsWith(docAttrName)) {
|
||||
currentDocAttrNameToElmt = null;
|
||||
docAttrName = null;
|
||||
}
|
||||
}
|
||||
|
||||
if(docAttrName != null && currentDocAttrNameToElmt != null) {
|
||||
String expression = "^\\* <<(nsa-.*),.*>>$";
|
||||
if(line.matches(expression)) {
|
||||
String elmtId = line.replaceAll(expression, "$1");
|
||||
currentDocAttrNameToElmt
|
||||
.computeIfAbsent(docAttrName, key -> new ArrayList<>())
|
||||
.add(elmtId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Element> elementNameToElement = this.xml.elementsByElementName(this.schemaDocumentLocation);
|
||||
|
||||
Map<String, List<String>> schemaAttrNameToChildren = new HashMap<>();
|
||||
Map<String, List<String>> schemaAttrNameToParents = new HashMap<>();
|
||||
|
||||
elementNameToElement.entrySet().stream()
|
||||
.forEach(entry -> {
|
||||
String key = "nsa-" + entry.getKey();
|
||||
if (this.ignoredIds.contains(key) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> parentIds =
|
||||
entry.getValue().getAllParentElmts().values().stream()
|
||||
.filter(element -> !this.ignoredIds.contains(element.getId()))
|
||||
.map(element -> element.getId())
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
if ( !parentIds.isEmpty() ) {
|
||||
schemaAttrNameToParents.put(key, parentIds);
|
||||
}
|
||||
|
||||
List<String> childIds =
|
||||
entry.getValue().getAllChildElmts().values().stream()
|
||||
.filter(element -> !this.ignoredIds.contains(element.getId()))
|
||||
.map(element -> element.getId())
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
if ( !childIds.isEmpty() ) {
|
||||
schemaAttrNameToChildren.put(key, childIds);
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(docAttrNameToChildren).isEqualTo(schemaAttrNameToChildren);
|
||||
assertThat(docAttrNameToParents).isEqualTo(schemaAttrNameToParents);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This test checks each xsd element and ensures there is documentation for it.
|
||||
* @return
|
||||
*/
|
||||
@Test
|
||||
public void countWhenReviewingDocumentationThenAllElementsDocumented()
|
||||
throws IOException {
|
||||
|
||||
Map<String, Element> elementNameToElement =
|
||||
this.xml.elementsByElementName(this.schemaDocumentLocation);
|
||||
|
||||
String notDocElmtIds =
|
||||
elementNameToElement.values().stream()
|
||||
.filter(element ->
|
||||
StringUtils.isEmpty(element.getDesc()) &&
|
||||
!this.ignoredIds.contains(element.getId()))
|
||||
.map(element -> element.getId())
|
||||
.sorted()
|
||||
.collect(Collectors.joining("\n"));
|
||||
|
||||
String notDocAttrIds =
|
||||
elementNameToElement.values().stream()
|
||||
.flatMap(element -> element.getAttrs().stream())
|
||||
.filter(element ->
|
||||
StringUtils.isEmpty(element.getDesc()) &&
|
||||
!this.ignoredIds.contains(element.getId()))
|
||||
.map(element -> element.getId())
|
||||
.sorted()
|
||||
.collect(Collectors.joining("\n"));
|
||||
|
||||
assertThat(notDocElmtIds).isEmpty();
|
||||
assertThat(notDocAttrIds).isEmpty();
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.http;
|
||||
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SecurityTestExecutionListeners
|
||||
public class AccessDeniedConfigTests {
|
||||
private static final String CONFIG_LOCATION_PREFIX =
|
||||
"classpath:org/springframework/security/config/http/AccessDeniedConfigTests";
|
||||
|
||||
@Autowired
|
||||
MockMvc mvc;
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Test
|
||||
public void configureWhenAccessDeniedHandlerIsMissingLeadingSlashThenException() {
|
||||
SpringTestContext context = this.spring.configLocations(this.xml("NoLeadingSlash"));
|
||||
|
||||
assertThatThrownBy(() -> context.autowire())
|
||||
.isInstanceOf(BeanCreationException.class)
|
||||
.hasMessageContaining("errorPage must begin with '/'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void configureWhenAccessDeniedHandlerRefThenAutowire()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("AccessDeniedHandler")).autowire();
|
||||
|
||||
this.mvc.perform(get("/"))
|
||||
.andExpect(status().is(HttpStatus.GONE_410));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenAccessDeniedHandlerUsesPathAndRefThenException() {
|
||||
SpringTestContext context = this.spring.configLocations(this.xml("UsesPathAndRef"));
|
||||
|
||||
assertThatThrownBy(() -> context.autowire())
|
||||
.isInstanceOf(BeanDefinitionParsingException.class)
|
||||
.hasMessageContaining("attribute error-page cannot be used together with the 'ref' attribute");
|
||||
}
|
||||
|
||||
private String xml(String configName) {
|
||||
return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml";
|
||||
}
|
||||
|
||||
public static class GoneAccessDeniedHandler implements AccessDeniedHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AccessDeniedException accessDeniedException) {
|
||||
|
||||
response.setStatus(HttpStatus.GONE_410);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,646 @@
|
||||
/*
|
||||
* 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.http;
|
||||
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.security.test.web.support.WebTestUtils;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.csrf.CsrfFilter;
|
||||
import org.springframework.security.web.csrf.CsrfToken;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.ResultMatcher;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.servlet.support.RequestDataValueProcessor;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.head;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.HEAD;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.OPTIONS;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.PATCH;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.TRACE;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SecurityTestExecutionListeners
|
||||
public class CsrfConfigTests {
|
||||
private static final String CONFIG_LOCATION_PREFIX =
|
||||
"classpath:org/springframework/security/config/http/CsrfConfigTests";
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
MockMvc mvc;
|
||||
|
||||
@Test
|
||||
public void postWhenDefaultConfigurationThenForbiddenSinceCsrfIsEnabled() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("AutoConfig")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(post("/csrf"))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(csrfCreated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putWhenDefaultConfigurationThenForbiddenSinceCsrfIsEnabled() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("AutoConfig")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(put("/csrf"))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(csrfCreated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patchWhenDefaultConfigurationThenForbiddenSinceCsrfIsEnabled() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("AutoConfig")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(patch("/csrf"))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(csrfCreated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteWhenDefaultConfigurationThenForbiddenSinceCsrfIsEnabled() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("AutoConfig")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(delete("/csrf"))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(csrfCreated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidWhenDefaultConfigurationThenForbiddenSinceCsrfIsEnabled() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("AutoConfig")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(request("INVALID", new URI("/csrf")))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(csrfCreated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenDefaultConfigurationThenCsrfIsEnabled() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("AutoConfig")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(get("/csrf"))
|
||||
.andExpect(csrfInBody());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void headWhenDefaultConfigurationThenCsrfIsEnabled() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("AutoConfig")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(head("/csrf-in-header"))
|
||||
.andExpect(csrfInHeader());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void traceWhenDefaultConfigurationThenCsrfIsEnabled() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("AutoConfig")
|
||||
).autowire();
|
||||
|
||||
MockMvc traceEnabled = MockMvcBuilders
|
||||
.webAppContextSetup((WebApplicationContext) this.spring.getContext())
|
||||
.apply(springSecurity())
|
||||
.addDispatcherServletCustomizer(dispatcherServlet -> dispatcherServlet.setDispatchTraceRequest(true))
|
||||
.build();
|
||||
|
||||
traceEnabled.perform(request(HttpMethod.TRACE, "/csrf-in-header"))
|
||||
.andExpect(csrfInHeader());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void optionsWhenDefaultConfigurationThenCsrfIsEnabled() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("AutoConfig")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(options("/csrf-in-header"))
|
||||
.andExpect(csrfInHeader());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWhenCsrfDisabledThenRequestAllowed() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("CsrfDisabled")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(post("/ok"))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
assertThat(getFilter(this.spring, CsrfFilter.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWhenCsrfElementEnabledThenForbidden() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("CsrfEnabled")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(post("/csrf"))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(csrfCreated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putWhenCsrfElementEnabledThenForbidden() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("CsrfEnabled")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(put("/csrf"))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(csrfCreated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patchWhenCsrfElementEnabledThenForbidden() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("CsrfEnabled")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(patch("/csrf"))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(csrfCreated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteWhenCsrfElementEnabledThenForbidden() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("CsrfEnabled")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(delete("/csrf"))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(csrfCreated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidWhenCsrfElementEnabledThenForbidden() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("CsrfEnabled")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(request("INVALID", new URI("/csrf")))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(csrfCreated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenCsrfElementEnabledThenOk() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("CsrfEnabled")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(get("/csrf"))
|
||||
.andExpect(csrfInBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headWhenCsrfElementEnabledThenOk() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("CsrfEnabled")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(head("/csrf-in-header"))
|
||||
.andExpect(csrfInHeader());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void traceWhenCsrfElementEnabledThenOk() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("CsrfEnabled")
|
||||
).autowire();
|
||||
|
||||
MockMvc traceEnabled = MockMvcBuilders
|
||||
.webAppContextSetup((WebApplicationContext) this.spring.getContext())
|
||||
.apply(springSecurity())
|
||||
.addDispatcherServletCustomizer(dispatcherServlet -> dispatcherServlet.setDispatchTraceRequest(true))
|
||||
.build();
|
||||
|
||||
traceEnabled.perform(request(HttpMethod.TRACE, "/csrf-in-header"))
|
||||
.andExpect(csrfInHeader());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void optionsWhenCsrfElementEnabledThenOk() throws Exception {
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("CsrfEnabled")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(options("/csrf-in-header"))
|
||||
.andExpect(csrfInHeader());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autowireWhenCsrfElementEnabledThenCreatesCsrfRequestDataValueProcessor() {
|
||||
this.spring.configLocations(
|
||||
this.xml("CsrfEnabled")
|
||||
).autowire();
|
||||
|
||||
assertThat(this.spring.getContext().getBean(RequestDataValueProcessor.class)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWhenUsingCsrfAndCustomAccessDeniedHandlerThenTheHandlerIsAppropriatelyEngaged()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(
|
||||
this.xml("WithAccessDeniedHandler"),
|
||||
this.xml("shared-access-denied-handler")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(post("/ok"))
|
||||
.andExpect(status().isIAmATeapot());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWhenHasCsrfTokenButSessionExpiresThenRequestIsCancelledAfterSuccessfulAuthentication()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(
|
||||
this.xml("CsrfEnabled")
|
||||
).autowire();
|
||||
|
||||
// simulates a request that has no authentication (e.g. session time-out)
|
||||
MvcResult result = this.mvc.perform(post("/authenticated")
|
||||
.with(csrf()))
|
||||
.andExpect(redirectedUrl("http://localhost/login"))
|
||||
.andReturn();
|
||||
|
||||
MockHttpSession session = (MockHttpSession) result.getRequest().getSession();
|
||||
|
||||
// if the request cache is consulted, then it will redirect back to /some-url, which we don't want
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "password")
|
||||
.session(session)
|
||||
.with(csrf()))
|
||||
.andExpect(redirectedUrl("/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenHasCsrfTokenButSessionExpiresThenRequestIsRememeberedAfterSuccessfulAuthentication()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(
|
||||
this.xml("CsrfEnabled")
|
||||
).autowire();
|
||||
|
||||
// simulates a request that has no authentication (e.g. session time-out)
|
||||
MvcResult result =
|
||||
this.mvc.perform(get("/authenticated"))
|
||||
.andExpect(redirectedUrl("http://localhost/login"))
|
||||
.andReturn();
|
||||
|
||||
MockHttpSession session = (MockHttpSession) result.getRequest().getSession();
|
||||
|
||||
// if the request cache is consulted, then it will redirect back to /some-url, which we do want
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "password")
|
||||
.session(session)
|
||||
.with(csrf()))
|
||||
.andExpect(redirectedUrl("http://localhost/authenticated"));
|
||||
}
|
||||
|
||||
/**
|
||||
* SEC-2422: csrf expire CSRF token and session-management invalid-session-url
|
||||
*/
|
||||
@Test
|
||||
public void postWhenUsingCsrfAndCustomSessionManagementAndNoSessionThenStillRedirectsToInvalidSessionUrl()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(
|
||||
this.xml("WithSessionManagement")
|
||||
).autowire();
|
||||
|
||||
MvcResult result = this.mvc.perform(post("/ok").param("_csrf", "abc"))
|
||||
.andExpect(redirectedUrl("/error/sessionError"))
|
||||
.andReturn();
|
||||
|
||||
MockHttpSession session = (MockHttpSession) result.getRequest().getSession();
|
||||
|
||||
this.mvc.perform(post("/csrf")
|
||||
.session(session))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenUsingCustomRequestMatcherConfiguredThenAppliesAccordingly()
|
||||
throws Exception {
|
||||
|
||||
SpringTestContext context =
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("WithRequestMatcher"),
|
||||
this.xml("mock-request-matcher")
|
||||
);
|
||||
|
||||
context.autowire();
|
||||
|
||||
RequestMatcher matcher = context.getContext().getBean(RequestMatcher.class);
|
||||
when(matcher.matches(any(HttpServletRequest.class))).thenReturn(false);
|
||||
|
||||
this.mvc.perform(post("/ok")).andExpect(status().isOk());
|
||||
|
||||
when(matcher.matches(any(HttpServletRequest.class))).thenReturn(true);
|
||||
|
||||
this.mvc.perform(get("/ok")).andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenDefaultConfigurationThenSessionNotImmediatelyCreated()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("AutoConfig")
|
||||
).autowire();
|
||||
|
||||
MvcResult result = this.mvc.perform(get("/ok"))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
|
||||
assertThat(result.getRequest().getSession(false)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void postWhenCsrfMismatchesThenForbidden()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("AutoConfig")
|
||||
).autowire();
|
||||
|
||||
MvcResult result = this.mvc.perform(get("/ok")).andReturn();
|
||||
|
||||
MockHttpSession session = (MockHttpSession) result.getRequest().getSession();
|
||||
|
||||
this.mvc.perform(post("/ok")
|
||||
.session(session)
|
||||
.with(csrf().useInvalidToken()))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenDefaultConfigurationThenCsrfCleared()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("AutoConfig")
|
||||
).autowire();
|
||||
|
||||
MvcResult result = this.mvc.perform(get("/csrf")).andReturn();
|
||||
|
||||
MockHttpSession session = (MockHttpSession) result.getRequest().getSession();
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "password")
|
||||
.session(session)
|
||||
.with(csrf()))
|
||||
.andExpect(status().isFound());
|
||||
|
||||
this.mvc.perform(get("/csrf").session(session))
|
||||
.andExpect(csrfChanged(result));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logoutWhenDefaultConfigurationThenCsrfCleared()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("AutoConfig")
|
||||
).autowire();
|
||||
|
||||
MvcResult result = this.mvc.perform(get("/csrf")).andReturn();
|
||||
|
||||
MockHttpSession session = (MockHttpSession) result.getRequest().getSession();
|
||||
|
||||
this.mvc.perform(post("/logout").session(session)
|
||||
.with(csrf()))
|
||||
.andExpect(status().isFound());
|
||||
|
||||
this.mvc.perform(get("/csrf").session(session))
|
||||
.andExpect(csrfChanged(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* SEC-2495: csrf disables logout on GET
|
||||
*/
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void logoutWhenDefaultConfigurationThenDisabled()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(
|
||||
this.xml("shared-controllers"),
|
||||
this.xml("CsrfEnabled")
|
||||
).autowire();
|
||||
|
||||
this.mvc.perform(get("/logout")).andExpect(status().isNotFound());
|
||||
|
||||
// still logged in
|
||||
this.mvc.perform(get("/authenticated")).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
private <T extends Filter> T getFilter(SpringTestContext context, Class<T> type) {
|
||||
FilterChainProxy chain = context.getContext().getBean(FilterChainProxy.class);
|
||||
|
||||
List<Filter> filters = chain.getFilters("/any");
|
||||
|
||||
for ( Filter filter : filters ) {
|
||||
if ( type.isAssignableFrom(filter.getClass()) ) {
|
||||
return (T) filter;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String xml(String configName) {
|
||||
return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml";
|
||||
}
|
||||
|
||||
@Controller
|
||||
public static class RootController {
|
||||
@RequestMapping(value = "/csrf-in-header", method = { HEAD, TRACE, OPTIONS })
|
||||
@ResponseBody
|
||||
String csrfInHeaderAndBody(CsrfToken token, HttpServletResponse response) {
|
||||
response.setHeader(token.getHeaderName(), token.getToken());
|
||||
return csrfInBody(token);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/csrf", method = { POST, PUT, PATCH, DELETE, GET })
|
||||
@ResponseBody
|
||||
String csrfInBody(CsrfToken token) {
|
||||
return token.getToken();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/ok", method = { POST, GET })
|
||||
@ResponseBody
|
||||
String ok() {
|
||||
return "ok";
|
||||
}
|
||||
|
||||
@GetMapping("/authenticated")
|
||||
@ResponseBody
|
||||
String authenticated() {
|
||||
return "authenticated";
|
||||
}
|
||||
}
|
||||
|
||||
private static class TeapotAccessDeniedHandler implements AccessDeniedHandler {
|
||||
|
||||
@Override
|
||||
public void handle(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AccessDeniedException accessDeniedException) {
|
||||
|
||||
response.setStatus(HttpStatus.IM_A_TEAPOT_418);
|
||||
}
|
||||
}
|
||||
|
||||
ResultMatcher csrfChanged(MvcResult first) {
|
||||
return (second) -> {
|
||||
assertThat(first).isNotNull();
|
||||
assertThat(second).isNotNull();
|
||||
assertThat(first.getResponse().getContentAsString())
|
||||
.isNotEqualTo(second.getResponse().getContentAsString());
|
||||
};
|
||||
}
|
||||
|
||||
ResultMatcher csrfCreated() {
|
||||
return new CsrfCreatedResultMatcher();
|
||||
}
|
||||
|
||||
ResultMatcher csrfInHeader() {
|
||||
return new CsrfReturnedResultMatcher(result -> result.getResponse().getHeader("X-CSRF-TOKEN"));
|
||||
}
|
||||
|
||||
ResultMatcher csrfInBody() {
|
||||
return new CsrfReturnedResultMatcher(result -> result.getResponse().getContentAsString());
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface ExceptionalFunction<IN, OUT> {
|
||||
OUT apply(IN in) throws Exception;
|
||||
}
|
||||
|
||||
static class CsrfCreatedResultMatcher implements ResultMatcher {
|
||||
@Override
|
||||
public void match(MvcResult result) throws Exception {
|
||||
MockHttpServletRequest request = result.getRequest();
|
||||
CsrfToken token = WebTestUtils.getCsrfTokenRepository(request).loadToken(request);
|
||||
assertThat(token).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
static class CsrfReturnedResultMatcher implements ResultMatcher {
|
||||
ExceptionalFunction<MvcResult, String> token;
|
||||
|
||||
public CsrfReturnedResultMatcher(ExceptionalFunction<MvcResult, String> token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void match(MvcResult result) throws Exception {
|
||||
MockHttpServletRequest request = result.getRequest();
|
||||
CsrfToken token = WebTestUtils.getCsrfTokenRepository(request).loadToken(request);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getToken()).isEqualTo(this.token.apply(result));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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.http;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.web.WebAttributes;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
import static org.hamcrest.core.IsNull.nullValue;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class FormLoginBeanDefinitionParserTests {
|
||||
private static final String CONFIG_LOCATION_PREFIX =
|
||||
"classpath:org/springframework/security/config/http/FormLoginBeanDefinitionParserTests";
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
MockMvc mvc;
|
||||
|
||||
@Test
|
||||
public void getLoginWhenAutoConfigThenShowsDefaultLoginPage()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("Simple")).autowire();
|
||||
|
||||
String expectedContent =
|
||||
"<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>\n" +
|
||||
"<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>\n" +
|
||||
"<table>\n" +
|
||||
" <tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>\n" +
|
||||
" <tr><td>Password:</td><td><input type='password' name='password'/></td></tr>\n" +
|
||||
" <tr><td colspan='2'><input name=\"submit\" type=\"submit\" value=\"Login\"/></td></tr>\n" +
|
||||
"</table>\n" +
|
||||
"</form></body></html>";
|
||||
|
||||
this.mvc.perform(get("/login")).andExpect(content().string(expectedContent));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLoginWhenConfiguredWithCustomAttributesThenLoginPageReflects()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("WithCustomAttributes")).autowire();
|
||||
|
||||
String expectedContent =
|
||||
"<html><head><title>Login Page</title></head><body onload='document.f.custom_user.focus();'>\n" +
|
||||
"<h3>Login with Username and Password</h3><form name='f' action='/signin' method='POST'>\n" +
|
||||
"<table>\n" +
|
||||
" <tr><td>User:</td><td><input type='text' name='custom_user' value=''></td></tr>\n" +
|
||||
" <tr><td>Password:</td><td><input type='password' name='custom_pass'/></td></tr>\n" +
|
||||
" <tr><td colspan='2'><input name=\"submit\" type=\"submit\" value=\"Login\"/></td></tr>\n" +
|
||||
"</table>\n" +
|
||||
"</form></body></html>";
|
||||
|
||||
this.mvc.perform(get("/login")).andExpect(content().string(expectedContent));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLoginWhenConfiguredForOpenIdThenLoginPageReflects()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("WithOpenId")).autowire();
|
||||
|
||||
String expectedContent =
|
||||
"<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>\n" +
|
||||
"<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>\n" +
|
||||
"<table>\n" +
|
||||
" <tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>\n" +
|
||||
" <tr><td>Password:</td><td><input type='password' name='password'/></td></tr>\n" +
|
||||
" <tr><td colspan='2'><input name=\"submit\" type=\"submit\" value=\"Login\"/></td></tr>\n" +
|
||||
"</table>\n" +
|
||||
"</form><h3>Login with OpenID Identity</h3><form name='oidf' action='/login/openid' method='POST'>\n" +
|
||||
"<table>\n" +
|
||||
" <tr><td>Identity:</td><td><input type='text' size='30' name='openid_identifier'/></td></tr>\n" +
|
||||
" <tr><td colspan='2'><input name=\"submit\" type=\"submit\" value=\"Login\"/></td></tr>\n" +
|
||||
"</table>\n" +
|
||||
"</form></body></html>";
|
||||
|
||||
this.mvc.perform(get("/login")).andExpect(content().string(expectedContent));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLoginWhenConfiguredForOpenIdWithCustomAttributesThenLoginPageReflects()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("WithOpenIdCustomAttributes")).autowire();
|
||||
|
||||
String expectedContent =
|
||||
"<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>\n" +
|
||||
"<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>\n" +
|
||||
"<table>\n" +
|
||||
" <tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>\n" +
|
||||
" <tr><td>Password:</td><td><input type='password' name='password'/></td></tr>\n" +
|
||||
" <tr><td colspan='2'><input name=\"submit\" type=\"submit\" value=\"Login\"/></td></tr>\n" +
|
||||
"</table>\n" +
|
||||
"</form><h3>Login with OpenID Identity</h3><form name='oidf' action='/signin' method='POST'>\n" +
|
||||
"<table>\n" +
|
||||
" <tr><td>Identity:</td><td><input type='text' size='30' name='openid_identifier'/></td></tr>\n" +
|
||||
" <tr><td colspan='2'><input name=\"submit\" type=\"submit\" value=\"Login\"/></td></tr>\n" +
|
||||
"</table>\n" +
|
||||
"</form></body></html>";
|
||||
|
||||
this.mvc.perform(get("/login")).andExpect(content().string(expectedContent));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failedLoginWhenConfiguredWithCustomAuthenticationFailureThenForwardsAccordingly()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("WithAuthenticationFailureForwardUrl")).autowire();
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "bob")
|
||||
.param("password", "invalidpassword"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(forwardedUrl("/failure_forward_url"))
|
||||
.andExpect(request().attribute(WebAttributes.AUTHENTICATION_EXCEPTION, not(nullValue())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successfulLoginWhenConfiguredWithCustomAuthenticationSuccessThenForwardsAccordingly()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("WithAuthenticationSuccessForwardUrl")).autowire();
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "password"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(forwardedUrl("/success_forward_url"));
|
||||
}
|
||||
|
||||
private String xml(String configName) {
|
||||
return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml";
|
||||
}
|
||||
}
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* 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.http;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class FormLoginConfigTests {
|
||||
private static final String CONFIG_LOCATION_PREFIX =
|
||||
"classpath:org/springframework/security/config/http/FormLoginConfigTests";
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
MockMvc mvc;
|
||||
|
||||
@Test
|
||||
public void getProtectedPageWhenFormLoginConfiguredThenRedirectsToDefaultLoginPage()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("WithAntRequestMatcher")).autowire();
|
||||
|
||||
this.mvc.perform(get("/"))
|
||||
.andExpect(redirectedUrl("http://localhost/login"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenDefaultTargetUrlConfiguredThenRedirectsAccordingly()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("WithDefaultTargetUrl")).autowire();
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "password")
|
||||
.with(csrf()))
|
||||
.andExpect(redirectedUrl("/default"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenConfiguredWithSpelThenRedirectsAccordingly()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("UsingSpel")).autowire();
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "password")
|
||||
.with(csrf()))
|
||||
.andExpect(redirectedUrl(WebConfigUtilsTest.URL + "/default"));
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "wrong")
|
||||
.with(csrf()))
|
||||
.andExpect(redirectedUrl(WebConfigUtilsTest.URL + "/failure"));
|
||||
|
||||
this.mvc.perform(get("/"))
|
||||
.andExpect(redirectedUrl("http://localhost" + WebConfigUtilsTest.URL + "/login"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autowireWhenLoginPageIsMisconfiguredThenDetects() {
|
||||
|
||||
assertThatThrownBy(() -> this.spring.configLocations(this.xml("NoLeadingSlashLoginPage")).autowire())
|
||||
.isInstanceOf(BeanCreationException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autowireWhenDefaultTargetUrlIsMisconfiguredThenDetects() {
|
||||
|
||||
assertThatThrownBy(() -> this.spring.configLocations(this.xml("NoLeadingSlashDefaultTargetUrl")).autowire())
|
||||
.isInstanceOf(BeanCreationException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenCustomHandlerBeansConfiguredThenInvokesAccordingly()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("WithSuccessAndFailureHandlers")).autowire();
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "password")
|
||||
.with(csrf()))
|
||||
.andExpect(status().isIAmATeapot());
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "wrong")
|
||||
.with(csrf()))
|
||||
.andExpect(status().isIAmATeapot());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void authenticateWhenCustomUsernameAndPasswordParametersThenSucceeds()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("WithUsernameAndPasswordParameters")).autowire();
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.param("xname", "user")
|
||||
.param("xpass", "password")
|
||||
.with(csrf()))
|
||||
.andExpect(redirectedUrl("/"));
|
||||
}
|
||||
|
||||
/**
|
||||
* SEC-2919 - DefaultLoginGeneratingFilter incorrectly used if login-url="/login"
|
||||
*/
|
||||
@Test
|
||||
public void autowireWhenCustomLoginPageIsSlashLoginThenNoDefaultLoginPageGeneratingFilterIsWired()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("ForSec2919")).autowire();
|
||||
|
||||
this.mvc.perform(get("/login"))
|
||||
.andExpect(content().string("teapot"));
|
||||
|
||||
assertThat(getFilter(this.spring.getContext(), DefaultLoginPageGeneratingFilter.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenCsrfIsEnabledThenRequiresToken()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("WithCsrfEnabled")).autowire();
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "password"))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenCsrfIsDisabledThenDoesNotRequireToken()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("WithCsrfDisabled")).autowire();
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "password"))
|
||||
.andExpect(status().isFound());
|
||||
}
|
||||
|
||||
/**
|
||||
* SEC-3147: authentication-failure-url should be contained "error" parameter if login-page="/login"
|
||||
*/
|
||||
@Test
|
||||
public void authenticateWhenLoginPageIsSlashLoginAndAuthenticationFailsThenRedirectContainsErrorParameter()
|
||||
throws Exception {
|
||||
|
||||
this.spring.configLocations(this.xml("ForSec3147")).autowire();
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.param("username", "user")
|
||||
.param("password", "wrong")
|
||||
.with(csrf()))
|
||||
.andExpect(redirectedUrl("/login?error"));
|
||||
}
|
||||
|
||||
@RestController
|
||||
public static class LoginController {
|
||||
@GetMapping("/login")
|
||||
public String ok() {
|
||||
return "teapot";
|
||||
}
|
||||
}
|
||||
|
||||
public static class TeapotAuthenticationHandler implements
|
||||
AuthenticationSuccessHandler,
|
||||
AuthenticationFailureHandler {
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailure(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AuthenticationException exception) throws IOException, ServletException {
|
||||
|
||||
response.setStatus(HttpStatus.I_AM_A_TEAPOT.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Authentication authentication) throws IOException, ServletException {
|
||||
|
||||
response.setStatus(HttpStatus.I_AM_A_TEAPOT.value());
|
||||
}
|
||||
}
|
||||
|
||||
private Filter getFilter(ApplicationContext context, Class<? extends Filter> filterClass) {
|
||||
FilterChainProxy filterChain = context.getBean(BeanIds.FILTER_CHAIN_PROXY, FilterChainProxy.class);
|
||||
|
||||
List<Filter> filters = filterChain.getFilters("/any");
|
||||
|
||||
for ( Filter filter : filters ) {
|
||||
if ( filter.getClass() == filterClass ) {
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String xml(String configName) {
|
||||
return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml";
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.http;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Assertions for tests that rely on confirming behavior of the package-private SecurityFilters enum
|
||||
*
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class SecurityFiltersAssertions {
|
||||
private static Collection<SecurityFilters> ordered = Arrays.asList(SecurityFilters.values());
|
||||
|
||||
public static void assertEquals(List<String> filters) {
|
||||
List<String> expected = ordered.stream()
|
||||
.map(SecurityFilters::name)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertThat(filters).isEqualTo(expected);
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -31,7 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
public class CommonOAuth2ProviderTests {
|
||||
|
||||
private static final String DEFAULT_LOGIN_REDIRECT_URL = "{baseUrl}/login/oauth2/code/{registrationId}";
|
||||
private static final String DEFAULT_REDIRECT_URL = "{baseUrl}/{action}/oauth2/code/{registrationId}";
|
||||
|
||||
@Test
|
||||
public void getBuilderWhenGoogleShouldHaveGoogleSettings() throws Exception {
|
||||
@@ -51,7 +51,7 @@ public class CommonOAuth2ProviderTests {
|
||||
.isEqualTo(ClientAuthenticationMethod.BASIC);
|
||||
assertThat(registration.getAuthorizationGrantType())
|
||||
.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
|
||||
assertThat(registration.getRedirectUriTemplate()).isEqualTo(DEFAULT_LOGIN_REDIRECT_URL);
|
||||
assertThat(registration.getRedirectUriTemplate()).isEqualTo(DEFAULT_REDIRECT_URL);
|
||||
assertThat(registration.getScopes()).containsOnly("openid", "profile", "email");
|
||||
assertThat(registration.getClientName()).isEqualTo("Google");
|
||||
assertThat(registration.getRegistrationId()).isEqualTo("123");
|
||||
@@ -74,7 +74,7 @@ public class CommonOAuth2ProviderTests {
|
||||
.isEqualTo(ClientAuthenticationMethod.BASIC);
|
||||
assertThat(registration.getAuthorizationGrantType())
|
||||
.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
|
||||
assertThat(registration.getRedirectUriTemplate()).isEqualTo(DEFAULT_LOGIN_REDIRECT_URL);
|
||||
assertThat(registration.getRedirectUriTemplate()).isEqualTo(DEFAULT_REDIRECT_URL);
|
||||
assertThat(registration.getScopes()).containsOnly("read:user");
|
||||
assertThat(registration.getClientName()).isEqualTo("GitHub");
|
||||
assertThat(registration.getRegistrationId()).isEqualTo("123");
|
||||
@@ -97,7 +97,7 @@ public class CommonOAuth2ProviderTests {
|
||||
.isEqualTo(ClientAuthenticationMethod.POST);
|
||||
assertThat(registration.getAuthorizationGrantType())
|
||||
.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
|
||||
assertThat(registration.getRedirectUriTemplate()).isEqualTo(DEFAULT_LOGIN_REDIRECT_URL);
|
||||
assertThat(registration.getRedirectUriTemplate()).isEqualTo(DEFAULT_REDIRECT_URL);
|
||||
assertThat(registration.getScopes()).containsOnly("public_profile", "email");
|
||||
assertThat(registration.getClientName()).isEqualTo("Facebook");
|
||||
assertThat(registration.getRegistrationId()).isEqualTo("123");
|
||||
@@ -122,7 +122,7 @@ public class CommonOAuth2ProviderTests {
|
||||
.isEqualTo(ClientAuthenticationMethod.BASIC);
|
||||
assertThat(registration.getAuthorizationGrantType())
|
||||
.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
|
||||
assertThat(registration.getRedirectUriTemplate()).isEqualTo(DEFAULT_LOGIN_REDIRECT_URL);
|
||||
assertThat(registration.getRedirectUriTemplate()).isEqualTo(DEFAULT_REDIRECT_URL);
|
||||
assertThat(registration.getScopes()).containsOnly("openid", "profile", "email",
|
||||
"address", "phone");
|
||||
assertThat(registration.getClientName()).isEqualTo("Okta");
|
||||
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.web.server;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;
|
||||
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;
|
||||
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.Credentials.basicAuthenticationCredentials;
|
||||
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
|
||||
|
||||
/**
|
||||
* @author Denys Ivano
|
||||
* @since 5.0.5
|
||||
*/
|
||||
public class ExceptionHandlingSpecTests {
|
||||
private ServerHttpSecurity http = ServerHttpSecurityConfigurationBuilder.httpWithDefaultAuthentication();
|
||||
|
||||
@Test
|
||||
public void defaultAuthenticationEntryPoint() {
|
||||
SecurityWebFilterChain securityWebFilter = this.http
|
||||
.csrf().disable()
|
||||
.authorizeExchange()
|
||||
.anyExchange().authenticated()
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
.and()
|
||||
.build();
|
||||
|
||||
WebTestClient client = WebTestClientBuilder
|
||||
.bindToWebFilters(securityWebFilter)
|
||||
.build();
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/test")
|
||||
.exchange()
|
||||
.expectStatus().isUnauthorized()
|
||||
.expectHeader().valueMatches("WWW-Authenticate", "Basic.*");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customAuthenticationEntryPoint() {
|
||||
SecurityWebFilterChain securityWebFilter = this.http
|
||||
.csrf().disable()
|
||||
.authorizeExchange()
|
||||
.anyExchange().authenticated()
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(redirectServerAuthenticationEntryPoint("/auth"))
|
||||
.and()
|
||||
.build();
|
||||
|
||||
WebTestClient client = WebTestClientBuilder
|
||||
.bindToWebFilters(securityWebFilter)
|
||||
.build();
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/test")
|
||||
.exchange()
|
||||
.expectStatus().isFound()
|
||||
.expectHeader().valueMatches("Location", ".*");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultAccessDeniedHandler() {
|
||||
SecurityWebFilterChain securityWebFilter = this.http
|
||||
.csrf().disable()
|
||||
.httpBasic().and()
|
||||
.authorizeExchange()
|
||||
.anyExchange().hasRole("ADMIN")
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
.and()
|
||||
.build();
|
||||
|
||||
WebTestClient client = WebTestClientBuilder
|
||||
.bindToWebFilters(securityWebFilter)
|
||||
.filter(basicAuthentication())
|
||||
.build();
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/admin")
|
||||
.attributes(basicAuthenticationCredentials("user", "password"))
|
||||
.exchange()
|
||||
.expectStatus().isForbidden();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customAccessDeniedHandler() {
|
||||
SecurityWebFilterChain securityWebFilter = this.http
|
||||
.csrf().disable()
|
||||
.httpBasic().and()
|
||||
.authorizeExchange()
|
||||
.anyExchange().hasRole("ADMIN")
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
.accessDeniedHandler(httpStatusServerAccessDeniedHandler(HttpStatus.BAD_REQUEST))
|
||||
.and()
|
||||
.build();
|
||||
|
||||
WebTestClient client = WebTestClientBuilder
|
||||
.bindToWebFilters(securityWebFilter)
|
||||
.filter(basicAuthentication())
|
||||
.build();
|
||||
|
||||
client
|
||||
.get()
|
||||
.uri("/admin")
|
||||
.attributes(basicAuthenticationCredentials("user", "password"))
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
}
|
||||
|
||||
private ServerAuthenticationEntryPoint redirectServerAuthenticationEntryPoint(String location) {
|
||||
return new RedirectServerAuthenticationEntryPoint(location);
|
||||
}
|
||||
|
||||
private ServerAccessDeniedHandler httpStatusServerAccessDeniedHandler(HttpStatus httpStatus) {
|
||||
return new HttpStatusServerAccessDeniedHandler(httpStatus);
|
||||
}
|
||||
}
|
||||
+35
-1
@@ -17,6 +17,8 @@
|
||||
package org.springframework.security.config.web.server;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.NoSuchElementException;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
@@ -36,6 +38,8 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
@@ -204,9 +208,10 @@ public class FormLoginTests {
|
||||
|
||||
private LoginForm loginForm;
|
||||
|
||||
private OAuth2Login oauth2Login = new OAuth2Login();
|
||||
|
||||
public DefaultLoginPage(WebDriver webDriver) {
|
||||
this.driver = webDriver;
|
||||
this.loginForm = PageFactory.initElements(webDriver, LoginForm.class);
|
||||
}
|
||||
|
||||
static DefaultLoginPage create(WebDriver driver) {
|
||||
@@ -228,10 +233,23 @@ public class FormLoginTests {
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultLoginPage assertLoginFormNotPresent() {
|
||||
assertThatThrownBy(() -> loginForm().username(""))
|
||||
.isInstanceOf(NoSuchElementException.class);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginForm loginForm() {
|
||||
if (this.loginForm == null) {
|
||||
this.loginForm = PageFactory.initElements(this.driver, LoginForm.class);
|
||||
}
|
||||
return this.loginForm;
|
||||
}
|
||||
|
||||
public OAuth2Login oauth2Login() {
|
||||
return this.oauth2Login;
|
||||
}
|
||||
|
||||
static DefaultLoginPage to(WebDriver driver) {
|
||||
driver.get("http://localhost/login");
|
||||
return PageFactory.initElements(driver, DefaultLoginPage.class);
|
||||
@@ -263,6 +281,22 @@ public class FormLoginTests {
|
||||
return PageFactory.initElements(this.driver, page);
|
||||
}
|
||||
}
|
||||
|
||||
public class OAuth2Login {
|
||||
public WebElement findClientRegistrationByName(String clientName) {
|
||||
return DefaultLoginPage.this.driver.findElement(By.linkText(clientName));
|
||||
}
|
||||
|
||||
public OAuth2Login assertClientRegistrationByName(String clientName) {
|
||||
assertThatCode(() -> findClientRegistrationByName(clientName))
|
||||
.doesNotThrowAnyException();
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultLoginPage and() {
|
||||
return DefaultLoginPage.this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DefaultLogoutPage {
|
||||
|
||||
+42
-16
@@ -28,7 +28,7 @@ import org.springframework.test.web.reactive.server.FluxExchangeResult;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -45,7 +45,7 @@ public class HeaderSpecTests {
|
||||
|
||||
HttpHeaders expectedHeaders = new HttpHeaders();
|
||||
|
||||
Set<String> ignoredHeaderNames = Collections.singleton(HttpHeaders.CONTENT_TYPE);
|
||||
Set<String> headerNamesNotPresent = new HashSet<>();
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@@ -60,6 +60,23 @@ public class HeaderSpecTests {
|
||||
.add(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1 ; mode=block");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWhenDisableThenNoSecurityHeaders() {
|
||||
new HashSet<>(this.expectedHeaders.keySet()).forEach(this::expectHeaderNamesNotPresent);
|
||||
|
||||
this.headers.disable();
|
||||
|
||||
assertHeaders();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWhenDisableAndInvokedExplicitlyThenDefautsUsed() {
|
||||
this.headers.disable()
|
||||
.headers();
|
||||
|
||||
assertHeaders();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWhenDefaultsThenAllDefaultsWritten() {
|
||||
assertHeaders();
|
||||
@@ -67,9 +84,7 @@ public class HeaderSpecTests {
|
||||
|
||||
@Test
|
||||
public void headersWhenCacheDisableThenCacheNotWritten() {
|
||||
this.expectedHeaders.remove(HttpHeaders.CACHE_CONTROL);
|
||||
this.expectedHeaders.remove(HttpHeaders.PRAGMA);
|
||||
this.expectedHeaders.remove(HttpHeaders.EXPIRES);
|
||||
expectHeaderNamesNotPresent(HttpHeaders.CACHE_CONTROL, HttpHeaders.PRAGMA, HttpHeaders.EXPIRES);
|
||||
this.headers.cache().disable();
|
||||
|
||||
assertHeaders();
|
||||
@@ -77,7 +92,7 @@ public class HeaderSpecTests {
|
||||
|
||||
@Test
|
||||
public void headersWhenContentOptionsDisableThenContentTypeOptionsNotWritten() {
|
||||
this.expectedHeaders.remove(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS);
|
||||
expectHeaderNamesNotPresent(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS);
|
||||
this.headers.contentTypeOptions().disable();
|
||||
|
||||
assertHeaders();
|
||||
@@ -85,7 +100,7 @@ public class HeaderSpecTests {
|
||||
|
||||
@Test
|
||||
public void headersWhenHstsDisableThenHstsNotWritten() {
|
||||
this.expectedHeaders.remove(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);
|
||||
expectHeaderNamesNotPresent(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);
|
||||
this.headers.hsts().disable();
|
||||
|
||||
assertHeaders();
|
||||
@@ -103,7 +118,7 @@ public class HeaderSpecTests {
|
||||
|
||||
@Test
|
||||
public void headersWhenFrameOptionsDisableThenFrameOptionsNotWritten() {
|
||||
this.expectedHeaders.remove(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS);
|
||||
expectHeaderNamesNotPresent(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS);
|
||||
this.headers.frameOptions().disable();
|
||||
|
||||
assertHeaders();
|
||||
@@ -111,22 +126,29 @@ public class HeaderSpecTests {
|
||||
|
||||
@Test
|
||||
public void headersWhenFrameOptionsModeThenFrameOptionsCustomMode() {
|
||||
this.expectedHeaders.remove(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS);
|
||||
this.expectedHeaders
|
||||
.add(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, "SAMEORIGIN");
|
||||
this.headers.frameOptions().mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN);
|
||||
this.expectedHeaders.set(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, "SAMEORIGIN");
|
||||
this.headers
|
||||
.frameOptions()
|
||||
.mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN);
|
||||
|
||||
assertHeaders();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWhenXssProtectionDisableThenXssProtectionNotWritten() {
|
||||
this.expectedHeaders.remove("X-Xss-Protection");
|
||||
expectHeaderNamesNotPresent("X-Xss-Protection");
|
||||
this.headers.xssProtection().disable();
|
||||
|
||||
assertHeaders();
|
||||
}
|
||||
|
||||
private void expectHeaderNamesNotPresent(String... headerNames) {
|
||||
for(String headerName : headerNames) {
|
||||
this.expectedHeaders.remove(headerName);
|
||||
this.headerNamesNotPresent.add(headerName);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertHeaders() {
|
||||
WebTestClient client = buildClient();
|
||||
FluxExchangeResult<String> response = client.get()
|
||||
@@ -135,10 +157,14 @@ public class HeaderSpecTests {
|
||||
.returnResult(String.class);
|
||||
|
||||
Map<String, List<String>> responseHeaders = response.getResponseHeaders();
|
||||
this.ignoredHeaderNames.stream().forEach(responseHeaders::remove);
|
||||
|
||||
assertThat(responseHeaders).describedAs(response.toString()).isEqualTo(
|
||||
this.expectedHeaders);
|
||||
if (!this.expectedHeaders.isEmpty()) {
|
||||
assertThat(responseHeaders).describedAs(response.toString())
|
||||
.containsAllEntriesOf(this.expectedHeaders);
|
||||
}
|
||||
if (!this.headerNamesNotPresent.isEmpty()) {
|
||||
assertThat(responseHeaders.keySet()).doesNotContainAnyElementsOf(this.headerNamesNotPresent);
|
||||
}
|
||||
}
|
||||
|
||||
private WebTestClient buildClient() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user