spring-projects/spring-security · error · UncategorizedLdapException
<namingException.getMessage()>
Error message
<namingException.getMessage()>
What it means
PasswordPolicyAwareContextSource.getContext catches NamingExceptions raised while obtaining an LDAP context. If a password policy response control indicates the account is locked with an error status, it throws PasswordPolicyException with that status; otherwise it converts the NamingException via LdapUtils.convertLdapException(ex), surfacing the raw naming exception message. This distinguishes password-policy failures (locked account, expired password) from generic directory errors.
Source
Thrown at ldap/src/main/java/org/springframework/security/ldap/ppolicy/PasswordPolicyAwareContextSource.java:71
this.logger.trace(LogMessage.format("Binding as %s, prior to reconnect as user %s", getUserDn(), principal));
// First bind as manager user before rebinding as the specific principal.
LdapContext ctx = (LdapContext) super.getContext(getUserDn(), getPassword());
Control[] rctls = { new PasswordPolicyControl(false) };
try {
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, principal);
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, credentials);
ctx.reconnect(rctls);
}
catch (javax.naming.NamingException ex) {
PasswordPolicyResponseControl ctrl = PasswordPolicyControlExtractor.extractControl(ctx);
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Failed to bind with %s", ctrl), ex);
}
LdapUtils.closeContext(ctx);
if (ctrl != null && ctrl.isLocked() && ctrl.getErrorStatus() != null) {
throw new PasswordPolicyException(ctrl.getErrorStatus());
}
throw LdapUtils.convertLdapException(ex);
}
this.logger.debug(LogMessage.of(() -> "Bound with " + PasswordPolicyControlExtractor.extractControl(ctx)));
return ctx;
}
@Override
@SuppressWarnings("unchecked")
protected Hashtable getAuthenticatedEnv(String principal, String credentials) {
Hashtable<String, Object> env = super.getAuthenticatedEnv(principal, credentials);
env.put(LdapContext.CONTROL_FACTORIES, PasswordPolicyControlFactory.class.getName());
return env;
}
}
View on GitHub (pinned to 96852e8860)
Solutions
- If PasswordPolicyException reports a lock, unlock the account in the directory or wait out the lockout policy.
- Check the converted exception message for expired-password or invalid-credentials hints and have the user reset/change the password.
- Verify bind credentials and the directory's password policy configuration (ppolicy overlay in OpenLDAP, fine-grained policy in 389DS).
- Confirm network connectivity to the LDAP server if the message indicates a communication problem.
Example fix
// before: application keeps retrying binds for a locked account
Authentication result = provider.authenticate(token);
// after: catch policy errors and surface account state
try {
result = provider.authenticate(token);
} catch (PasswordPolicyException ex) {
throw new LockedException("Account locked: " + ex.getMessage());
} Defensive patterns
Strategy: try-catch
Try / catch
try {
Authentication a = provider.authenticate(token);
} catch (PasswordPolicyException e) {
// map to LockedException/CredentialsExpiredException for user-friendly UI
} catch (org.springframework.security.authentication.BadCredentialsException e) {
// wrong username/password
} Prevention
- Configure password policy handling (ppolicy) and map its error statuses to UX flows.
- Expose account-lock/expired states distinctly instead of generic login failure.
- Monitor lockout rates to catch credential-stuffing attacks.
- Set sensible lockout durations so legitimate users are not stranded.
When it happens
Trigger: Calling getContext (used during authentication binds) when the LDAP bind fails: account locked per password policy control (PasswordPolicyException with error status) or any underlying JNDI error (bad credentials, communication failure).
Common situations: User exceeded failed-login attempts and the directory locked the account; password expired and mustChangePassword is set; directory unreachable; wrong bind DN/password for the manager or user account.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- ${ex.getStatus().getDefaultMessage()}
- ${ctrl.getErrorStatus()}
- AbstractUserDetailsAuthenticationProvider.locked
- AbstractUserDetailsAuthenticationProvider.credentialsExpired
- AccountStatusUserDetailsChecker.locked
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/55fbd1c315ff7508.
Report an issue: GitHub.