Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bcaad3a51c | |||
| 66f8a56b1b | |||
| 3e5b65f647 | |||
| c62789c976 | |||
| b02cae0a0c | |||
| af278b58b5 | |||
| ba81f6a06a | |||
| a39efaf883 | |||
| 7d5e032e25 | |||
| e21ef422e7 | |||
| 66f923dab7 | |||
| e8daeacd89 | |||
| 27f30b04cd | |||
| baa238e339 | |||
| cf5bd52121 | |||
| ef78626045 | |||
| 7f8efb7680 | |||
| 2c4d13ddef | |||
| ed2a646a69 | |||
| f8f4d960b5 | |||
| 76a4df0461 | |||
| 7eee6b102f | |||
| d69288e665 | |||
| 62bc17ea3f | |||
| fd2798ca95 | |||
| 173660c6ef | |||
| d6aa6a2246 |
+3
-4
@@ -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
|
||||
// ==================================================================================================
|
||||
|
||||
|
||||
+4
@@ -16,8 +16,10 @@
|
||||
package org.springframework.security.config.annotation.configuration;
|
||||
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
@@ -34,9 +36,11 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
* @since 3.2
|
||||
*/
|
||||
@Configuration
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public class ObjectPostProcessorConfiguration {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public ObjectPostProcessor<Object> objectPostProcessor(
|
||||
AutowireCapableBeanFactory beanFactory) {
|
||||
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
|
||||
|
||||
+3
@@ -28,8 +28,10 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
@@ -80,6 +82,7 @@ import org.springframework.util.Assert;
|
||||
* @see EnableGlobalMethodSecurity
|
||||
*/
|
||||
@Configuration
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public class GlobalMethodSecurityConfiguration
|
||||
implements ImportAware, SmartInitializingSingleton {
|
||||
private static final Log logger = LogFactory
|
||||
|
||||
+4
@@ -15,14 +15,18 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
|
||||
|
||||
@Configuration
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
class Jsr250MetadataSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource() {
|
||||
return new Jsr250MethodSecurityMetadataSource();
|
||||
}
|
||||
|
||||
+2
@@ -49,6 +49,7 @@ class ReactiveMethodSecurityConfiguration implements ImportAware {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public DelegatingMethodSecurityMetadataSource methodMetadataSource() {
|
||||
ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory(
|
||||
new DefaultMethodSecurityExpressionHandler());
|
||||
@@ -69,6 +70,7 @@ class ReactiveMethodSecurityConfiguration implements ImportAware {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler() {
|
||||
return new DefaultMethodSecurityExpressionHandler();
|
||||
}
|
||||
|
||||
+1
@@ -584,6 +584,7 @@ public final class HttpSecurity extends
|
||||
|
||||
/**
|
||||
* Allows restricting access based upon the {@link HttpServletRequest} using
|
||||
* {@link RequestMatcher} implementations (i.e. via URL patterns).
|
||||
*
|
||||
* <h2>Example Configurations</h2>
|
||||
*
|
||||
|
||||
+1
-1
@@ -675,7 +675,7 @@ public class ServerHttpSecurity {
|
||||
|
||||
/**
|
||||
* Require a specific authority.
|
||||
* @param authority the authority to require (i.e. "USER" woudl require authority of "USER").
|
||||
* @param authority the authority to require (i.e. "USER" would require authority of "USER").
|
||||
* @return the {@link AuthorizeExchangeSpec} to configure
|
||||
*/
|
||||
public AuthorizeExchangeSpec hasAuthority(String authority) {
|
||||
|
||||
@@ -31,6 +31,7 @@ DigestAuthenticationFilter.usernameNotFound=Username {0} not found
|
||||
JdbcDaoImpl.noAuthority=User {0} has no GrantedAuthority
|
||||
JdbcDaoImpl.notFound=User {0} not found
|
||||
LdapAuthenticationProvider.badCredentials=Bad credentials
|
||||
LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed
|
||||
LdapAuthenticationProvider.credentialsExpired=User credentials have expired
|
||||
LdapAuthenticationProvider.disabled=User is disabled
|
||||
LdapAuthenticationProvider.expired=User account has expired
|
||||
|
||||
+8
@@ -65,6 +65,10 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
public String encode(CharSequence rawPassword) {
|
||||
if (rawPassword == null) {
|
||||
throw new IllegalArgumentException("rawPassword cannot be null");
|
||||
}
|
||||
|
||||
String salt;
|
||||
if (strength > 0) {
|
||||
if (random != null) {
|
||||
@@ -81,6 +85,10 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
if (rawPassword == null) {
|
||||
throw new IllegalArgumentException("rawPassword cannot be null");
|
||||
}
|
||||
|
||||
if (encodedPassword == null || encodedPassword.length() == 0) {
|
||||
logger.warn("Empty encoded password");
|
||||
return false;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
* Copyright 2011-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.
|
||||
@@ -32,7 +32,7 @@ public class Encryptors {
|
||||
* (Password-Based Key Derivation Function #2). Salts the password to prevent
|
||||
* dictionary attacks against the key. The provided salt is expected to be
|
||||
* hex-encoded; it should be random and at least 8 bytes in length. Also applies a
|
||||
* random 16 byte initialization vector to ensure each encrypted message will be
|
||||
* random 16-byte initialization vector to ensure each encrypted message will be
|
||||
* unique. Requires Java 6.
|
||||
*
|
||||
* @param password the password used to generate the encryptor's secret key; should
|
||||
@@ -50,7 +50,7 @@ public class Encryptors {
|
||||
* Derives the secret key using PKCS #5's PBKDF2 (Password-Based Key Derivation
|
||||
* Function #2). Salts the password to prevent dictionary attacks against the key. The
|
||||
* provided salt is expected to be hex-encoded; it should be random and at least 8
|
||||
* bytes in length. Also applies a random 16 byte initialization vector to ensure each
|
||||
* bytes in length. Also applies a random 16-byte initialization vector to ensure each
|
||||
* encrypted message will be unique. Requires Java 6.
|
||||
* NOTE: This mode is not
|
||||
* <a href="https://en.wikipedia.org/wiki/Authenticated_encryption">authenticated</a>
|
||||
@@ -63,7 +63,7 @@ public class Encryptors {
|
||||
* @param salt a hex-encoded, random, site-global salt value to use to generate the
|
||||
* key
|
||||
*
|
||||
* @see #stronger(CharSequence, CharSequence) which uses the significatly more secure
|
||||
* @see #stronger(CharSequence, CharSequence), which uses the significatly more secure
|
||||
* GCM (instead of CBC)
|
||||
*/
|
||||
public static BytesEncryptor standard(CharSequence password, CharSequence salt) {
|
||||
@@ -105,7 +105,10 @@ public class Encryptors {
|
||||
* not be shared
|
||||
* @param salt a hex-encoded, random, site-global salt value to use to generate the
|
||||
* secret key
|
||||
* @deprecated This encryptor is not secure. Instead, look to your data store for a
|
||||
* mechanism to query encrypted data.
|
||||
*/
|
||||
@Deprecated
|
||||
public static TextEncryptor queryableText(CharSequence password, CharSequence salt) {
|
||||
return new HexEncodingTextEncryptor(new AesBytesEncryptor(password.toString(),
|
||||
salt));
|
||||
|
||||
-2
@@ -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;
|
||||
|
||||
+2
-1
@@ -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 {
|
||||
|
||||
+11
@@ -92,4 +92,15 @@ public class BCryptPasswordEncoderTests {
|
||||
assertThat(encoder.matches("password", "012345678901234567890123456789")).isFalse();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void encodeNullRawPassword() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
encoder.encode(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void matchNullRawPassword() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
encoder.matches(null, "does-not-matter");
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
gaeVersion=1.9.79
|
||||
gaeVersion=1.9.80
|
||||
springBootVersion=2.0.9.RELEASE
|
||||
version=5.0.15.RELEASE
|
||||
version=5.0.17.RELEASE
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'io.projectreactor:reactor-bom:Bismuth-SR17'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.0.16.RELEASE'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.0.17.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'
|
||||
dependency 'org.powermock:powermock-api-mockito2:2.0.6'
|
||||
dependency 'org.powermock:powermock-api-support:2.0.6'
|
||||
dependency 'org.powermock:powermock-core:2.0.6'
|
||||
dependency 'org.powermock:powermock-module-junit4-common:2.0.6'
|
||||
dependency 'org.powermock:powermock-module-junit4:2.0.6'
|
||||
dependency 'org.powermock:powermock-reflect:2.0.6'
|
||||
dependency 'org.python:jython:2.5.0'
|
||||
dependency 'org.powermock:powermock-api-mockito2:2.0.7'
|
||||
dependency 'org.powermock:powermock-api-support:2.0.7'
|
||||
dependency 'org.powermock:powermock-core:2.0.7'
|
||||
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.3'
|
||||
dependency 'org.spockframework:spock-core:1.0-groovy-2.4'
|
||||
dependency 'org.spockframework:spock-spring:1.0-groovy-2.4'
|
||||
}
|
||||
@@ -33,18 +33,18 @@ dependencyManagement {
|
||||
dependency 'com.fasterxml.jackson.core:jackson-databind:2.9.10.2'
|
||||
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.76'
|
||||
dependency 'com.google.appengine:appengine-api-labs:1.9.76'
|
||||
dependency 'com.google.appengine:appengine-api-stubs:1.9.76'
|
||||
dependency 'com.google.appengine:appengine-testing:1.9.76'
|
||||
dependency 'com.google.appengine:appengine:1.9.76'
|
||||
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.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'
|
||||
|
||||
+17
-4
@@ -16,6 +16,7 @@
|
||||
package org.springframework.security.ldap.authentication.ad;
|
||||
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.ldap.CommunicationException;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
import org.springframework.ldap.core.support.DefaultDirObjectFactory;
|
||||
@@ -24,6 +25,7 @@ import org.springframework.security.authentication.AccountExpiredException;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.CredentialsExpiredException;
|
||||
import org.springframework.security.authentication.DisabledException;
|
||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
@@ -141,12 +143,15 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
UsernamePasswordAuthenticationToken auth) {
|
||||
String username = auth.getName();
|
||||
String password = (String) auth.getCredentials();
|
||||
|
||||
DirContext ctx = bindAsUser(username, password);
|
||||
DirContext ctx = null;
|
||||
|
||||
try {
|
||||
ctx = bindAsUser(username, password);
|
||||
return searchForUser(ctx, username);
|
||||
}
|
||||
catch (CommunicationException e) {
|
||||
throw badLdapConnection(e);
|
||||
}
|
||||
catch (NamingException e) {
|
||||
logger.error("Failed to locate directory entry for authenticated user: "
|
||||
+ username, e);
|
||||
@@ -208,8 +213,7 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
|| (e instanceof OperationNotSupportedException)) {
|
||||
handleBindException(bindPrincipal, e);
|
||||
throw badCredentials(e);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
throw LdapUtils.convertLdapException(e);
|
||||
}
|
||||
}
|
||||
@@ -311,6 +315,12 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
return (BadCredentialsException) badCredentials().initCause(cause);
|
||||
}
|
||||
|
||||
private InternalAuthenticationServiceException badLdapConnection(Throwable cause) {
|
||||
return new InternalAuthenticationServiceException(messages.getMessage(
|
||||
"LdapAuthenticationProvider.badLdapConnection",
|
||||
"Connection to LDAP server failed."), cause);
|
||||
}
|
||||
|
||||
private DirContextOperations searchForUser(DirContext context, String username)
|
||||
throws NamingException {
|
||||
SearchControls searchControls = new SearchControls();
|
||||
@@ -325,6 +335,9 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends
|
||||
searchControls, searchRoot, searchFilter,
|
||||
new Object[] { bindPrincipal, username });
|
||||
}
|
||||
catch (CommunicationException ldapCommunicationException) {
|
||||
throw badLdapConnection(ldapCommunicationException);
|
||||
}
|
||||
catch (IncorrectResultSizeDataAccessException incorrectResults) {
|
||||
// Search should never return multiple results if properly configured - just
|
||||
// rethrow
|
||||
|
||||
+22
-6
@@ -32,6 +32,7 @@ import org.springframework.security.authentication.AccountExpiredException;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.CredentialsExpiredException;
|
||||
import org.springframework.security.authentication.DisabledException;
|
||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -58,6 +59,9 @@ import static org.springframework.security.ldap.authentication.ad.ActiveDirector
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
public static final String EXISTING_LDAP_PROVIDER = "ldap://192.168.1.200/";
|
||||
public static final String NON_EXISTING_LDAP_PROVIDER = "ldap://192.168.1.201/";
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@@ -378,17 +382,29 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
|
||||
}
|
||||
|
||||
@Test(expected = org.springframework.ldap.CommunicationException.class)
|
||||
public void nonAuthenticationExceptionIsConvertedToSpringLdapException()
|
||||
throws Exception {
|
||||
provider.contextFactory = createContextFactoryThrowing(new CommunicationException(
|
||||
msg));
|
||||
provider.authenticate(joe);
|
||||
public void nonAuthenticationExceptionIsConvertedToSpringLdapException() throws Throwable {
|
||||
try {
|
||||
provider.contextFactory = createContextFactoryThrowing(new CommunicationException(
|
||||
msg));
|
||||
provider.authenticate(joe);
|
||||
} catch (InternalAuthenticationServiceException e) {
|
||||
// Since GH-8418 ldap communication exception is wrapped into InternalAuthenticationServiceException.
|
||||
// This test is about the wrapped exception, so we throw it.
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = org.springframework.security.authentication.InternalAuthenticationServiceException.class )
|
||||
public void connectionExceptionIsWrappedInInternalException() throws Exception {
|
||||
ActiveDirectoryLdapAuthenticationProvider noneReachableProvider = new ActiveDirectoryLdapAuthenticationProvider(
|
||||
"mydomain.eu", NON_EXISTING_LDAP_PROVIDER, "dc=ad,dc=eu,dc=mydomain");
|
||||
noneReachableProvider.doAuthentication(joe);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rootDnProvidedSeparatelyFromDomainAlsoWorks() throws Exception {
|
||||
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(
|
||||
"mydomain.eu", "ldap://192.168.1.200/", "dc=ad,dc=eu,dc=mydomain");
|
||||
"mydomain.eu", EXISTING_LDAP_PROVIDER, "dc=ad,dc=eu,dc=mydomain");
|
||||
checkAuthentication("dc=ad,dc=eu,dc=mydomain", provider);
|
||||
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ import java.util.*;
|
||||
* requests which match the pattern. An example configuration might look like this:
|
||||
*
|
||||
* <pre>
|
||||
* <bean id="myfilterChainProxy" class="org.springframework.security.util.FilterChainProxy">
|
||||
* <bean id="myfilterChainProxy" class="org.springframework.security.web.FilterChainProxy">
|
||||
* <constructor-arg>
|
||||
* <util:list>
|
||||
* <security:filter-chain pattern="/do/not/filter*" filters="none"/>
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
-3
@@ -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());
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+28
-1
@@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user