/* See LICENSE for licensing and NOTICE for copyright. */
package org.ldaptive.auth.ext;
import java.time.ZonedDateTime;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.security.auth.login.AccountException;
import javax.security.auth.login.AccountExpiredException;
import javax.security.auth.login.AccountLockedException;
import javax.security.auth.login.AccountNotFoundException;
import javax.security.auth.login.CredentialExpiredException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import org.ldaptive.auth.AccountState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents the state of an Active Directory account. Note that the warning returned by this implementation always
* returns -1 for logins remaining.
*
* @author Middleware Services
*/
public class ActiveDirectoryAccountState extends AccountState
{
/**
* Enum to define active directory errors. See http://ldapwiki.willeke.com/wiki/
* Common%20Active%20Directory%20Bind%20Errors
*/
public enum Error implements AccountState.Error {
/** no such user. 0x525. */
NO_SUCH_USER(1317),
/** logon failure. 0x52e. */
LOGON_FAILURE(1326),
/** invalid logon hours. 0x530. */
INVALID_LOGON_HOURS(1328),
/** invalid workstation. 0x531. */
INVALID_WORKSTATION(1329),
/** password expired. 0x532. */
PASSWORD_EXPIRED(1330),
/** account disabled. 0x533. */
ACCOUNT_DISABLED(1331),
/** account expired. 0x701. */
ACCOUNT_EXPIRED(1793),
/** password must change. 0x773. */
PASSWORD_MUST_CHANGE(1907),
/** account locked out. 0x775. */
ACCOUNT_LOCKED_OUT(1909);
/** hex radix for hex to decimal conversion. */
private static final int HEX_RADIX = 16;
/** pattern to find hex code in active directory messages. */
private static final Pattern PATTERN = Pattern.compile("data (\\w+)");
/** underlying error code. */
private final int code;
/**
* Creates a new active directory error.
*
* @param i error code
*/
Error(final int i)
{
code = i;
}
@Override
public int getCode()
{
return code;
}
@Override
public String getMessage()
{
return name();
}
@Override
public void throwSecurityException()
throws LoginException
{
switch (this) {
case NO_SUCH_USER:
throw new AccountNotFoundException(name());
case LOGON_FAILURE:
throw new FailedLoginException(name());
case INVALID_LOGON_HOURS:
throw new AccountLockedException(name());
case INVALID_WORKSTATION:
throw new AccountException(name());
case PASSWORD_EXPIRED:
throw new CredentialExpiredException(name());
case ACCOUNT_DISABLED:
throw new AccountLockedException(name());
case ACCOUNT_EXPIRED:
throw new AccountExpiredException(name());
case PASSWORD_MUST_CHANGE:
throw new CredentialExpiredException(name());
case ACCOUNT_LOCKED_OUT:
throw new AccountLockedException(name());
default:
throw new IllegalStateException("Unknown active directory error: " + this);
}
}
/**
* Returns the error for the supplied integer constant.
*
* @param code to find error for
*
* @return error
*/
public static Error valueOf(final int code)
{
for (Error e : Error.values()) {
if (e.getCode() == code) {
return e;
}
}
return null;
}
/**
* Parses the supplied error messages and returns the corresponding error enum. Attempts to find {@link #PATTERN}
* and parses the first group match as a hexadecimal integer.
*
* @param message to parse
*
* @return active directory error
*/
public static Error parse(final String message)
{
if (message != null) {
final Matcher matcher = PATTERN.matcher(message);
if (matcher.find()) {
try {
return Error.valueOf(Integer.parseInt(matcher.group(1).toUpperCase(), HEX_RADIX));
} catch (NumberFormatException e) {
final Logger l = LoggerFactory.getLogger(Error.class);
l.warn("Error parsing active directory error", e);
}
}
}
return null;
}
}
/** active directory specific enum. */
private final Error adError;
/**
* Creates a new active directory account state.
*
* @param exp account expiration
*/
public ActiveDirectoryAccountState(final ZonedDateTime exp)
{
super(new AccountState.DefaultWarning(exp, -1));
adError = null;
}
/**
* Creates a new active directory account state.
*
* @param error containing authentication failure details
*/
public ActiveDirectoryAccountState(final ActiveDirectoryAccountState.Error error)
{
super(error);
adError = error;
}
/**
* Returns the active directory error for this account state.
*
* @return active directory error
*/
public ActiveDirectoryAccountState.Error getActiveDirectoryError()
{
return adError;
}
}