parent
55895f3b08
commit
78d2be9bd5
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 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,7 +16,6 @@
|
|||
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -84,7 +83,7 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource {
|
|||
setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy() {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setupEnvironment(Hashtable env, String dn, String password) {
|
||||
super.setupEnvironment(env, dn, password);
|
||||
// Remove the pooling flag unless authenticating as the 'manager' user.
|
||||
|
@ -145,7 +144,7 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource {
|
|||
StringBuilder providerUrl = new StringBuilder();
|
||||
for (String serverUrl : urls) {
|
||||
String trimmedUrl = serverUrl.trim();
|
||||
if ("".equals(trimmedUrl)) {
|
||||
if (trimmedUrl.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
providerUrl.append(trimmedUrl);
|
||||
|
@ -160,21 +159,11 @@ public class DefaultSpringSecurityContextSource extends LdapContextSource {
|
|||
}
|
||||
|
||||
private static String encodeUrl(String url) {
|
||||
try {
|
||||
return URLEncoder.encode(url, StandardCharsets.UTF_8.toString());
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
return URLEncoder.encode(url, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private String decodeUrl(String url) {
|
||||
try {
|
||||
return URLDecoder.decode(url, StandardCharsets.UTF_8.toString());
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
return URLDecoder.decode(url, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2005-2010 the original author or authors.
|
||||
* Copyright 2005-2024 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,6 @@
|
|||
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import org.springframework.ldap.BadLdapGrammarException;
|
||||
|
||||
/**
|
||||
* Helper class to encode and decode ldap names and values.
|
||||
*
|
||||
|
@ -31,26 +29,7 @@ import org.springframework.ldap.BadLdapGrammarException;
|
|||
*/
|
||||
final class LdapEncoder {
|
||||
|
||||
private static final int HEX = 16;
|
||||
|
||||
private static String[] NAME_ESCAPE_TABLE = new String[96];
|
||||
static {
|
||||
// all below 0x20 (control chars)
|
||||
for (char c = 0; c < ' '; c++) {
|
||||
NAME_ESCAPE_TABLE[c] = "\\" + toTwoCharHex(c);
|
||||
}
|
||||
NAME_ESCAPE_TABLE['#'] = "\\#";
|
||||
NAME_ESCAPE_TABLE[','] = "\\,";
|
||||
NAME_ESCAPE_TABLE[';'] = "\\;";
|
||||
NAME_ESCAPE_TABLE['='] = "\\=";
|
||||
NAME_ESCAPE_TABLE['+'] = "\\+";
|
||||
NAME_ESCAPE_TABLE['<'] = "\\<";
|
||||
NAME_ESCAPE_TABLE['>'] = "\\>";
|
||||
NAME_ESCAPE_TABLE['\"'] = "\\\"";
|
||||
NAME_ESCAPE_TABLE['\\'] = "\\\\";
|
||||
}
|
||||
|
||||
private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];
|
||||
private static final String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];
|
||||
|
||||
static {
|
||||
// fill with char itself
|
||||
|
@ -71,11 +50,6 @@ final class LdapEncoder {
|
|||
private LdapEncoder() {
|
||||
}
|
||||
|
||||
protected static String toTwoCharHex(char c) {
|
||||
String raw = Integer.toHexString(c).toUpperCase();
|
||||
return (raw.length() > 1) ? raw : "0" + raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a value for use in a filter.
|
||||
* @param value the value to escape.
|
||||
|
@ -94,102 +68,4 @@ final class LdapEncoder {
|
|||
return encodedValue.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP Encodes a value for use with a DN. Escapes for LDAP, not JNDI!
|
||||
*
|
||||
* <br/>
|
||||
* Escapes:<br/>
|
||||
* ' ' [space] - "\ " [if first or last] <br/>
|
||||
* '#' [hash] - "\#" <br/>
|
||||
* ',' [comma] - "\," <br/>
|
||||
* ';' [semicolon] - "\;" <br/>
|
||||
* '= [equals] - "\=" <br/>
|
||||
* '+' [plus] - "\+" <br/>
|
||||
* '<' [less than] - "\<" <br/>
|
||||
* '>' [greater than] - "\>" <br/>
|
||||
* '"' [double quote] - "\"" <br/>
|
||||
* '\' [backslash] - "\\" <br/>
|
||||
* @param value the value to escape.
|
||||
* @return The escaped value.
|
||||
*/
|
||||
static String nameEncode(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder encodedValue = new StringBuilder(value.length() * 2);
|
||||
int length = value.length();
|
||||
int last = length - 1;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = value.charAt(i);
|
||||
// space first or last
|
||||
if (c == ' ' && (i == 0 || i == last)) {
|
||||
encodedValue.append("\\ ");
|
||||
continue;
|
||||
}
|
||||
// check in table for escapes
|
||||
if (c < NAME_ESCAPE_TABLE.length) {
|
||||
String esc = NAME_ESCAPE_TABLE[c];
|
||||
if (esc != null) {
|
||||
encodedValue.append(esc);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// default: add the char
|
||||
encodedValue.append(c);
|
||||
}
|
||||
return encodedValue.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a value. Converts escaped chars to ordinary chars.
|
||||
* @param value Trimmed value, so no leading an trailing blanks, except an escaped
|
||||
* space last.
|
||||
* @return The decoded value as a string.
|
||||
* @throws BadLdapGrammarException
|
||||
*/
|
||||
static String nameDecode(String value) throws BadLdapGrammarException {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder decoded = new StringBuilder(value.length());
|
||||
int i = 0;
|
||||
while (i < value.length()) {
|
||||
char currentChar = value.charAt(i);
|
||||
if (currentChar == '\\') {
|
||||
// Ending with a single backslash is not allowed
|
||||
if (value.length() <= i + 1) {
|
||||
throw new BadLdapGrammarException("Unexpected end of value " + "unterminated '\\'");
|
||||
}
|
||||
char nextChar = value.charAt(i + 1);
|
||||
if (isNormalBackslashEscape(nextChar)) {
|
||||
decoded.append(nextChar);
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
if (value.length() <= i + 2) {
|
||||
throw new BadLdapGrammarException(
|
||||
"Unexpected end of value " + "expected special or hex, found '" + nextChar + "'");
|
||||
}
|
||||
// This should be a hex value
|
||||
String hexString = "" + nextChar + value.charAt(i + 2);
|
||||
decoded.append((char) Integer.parseInt(hexString, HEX));
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This character wasn't escaped - just append it
|
||||
decoded.append(currentChar);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded.toString();
|
||||
|
||||
}
|
||||
|
||||
private static boolean isNormalBackslashEscape(char nextChar) {
|
||||
return nextChar == ',' || nextChar == '=' || nextChar == '+' || nextChar == '<' || nextChar == '>'
|
||||
|| nextChar == '#' || nextChar == ';' || nextChar == '\\' || nextChar == '\"' || nextChar == ' ';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ public final class LdapUtils {
|
|||
*/
|
||||
public static String getRelativeName(String fullDn, Context baseCtx) throws NamingException {
|
||||
String baseDn = baseCtx.getNameInNamespace();
|
||||
if (baseDn.length() == 0) {
|
||||
if (baseDn.isEmpty()) {
|
||||
return fullDn;
|
||||
}
|
||||
LdapName base = LdapNameBuilder.newInstance(baseDn).build();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
|
@ -18,7 +18,7 @@ package org.springframework.security.ldap;
|
|||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -40,7 +40,6 @@ import org.apache.commons.logging.LogFactory;
|
|||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.ldap.core.ContextExecutor;
|
||||
import org.springframework.ldap.core.ContextMapper;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
|
@ -98,7 +97,7 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
|||
searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
|
||||
Object[] params = new Object[] { value };
|
||||
NamingEnumeration<SearchResult> results = ctx.search(dn, comparisonFilter, params, searchControls);
|
||||
Boolean match = results.hasMore();
|
||||
boolean match = results.hasMore();
|
||||
LdapUtils.closeEnumeration(results);
|
||||
return match;
|
||||
});
|
||||
|
@ -112,7 +111,7 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
|||
* @return the object created by the mapper
|
||||
*/
|
||||
public DirContextOperations retrieveEntry(final String dn, final String[] attributesToRetrieve) {
|
||||
return (DirContextOperations) executeReadOnly((ContextExecutor) (ctx) -> {
|
||||
return executeReadOnly((ctx) -> {
|
||||
Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve);
|
||||
return new DirContextAdapter(attrs, LdapNameBuilder.newInstance(dn).build(),
|
||||
LdapNameBuilder.newInstance(ctx.getNameInNamespace()).build());
|
||||
|
@ -169,18 +168,19 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
|||
String formattedFilter = MessageFormat.format(filter, encodedParams);
|
||||
logger.trace(LogMessage.format("Using filter: %s", formattedFilter));
|
||||
HashSet<Map<String, List<String>>> result = new HashSet<>();
|
||||
ContextMapper roleMapper = (ctx) -> {
|
||||
ContextMapper<?> roleMapper = (ctx) -> {
|
||||
DirContextAdapter adapter = (DirContextAdapter) ctx;
|
||||
Map<String, List<String>> record = new HashMap<>();
|
||||
if (ObjectUtils.isEmpty(attributeNames)) {
|
||||
try {
|
||||
for (NamingEnumeration enumeration = adapter.getAttributes().getAll(); enumeration.hasMore();) {
|
||||
Attribute attr = (Attribute) enumeration.next();
|
||||
for (NamingEnumeration<? extends Attribute> enumeration = adapter.getAttributes()
|
||||
.getAll(); enumeration.hasMore();) {
|
||||
Attribute attr = enumeration.next();
|
||||
extractStringAttributeValues(adapter, record, attr.getID());
|
||||
}
|
||||
}
|
||||
catch (NamingException ex) {
|
||||
org.springframework.ldap.support.LdapUtils.convertLdapException(ex);
|
||||
throw org.springframework.ldap.support.LdapUtils.convertLdapException(ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -188,7 +188,7 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
|||
extractStringAttributeValues(adapter, record, attributeName);
|
||||
}
|
||||
}
|
||||
record.put(DN_KEY, Arrays.asList(getAdapterDN(adapter)));
|
||||
record.put(DN_KEY, Collections.singletonList(getAdapterDN(adapter)));
|
||||
result.add(record);
|
||||
return null;
|
||||
};
|
||||
|
@ -258,8 +258,7 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
|||
* search returns more than one result.
|
||||
*/
|
||||
public DirContextOperations searchForSingleEntry(String base, String filter, Object[] params) {
|
||||
return (DirContextOperations) executeReadOnly((ContextExecutor) (ctx) -> searchForSingleEntryInternal(ctx,
|
||||
this.searchControls, base, filter, params));
|
||||
return executeReadOnly((ctx) -> searchForSingleEntryInternal(ctx, this.searchControls, base, filter, params));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -296,8 +295,9 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
|||
/**
|
||||
* We need to make sure the search controls has the return object flag set to true, in
|
||||
* order for the search to return DirContextAdapter instances.
|
||||
* @param originalControls
|
||||
* @return
|
||||
* @param originalControls the {@link SearchControls} that might have the return
|
||||
* object flag set to true
|
||||
* @return a {@link SearchControls} that does have the return object flag set to true
|
||||
*/
|
||||
private static SearchControls buildControls(SearchControls originalControls) {
|
||||
return new SearchControls(originalControls.getSearchScope(), originalControls.getCountLimit(),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
|
@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory;
|
|||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
|
@ -117,14 +118,15 @@ public abstract class AbstractLdapAuthenticationProvider implements Authenticati
|
|||
* obtained from the UserDetails object created by the configured
|
||||
* {@code UserDetailsContextMapper}. Often it will not be possible to read the
|
||||
* password from the directory, so defaults to true.
|
||||
* @param useAuthenticationRequestCredentials
|
||||
* @param useAuthenticationRequestCredentials whether to use the credentials in the
|
||||
* authentication request
|
||||
*/
|
||||
public void setUseAuthenticationRequestCredentials(boolean useAuthenticationRequestCredentials) {
|
||||
this.useAuthenticationRequestCredentials = useAuthenticationRequestCredentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
public void setMessageSource(@NonNull MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.springframework.beans.factory.InitializingBean;
|
|||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
import org.springframework.security.ldap.search.LdapUserSearch;
|
||||
|
@ -37,6 +38,8 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, InitializingBean, MessageSourceAware {
|
||||
|
||||
private final Object mutex = new Object();
|
||||
|
||||
private final ContextSource contextSource;
|
||||
|
||||
/**
|
||||
|
@ -59,7 +62,7 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
|
|||
|
||||
/**
|
||||
* Create an initialized instance with the {@link ContextSource} provided.
|
||||
* @param contextSource
|
||||
* @param contextSource the {@link ContextSource} to use
|
||||
*/
|
||||
public AbstractLdapAuthenticator(ContextSource contextSource) {
|
||||
Assert.notNull(contextSource, "contextSource must not be null.");
|
||||
|
@ -93,7 +96,7 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
|
|||
}
|
||||
List<String> userDns = new ArrayList<>(this.userDnFormat.length);
|
||||
String[] args = new String[] { LdapEncoder.nameEncode(username) };
|
||||
synchronized (this.userDnFormat) {
|
||||
synchronized (this.mutex) {
|
||||
for (MessageFormat formatter : this.userDnFormat) {
|
||||
userDns.add(formatter.format(args));
|
||||
}
|
||||
|
@ -106,14 +109,14 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
public void setMessageSource(@NonNull MessageSource messageSource) {
|
||||
Assert.notNull(messageSource, "Message source must not be null");
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user attributes which will be retrieved from the directory.
|
||||
* @param userAttributes
|
||||
* @param userAttributes the set of user attributes to retrieve
|
||||
*/
|
||||
public void setUserAttributes(String[] userAttributes) {
|
||||
Assert.notNull(userAttributes, "The userAttributes property cannot be set to null");
|
||||
|
|
|
@ -33,7 +33,7 @@ public interface LdapAuthenticator {
|
|||
|
||||
/**
|
||||
* Authenticates as a user and obtains additional user information from the directory.
|
||||
* @param authentication
|
||||
* @param authentication the authentication request
|
||||
* @return the details of the successfully authenticated user.
|
||||
*/
|
||||
DirContextOperations authenticate(Authentication authentication);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2005-2010 the original author or authors.
|
||||
* Copyright 2005-2024 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,6 @@
|
|||
|
||||
package org.springframework.security.ldap.authentication;
|
||||
|
||||
import org.springframework.ldap.BadLdapGrammarException;
|
||||
|
||||
/**
|
||||
* Helper class to encode and decode ldap names and values.
|
||||
*
|
||||
|
@ -31,9 +29,7 @@ import org.springframework.ldap.BadLdapGrammarException;
|
|||
*/
|
||||
final class LdapEncoder {
|
||||
|
||||
private static final int HEX = 16;
|
||||
|
||||
private static String[] NAME_ESCAPE_TABLE = new String[96];
|
||||
private static final String[] NAME_ESCAPE_TABLE = new String[96];
|
||||
static {
|
||||
// all below 0x20 (control chars)
|
||||
for (char c = 0; c < ' '; c++) {
|
||||
|
@ -50,54 +46,19 @@ final class LdapEncoder {
|
|||
NAME_ESCAPE_TABLE['\\'] = "\\\\";
|
||||
}
|
||||
|
||||
private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];
|
||||
|
||||
static {
|
||||
// fill with char itself
|
||||
for (char c = 0; c < FILTER_ESCAPE_TABLE.length; c++) {
|
||||
FILTER_ESCAPE_TABLE[c] = String.valueOf(c);
|
||||
}
|
||||
// escapes (RFC2254)
|
||||
FILTER_ESCAPE_TABLE['*'] = "\\2a";
|
||||
FILTER_ESCAPE_TABLE['('] = "\\28";
|
||||
FILTER_ESCAPE_TABLE[')'] = "\\29";
|
||||
FILTER_ESCAPE_TABLE['\\'] = "\\5c";
|
||||
FILTER_ESCAPE_TABLE[0] = "\\00";
|
||||
}
|
||||
|
||||
/**
|
||||
* All static methods - not to be instantiated.
|
||||
*/
|
||||
private LdapEncoder() {
|
||||
}
|
||||
|
||||
protected static String toTwoCharHex(char c) {
|
||||
static String toTwoCharHex(char c) {
|
||||
String raw = Integer.toHexString(c).toUpperCase();
|
||||
return (raw.length() > 1) ? raw : "0" + raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a value for use in a filter.
|
||||
* @param value the value to escape.
|
||||
* @return a properly escaped representation of the supplied value.
|
||||
*/
|
||||
static String filterEncode(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder encodedValue = new StringBuilder(value.length() * 2);
|
||||
int length = value.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = value.charAt(i);
|
||||
encodedValue.append((ch < FILTER_ESCAPE_TABLE.length) ? FILTER_ESCAPE_TABLE[ch] : ch);
|
||||
}
|
||||
return encodedValue.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP Encodes a value for use with a DN. Escapes for LDAP, not JNDI!
|
||||
*
|
||||
* <br/>
|
||||
* LDAP Encodes a value for use with a DN. Escapes for LDAP, not JNDI! <br/>
|
||||
* Escapes:<br/>
|
||||
* ' ' [space] - "\ " [if first or last] <br/>
|
||||
* '#' [hash] - "\#" <br/>
|
||||
|
@ -140,56 +101,4 @@ final class LdapEncoder {
|
|||
return encodedValue.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a value. Converts escaped chars to ordinary chars.
|
||||
* @param value Trimmed value, so no leading an trailing blanks, except an escaped
|
||||
* space last.
|
||||
* @return The decoded value as a string.
|
||||
* @throws BadLdapGrammarException
|
||||
*/
|
||||
static String nameDecode(String value) throws BadLdapGrammarException {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder decoded = new StringBuilder(value.length());
|
||||
int i = 0;
|
||||
while (i < value.length()) {
|
||||
char currentChar = value.charAt(i);
|
||||
if (currentChar == '\\') {
|
||||
// Ending with a single backslash is not allowed
|
||||
if (value.length() <= i + 1) {
|
||||
throw new BadLdapGrammarException("Unexpected end of value " + "unterminated '\\'");
|
||||
}
|
||||
char nextChar = value.charAt(i + 1);
|
||||
if (isNormalBackslashEscape(nextChar)) {
|
||||
decoded.append(nextChar);
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
if (value.length() <= i + 2) {
|
||||
throw new BadLdapGrammarException(
|
||||
"Unexpected end of value " + "expected special or hex, found '" + nextChar + "'");
|
||||
}
|
||||
// This should be a hex value
|
||||
String hexString = "" + nextChar + value.charAt(i + 2);
|
||||
decoded.append((char) Integer.parseInt(hexString, HEX));
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This character wasn't escaped - just append it
|
||||
decoded.append(currentChar);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded.toString();
|
||||
|
||||
}
|
||||
|
||||
private static boolean isNormalBackslashEscape(char nextChar) {
|
||||
return nextChar == ',' || nextChar == '=' || nextChar == '+' || nextChar == '<' || nextChar == '>'
|
||||
|| nextChar == '#' || nextChar == ';' || nextChar == '\\' || nextChar == '\"' || nextChar == ' ';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -57,8 +57,7 @@ public class SpringSecurityAuthenticationSource implements AuthenticationSource
|
|||
return "";
|
||||
}
|
||||
Object principal = authentication.getPrincipal();
|
||||
if (principal instanceof LdapUserDetails) {
|
||||
LdapUserDetails details = (LdapUserDetails) principal;
|
||||
if (principal instanceof LdapUserDetails details) {
|
||||
return details.getDn();
|
||||
}
|
||||
if (authentication instanceof AnonymousAuthenticationToken) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
|
@ -40,7 +40,6 @@ import org.springframework.security.core.AuthenticationException;
|
|||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public final class ActiveDirectoryAuthenticationException extends AuthenticationException {
|
||||
|
||||
private final String dataCode;
|
||||
|
|
|
@ -189,12 +189,11 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLda
|
|||
|
||||
private DirContext bindAsUser(String username, String password) {
|
||||
// TODO. add DNS lookup based on domain
|
||||
final String bindUrl = this.url;
|
||||
Hashtable<String, Object> env = new Hashtable<>();
|
||||
env.put(Context.SECURITY_AUTHENTICATION, "simple");
|
||||
String bindPrincipal = createBindPrincipal(username);
|
||||
env.put(Context.SECURITY_PRINCIPAL, bindPrincipal);
|
||||
env.put(Context.PROVIDER_URL, bindUrl);
|
||||
env.put(Context.PROVIDER_URL, this.url);
|
||||
env.put(Context.SECURITY_CREDENTIALS, password);
|
||||
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
|
||||
env.put(Context.OBJECT_FACTORIES, DefaultDirObjectFactory.class.getName());
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2015-2021 the original author or authors.
|
||||
* Copyright 2015-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -30,7 +30,7 @@ import org.springframework.security.ldap.userdetails.InetOrgPerson;
|
|||
* @see LdapJackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2015-2021 the original author or authors.
|
||||
* Copyright 2015-2024 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.
|
||||
|
@ -35,7 +35,7 @@ import org.springframework.security.ldap.userdetails.LdapAuthority;
|
|||
* @see LdapJackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
abstract class LdapAuthorityMixin {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2015-2021 the original author or authors.
|
||||
* Copyright 2015-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -30,6 +30,7 @@ import org.springframework.security.ldap.userdetails.Person;
|
|||
* {@link LdapAuthorityMixin}, {@link LdapUserDetailsImplMixin}, {@link PersonMixin},
|
||||
* {@link InetOrgPersonMixin}.
|
||||
*
|
||||
* <p>
|
||||
* If not already enabled, default typing will be automatically enabled as type info is
|
||||
* required to properly serialize/deserialize objects. In order to use this module just
|
||||
* add it to your {@code ObjectMapper} configuration.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2015-2021 the original author or authors.
|
||||
* Copyright 2015-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -30,7 +30,7 @@ import org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;
|
|||
* @see LdapJackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2015-2021 the original author or authors.
|
||||
* Copyright 2015-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -30,7 +30,7 @@ import org.springframework.security.ldap.userdetails.Person;
|
|||
* @see LdapJackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
|
@ -77,7 +77,7 @@ public class PasswordPolicyAwareContextSource extends DefaultSpringSecurityConte
|
|||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Hashtable getAuthenticatedEnv(String principal, String credentials) {
|
||||
Hashtable env = super.getAuthenticatedEnv(principal, credentials);
|
||||
Hashtable<String, Object> env = super.getAuthenticatedEnv(principal, credentials);
|
||||
env.put(LdapContext.CONTROL_FACTORIES, PasswordPolicyControlFactory.class.getName());
|
||||
return env;
|
||||
}
|
||||
|
|
|
@ -220,7 +220,7 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
|||
}
|
||||
}
|
||||
|
||||
class SpecificTagDecoder extends BERTagDecoder {
|
||||
static class SpecificTagDecoder extends BERTagDecoder {
|
||||
|
||||
/** Allows us to remember which of the two options we're decoding */
|
||||
private Boolean inChoice = null;
|
||||
|
|
|
@ -52,7 +52,7 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
|||
/**
|
||||
* Context name to search in, relative to the base of the configured ContextSource.
|
||||
*/
|
||||
private String searchBase = "";
|
||||
private final String searchBase;
|
||||
|
||||
/**
|
||||
* The filter expression used in the user search. This is an LDAP search filter (as
|
||||
|
@ -78,9 +78,9 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
|||
this.contextSource = contextSource;
|
||||
this.searchBase = searchBase;
|
||||
setSearchSubtree(true);
|
||||
if (searchBase.length() == 0) {
|
||||
if (searchBase.isEmpty()) {
|
||||
logger.info(LogMessage.format("Searches will be performed from the root %s since SearchBase not set",
|
||||
contextSource.getBaseLdapPath()));
|
||||
contextSource.getBaseLdapName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
|
@ -33,6 +33,7 @@ import org.springframework.context.ApplicationContext;
|
|||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
@ -43,7 +44,7 @@ public class UnboundIdContainer
|
|||
|
||||
private InMemoryDirectoryServer directoryServer;
|
||||
|
||||
private String defaultPartitionSuffix;
|
||||
private final String defaultPartitionSuffix;
|
||||
|
||||
private int port = 53389;
|
||||
|
||||
|
@ -51,7 +52,7 @@ public class UnboundIdContainer
|
|||
|
||||
private boolean running;
|
||||
|
||||
private String ldif;
|
||||
private final String ldif;
|
||||
|
||||
public UnboundIdContainer(String defaultPartitionSuffix, String ldif) {
|
||||
this.defaultPartitionSuffix = defaultPartitionSuffix;
|
||||
|
@ -79,7 +80,7 @@ public class UnboundIdContainer
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
|
||||
this.context = applicationContext;
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
|||
/**
|
||||
* The base DN from which the search for group membership should be performed
|
||||
*/
|
||||
private String groupSearchBase;
|
||||
private final String groupSearchBase;
|
||||
|
||||
/**
|
||||
* The pattern to be used for the user search. {0} is the user's DN
|
||||
|
@ -166,7 +166,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
|||
if (groupSearchBase == null) {
|
||||
logger.info("Will not perform group search since groupSearchBase is null.");
|
||||
}
|
||||
else if (groupSearchBase.length() == 0) {
|
||||
else if (groupSearchBase.isEmpty()) {
|
||||
logger.info("Will perform group search from the context source base since groupSearchBase is empty.");
|
||||
}
|
||||
this.authorityMapper = (record) -> {
|
||||
|
@ -365,16 +365,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
|||
return this.convertToUpperCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default role Method available so that classes extending this can
|
||||
* override
|
||||
* @return the default role used
|
||||
* @see #setDefaultRole(String)
|
||||
*/
|
||||
private GrantedAuthority getDefaultRole() {
|
||||
return this.defaultRole;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the search controls Method available so that classes extending this can
|
||||
* override the search controls used
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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,16 +31,16 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
public class LdapAuthority implements GrantedAuthority {
|
||||
|
||||
private String dn;
|
||||
private final String dn;
|
||||
|
||||
private String role;
|
||||
private final String role;
|
||||
|
||||
private Map<String, List<String>> attributes;
|
||||
private final Map<String, List<String>> attributes;
|
||||
|
||||
/**
|
||||
* Constructs an LdapAuthority that has a role and a DN but no other attributes
|
||||
* @param role
|
||||
* @param dn
|
||||
* @param role the principal's role
|
||||
* @param dn the distinguished name
|
||||
*/
|
||||
public LdapAuthority(String role, String dn) {
|
||||
this(role, dn, null);
|
||||
|
@ -48,9 +48,9 @@ public class LdapAuthority implements GrantedAuthority {
|
|||
|
||||
/**
|
||||
* Constructs an LdapAuthority with the given role, DN and other LDAP attributes
|
||||
* @param role
|
||||
* @param dn
|
||||
* @param attributes
|
||||
* @param role the principal's role
|
||||
* @param dn the distinguished name
|
||||
* @param attributes additional LDAP attributes
|
||||
*/
|
||||
public LdapAuthority(String role, String dn, Map<String, List<String>> attributes) {
|
||||
Assert.notNull(role, "role can not be null");
|
||||
|
@ -70,7 +70,7 @@ public class LdapAuthority implements GrantedAuthority {
|
|||
|
||||
/**
|
||||
* Returns the DN for this LDAP authority
|
||||
* @return
|
||||
* @return the distinguished name
|
||||
*/
|
||||
public String getDn() {
|
||||
return this.dn;
|
||||
|
@ -91,7 +91,7 @@ public class LdapAuthority implements GrantedAuthority {
|
|||
|
||||
/**
|
||||
* Returns the first attribute value for a specified attribute
|
||||
* @param name
|
||||
* @param name the attribute name
|
||||
* @return the first attribute value for a specified attribute, may be null
|
||||
*/
|
||||
public String getFirstAttributeValue(String name) {
|
||||
|
|
|
@ -44,7 +44,6 @@ import org.apache.commons.logging.LogFactory;
|
|||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.ldap.core.AttributesMapper;
|
||||
import org.springframework.ldap.core.AttributesMapperCallbackHandler;
|
||||
import org.springframework.ldap.core.ContextExecutor;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
|
@ -121,7 +120,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||
private final LdapTemplate template;
|
||||
|
||||
/** Default context mapper used to create a set of roles from a list of attributes */
|
||||
private AttributesMapper roleMapper = (attributes) -> {
|
||||
private AttributesMapper<GrantedAuthority> roleMapper = (attributes) -> {
|
||||
Attribute roleAttr = attributes.get(this.groupRoleAttributeName);
|
||||
NamingEnumeration<?> ne = roleAttr.getAll();
|
||||
Object group = ne.next();
|
||||
|
@ -147,7 +146,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||
}
|
||||
|
||||
private DirContextAdapter loadUserAsContext(final LdapName dn, final String username) {
|
||||
return (DirContextAdapter) this.template.executeReadOnly((ContextExecutor) (ctx) -> {
|
||||
return this.template.executeReadOnly((ctx) -> {
|
||||
try {
|
||||
Attributes attrs = ctx.getAttributes(dn, this.attributesToRetrieve);
|
||||
return new DirContextAdapter(attrs, LdapUtils.getFullDn(dn, ctx));
|
||||
|
@ -162,6 +161,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||
* Changes the password for the current user. The username is obtained from the
|
||||
* security context.
|
||||
*
|
||||
* <p>
|
||||
* There are two supported strategies for modifying the user's password depending on
|
||||
* the capabilities of the corresponding LDAP server.
|
||||
*
|
||||
|
@ -170,6 +170,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||
* <a target="_blank" href="https://tools.ietf.org/html/rfc3062"> LDAP Password Modify
|
||||
* Extended Operation </a>.
|
||||
*
|
||||
* <p>
|
||||
* See {@link LdapUserDetailsManager#setUsePasswordModifyExtensionOperation(boolean)}
|
||||
* for details.
|
||||
* </p>
|
||||
|
@ -205,7 +206,6 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||
* @param username the user whose roles are required.
|
||||
* @return the granted authorities returned by the group search
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
List<GrantedAuthority> getUserAuthorities(final LdapName dn, final String username) {
|
||||
SearchExecutor se = (ctx) -> {
|
||||
LdapName fullDn = LdapUtils.getFullDn(dn, ctx);
|
||||
|
@ -214,7 +214,8 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||
return ctx.search(this.groupSearchBase, this.groupSearchFilter,
|
||||
new String[] { fullDn.toString(), username }, ctrls);
|
||||
};
|
||||
AttributesMapperCallbackHandler roleCollector = new AttributesMapperCallbackHandler(this.roleMapper);
|
||||
AttributesMapperCallbackHandler<GrantedAuthority> roleCollector = new AttributesMapperCallbackHandler<>(
|
||||
this.roleMapper);
|
||||
this.template.search(se, roleCollector);
|
||||
return roleCollector.getList();
|
||||
}
|
||||
|
@ -229,7 +230,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||
// Check for any existing authorities which might be set for this
|
||||
// DN and remove them
|
||||
List<GrantedAuthority> authorities = getUserAuthorities(dn, user.getUsername());
|
||||
if (authorities.size() > 0) {
|
||||
if (!authorities.isEmpty()) {
|
||||
removeAuthorities(dn, authorities);
|
||||
}
|
||||
addAuthorities(dn, user.getAuthorities());
|
||||
|
@ -322,7 +323,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||
|
||||
private void modifyAuthorities(final LdapName userDn, final Collection<? extends GrantedAuthority> authorities,
|
||||
final int modType) {
|
||||
this.template.executeReadWrite((ContextExecutor) (ctx) -> {
|
||||
this.template.executeReadWrite((ctx) -> {
|
||||
for (GrantedAuthority authority : authorities) {
|
||||
String group = convertAuthorityToGroup(authority);
|
||||
LdapName fullDn = LdapUtils.getFullDn(userDn, ctx);
|
||||
|
@ -389,20 +390,26 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||
/**
|
||||
* Sets the method by which a user's password gets modified.
|
||||
*
|
||||
* <p>
|
||||
* If set to {@code true}, then {@link LdapUserDetailsManager#changePassword} will
|
||||
* modify the user's password by way of the
|
||||
* <a target="_blank" href="https://tools.ietf.org/html/rfc3062">Password Modify
|
||||
* Extension Operation</a>.
|
||||
*
|
||||
* <p>
|
||||
* If set to {@code false}, then {@link LdapUserDetailsManager#changePassword} will
|
||||
* modify the user's password by directly modifying attributes on the corresponding
|
||||
* entry.
|
||||
*
|
||||
* <p>
|
||||
* Before using this setting, ensure that the corresponding LDAP server supports this
|
||||
* extended operation.
|
||||
*
|
||||
* <p>
|
||||
* By default, {@code usePasswordModifyExtensionOperation} is false.
|
||||
* @param usePasswordModifyExtensionOperation
|
||||
* @param usePasswordModifyExtensionOperation whether to use the
|
||||
* <a target="_blank" href="https://tools.ietf.org/html/rfc3062">Password Modify
|
||||
* Extension Operation</a> to modify the password
|
||||
* @since 4.2.9
|
||||
*/
|
||||
public void setUsePasswordModifyExtensionOperation(boolean usePasswordModifyExtensionOperation) {
|
||||
|
@ -473,6 +480,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
|||
* <a target="_blank" href="https://tools.ietf.org/html/rfc3062"> LDAP Password Modify
|
||||
* Extended Operation </a> client request.
|
||||
*
|
||||
* <p>
|
||||
* Can be directed at any LDAP server that supports the Password Modify Extended
|
||||
* Operation.
|
||||
*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
|
@ -176,7 +176,7 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
|||
if (getAttributeNames() == null) {
|
||||
setAttributeNames(new HashSet<>());
|
||||
}
|
||||
if (StringUtils.hasText(getGroupRoleAttribute()) && !getAttributeNames().contains(getGroupRoleAttribute())) {
|
||||
if (StringUtils.hasText(getGroupRoleAttribute())) {
|
||||
getAttributeNames().add(getGroupRoleAttribute());
|
||||
}
|
||||
Set<Map<String, List<String>>> userRoles = getLdapTemplate().searchForMultipleAttributeValues(
|
||||
|
@ -200,7 +200,7 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
|||
// this prevents a forever loop for a misconfigured ldap directory
|
||||
circular = circular | (!authorities.add(new LdapAuthority(role, dn, record)));
|
||||
}
|
||||
String roleName = (roles.size() > 0) ? roles.iterator().next() : dn;
|
||||
String roleName = (!roles.isEmpty()) ? roles.iterator().next() : dn;
|
||||
if (!circular) {
|
||||
performNestedSearch(dn, roleName, authorities, (depth - 1));
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ public interface UserDetailsContextMapper {
|
|||
* Creates a fully populated UserDetails object for use by the security framework.
|
||||
* @param ctx the context object which contains the user information.
|
||||
* @param username the user's supplied login name.
|
||||
* @param authorities
|
||||
* @param authorities the authorities to add to the {@code UserDetails} instance
|
||||
* @return the user object.
|
||||
*/
|
||||
UserDetails mapUserFromContext(DirContextOperations ctx, String username,
|
||||
|
|
Loading…
Reference in New Issue