Add security-nullability to ldap
Closes gh-17818 Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
This commit is contained in:
+3
@@ -20,6 +20,7 @@ import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
@@ -98,12 +99,14 @@ public class LdapBindAuthenticationManagerFactoryITests {
|
||||
public void authenticationManagerFactoryWhenCustomUserDetailsContextMapperThenUsed() throws Exception {
|
||||
CustomUserDetailsContextMapperConfig.CONTEXT_MAPPER = new UserDetailsContextMapper() {
|
||||
@Override
|
||||
@NullMarked
|
||||
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
|
||||
Collection<? extends GrantedAuthority> authorities) {
|
||||
return User.withUsername("other").password("password").roles("USER").build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NullMarked
|
||||
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
apply plugin: 'io.spring.convention.spring-module'
|
||||
apply plugin: 'javadoc-warnings-error'
|
||||
apply plugin: 'compile-warnings-error'
|
||||
apply plugin: 'security-nullability'
|
||||
|
||||
dependencies {
|
||||
management platform(project(":spring-security-dependencies"))
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Helper class to encode and decode ldap names and values.
|
||||
*
|
||||
@@ -29,7 +31,7 @@ package org.springframework.security.ldap;
|
||||
*/
|
||||
final class LdapEncoder {
|
||||
|
||||
private static final String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];
|
||||
private static final @Nullable String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];
|
||||
|
||||
static {
|
||||
// fill with char itself
|
||||
@@ -56,9 +58,6 @@ final class LdapEncoder {
|
||||
* @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++) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import javax.naming.ldap.LdapName;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.support.LdapNameBuilder;
|
||||
@@ -44,7 +45,7 @@ public final class LdapUtils {
|
||||
private LdapUtils() {
|
||||
}
|
||||
|
||||
public static void closeContext(Context ctx) {
|
||||
public static void closeContext(@Nullable Context ctx) {
|
||||
if (ctx instanceof DirContextAdapter) {
|
||||
return;
|
||||
}
|
||||
@@ -58,7 +59,7 @@ public final class LdapUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void closeEnumeration(NamingEnumeration ne) {
|
||||
public static void closeEnumeration(@Nullable NamingEnumeration ne) {
|
||||
try {
|
||||
if (ne != null) {
|
||||
ne.close();
|
||||
|
||||
+6
-4
@@ -37,6 +37,7 @@ import javax.naming.ldap.LdapName;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
@@ -110,7 +111,7 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
* directory entry.
|
||||
* @return the object created by the mapper
|
||||
*/
|
||||
public DirContextOperations retrieveEntry(final String dn, final String[] attributesToRetrieve) {
|
||||
public DirContextOperations retrieveEntry(final String dn, final String @Nullable [] attributesToRetrieve) {
|
||||
return executeReadOnly((ctx) -> {
|
||||
Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve);
|
||||
return new DirContextAdapter(attrs, LdapNameBuilder.newInstance(dn).build(),
|
||||
@@ -153,13 +154,14 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
* @param base the DN to search in
|
||||
* @param filter search filter to use
|
||||
* @param params the parameters to substitute in the search filter
|
||||
* @param attributeNames the attributes' values that are to be retrieved.
|
||||
* @param attributeNames the attributes' values that are to be retrieved; a
|
||||
* {@code null} array means retrieve all attributes
|
||||
* @return the set of String values for each attribute found in all the matching
|
||||
* entries. The attribute name is the key for each set of values. In addition each map
|
||||
* contains the DN as a String with the key predefined key {@link #DN_KEY}.
|
||||
*/
|
||||
public Set<Map<String, List<String>>> searchForMultipleAttributeValues(String base, String filter, Object[] params,
|
||||
String[] attributeNames) {
|
||||
String @Nullable [] attributeNames) {
|
||||
// Escape the params acording to RFC2254
|
||||
Object[] encodedParams = new String[params.length];
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
@@ -190,7 +192,7 @@ public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
}
|
||||
record.put(DN_KEY, Collections.singletonList(getAdapterDN(adapter)));
|
||||
result.add(record);
|
||||
return null;
|
||||
return void.class;
|
||||
};
|
||||
SearchControls ctls = new SearchControls();
|
||||
ctls.setSearchScope(this.searchControls.getSearchScope());
|
||||
|
||||
+3
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.security.ldap.aot.hint;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
@@ -30,7 +32,7 @@ import org.springframework.aot.hint.TypeReference;
|
||||
class LdapSecurityRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
hints.reflection()
|
||||
.registerType(TypeReference.of("com.sun.jndi.ldap.LdapCtxFactory"),
|
||||
(builder) -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Package for AOT Hints
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.ldap.aot.hint;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
+2
-3
@@ -21,7 +21,6 @@ import java.util.LinkedHashSet;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
@@ -83,7 +82,7 @@ public abstract class AbstractLdapAuthenticationProvider implements Authenticati
|
||||
Assert.notNull(password, "Null password was supplied in authentication token");
|
||||
DirContextOperations userData = doAuthentication(userToken);
|
||||
UserDetails user = this.userDetailsContextMapper.mapUserFromContext(userData, authentication.getName(),
|
||||
loadUserAuthorities(userData, authentication.getName(), (String) authentication.getCredentials()));
|
||||
loadUserAuthorities(userData, authentication.getName(), password));
|
||||
return createSuccessfulAuthentication(userToken, user);
|
||||
}
|
||||
|
||||
@@ -133,7 +132,7 @@ public abstract class AbstractLdapAuthenticationProvider implements Authenticati
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageSource(@NonNull MessageSource messageSource) {
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -21,7 +21,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
@@ -47,7 +47,7 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
|
||||
* Optional search object which can be used to locate a user when a simple DN match
|
||||
* isn't sufficient
|
||||
*/
|
||||
private LdapUserSearch userSearch;
|
||||
private @Nullable LdapUserSearch userSearch;
|
||||
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
@@ -55,11 +55,11 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
|
||||
* The attributes which will be retrieved from the directory. Null means all
|
||||
* attributes
|
||||
*/
|
||||
private String[] userAttributes = null;
|
||||
private String @Nullable [] userAttributes = null;
|
||||
|
||||
// private String[] userDnPattern = null;
|
||||
/** Stores the patterns which are used as potential DN matches */
|
||||
private MessageFormat[] userDnFormat = null;
|
||||
private MessageFormat @Nullable [] userDnFormat = null;
|
||||
|
||||
/**
|
||||
* Create an initialized instance with the {@link ContextSource} provided.
|
||||
@@ -80,7 +80,7 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
|
||||
return this.contextSource;
|
||||
}
|
||||
|
||||
public String[] getUserAttributes() {
|
||||
public String @Nullable [] getUserAttributes() {
|
||||
return this.userAttributes;
|
||||
}
|
||||
|
||||
@@ -105,12 +105,12 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
|
||||
return userDns;
|
||||
}
|
||||
|
||||
protected LdapUserSearch getUserSearch() {
|
||||
protected @Nullable LdapUserSearch getUserSearch() {
|
||||
return this.userSearch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageSource(@NonNull MessageSource messageSource) {
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
Assert.notNull(messageSource, "Message source must not be null");
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
+4
-2
@@ -22,6 +22,7 @@ import javax.naming.directory.DirContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.ldap.NamingException;
|
||||
@@ -98,11 +99,12 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
|
||||
return user;
|
||||
}
|
||||
|
||||
private DirContextOperations bindWithDn(String userDnStr, String username, String password) {
|
||||
private @Nullable DirContextOperations bindWithDn(String userDnStr, String username, String password) {
|
||||
return bindWithDn(userDnStr, username, password, null);
|
||||
}
|
||||
|
||||
private DirContextOperations bindWithDn(String userDnStr, String username, String password, Attributes attrs) {
|
||||
private @Nullable DirContextOperations bindWithDn(String userDnStr, String username, String password,
|
||||
@Nullable Attributes attrs) {
|
||||
BaseLdapPathContextSource ctxSource = (BaseLdapPathContextSource) getContextSource();
|
||||
Name userDn = LdapUtils.newLdapName(userDnStr);
|
||||
Name fullDn = LdapUtils.prepend(userDn, ctxSource.getBaseLdapName());
|
||||
|
||||
+3
-4
@@ -18,6 +18,8 @@ package org.springframework.security.ldap.authentication;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Helper class to encode and decode ldap names and values.
|
||||
*
|
||||
@@ -31,7 +33,7 @@ import java.util.Locale;
|
||||
*/
|
||||
final class LdapEncoder {
|
||||
|
||||
private static final String[] NAME_ESCAPE_TABLE = new String[96];
|
||||
private static final @Nullable String[] NAME_ESCAPE_TABLE = new String[96];
|
||||
static {
|
||||
// all below 0x20 (control chars)
|
||||
for (char c = 0; c < ' '; c++) {
|
||||
@@ -76,9 +78,6 @@ final class LdapEncoder {
|
||||
* @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;
|
||||
|
||||
+4
-5
@@ -18,6 +18,7 @@ package org.springframework.security.ldap.authentication;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.ldap.NameNotFoundException;
|
||||
@@ -101,9 +102,6 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
|
||||
if (user == null && getUserSearch() != null) {
|
||||
logger.trace("Searching for user using " + getUserSearch());
|
||||
user = getUserSearch().searchForUser(username);
|
||||
if (user == null) {
|
||||
logger.debug("Failed to find user using " + getUserSearch());
|
||||
}
|
||||
}
|
||||
if (user == null) {
|
||||
throw UsernameNotFoundException.fromUsername(username);
|
||||
@@ -117,6 +115,7 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
|
||||
this.passwordAttributeName, user.getDn()));
|
||||
return user;
|
||||
}
|
||||
Assert.notNull(password, "LDAP password cannot be null");
|
||||
if (isLdapPasswordCompare(user, ldapTemplate, password)) {
|
||||
logger.debug(LogMessage.format("LDAP-matched password attribute '%s' for user '%s'",
|
||||
this.passwordAttributeName, user.getDn()));
|
||||
@@ -126,12 +125,12 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
|
||||
this.messages.getMessage("PasswordComparisonAuthenticator.badCredentials", "Bad credentials"));
|
||||
}
|
||||
|
||||
private boolean isPasswordAttrCompare(DirContextOperations user, String password) {
|
||||
private boolean isPasswordAttrCompare(DirContextOperations user, @Nullable String password) {
|
||||
String passwordAttrValue = getPassword(user);
|
||||
return this.passwordEncoder.matches(password, passwordAttrValue);
|
||||
}
|
||||
|
||||
private String getPassword(DirContextOperations user) {
|
||||
private @Nullable String getPassword(DirContextOperations user) {
|
||||
Object passwordAttrValue = user.getObjectAttribute(this.passwordAttributeName);
|
||||
if (passwordAttrValue == null) {
|
||||
return null;
|
||||
|
||||
+6
-1
@@ -78,7 +78,12 @@ public class SpringSecurityAuthenticationSource implements AuthenticationSource
|
||||
log.debug("Returning empty String as Credentials since authentication is null");
|
||||
return "";
|
||||
}
|
||||
return (String) authentication.getCredentials();
|
||||
String password = (String) authentication.getCredentials();
|
||||
if (password == null) {
|
||||
log.debug("Returning empty String as Credentials since password is null");
|
||||
return "";
|
||||
}
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.security.ldap.authentication.ad;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
/**
|
||||
@@ -45,7 +47,7 @@ public final class ActiveDirectoryAuthenticationException extends Authentication
|
||||
|
||||
private final String dataCode;
|
||||
|
||||
ActiveDirectoryAuthenticationException(String dataCode, String message, Throwable cause) {
|
||||
ActiveDirectoryAuthenticationException(String dataCode, @Nullable String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.dataCode = dataCode;
|
||||
}
|
||||
|
||||
+9
-3
@@ -33,6 +33,8 @@ import javax.naming.directory.DirContext;
|
||||
import javax.naming.directory.SearchControls;
|
||||
import javax.naming.ldap.InitialLdapContext;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.ldap.CommunicationException;
|
||||
@@ -117,9 +119,9 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLda
|
||||
|
||||
private static final int ACCOUNT_LOCKED = 0x775;
|
||||
|
||||
private final String domain;
|
||||
private final @Nullable String domain;
|
||||
|
||||
private final String rootDn;
|
||||
private final @Nullable String rootDn;
|
||||
|
||||
private final String url;
|
||||
|
||||
@@ -163,6 +165,7 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLda
|
||||
protected DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken auth) {
|
||||
String username = auth.getName();
|
||||
String password = (String) auth.getCredentials();
|
||||
Assert.notNull(password, "password cannot be null");
|
||||
DirContext ctx = null;
|
||||
try {
|
||||
ctx = bindAsUser(username, password);
|
||||
@@ -236,7 +239,10 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLda
|
||||
}
|
||||
}
|
||||
|
||||
private int parseSubErrorCode(String message) {
|
||||
private int parseSubErrorCode(@Nullable String message) {
|
||||
if (message == null) {
|
||||
return -1;
|
||||
}
|
||||
Matcher matcher = SUB_ERROR_CODE.matcher(message);
|
||||
if (matcher.matches()) {
|
||||
return Integer.parseInt(matcher.group(1), 16);
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Package for ActiveDirectory support
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.ldap.authentication.ad;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -22,4 +22,7 @@
|
||||
* an <tt>LdapAuthenticator</tt> instance and an <tt>LdapAuthoritiesPopulator</tt>. The
|
||||
* latter is used to obtain the list of roles for the user.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.ldap.authentication;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* Jackson 3+ serialization support for LDAP.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.ldap.jackson;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* Jackson 2 serialization support for LDAP.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.ldap.jackson2;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* Spring Security's LDAP module.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ public class PasswordPolicyAwareContextSource extends DefaultSpringSecurityConte
|
||||
this.logger.debug(LogMessage.format("Failed to bind with %s", ctrl), ex);
|
||||
}
|
||||
LdapUtils.closeContext(ctx);
|
||||
if (ctrl != null && ctrl.isLocked()) {
|
||||
if (ctrl != null && ctrl.isLocked() && ctrl.getErrorStatus() != null) {
|
||||
throw new PasswordPolicyException(ctrl.getErrorStatus());
|
||||
}
|
||||
throw LdapUtils.convertLdapException(ex);
|
||||
|
||||
+3
-1
@@ -20,6 +20,8 @@ import java.io.Serial;
|
||||
|
||||
import javax.naming.ldap.Control;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
*
|
||||
* A Password Policy request control.
|
||||
@@ -65,7 +67,7 @@ public class PasswordPolicyControl implements Control {
|
||||
* @return always null
|
||||
*/
|
||||
@Override
|
||||
public byte[] getEncodedValue() {
|
||||
public byte @Nullable [] getEncodedValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -22,6 +22,7 @@ import javax.naming.ldap.LdapContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Obtains the <tt>PasswordPolicyControl</tt> from a context for use by other classes.
|
||||
@@ -36,7 +37,7 @@ public final class PasswordPolicyControlExtractor {
|
||||
private PasswordPolicyControlExtractor() {
|
||||
}
|
||||
|
||||
public static PasswordPolicyResponseControl extractControl(DirContext dirCtx) {
|
||||
public static @Nullable PasswordPolicyResponseControl extractControl(DirContext dirCtx) {
|
||||
LdapContext ctx = (LdapContext) dirCtx;
|
||||
Control[] ctrls = null;
|
||||
try {
|
||||
|
||||
+3
-1
@@ -19,6 +19,8 @@ package org.springframework.security.ldap.ppolicy;
|
||||
import javax.naming.ldap.Control;
|
||||
import javax.naming.ldap.ControlFactory;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Transforms a control object to a PasswordPolicyResponseControl object, if appropriate.
|
||||
*
|
||||
@@ -35,7 +37,7 @@ public class PasswordPolicyControlFactory extends ControlFactory {
|
||||
* @return a response control of type PasswordPolicyResponseControl, or null
|
||||
*/
|
||||
@Override
|
||||
public Control getControlInstance(Control ctl) {
|
||||
public @Nullable Control getControlInstance(Control ctl) {
|
||||
if (ctl.getID().equals(PasswordPolicyControl.OID)) {
|
||||
return new PasswordPolicyResponseControl(ctl.getEncodedValue());
|
||||
}
|
||||
|
||||
+5
-4
@@ -31,6 +31,7 @@ import netscape.ldap.ber.stream.BERTag;
|
||||
import netscape.ldap.ber.stream.BERTagDecoder;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
@@ -59,7 +60,7 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
|
||||
private final byte[] encodedValue;
|
||||
|
||||
private PasswordPolicyErrorStatus errorStatus;
|
||||
private @Nullable PasswordPolicyErrorStatus errorStatus;
|
||||
|
||||
private int graceLoginsRemaining = Integer.MAX_VALUE;
|
||||
|
||||
@@ -100,7 +101,7 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
return this.encodedValue;
|
||||
}
|
||||
|
||||
public PasswordPolicyErrorStatus getErrorStatus() {
|
||||
public @Nullable PasswordPolicyErrorStatus getErrorStatus() {
|
||||
return this.errorStatus;
|
||||
}
|
||||
|
||||
@@ -165,7 +166,7 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getClass().getSimpleName()).append(" [");
|
||||
if (hasError()) {
|
||||
if (hasError() && this.errorStatus != null) {
|
||||
sb.append("error=").append(this.errorStatus.getDefaultMessage()).append("; ");
|
||||
}
|
||||
if (this.graceLoginsRemaining != Integer.MAX_VALUE) {
|
||||
@@ -227,7 +228,7 @@ public class PasswordPolicyResponseControl extends PasswordPolicyControl {
|
||||
static class SpecificTagDecoder extends BERTagDecoder {
|
||||
|
||||
/** Allows us to remember which of the two options we're decoding */
|
||||
private Boolean inChoice = null;
|
||||
private @Nullable Boolean inChoice = null;
|
||||
|
||||
@Override
|
||||
public BERElement getElement(BERTagDecoder decoder, int tag, InputStream stream, int[] bytesRead,
|
||||
|
||||
@@ -22,4 +22,7 @@
|
||||
* This code will not work with servers such as Active Directory, which do not implement
|
||||
* this standard.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.ldap.ppolicy;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -18,4 +18,7 @@
|
||||
* {@code LdapUserSearch} implementations. These may be used to locate the user in the
|
||||
* directory.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.ldap.search;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
+15
-8
@@ -24,8 +24,9 @@ import com.unboundid.ldap.listener.InMemoryListenerConfig;
|
||||
import com.unboundid.ldap.sdk.DN;
|
||||
import com.unboundid.ldap.sdk.Entry;
|
||||
import com.unboundid.ldap.sdk.LDAPException;
|
||||
import com.unboundid.ldap.sdk.RDN;
|
||||
import com.unboundid.ldif.LDIFReader;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
@@ -35,6 +36,7 @@ import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -43,7 +45,7 @@ import org.springframework.util.StringUtils;
|
||||
public class UnboundIdContainer
|
||||
implements EmbeddedLdapServerContainer, InitializingBean, DisposableBean, Lifecycle, ApplicationContextAware {
|
||||
|
||||
private InMemoryDirectoryServer directoryServer;
|
||||
private @Nullable InMemoryDirectoryServer directoryServer;
|
||||
|
||||
private final String defaultPartitionSuffix;
|
||||
|
||||
@@ -51,13 +53,13 @@ public class UnboundIdContainer
|
||||
|
||||
private boolean isEphemeral;
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
private @Nullable ConfigurableApplicationContext context;
|
||||
|
||||
private boolean running;
|
||||
|
||||
private final String ldif;
|
||||
private final @Nullable String ldif;
|
||||
|
||||
public UnboundIdContainer(String defaultPartitionSuffix, String ldif) {
|
||||
public UnboundIdContainer(String defaultPartitionSuffix, @Nullable String ldif) {
|
||||
this.defaultPartitionSuffix = defaultPartitionSuffix;
|
||||
this.ldif = ldif;
|
||||
}
|
||||
@@ -84,7 +86,7 @@ public class UnboundIdContainer
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.context = (ConfigurableApplicationContext) applicationContext;
|
||||
}
|
||||
|
||||
@@ -100,9 +102,11 @@ public class UnboundIdContainer
|
||||
config.setEnforceSingleStructuralObjectClass(false);
|
||||
config.setEnforceAttributeSyntaxCompliance(true);
|
||||
DN dn = new DN(this.defaultPartitionSuffix);
|
||||
RDN rdn = dn.getRDN();
|
||||
Assert.notNull(rdn, "defaultPartitionSuffix cannot be the empty DN");
|
||||
Entry entry = new Entry(dn);
|
||||
entry.addAttribute("objectClass", "top", "domain", "extensibleObject");
|
||||
entry.addAttribute("dc", dn.getRDN().getAttributeValues()[0]);
|
||||
entry.addAttribute("dc", rdn.getAttributeValues()[0]);
|
||||
InMemoryDirectoryServer directoryServer = new InMemoryDirectoryServer(config);
|
||||
directoryServer.add(entry);
|
||||
importLdif(directoryServer);
|
||||
@@ -118,6 +122,7 @@ public class UnboundIdContainer
|
||||
|
||||
private void importLdif(InMemoryDirectoryServer directoryServer) {
|
||||
if (StringUtils.hasText(this.ldif)) {
|
||||
Assert.notNull(this.context, "context cannot be null if ldif has a value");
|
||||
try {
|
||||
Resource[] resources = this.context.getResources(this.ldif);
|
||||
if (resources.length > 0) {
|
||||
@@ -140,7 +145,9 @@ public class UnboundIdContainer
|
||||
if (this.isEphemeral && this.context != null && !this.context.isClosed()) {
|
||||
return;
|
||||
}
|
||||
this.directoryServer.shutDown(true);
|
||||
if (this.directoryServer != null) {
|
||||
this.directoryServer.shutDown(true);
|
||||
}
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* Embedded UnboundID Server implementation, as used by the configuration namespace.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.ldap.server;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
+13
-14
@@ -18,6 +18,7 @@ package org.springframework.security.ldap.userdetails;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -29,6 +30,7 @@ import javax.naming.directory.SearchControls;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
@@ -109,7 +111,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
/**
|
||||
* A default role which will be assigned to all authenticated users if set
|
||||
*/
|
||||
private GrantedAuthority defaultRole;
|
||||
private @Nullable GrantedAuthority defaultRole;
|
||||
|
||||
/**
|
||||
* Template that will be used for searching
|
||||
@@ -130,7 +132,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
/**
|
||||
* The base DN from which the search for group membership should be performed
|
||||
*/
|
||||
private final String groupSearchBase;
|
||||
private final @Nullable String groupSearchBase;
|
||||
|
||||
/**
|
||||
* The pattern to be used for the user search. {0} is the user's DN
|
||||
@@ -150,7 +152,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
/**
|
||||
* The mapping function to be used to populate authorities.
|
||||
*/
|
||||
private Function<Map<String, List<String>>, GrantedAuthority> authorityMapper;
|
||||
private Function<Map<String, List<String>>, @Nullable GrantedAuthority> authorityMapper;
|
||||
|
||||
/**
|
||||
* Constructor for group search scenarios. <tt>userRoleAttributes</tt> may still be
|
||||
@@ -159,7 +161,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
* @param groupSearchBase if this is an empty string the search will be performed from
|
||||
* the root DN of the context factory. If null, no search will be performed.
|
||||
*/
|
||||
public DefaultLdapAuthoritiesPopulator(ContextSource contextSource, String groupSearchBase) {
|
||||
public DefaultLdapAuthoritiesPopulator(ContextSource contextSource, @Nullable String groupSearchBase) {
|
||||
Assert.notNull(contextSource, "contextSource must not be null");
|
||||
this.ldapTemplate = new SpringSecurityLdapTemplate(contextSource);
|
||||
getLdapTemplate().setSearchControls(getSearchControls());
|
||||
@@ -176,9 +178,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
return null;
|
||||
}
|
||||
String role = roles.get(0);
|
||||
if (role == null) {
|
||||
return null;
|
||||
}
|
||||
if (this.convertToUpperCase) {
|
||||
role = role.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
@@ -196,7 +195,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
*/
|
||||
|
||||
protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {
|
||||
return null;
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,15 +222,15 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
}
|
||||
|
||||
public Set<GrantedAuthority> getGroupMembershipRoles(String userDn, String username) {
|
||||
if (getGroupSearchBase() == null) {
|
||||
String base = getGroupSearchBase();
|
||||
if (base == null) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
Set<GrantedAuthority> authorities = new HashSet<>();
|
||||
logger.trace(LogMessage.of(() -> "Searching for roles for user " + username + " with DN " + userDn
|
||||
+ " and filter " + this.groupSearchFilter + " in search base " + getGroupSearchBase()));
|
||||
Set<Map<String, List<String>>> userRoles = getLdapTemplate().searchForMultipleAttributeValues(
|
||||
getGroupSearchBase(), this.groupSearchFilter, new String[] { userDn, username },
|
||||
new String[] { this.groupRoleAttribute });
|
||||
Set<Map<String, List<String>>> userRoles = getLdapTemplate().searchForMultipleAttributeValues(base,
|
||||
this.groupSearchFilter, new String[] { userDn, username }, new String[] { this.groupRoleAttribute });
|
||||
logger.debug(LogMessage.of(() -> "Found roles from search " + userRoles));
|
||||
for (Map<String, List<String>> role : userRoles) {
|
||||
GrantedAuthority authority = this.authorityMapper.apply(role);
|
||||
@@ -246,7 +245,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
return getLdapTemplate().getContextSource();
|
||||
}
|
||||
|
||||
protected String getGroupSearchBase() {
|
||||
protected @Nullable String getGroupSearchBase() {
|
||||
return this.groupSearchBase;
|
||||
}
|
||||
|
||||
@@ -311,7 +310,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||
* {@link GrantedAuthority} given the context record.
|
||||
* @param authorityMapper the mapping function
|
||||
*/
|
||||
public void setAuthorityMapper(Function<Map<String, List<String>>, GrantedAuthority> authorityMapper) {
|
||||
public void setAuthorityMapper(Function<Map<String, List<String>>, @Nullable GrantedAuthority> authorityMapper) {
|
||||
Assert.notNull(authorityMapper, "authorityMapper must not be null");
|
||||
this.authorityMapper = authorityMapper;
|
||||
}
|
||||
|
||||
+87
-55
@@ -16,8 +16,13 @@
|
||||
|
||||
package org.springframework.security.ldap.userdetails;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* UserDetails implementation whose properties are based on a subset of the LDAP schema
|
||||
@@ -32,115 +37,115 @@ public class InetOrgPerson extends Person {
|
||||
|
||||
private static final long serialVersionUID = 620L;
|
||||
|
||||
private String carLicense;
|
||||
private @Nullable String carLicense;
|
||||
|
||||
// Person.cn
|
||||
private String destinationIndicator;
|
||||
private @Nullable String destinationIndicator;
|
||||
|
||||
private String departmentNumber;
|
||||
private @Nullable String departmentNumber;
|
||||
|
||||
// Person.description
|
||||
private String displayName;
|
||||
private @Nullable String displayName;
|
||||
|
||||
private String employeeNumber;
|
||||
private @Nullable String employeeNumber;
|
||||
|
||||
private String homePhone;
|
||||
private @Nullable String homePhone;
|
||||
|
||||
private String homePostalAddress;
|
||||
private @Nullable String homePostalAddress;
|
||||
|
||||
private String initials;
|
||||
private @Nullable String initials;
|
||||
|
||||
private String mail;
|
||||
private @Nullable String mail;
|
||||
|
||||
private String mobile;
|
||||
private @Nullable String mobile;
|
||||
|
||||
private String o;
|
||||
private @Nullable String o;
|
||||
|
||||
private String ou;
|
||||
private @Nullable String ou;
|
||||
|
||||
private String postalAddress;
|
||||
private @Nullable String postalAddress;
|
||||
|
||||
private String postalCode;
|
||||
private @Nullable String postalCode;
|
||||
|
||||
private String roomNumber;
|
||||
private @Nullable String roomNumber;
|
||||
|
||||
private String street;
|
||||
private @Nullable String street;
|
||||
|
||||
// Person.sn
|
||||
// Person.telephoneNumber
|
||||
private String title;
|
||||
private @Nullable String title;
|
||||
|
||||
private String uid;
|
||||
private @Nullable String uid;
|
||||
|
||||
public String getUid() {
|
||||
return this.uid;
|
||||
return Objects.requireNonNull(this.uid, "uid cannot be null");
|
||||
}
|
||||
|
||||
public String getMail() {
|
||||
public @Nullable String getMail() {
|
||||
return this.mail;
|
||||
}
|
||||
|
||||
public String getEmployeeNumber() {
|
||||
public @Nullable String getEmployeeNumber() {
|
||||
return this.employeeNumber;
|
||||
}
|
||||
|
||||
public String getInitials() {
|
||||
public @Nullable String getInitials() {
|
||||
return this.initials;
|
||||
}
|
||||
|
||||
public String getDestinationIndicator() {
|
||||
public @Nullable String getDestinationIndicator() {
|
||||
return this.destinationIndicator;
|
||||
}
|
||||
|
||||
public String getO() {
|
||||
public @Nullable String getO() {
|
||||
return this.o;
|
||||
}
|
||||
|
||||
public String getOu() {
|
||||
public @Nullable String getOu() {
|
||||
return this.ou;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
public @Nullable String getTitle() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public String getCarLicense() {
|
||||
public @Nullable String getCarLicense() {
|
||||
return this.carLicense;
|
||||
}
|
||||
|
||||
public String getDepartmentNumber() {
|
||||
public @Nullable String getDepartmentNumber() {
|
||||
return this.departmentNumber;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
public @Nullable String getDisplayName() {
|
||||
return this.displayName;
|
||||
}
|
||||
|
||||
public String getHomePhone() {
|
||||
public @Nullable String getHomePhone() {
|
||||
return this.homePhone;
|
||||
}
|
||||
|
||||
public String getRoomNumber() {
|
||||
public @Nullable String getRoomNumber() {
|
||||
return this.roomNumber;
|
||||
}
|
||||
|
||||
public String getHomePostalAddress() {
|
||||
public @Nullable String getHomePostalAddress() {
|
||||
return this.homePostalAddress;
|
||||
}
|
||||
|
||||
public String getMobile() {
|
||||
public @Nullable String getMobile() {
|
||||
return this.mobile;
|
||||
}
|
||||
|
||||
public String getPostalAddress() {
|
||||
public @Nullable String getPostalAddress() {
|
||||
return this.postalAddress;
|
||||
}
|
||||
|
||||
public String getPostalCode() {
|
||||
public @Nullable String getPostalCode() {
|
||||
return this.postalCode;
|
||||
}
|
||||
|
||||
public String getStreet() {
|
||||
public @Nullable String getStreet() {
|
||||
return this.street;
|
||||
}
|
||||
|
||||
@@ -170,6 +175,8 @@ public class InetOrgPerson extends Person {
|
||||
|
||||
public static class Essence extends Person.Essence {
|
||||
|
||||
private @Nullable String username;
|
||||
|
||||
public Essence() {
|
||||
}
|
||||
|
||||
@@ -214,7 +221,9 @@ public class InetOrgPerson extends Person {
|
||||
setRoomNumber(ctx.getStringAttribute("roomNumber"));
|
||||
setStreet(ctx.getStringAttribute("street"));
|
||||
setTitle(ctx.getStringAttribute("title"));
|
||||
setUid(ctx.getStringAttribute("uid"));
|
||||
String uid = ctx.getStringAttribute("uid");
|
||||
Assert.notNull(uid, "uid cannot be null");
|
||||
setUid(uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -222,79 +231,102 @@ public class InetOrgPerson extends Person {
|
||||
return new InetOrgPerson();
|
||||
}
|
||||
|
||||
public void setMail(String email) {
|
||||
public void setMail(@Nullable String email) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).mail = email;
|
||||
}
|
||||
|
||||
public void setUid(String uid) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).uid = uid;
|
||||
|
||||
if (this.instance.getUsername() == null) {
|
||||
if (this.username == null) {
|
||||
setUsername(uid);
|
||||
}
|
||||
}
|
||||
|
||||
public void setInitials(String initials) {
|
||||
public void setUsername(String username) {
|
||||
super.setUsername(username);
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public void setInitials(@Nullable String initials) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).initials = initials;
|
||||
}
|
||||
|
||||
public void setO(String organization) {
|
||||
public void setO(@Nullable String organization) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).o = organization;
|
||||
}
|
||||
|
||||
public void setOu(String ou) {
|
||||
public void setOu(@Nullable String ou) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).ou = ou;
|
||||
}
|
||||
|
||||
public void setRoomNumber(String no) {
|
||||
public void setRoomNumber(@Nullable String no) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).roomNumber = no;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
public void setTitle(@Nullable String title) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).title = title;
|
||||
}
|
||||
|
||||
public void setCarLicense(String carLicense) {
|
||||
public void setCarLicense(@Nullable String carLicense) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).carLicense = carLicense;
|
||||
}
|
||||
|
||||
public void setDepartmentNumber(String departmentNumber) {
|
||||
public void setDepartmentNumber(@Nullable String departmentNumber) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).departmentNumber = departmentNumber;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
public void setDisplayName(@Nullable String displayName) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).displayName = displayName;
|
||||
}
|
||||
|
||||
public void setEmployeeNumber(String no) {
|
||||
public void setEmployeeNumber(@Nullable String no) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).employeeNumber = no;
|
||||
}
|
||||
|
||||
public void setDestinationIndicator(String destination) {
|
||||
public void setDestinationIndicator(@Nullable String destination) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).destinationIndicator = destination;
|
||||
}
|
||||
|
||||
public void setHomePhone(String homePhone) {
|
||||
public void setHomePhone(@Nullable String homePhone) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).homePhone = homePhone;
|
||||
}
|
||||
|
||||
public void setStreet(String street) {
|
||||
public void setStreet(@Nullable String street) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).street = street;
|
||||
}
|
||||
|
||||
public void setPostalCode(String postalCode) {
|
||||
public void setPostalCode(@Nullable String postalCode) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).postalCode = postalCode;
|
||||
}
|
||||
|
||||
public void setPostalAddress(String postalAddress) {
|
||||
public void setPostalAddress(@Nullable String postalAddress) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).postalAddress = postalAddress;
|
||||
}
|
||||
|
||||
public void setMobile(String mobile) {
|
||||
public void setMobile(@Nullable String mobile) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).mobile = mobile;
|
||||
}
|
||||
|
||||
public void setHomePostalAddress(String homePostalAddress) {
|
||||
public void setHomePostalAddress(@Nullable String homePostalAddress) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((InetOrgPerson) this.instance).homePostalAddress = homePostalAddress;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -39,7 +41,7 @@ public class LdapAuthority implements GrantedAuthority {
|
||||
|
||||
private final String role;
|
||||
|
||||
private final Map<String, List<String>> attributes;
|
||||
private final @Nullable Map<String, List<String>> attributes;
|
||||
|
||||
/**
|
||||
* Constructs an LdapAuthority that has a role and a DN but no other attributes
|
||||
@@ -56,7 +58,7 @@ public class LdapAuthority implements GrantedAuthority {
|
||||
* @param dn the distinguished name
|
||||
* @param attributes additional LDAP attributes
|
||||
*/
|
||||
public LdapAuthority(String role, String dn, Map<String, List<String>> attributes) {
|
||||
public LdapAuthority(String role, String dn, @Nullable Map<String, List<String>> attributes) {
|
||||
Assert.notNull(role, "role can not be null");
|
||||
Assert.notNull(dn, "dn can not be null");
|
||||
this.role = role;
|
||||
@@ -68,7 +70,7 @@ public class LdapAuthority implements GrantedAuthority {
|
||||
* Returns the LDAP attributes
|
||||
* @return the LDAP attributes, map can be null
|
||||
*/
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
public @Nullable Map<String, List<String>> getAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
@@ -98,7 +100,7 @@ public class LdapAuthority implements GrantedAuthority {
|
||||
* @param name the attribute name
|
||||
* @return the first attribute value for a specified attribute, may be null
|
||||
*/
|
||||
public String getFirstAttributeValue(String name) {
|
||||
public @Nullable String getFirstAttributeValue(String name) {
|
||||
List<String> result = getAttributeValues(name);
|
||||
return (!result.isEmpty()) ? result.get(0) : null;
|
||||
}
|
||||
|
||||
+24
-8
@@ -20,9 +20,12 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.naming.Name;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
@@ -50,11 +53,11 @@ public class LdapUserDetailsImpl implements LdapUserDetails, PasswordPolicyData
|
||||
|
||||
private static final long serialVersionUID = 620L;
|
||||
|
||||
private String dn;
|
||||
private @Nullable String dn;
|
||||
|
||||
private String password;
|
||||
private @Nullable String password;
|
||||
|
||||
private String username;
|
||||
private @Nullable String username;
|
||||
|
||||
private Collection<GrantedAuthority> authorities = AuthorityUtils.NO_AUTHORITIES;
|
||||
|
||||
@@ -81,17 +84,17 @@ public class LdapUserDetailsImpl implements LdapUserDetails, PasswordPolicyData
|
||||
|
||||
@Override
|
||||
public String getDn() {
|
||||
return this.dn;
|
||||
return Objects.requireNonNull(this.dn, "dn cannot be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
public @Nullable String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
return Objects.requireNonNull(this.username, "username cannot be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,6 +135,7 @@ public class LdapUserDetailsImpl implements LdapUserDetails, PasswordPolicyData
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof LdapUserDetailsImpl) {
|
||||
Assert.notNull(this.dn, "dn cannot be null");
|
||||
return this.dn.equals(((LdapUserDetailsImpl) obj).dn);
|
||||
}
|
||||
return false;
|
||||
@@ -139,6 +143,7 @@ public class LdapUserDetailsImpl implements LdapUserDetails, PasswordPolicyData
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
Assert.notNull(this.dn, "dn cannot be null");
|
||||
return this.dn.hashCode();
|
||||
}
|
||||
|
||||
@@ -163,7 +168,7 @@ public class LdapUserDetailsImpl implements LdapUserDetails, PasswordPolicyData
|
||||
*/
|
||||
public static class Essence {
|
||||
|
||||
protected LdapUserDetailsImpl instance = createTarget();
|
||||
protected @Nullable LdapUserDetailsImpl instance = createTarget();
|
||||
|
||||
private List<GrantedAuthority> mutableAuthorities = new ArrayList<>();
|
||||
|
||||
@@ -223,47 +228,58 @@ public class LdapUserDetailsImpl implements LdapUserDetails, PasswordPolicyData
|
||||
}
|
||||
|
||||
public void setAccountNonExpired(boolean accountNonExpired) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
this.instance.accountNonExpired = accountNonExpired;
|
||||
}
|
||||
|
||||
public void setAccountNonLocked(boolean accountNonLocked) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
this.instance.accountNonLocked = accountNonLocked;
|
||||
}
|
||||
|
||||
public void setAuthorities(Collection<? extends GrantedAuthority> authorities) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
this.mutableAuthorities = new ArrayList<>();
|
||||
this.mutableAuthorities.addAll(authorities);
|
||||
}
|
||||
|
||||
public void setCredentialsNonExpired(boolean credentialsNonExpired) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
this.instance.credentialsNonExpired = credentialsNonExpired;
|
||||
}
|
||||
|
||||
public void setDn(String dn) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
this.instance.dn = dn;
|
||||
}
|
||||
|
||||
public void setDn(Name dn) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
this.instance.dn = dn.toString();
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
this.instance.enabled = enabled;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
public void setPassword(@Nullable String password) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
this.instance.password = password;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
this.instance.username = username;
|
||||
}
|
||||
|
||||
public void setTimeBeforeExpiration(int timeBeforeExpiration) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
this.instance.timeBeforeExpiration = timeBeforeExpiration;
|
||||
}
|
||||
|
||||
public void setGraceLoginsRemaining(int graceLoginsRemaining) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
this.instance.graceLoginsRemaining = graceLoginsRemaining;
|
||||
}
|
||||
|
||||
|
||||
+19
-9
@@ -42,6 +42,7 @@ import javax.naming.ldap.LdapName;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.ldap.core.AttributesMapper;
|
||||
@@ -129,7 +130,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
return new SimpleGrantedAuthority(this.rolePrefix + role.toUpperCase(Locale.ROOT));
|
||||
};
|
||||
|
||||
private String[] attributesToRetrieve;
|
||||
private String @Nullable [] attributesToRetrieve;
|
||||
|
||||
private boolean usePasswordModifyExtensionOperation = false;
|
||||
|
||||
@@ -186,7 +187,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
* @param newPassword the new value of the password.
|
||||
*/
|
||||
@Override
|
||||
public void changePassword(final String oldPassword, final String newPassword) {
|
||||
public void changePassword(final @Nullable String oldPassword, final @Nullable String newPassword) {
|
||||
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
|
||||
Assert.notNull(authentication,
|
||||
"No authentication object found in security context. Can't change current user's password!");
|
||||
@@ -312,17 +313,23 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
this.template.executeReadWrite((ctx) -> {
|
||||
for (GrantedAuthority authority : authorities) {
|
||||
String group = convertAuthorityToGroup(authority);
|
||||
if (group == null) {
|
||||
continue;
|
||||
}
|
||||
LdapName fullDn = LdapUtils.getFullDn(userDn, ctx);
|
||||
ModificationItem addGroup = new ModificationItem(modType,
|
||||
new BasicAttribute(this.groupMemberAttributeName, fullDn.toString()));
|
||||
ctx.modifyAttributes(buildGroupDn(group), new ModificationItem[] { addGroup });
|
||||
}
|
||||
return null;
|
||||
return void.class;
|
||||
});
|
||||
}
|
||||
|
||||
private String convertAuthorityToGroup(GrantedAuthority authority) {
|
||||
private @Nullable String convertAuthorityToGroup(GrantedAuthority authority) {
|
||||
String group = authority.getAuthority();
|
||||
if (group == null) {
|
||||
return null;
|
||||
}
|
||||
if (group.startsWith(this.rolePrefix)) {
|
||||
group = group.substring(this.rolePrefix.length());
|
||||
}
|
||||
@@ -424,7 +431,8 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
this.rolePrefix = rolePrefix;
|
||||
}
|
||||
|
||||
private void changePasswordUsingAttributeModification(LdapName userDn, String oldPassword, String newPassword) {
|
||||
private void changePasswordUsingAttributeModification(LdapName userDn, @Nullable String oldPassword,
|
||||
@Nullable String newPassword) {
|
||||
ModificationItem[] passwordChange = new ModificationItem[] { new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
|
||||
new BasicAttribute(this.passwordAttributeName, newPassword)) };
|
||||
if (oldPassword == null) {
|
||||
@@ -444,11 +452,12 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
throw new BadCredentialsException("Authentication for password change failed.");
|
||||
}
|
||||
ctx.modifyAttributes(userDn, passwordChange);
|
||||
return null;
|
||||
return void.class;
|
||||
});
|
||||
}
|
||||
|
||||
private void changePasswordUsingExtensionOperation(LdapName userDn, String oldPassword, String newPassword) {
|
||||
private void changePasswordUsingExtensionOperation(LdapName userDn, @Nullable String oldPassword,
|
||||
@Nullable String newPassword) {
|
||||
this.template.executeReadWrite((dirCtx) -> {
|
||||
LdapContext ctx = (LdapContext) dirCtx;
|
||||
String userIdentity = LdapUtils.getFullDn(userDn, ctx).toString();
|
||||
@@ -491,7 +500,8 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
|
||||
private final ByteArrayOutputStream value = new ByteArrayOutputStream();
|
||||
|
||||
PasswordModifyRequest(String userIdentity, String oldPassword, String newPassword) {
|
||||
PasswordModifyRequest(@Nullable String userIdentity, @Nullable String oldPassword,
|
||||
@Nullable String newPassword) {
|
||||
ByteArrayOutputStream elements = new ByteArrayOutputStream();
|
||||
if (userIdentity != null) {
|
||||
berEncode(USER_IDENTITY_OCTET_TYPE, userIdentity.getBytes(), elements);
|
||||
@@ -516,7 +526,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtendedResponse createExtendedResponse(String id, byte[] berValue, int offset, int length) {
|
||||
public @Nullable ExtendedResponse createExtendedResponse(String id, byte[] berValue, int offset, int length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -21,6 +21,7 @@ import java.util.Locale;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
@@ -47,7 +48,7 @@ public class LdapUserDetailsMapper implements UserDetailsContextMapper {
|
||||
|
||||
private String rolePrefix = "ROLE_";
|
||||
|
||||
private String[] roleAttributes = null;
|
||||
private String @Nullable [] roleAttributes = null;
|
||||
|
||||
private boolean convertToUpperCase = true;
|
||||
|
||||
@@ -125,7 +126,7 @@ public class LdapUserDetailsMapper implements UserDetailsContextMapper {
|
||||
* @return the authority to be added to the list of authorities for the user, or null
|
||||
* if this attribute should be ignored.
|
||||
*/
|
||||
protected GrantedAuthority createAuthority(Object role) {
|
||||
protected @Nullable GrantedAuthority createAuthority(Object role) {
|
||||
if (role instanceof String) {
|
||||
if (this.convertToUpperCase) {
|
||||
role = ((String) role).toUpperCase(Locale.ROOT);
|
||||
|
||||
+12
-13
@@ -20,6 +20,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -128,7 +129,7 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
||||
/**
|
||||
* The attribute names to retrieve for each LDAP group
|
||||
*/
|
||||
private Set<String> attributeNames;
|
||||
private Set<String> attributeNames = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Maximum search depth - represents the number of recursive searches performed
|
||||
@@ -148,11 +149,12 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
||||
|
||||
@Override
|
||||
public Set<GrantedAuthority> getGroupMembershipRoles(String userDn, String username) {
|
||||
if (getGroupSearchBase() == null) {
|
||||
String base = getGroupSearchBase();
|
||||
if (base == null) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
Set<GrantedAuthority> authorities = new HashSet<>();
|
||||
performNestedSearch(userDn, username, authorities, getMaxSearchDepth());
|
||||
performNestedSearch(base, userDn, username, authorities, getMaxSearchDepth());
|
||||
return authorities;
|
||||
}
|
||||
|
||||
@@ -164,7 +166,8 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
||||
* @param authorities - the authorities set that will be populated, must not be null
|
||||
* @param depth - the depth remaining, when 0 recursion will end
|
||||
*/
|
||||
private void performNestedSearch(String userDn, String username, Set<GrantedAuthority> authorities, int depth) {
|
||||
private void performNestedSearch(String base, String userDn, String username, Set<GrantedAuthority> authorities,
|
||||
int depth) {
|
||||
if (depth == 0) {
|
||||
// back out of recursion
|
||||
logger.debug(LogMessage.of(() -> "Aborted search since max depth reached," + " for roles for user '"
|
||||
@@ -174,19 +177,15 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
||||
}
|
||||
logger.trace(LogMessage.of(() -> "Searching for roles for user " + username + " with DN " + userDn
|
||||
+ " and filter " + getGroupSearchFilter() + " in search base " + getGroupSearchBase()));
|
||||
if (getAttributeNames() == null) {
|
||||
setAttributeNames(new HashSet<>());
|
||||
}
|
||||
if (StringUtils.hasText(getGroupRoleAttribute())) {
|
||||
getAttributeNames().add(getGroupRoleAttribute());
|
||||
}
|
||||
Set<Map<String, List<String>>> userRoles = getLdapTemplate().searchForMultipleAttributeValues(
|
||||
getGroupSearchBase(), getGroupSearchFilter(), new String[] { userDn, username },
|
||||
getAttributeNames().toArray(new String[0]));
|
||||
Set<Map<String, List<String>>> userRoles = getLdapTemplate().searchForMultipleAttributeValues(base,
|
||||
getGroupSearchFilter(), new String[] { userDn, username }, getAttributeNames().toArray(new String[0]));
|
||||
logger.debug(LogMessage.format("Found roles from search %s", userRoles));
|
||||
for (Map<String, List<String>> record : userRoles) {
|
||||
boolean circular = false;
|
||||
String dn = record.get(SpringSecurityLdapTemplate.DN_KEY).get(0);
|
||||
String dn = Objects.requireNonNull(record.get(SpringSecurityLdapTemplate.DN_KEY)).get(0);
|
||||
List<String> roleValues = record.get(getGroupRoleAttribute());
|
||||
Set<String> roles = new HashSet<>();
|
||||
if (roleValues != null) {
|
||||
@@ -203,7 +202,7 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
||||
}
|
||||
String roleName = (!roles.isEmpty()) ? roles.iterator().next() : dn;
|
||||
if (!circular) {
|
||||
performNestedSearch(dn, roleName, authorities, (depth - 1));
|
||||
performNestedSearch(base, dn, roleName, authorities, (depth - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,7 +221,7 @@ public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopula
|
||||
* @param attributeNames - the names of the LDAP attributes to retrieve
|
||||
*/
|
||||
public void setAttributeNames(Set<String> attributeNames) {
|
||||
this.attributeNames = attributeNames;
|
||||
this.attributeNames = (attributeNames != null) ? attributeNames : new HashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,6 +20,8 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.security.ldap.LdapUtils;
|
||||
@@ -36,24 +38,24 @@ public class Person extends LdapUserDetailsImpl {
|
||||
|
||||
private static final long serialVersionUID = 620L;
|
||||
|
||||
private String givenName;
|
||||
private @Nullable String givenName;
|
||||
|
||||
private String sn;
|
||||
private @Nullable String sn;
|
||||
|
||||
private String description;
|
||||
private @Nullable String description;
|
||||
|
||||
private String telephoneNumber;
|
||||
private @Nullable String telephoneNumber;
|
||||
|
||||
private List<String> cn = new ArrayList<>();
|
||||
|
||||
protected Person() {
|
||||
}
|
||||
|
||||
public String getGivenName() {
|
||||
public @Nullable String getGivenName() {
|
||||
return this.givenName;
|
||||
}
|
||||
|
||||
public String getSn() {
|
||||
public @Nullable String getSn() {
|
||||
return this.sn;
|
||||
}
|
||||
|
||||
@@ -61,11 +63,11 @@ public class Person extends LdapUserDetailsImpl {
|
||||
return this.cn.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
public @Nullable String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public String getTelephoneNumber() {
|
||||
public @Nullable String getTelephoneNumber() {
|
||||
return this.telephoneNumber;
|
||||
}
|
||||
|
||||
@@ -88,7 +90,9 @@ public class Person extends LdapUserDetailsImpl {
|
||||
|
||||
public Essence(DirContextOperations ctx) {
|
||||
super(ctx);
|
||||
setCn(ctx.getStringAttributes("cn"));
|
||||
String[] cns = ctx.getStringAttributes("cn");
|
||||
cns = (cns != null) ? cns : new String[0];
|
||||
setCn(cns);
|
||||
setGivenName(ctx.getStringAttribute("givenName"));
|
||||
setSn(ctx.getStringAttribute("sn"));
|
||||
setDescription(ctx.getStringAttribute("description"));
|
||||
@@ -105,7 +109,7 @@ public class Person extends LdapUserDetailsImpl {
|
||||
setSn(copyMe.sn);
|
||||
setDescription(copyMe.getDescription());
|
||||
setTelephoneNumber(copyMe.getTelephoneNumber());
|
||||
((Person) this.instance).cn = new ArrayList<>(copyMe.cn);
|
||||
setCn(copyMe.cn.toArray(String[]::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -113,27 +117,33 @@ public class Person extends LdapUserDetailsImpl {
|
||||
return new Person();
|
||||
}
|
||||
|
||||
public void setGivenName(String givenName) {
|
||||
public void setGivenName(@Nullable String givenName) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((Person) this.instance).givenName = givenName;
|
||||
}
|
||||
|
||||
public void setSn(String sn) {
|
||||
public void setSn(@Nullable String sn) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((Person) this.instance).sn = sn;
|
||||
}
|
||||
|
||||
public void setCn(String[] cn) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((Person) this.instance).cn = Arrays.asList(cn);
|
||||
}
|
||||
|
||||
public void addCn(String value) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((Person) this.instance).cn.add(value);
|
||||
}
|
||||
|
||||
public void setTelephoneNumber(String tel) {
|
||||
public void setTelephoneNumber(@Nullable String tel) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((Person) this.instance).telephoneNumber = tel;
|
||||
}
|
||||
|
||||
public void setDescription(String desc) {
|
||||
public void setDescription(@Nullable String desc) {
|
||||
Assert.notNull(this.instance, "Essence can only be used to create a single instance");
|
||||
((Person) this.instance).description = desc;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,4 +18,7 @@
|
||||
* LDAP-focused {@code UserDetails} implementations which map from a ubset of the data
|
||||
* contained in some of the standard LDAP types (such as {@code InetOrgPerson}).
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.ldap.userdetails;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
+9
@@ -55,6 +55,15 @@ public class SpringSecurityAuthenticationSourceTests {
|
||||
assertThat(source.getCredentials()).isEqualTo("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void credentialsAreEmptyWithNullCredentials() {
|
||||
AuthenticationSource source = new SpringSecurityAuthenticationSource();
|
||||
SecurityContextHolder.getContext()
|
||||
.setAuthentication(
|
||||
new AnonymousAuthenticationToken("key", "anonUser", AuthorityUtils.createAuthorityList("ignored")));
|
||||
assertThat(source.getCredentials()).isEqualTo("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void principalIsEmptyForAnonymousUser() {
|
||||
AuthenticationSource source = new SpringSecurityAuthenticationSource();
|
||||
|
||||
+2
@@ -18,6 +18,7 @@ package org.springframework.security.ldap.authentication;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.ldap.CommunicationException;
|
||||
@@ -168,6 +169,7 @@ public class LdapAuthenticationProviderTests {
|
||||
SecurityAssertions.assertThat(result).hasAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY);
|
||||
}
|
||||
|
||||
@NullMarked
|
||||
class MockAuthenticator implements LdapAuthenticator {
|
||||
|
||||
@Override
|
||||
|
||||
+9
-1
@@ -16,15 +16,20 @@
|
||||
|
||||
package org.springframework.security.ldap.authentication;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.ldap.search.LdapUserSearch;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@NullMarked
|
||||
public class MockUserSearch implements LdapUserSearch {
|
||||
|
||||
DirContextOperations user;
|
||||
@Nullable DirContextOperations user;
|
||||
|
||||
public MockUserSearch() {
|
||||
}
|
||||
@@ -35,6 +40,9 @@ public class MockUserSearch implements LdapUserSearch {
|
||||
|
||||
@Override
|
||||
public DirContextOperations searchForUser(String username) {
|
||||
if (this.user == null) {
|
||||
throw UsernameNotFoundException.fromUsername(username);
|
||||
}
|
||||
return this.user;
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -19,6 +19,8 @@ package org.springframework.security.ldap.userdetails;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
@@ -71,10 +73,12 @@ public class LdapUserDetailsServiceTests {
|
||||
assertThat(user.getAuthorities()).isEmpty();
|
||||
}
|
||||
|
||||
@NullUnmarked
|
||||
class MockAuthoritiesPopulator implements LdapAuthoritiesPopulator {
|
||||
|
||||
@Override
|
||||
public Collection<GrantedAuthority> getGrantedAuthorities(DirContextOperations userCtx, String username) {
|
||||
public Collection<GrantedAuthority> getGrantedAuthorities(@NonNull DirContextOperations userCtx,
|
||||
String username) {
|
||||
return AuthorityUtils.createAuthorityList("ROLE_FROM_POPULATOR");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user