1
0
mirror of synced 2026-07-13 06:40:03 +00:00

Compare commits

...

42 Commits

Author SHA1 Message Date
Rob Winch 21e092c141 rm .github/workflows for unsupported branch 2025-05-02 12:34:48 -05:00
Josh Cummings c7d9b4e7ec Add End-of-Life Notice 2020-10-07 12:11:11 -06:00
Josh Cummings 764685dd58 Remove from CI 2020-10-07 12:10:58 -06:00
Josh Cummings 05349139a4 Next Development Version 2020-10-07 11:54:06 -06:00
Josh Cummings 33aba4fd64 Release 5.0.19.RELEASE 2020-10-07 10:47:38 -06:00
Eleftheria Stein 7641dc0f3b Update to Hibernate Validator 6.0.21
Closes gh-9069
2020-10-01 19:09:15 +02:00
Eleftheria Stein 66b52c5d5f Update to org.aspectj 1.9.6
Closes gh-9067
2020-10-01 19:08:55 +02:00
Eleftheria Stein d86b1492a2 Update to Jackson Databind 2.9.10.6
Closes gh-9065
2020-10-01 19:08:33 +02:00
Eleftheria Stein 50c1c5e082 Update to Spring Framework 5.0.19
Closes gh-9064
2020-10-01 19:08:06 +02:00
Eleftheria Stein 2a9b41df61 Update to GAE 1.9.82
Closes gh-9066
2020-10-01 19:07:34 +02:00
Malyshau Stanislau 430407ea45 Add try-with-resources to close stream
Closes gh-9041
2020-09-29 08:56:40 -06:00
Artem Grankin 7358eb2b48 Replace expired msdn link with latest web archive copy
Initial link expired in March, 2016. Latest copy found in web archive is from February, 2016
2020-09-28 17:47:25 -06:00
Josh Cummings 50faf5b3f2 Next Development Version 2020-08-05 09:11:50 -06:00
Josh Cummings 1fd4e940f4 Release 5.0.18.RELEASE 2020-08-05 07:47:14 -06:00
Josh Cummings 8a8b389907 Update to Spring Ldap 2.3.3
Closes gh-8933
2020-08-05 07:45:14 -06:00
Josh Cummings d51dd0450d Update to Hibernate Validator 6.0.20
Closes gh-8932
2020-08-05 07:45:09 -06:00
Josh Cummings 5c0b080eb6 Update to Groovy 2.4.20
Closes gh-8931
2020-08-05 07:44:44 -06:00
Josh Cummings b5b3ef979f Update to Google App Engine 1.9.81
Closes gh-8930
2020-08-05 07:44:37 -06:00
Josh Cummings d5c264792a Update to Jackson Databind 2.9.10.5
Closes gh-8929
2020-08-05 07:44:30 -06:00
Josh Cummings f280d6e90e Update to Spring Framework 5.0.18
Closes gh-8928
2020-08-05 07:44:03 -06:00
Josh Cummings 2303ac3d6f Polish WebSecurityConfigurerAdapter JavaDoc
Issue gh-8784
2020-07-20 15:25:39 -06:00
Romil Patel ef442def43 WebSecurityConfigurerAdapter JavaDoc
Closes gh-8784
2020-07-20 15:25:36 -06:00
wangsong 502d28f1b9 Fix ProviderManager Javadoc typo
Closes gh-8800
2020-07-07 17:18:04 -05:00
Ellie Bahadori aa5a42cfbc Use Github Actions workflow for PRs and remove Travis
Closes gh-8716
2020-06-30 05:24:55 -04:00
Rob Winch 5802954130 Better scp Retry Settings 2020-06-25 11:37:09 -05:00
Evgeniy Cheban b4e51e7740 DefaultWebSecurityExpressionHandler uses RoleHierarchy bean
Fixes gh-7059
2020-06-11 08:37:09 -04:00
Eleftheria Stein e0169eaea8 Next development version 2020-06-03 18:36:01 -04:00
Eleftheria Stein bcaad3a51c Release 5.0.17.RELEASE 2020-06-03 18:12:04 -04:00
Josh Cummings 66f8a56b1b Polish setAllowedHostnames
Added JavaDoc to method, including @since attribute

Issue gh-4310
2020-06-03 08:48:46 -06:00
Eddú Meléndez 3e5b65f647 Add support for allowedHostnames in StrictHttpFirewall
Introduce a new method `setAllowedHostnames` which perform the validation
against untrusted hostnames.

Fixes gh-4310
2020-06-03 08:48:38 -06:00
Eleftheria Stein c62789c976 Update to okhttp 3.12.12
Fixes gh-8637
2020-06-02 22:05:05 -04:00
Eleftheria Stein b02cae0a0c Update to Jython 2.5.3
Fixes gh-8638
2020-06-02 22:04:02 -04:00
Eleftheria Stein af278b58b5 Update to mockwebserver 3.12.12
Fixes gh-8636
2020-06-02 22:03:22 -04:00
justmehyp ba81f6a06a Remove unused field 'digester' in Md4PasswordEncoder
`private Digester digester;`  defined in Md4PasswordEncoder is never used. So remove it.

Closes gh-8553
2020-05-21 11:21:58 -05:00
Maksim Vinogradov a39efaf883 Prevent StackOverflowError for AccessControlEntryImpl.hashCode
Getting StackOverflowError when invoke AclImpl.hashCode because of
cross-references between AclImpl and AccessControlEntryImpl

Remove from AccessControlEntryImpl.hashCode method invocation of
acl.hashCode

fixes gh-5401
2020-05-21 10:09:22 -05:00
Rob Winch 7d5e032e25 Revert "Create the CSRF token on the bounded elactic scheduler"
This reverts commit e21ef422e7.
2020-05-18 11:09:41 -05:00
cbornet e21ef422e7 Create the CSRF token on the bounded elactic scheduler
The CSRF token is created with a call to UUID.randomUUID which is blocking.
This change ensures this blocking call is done on the bounded elastic scheduler which supports blocking calls.

Fixes gh-8128
2020-05-18 11:07:41 -05:00
Artyom Tarynin 66f923dab7 Update AntPathRequestMatcher.java
Fixes gh-8512
2020-05-14 10:58:32 -04:00
Dávid Kovács e8daeacd89 Document NoOpPasswordEncoder will not be removed
This commit adds extension to deprecation notice.

Fixes gh-8506
2020-05-13 12:57:04 -05:00
Rob Winch 27f30b04cd Remove Broken Test
Issue gh-8518
2020-05-12 13:38:42 -05:00
Rob Winch baa238e339 Fix non-standard HTTP method for CsrfWebFilter
Closes gh-8452
2020-05-12 13:22:04 -05:00
Eleftheria Stein cf5bd52121 Next development version 2020-05-06 16:40:35 -04:00
21 changed files with 185 additions and 179 deletions
-16
View File
@@ -1,16 +0,0 @@
language: java
jdk:
- openjdk8
os:
- linux
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
script: ./gradlew build --refresh-dependencies --no-daemon --continue
Vendored
-118
View File
@@ -1,118 +0,0 @@
def projectProperties = [
[$class: 'BuildDiscarderProperty',
strategy: [$class: 'LogRotator', numToKeepStr: '5']],
pipelineTriggers([cron('@daily')])
]
properties(projectProperties)
def SUCCESS = hudson.model.Result.SUCCESS.toString()
currentBuild.result = SUCCESS
try {
parallel check: {
stage('Check') {
node {
checkout scm
try {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
sh "./gradlew clean check --refresh-dependencies --no-daemon --stacktrace"
}
} catch(Exception e) {
currentBuild.result = 'FAILED: check'
throw e
} finally {
junit '**/build/*-results/*.xml'
}
}
}
},
sonar: {
stage('Sonar') {
node {
checkout scm
withCredentials([string(credentialsId: 'spring-sonar.login', variable: 'SONAR_LOGIN')]) {
try {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
if ("master" == env.BRANCH_NAME) {
sh "./gradlew sonarqube -PexcludeProjects='**/samples/**' -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon --stacktrace"
} else {
sh "./gradlew sonarqube -PexcludeProjects='**/samples/**' -Dsonar.projectKey='spring-security-${env.BRANCH_NAME}' -Dsonar.projectName='spring-security-${env.BRANCH_NAME}' -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon --stacktrace"
}
}
} catch(Exception e) {
currentBuild.result = 'FAILED: sonar'
throw e
}
}
}
}
}
if(currentBuild.result == 'SUCCESS') {
parallel artifacts: {
stage('Deploy Artifacts') {
node {
checkout scm
withCredentials([file(credentialsId: 'spring-signing-secring.gpg', variable: 'SIGNING_KEYRING_FILE')]) {
withCredentials([string(credentialsId: 'spring-gpg-passphrase', variable: 'SIGNING_PASSWORD')]) {
withCredentials([usernamePassword(credentialsId: 'oss-token', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USERNAME')]) {
withCredentials([usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')]) {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
sh "./gradlew deployArtifacts finalizeDeployArtifacts -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password='$SIGNING_PASSWORD' -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --refresh-dependencies --no-daemon --stacktrace"
}
}
}
}
}
}
}
},
docs: {
stage('Deploy Docs') {
node {
checkout scm
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
sh "./gradlew deployDocs -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace"
}
}
}
}
},
schema: {
stage('Deploy Schema') {
node {
checkout scm
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
sh "./gradlew deploySchema -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace"
}
}
}
}
}
}
} finally {
def buildStatus = currentBuild.result
def buildNotSuccess = !SUCCESS.equals(buildStatus)
def lastBuildNotSuccess = !SUCCESS.equals(currentBuild.previousBuild?.result)
if(buildNotSuccess || lastBuildNotSuccess) {
stage('Notifiy') {
node {
final def RECIPIENTS = [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']]
def subject = "${buildStatus}: Build ${env.JOB_NAME} ${env.BUILD_NUMBER} status is now ${buildStatus}"
def details = """The build status changed to ${buildStatus}. For details see ${env.BUILD_URL}"""
emailext (
subject: subject,
body: details,
recipientProviders: RECIPIENTS,
to: "$SPRING_SECURITY_TEAM_EMAILS"
)
}
}
}
}
+5 -1
View File
@@ -1,6 +1,10 @@
image::https://badges.gitter.im/Join%20Chat.svg[Gitter,link=https://gitter.im/spring-projects/spring-security?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge]
image:https://travis-ci.org/spring-projects/spring-security.svg?branch=master["Build Status", link="https://travis-ci.org/spring-projects/spring-security"]
[NOTE]
======
This branch of Spring Security has reached its https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions#supported-versions[End of Life], meaning that there are no further maintenance releases or security patches planned.
Please migrate to a supported branch as soon as possible.
======
= Spring Security
@@ -1,5 +1,5 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -131,10 +131,9 @@ public class AccessControlEntryImpl implements AccessControlEntry,
@Override
public int hashCode() {
int result = this.acl.hashCode();
result = 31 * result + this.permission.hashCode();
int result = this.permission.hashCode();
result = 31 * result + (this.id != null ? this.id.hashCode() : 0);
result = 31 * result + this.sid.hashCode();
result = 31 * result + (this.sid.hashCode());
result = 31 * result + (this.auditFailure ? 1 : 0);
result = 31 * result + (this.auditSuccess ? 1 : 0);
result = 31 * result + (this.granting ? 1 : 0);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -560,6 +560,25 @@ public class AclImplTests {
childAcl.setParent(changeParentAcl);
}
@Test
public void hashCodeWithoutStackOverFlow() throws Exception {
//given
Sid sid = new PrincipalSid("pSid");
ObjectIdentity oid = new ObjectIdentityImpl("type", 1);
AclAuthorizationStrategy authStrategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("role"));
PermissionGrantingStrategy grantingStrategy = new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());
AclImpl acl = new AclImpl(oid, 1L, authStrategy, grantingStrategy, null, null, false, sid);
AccessControlEntryImpl ace = new AccessControlEntryImpl(1L, acl, sid, BasePermission.READ, true, true, true);
Field fieldAces = FieldUtils.getField(AclImpl.class, "aces");
fieldAces.setAccessible(true);
List<AccessControlEntryImpl> aces = (List<AccessControlEntryImpl>) fieldAces.get(acl);
aces.add(ace);
//when - then none StackOverFlowError been raised
ace.hashCode();
}
// ~ Inner Classes
// ==================================================================================================
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ 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.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder;
@@ -74,6 +75,7 @@ import org.springframework.web.filter.DelegatingFilterProxy;
* @see WebSecurityConfiguration
*
* @author Rob Winch
* @author Evgeniy Cheban
* @since 3.2
*/
public final class WebSecurity extends
@@ -383,6 +385,11 @@ public final class WebSecurity extends
throws BeansException {
this.defaultWebSecurityExpressionHandler
.setApplicationContext(applicationContext);
try {
this.defaultWebSecurityExpressionHandler.setRoleHierarchy(applicationContext.getBean(RoleHierarchy.class));
} catch (NoSuchBeanDefinitionException e) {}
try {
this.defaultWebSecurityExpressionHandler.setPermissionEvaluator(applicationContext.getBean(
PermissionEvaluator.class));
@@ -332,6 +332,13 @@ public abstract class WebSecurityConfigurerAdapter implements
/**
* Override this method to configure {@link WebSecurity}. For example, if you wish to
* ignore certain requests.
*
* Endpoints specified in this method will be ignored by Spring Security, meaning it
* will not protect them from CSRF, XSS, Clickjacking, and so on.
*
* Instead, if you want to protect endpoints against common vulnerabilities, then see
* {@link #configure(HttpSecurity)} and the {@link HttpSecurity#authorizeRequests}
* configuration method.
*/
public void configure(WebSecurity web) throws Exception {
}
@@ -345,6 +352,10 @@ public abstract class WebSecurityConfigurerAdapter implements
* http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
* </pre>
*
* Any endpoint that requires defense against common vulnerabilities can be specified here, including public ones.
* See {@link HttpSecurity#authorizeRequests} and the `permitAll()` authorization rule
* for more details on public endpoints.
*
* @param http the {@link HttpSecurity} to modify
* @throws Exception if an error occurs
*/
@@ -160,7 +160,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends
*
* <p>
* Allows customizing the {@link XXssProtectionHeaderWriter} which adds the <a href=
* "https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx"
* "https://web.archive.org/web/20160201174302/https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx"
* >X-XSS-Protection header</a>
* </p>
*
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,6 +45,8 @@ 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.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -68,6 +70,7 @@ import org.springframework.web.bind.annotation.RestController;
*
* @author Rob Winch
* @author Joe Grandja
* @author Evgeniy Cheban
*/
public class WebSecurityConfigurationTests {
@Rule
@@ -270,6 +273,31 @@ public class WebSecurityConfigurationTests {
}
}
@Test
public void securityExpressionHandlerWhenRoleHierarchyBeanThenRoleHierarchyUsed() {
this.spring.register(WebSecurityExpressionHandlerRoleHierarchyBeanConfig.class).autowire();
TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "notused", "ROLE_ADMIN");
FilterInvocation invocation = new FilterInvocation(new MockHttpServletRequest("GET", ""),
new MockHttpServletResponse(), new MockFilterChain());
AbstractSecurityExpressionHandler handler = this.spring.getContext().getBean(AbstractSecurityExpressionHandler.class);
EvaluationContext evaluationContext = handler.createEvaluationContext(authentication, invocation);
Expression expression = handler.getExpressionParser()
.parseExpression("hasRole('ROLE_USER')");
boolean granted = expression.getValue(evaluationContext, Boolean.class);
assertThat(granted).isTrue();
}
@EnableWebSecurity
static class WebSecurityExpressionHandlerRoleHierarchyBeanConfig extends WebSecurityConfigurerAdapter {
@Bean
RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
return roleHierarchy;
}
}
@Test
public void securityExpressionHandlerWhenPermissionEvaluatorBeanThenPermissionEvaluatorUsed() throws Exception {
this.spring.register(WebSecurityExpressionHandlerPermissionEvaluatorBeanConfig.class).autowire();
@@ -218,7 +218,7 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
((CredentialsContainer) result).eraseCredentials();
}
// If the parent AuthenticationManager was attempted and successful than it will publish an AuthenticationSuccessEvent
// If the parent AuthenticationManager was attempted and successful then it will publish an AuthenticationSuccessEvent
// This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published it
if (parentResult == null) {
eventPublisher.publishAuthenticationSuccess(result);
@@ -235,7 +235,7 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
"No AuthenticationProvider found for {0}"));
}
// If the parent AuthenticationManager was attempted and failed than it will publish an AbstractAuthenticationFailureEvent
// If the parent AuthenticationManager was attempted and failed then it will publish an AbstractAuthenticationFailureEvent
// This check prevents a duplicate AbstractAuthenticationFailureEvent if the parent AuthenticationManager already published it
if (parentException == null) {
prepareException(lastException, authentication);
@@ -21,6 +21,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.SpringVersion;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
@@ -108,8 +109,9 @@ public class SpringSecurityCoreVersion {
*/
private static String getSpringVersion() {
Properties properties = new Properties();
try {
properties.load(SpringSecurityCoreVersion.class.getClassLoader().getResourceAsStream("META-INF/spring-security.versions"));
try (InputStream is = SpringSecurityCoreVersion.class.getClassLoader()
.getResourceAsStream("META-INF/spring-security.versions")) {
properties.load(is);
} catch (IOException e) {
return null;
}
@@ -83,8 +83,6 @@ public class Md4PasswordEncoder implements PasswordEncoder {
private StringKeyGenerator saltGenerator = new Base64StringKeyGenerator();
private boolean encodeHashAsBase64;
private Digester digester;
public void setEncodeHashAsBase64(boolean encodeHashAsBase64) {
this.encodeHashAsBase64 = encodeHashAsBase64;
@@ -26,7 +26,8 @@ package org.springframework.security.crypto.password;
* @deprecated This PasswordEncoder is not secure. Instead use an
* adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or
* SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports
* password upgrades.
* password upgrades. There are no plans to remove this support. It is deprecated to indicate that
* this is a legacy implementation and using it is considered insecure.
*/
@Deprecated
public final class NoOpPasswordEncoder implements PasswordEncoder {
@@ -10,6 +10,13 @@ asciidoctor {
'gh-samples-url': "$ghUrl/samples"
}
remotes {
docs {
retryCount = 5 // retry 5 times (default is 0)
retryWaitSec = 10 // wait 10 seconds between retries (default is 0)
}
}
docsZip {
from(project(':spring-security-docs-guides').asciidoctor) {
into 'guides'
+2 -2
View File
@@ -1,3 +1,3 @@
gaeVersion=1.9.80
gaeVersion=1.9.82
springBootVersion=2.0.9.RELEASE
version=5.0.16.RELEASE
version=5.0.20.BUILD-SNAPSHOT
+18 -18
View File
@@ -1,12 +1,12 @@
dependencyManagement {
imports {
mavenBom 'io.projectreactor:reactor-bom:Bismuth-SR17'
mavenBom 'org.springframework:spring-framework-bom:5.0.17.RELEASE'
mavenBom 'org.springframework:spring-framework-bom:5.0.19.RELEASE'
mavenBom 'org.springframework.data:spring-data-releasetrain:Kay-SR14'
}
dependencies {
dependency 'cglib:cglib-nodep:3.2.12'
dependency 'com.squareup.okhttp3:mockwebserver:3.12.10'
dependency 'com.squareup.okhttp3:mockwebserver:3.12.12'
dependency 'opensymphony:sitemesh:2.4.2'
dependency 'org.gebish:geb-spock:0.10.0'
dependency 'org.jasig.cas:cas-server-webapp:4.2.7'
@@ -16,7 +16,7 @@ dependencyManagement {
dependency 'org.powermock:powermock-module-junit4-common:2.0.7'
dependency 'org.powermock:powermock-module-junit4:2.0.7'
dependency 'org.powermock:powermock-reflect:2.0.7'
dependency 'org.python:jython:2.5.0'
dependency 'org.python:jython:2.5.3'
dependency 'org.spockframework:spock-core:1.0-groovy-2.4'
dependency 'org.spockframework:spock-spring:1.0-groovy-2.4'
}
@@ -30,21 +30,21 @@ dependencyManagement {
dependency 'ch.qos.logback:logback-core:1.2.3'
dependency 'com.fasterxml.jackson.core:jackson-annotations:2.9.10'
dependency 'com.fasterxml.jackson.core:jackson-core:2.9.10'
dependency 'com.fasterxml.jackson.core:jackson-databind:2.9.10.2'
dependency 'com.fasterxml.jackson.core:jackson-databind:2.9.10.6'
dependency 'com.fasterxml:classmate:1.3.4'
dependency 'com.github.stephenc.jcip:jcip-annotations:1.0-1'
dependency 'com.google.appengine:appengine-api-1.0-sdk:1.9.80'
dependency 'com.google.appengine:appengine-api-labs:1.9.80'
dependency 'com.google.appengine:appengine-api-stubs:1.9.80'
dependency 'com.google.appengine:appengine-testing:1.9.80'
dependency 'com.google.appengine:appengine:1.9.80'
dependency 'com.google.appengine:appengine-api-1.0-sdk:1.9.82'
dependency 'com.google.appengine:appengine-api-labs:1.9.82'
dependency 'com.google.appengine:appengine-api-stubs:1.9.82'
dependency 'com.google.appengine:appengine-testing:1.9.82'
dependency 'com.google.appengine:appengine:1.9.82'
dependency 'com.google.code.gson:gson:2.8.2'
dependency 'com.google.guava:guava:20.0'
dependency 'com.google.inject:guice:3.0'
dependency 'com.nimbusds:lang-tag:1.4.3'
dependency 'com.nimbusds:nimbus-jose-jwt:5.14'
dependency 'com.nimbusds:oauth2-oidc-sdk:5.64.4'
dependency 'com.squareup.okhttp3:okhttp:3.12.10'
dependency 'com.squareup.okhttp3:okhttp:3.12.12'
dependency 'com.squareup.okio:okio:1.13.0'
dependency 'com.sun.xml.bind:jaxb-core:2.3.0.1'
dependency 'com.sun.xml.bind:jaxb-impl:2.3.0.1'
@@ -128,16 +128,16 @@ dependencyManagement {
dependency 'org.apache.tomcat.embed:tomcat-embed-logging-log4j:8.0.44'
dependency 'org.apache.tomcat.embed:tomcat-embed-websocket:8.5.23'
dependency 'org.apache.tomcat:tomcat-annotations-api:8.5.23'
dependency 'org.aspectj:aspectjrt:1.9.5'
dependency 'org.aspectj:aspectjtools:1.9.5'
dependency 'org.aspectj:aspectjweaver:1.9.5'
dependency 'org.aspectj:aspectjrt:1.9.6'
dependency 'org.aspectj:aspectjtools:1.9.6'
dependency 'org.aspectj:aspectjweaver:1.9.6'
dependency 'org.assertj:assertj-core:3.11.1'
dependency 'org.attoparser:attoparser:2.0.4.RELEASE'
dependency 'org.bouncycastle:bcpkix-jdk15on:1.64'
dependency 'org.bouncycastle:bcprov-jdk15on:1.64'
dependency 'org.codehaus.groovy:groovy-all:2.4.19'
dependency 'org.codehaus.groovy:groovy-json:2.4.19'
dependency 'org.codehaus.groovy:groovy:2.4.19'
dependency 'org.codehaus.groovy:groovy-all:2.4.20'
dependency 'org.codehaus.groovy:groovy-json:2.4.20'
dependency 'org.codehaus.groovy:groovy:2.4.20'
dependency 'org.eclipse.jdt:ecj:3.12.3'
dependency 'org.eclipse.jetty.websocket:websocket-api:9.4.7.v20170914'
dependency 'org.eclipse.jetty.websocket:websocket-client:9.4.7.v20170914'
@@ -160,7 +160,7 @@ dependencyManagement {
dependency 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final'
dependency 'org.hibernate:hibernate-core:5.2.18.Final'
dependency 'org.hibernate:hibernate-entitymanager:5.2.18.Final'
dependency 'org.hibernate:hibernate-validator:6.0.19.Final'
dependency 'org.hibernate:hibernate-validator:6.0.21.Final'
dependency 'org.hsqldb:hsqldb:2.4.1'
dependency 'org.jasig.cas.client:cas-client-core:3.5.1'
dependency 'org.javassist:javassist:3.22.0-CR2'
@@ -183,7 +183,7 @@ dependencyManagement {
dependency 'org.slf4j:slf4j-api:1.7.30'
dependency 'org.slf4j:slf4j-nop:1.7.30'
dependency 'org.sonatype.sisu.inject:cglib:2.2.1-v20090111'
dependency 'org.springframework.ldap:spring-ldap-core:2.3.2.RELEASE'
dependency 'org.springframework.ldap:spring-ldap-core:2.3.3.RELEASE'
dependency 'org.thymeleaf:thymeleaf-spring5:3.0.11.RELEASE'
dependency 'org.unbescape:unbescape:1.1.5.RELEASE'
dependency 'org.w3c.css:sac:1.3'
@@ -228,10 +228,15 @@ class DummyRequest extends HttpServletRequestWrapper {
public void setQueryString(String queryString) {
this.queryString = queryString;
}
@Override
public String getServerName() {
return null;
}
}
final class UnsupportedOperationExceptionInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
throw new UnsupportedOperationException(method + " is not supported");
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,14 +16,15 @@
package org.springframework.security.web.firewall;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* <p>
@@ -59,10 +60,15 @@ import java.util.Set;
* Rejects URLs that contain a URL encoded percent. See
* {@link #setAllowUrlEncodedPercent(boolean)}
* </li>
* <li>
* Rejects hosts that are not allowed. See
* {@link #setAllowedHostnames(Predicate)}
* </li>
* </ul>
*
* @see DefaultHttpFirewall
* @author Rob Winch
* @author Eddú Meléndez
* @since 5.0.1
*/
public class StrictHttpFirewall implements HttpFirewall {
@@ -82,6 +88,8 @@ public class StrictHttpFirewall implements HttpFirewall {
private Set<String> decodedUrlBlacklist = new HashSet<String>();
private Predicate<String> allowedHostnames = hostname -> true;
public StrictHttpFirewall() {
urlBlacklistsAddAll(FORBIDDEN_SEMICOLON);
urlBlacklistsAddAll(FORBIDDEN_FORWARDSLASH);
@@ -230,6 +238,21 @@ public class StrictHttpFirewall implements HttpFirewall {
}
}
/**
* <p>
* Determines which hostnames should be allowed. The default is to allow any hostname.
* </p>
*
* @param allowedHostnames the predicate for testing hostnames
* @since 5.0.17
*/
public void setAllowedHostnames(Predicate<String> allowedHostnames) {
if (allowedHostnames == null) {
throw new IllegalArgumentException("allowedHostnames cannot be null");
}
this.allowedHostnames = allowedHostnames;
}
private void urlBlacklistsAddAll(Collection<String> values) {
this.encodedUrlBlacklist.addAll(values);
this.decodedUrlBlacklist.addAll(values);
@@ -243,6 +266,7 @@ public class StrictHttpFirewall implements HttpFirewall {
@Override
public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
rejectedBlacklistedUrls(request);
rejectedUntrustedHosts(request);
if (!isNormalized(request)) {
throw new RequestRejectedException("The request was rejected because the URL was not normalized.");
@@ -272,6 +296,13 @@ public class StrictHttpFirewall implements HttpFirewall {
}
}
private void rejectedUntrustedHosts(HttpServletRequest request) {
String serverName = request.getServerName();
if (serverName != null && !this.allowedHostnames.test(serverName)) {
throw new RequestRejectedException("The request was rejected because the domain " + serverName + " is untrusted.");
}
}
@Override
public HttpServletResponse getFirewalledResponse(HttpServletResponse response) {
return new FirewalledResponse(response);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,6 +54,7 @@ import java.util.Set;
* </p>
*
* @author Rob Winch
* @author Parikshit Dutta
* @since 5.0
*/
public class CsrfWebFilter implements WebFilter {
@@ -133,7 +134,7 @@ public class CsrfWebFilter implements WebFilter {
@Override
public Mono<MatchResult> matches(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest())
.map(r -> r.getMethod())
.flatMap(r -> Mono.justOrEmpty(r.getMethod()))
.filter(m -> ALLOWED_METHODS.contains(m))
.flatMap(m -> MatchResult.notMatch())
.switchIfEmpty(MatchResult.match());
@@ -67,7 +67,7 @@ public final class AntPathRequestMatcher
/**
* Creates a matcher with the specific pattern which will match all HTTP methods in a
* case insensitive manner.
* case sensitive manner.
*
* @param pattern the ant pattern to use for matching
*/
@@ -76,7 +76,7 @@ public final class AntPathRequestMatcher
}
/**
* Creates a matcher with the supplied pattern and HTTP method in a case insensitive
* Creates a matcher with the supplied pattern and HTTP method in a case sensitive
* manner.
*
* @param pattern the ant pattern to use for matching
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import static org.assertj.core.api.Assertions.fail;
/**
* @author Rob Winch
* @author Eddú Meléndez
*/
public class StrictHttpFirewallTests {
public String[] unnormalizedPaths = { "/..", "/./path/", "/path/path/.", "/path/path//.", "./path/../path//.",
@@ -373,4 +374,30 @@ public class StrictHttpFirewallTests {
this.firewall.getFirewalledRequest(request);
}
@Test
public void getFirewalledRequestWhenTrustedDomainThenNoException() {
String host = "example.org";
this.request.addHeader("Host", host);
this.firewall.setAllowedHostnames(hostname -> hostname.equals("example.org"));
try {
this.firewall.getFirewalledRequest(this.request);
} catch (RequestRejectedException fail) {
fail("Host " + host + " was rejected");
}
}
@Test
public void getFirewalledRequestWhenUntrustedDomainThenException() {
String host = "example.org";
this.request.addHeader("Host", host);
this.firewall.setAllowedHostnames(hostname -> hostname.equals("myexample.org"));
try {
this.firewall.getFirewalledRequest(this.request);
fail("Host " + host + " was accepted");
} catch (RequestRejectedException expected) {
}
}
}