From af1b4bead731b2dc8f25e4db507afab0428054d0 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Tue, 20 Sep 2011 11:29:12 +0000
Subject: [PATCH] Issue OPENDJ-262: Implement pass through authentication (PTA)
---
opendj-sdk/opends/src/server/org/opends/server/api/AuthenticationPolicyState.java | 295 +++++++++++++++++++-
opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java | 241 ----------------
opendj-sdk/opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java | 21 +
opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java | 8
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/api/AuthenticationPolicyTestCase.java | 141 ++++++----
opendj-sdk/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java | 4
opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationBasis.java | 40 +-
opendj-sdk/opends/src/server/org/opends/server/controls/ProxiedAuthV1Control.java | 21 +
opendj-sdk/opends/src/server/org/opends/server/extensions/PlainSASLMechanismHandler.java | 12
9 files changed, 442 insertions(+), 341 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/AuthenticationPolicyState.java b/opendj-sdk/opends/src/server/org/opends/server/api/AuthenticationPolicyState.java
index b71ae6c..7b2973e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/api/AuthenticationPolicyState.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/AuthenticationPolicyState.java
@@ -29,9 +29,20 @@
-import org.opends.server.types.ByteString;
-import org.opends.server.types.DirectoryException;
-import org.opends.server.types.Entry;
+import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.config.ConfigConstants.OP_ATTR_ACCOUNT_DISABLED;
+import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
+import static org.opends.server.loggers.debug.DebugLogger.getTracer;
+import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
+import static org.opends.server.util.StaticUtils.toLowerCase;
+
+import java.util.List;
+
+import org.opends.messages.Message;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.schema.GeneralizedTimeSyntax;
+import org.opends.server.types.*;
@@ -43,6 +54,13 @@
public abstract class AuthenticationPolicyState
{
/**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+
+
+ /**
* Returns the authentication policy state for the user provided user. This
* method is equivalent to the following:
*
@@ -68,10 +86,10 @@
* policy for the user.
* @see AuthenticationPolicy#forUser(Entry, boolean)
*/
- public final static AuthenticationPolicyState forUser(Entry userEntry,
- boolean useDefaultOnError) throws DirectoryException
+ public final static AuthenticationPolicyState forUser(final Entry userEntry,
+ final boolean useDefaultOnError) throws DirectoryException
{
- AuthenticationPolicy policy = AuthenticationPolicy.forUser(userEntry,
+ final AuthenticationPolicy policy = AuthenticationPolicy.forUser(userEntry,
useDefaultOnError);
return policy.createAuthenticationPolicyState(userEntry);
}
@@ -79,37 +97,184 @@
/**
- * Creates a new abstract authentication policy context.
+ * A utility method which may be used by implementations in order to obtain
+ * the value of the specified attribute from the provided entry as a boolean.
+ *
+ * @param entry
+ * The entry whose attribute is to be parsed as a boolean.
+ * @param attributeType
+ * The attribute type whose value should be parsed as a boolean.
+ * @return The attribute's value represented as a ConditionResult value, or
+ * ConditionResult.UNDEFINED if the specified attribute does not exist
+ * in the entry.
+ * @throws DirectoryException
+ * If the value cannot be decoded as a boolean.
*/
- protected AuthenticationPolicyState()
+ protected static final ConditionResult getBoolean(final Entry entry,
+ final AttributeType attributeType) throws DirectoryException
{
- // No implementation required.
+ final List<Attribute> attrList = entry.getAttribute(attributeType);
+ if (attrList != null)
+ {
+ for (final Attribute a : attrList)
+ {
+ if (a.isEmpty())
+ {
+ continue;
+ }
+
+ final String valueString = toLowerCase(a.iterator().next().getValue()
+ .toString());
+
+ if (valueString.equals("true") || valueString.equals("yes")
+ || valueString.equals("on") || valueString.equals("1"))
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugInfo("Attribute %s resolves to true for user entry "
+ + "%s", attributeType.getNameOrOID(), entry.getDN().toString());
+ }
+
+ return ConditionResult.TRUE;
+ }
+
+ if (valueString.equals("false") || valueString.equals("no")
+ || valueString.equals("off") || valueString.equals("0"))
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugInfo("Attribute %s resolves to false for user "
+ + "entry %s", attributeType.getNameOrOID(), entry.getDN()
+ .toString());
+ }
+
+ return ConditionResult.FALSE;
+ }
+
+ if (debugEnabled())
+ {
+ TRACER.debugError("Unable to resolve value %s for attribute %s "
+ + "in user entry %s as a Boolean.", valueString,
+ attributeType.getNameOrOID(), entry.getDN().toString());
+ }
+
+ final Message message = ERR_PWPSTATE_CANNOT_DECODE_BOOLEAN
+ .get(valueString, attributeType.getNameOrOID(), entry.getDN()
+ .toString());
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
+ }
+ }
+
+ if (debugEnabled())
+ {
+ TRACER.debugInfo("Returning %s because attribute %s does not exist "
+ + "in user entry %s", ConditionResult.UNDEFINED.toString(),
+ attributeType.getNameOrOID(), entry.getDN().toString());
+ }
+
+ return ConditionResult.UNDEFINED;
}
/**
- * Returns {@code true} if the provided password value matches any of the
- * user's passwords.
+ * A utility method which may be used by implementations in order to obtain
+ * the value of the specified attribute from the provided entry as a time in
+ * generalized time format.
*
- * @param password
- * The user-provided password to verify.
- * @return {@code true} if the provided password value matches any of the
- * user's passwords.
+ * @param entry
+ * The entry whose attribute is to be parsed as a boolean.
+ * @param attributeType
+ * The attribute type whose value should be parsed as a generalized
+ * time value.
+ * @return The requested time, or -1 if it could not be determined.
* @throws DirectoryException
- * If verification unexpectedly failed.
+ * If a problem occurs while attempting to decode the value as a
+ * generalized time.
*/
- public abstract boolean passwordMatches(ByteString password)
- throws DirectoryException;
+ protected static final long getGeneralizedTime(final Entry entry,
+ final AttributeType attributeType) throws DirectoryException
+ {
+ long timeValue = -1;
+
+ final List<Attribute> attrList = entry.getAttribute(attributeType);
+ if (attrList != null)
+ {
+ for (final Attribute a : attrList)
+ {
+ if (a.isEmpty())
+ {
+ continue;
+ }
+
+ final AttributeValue v = a.iterator().next();
+ try
+ {
+ timeValue = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(v
+ .getNormalizedValue());
+ }
+ catch (final Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+
+ TRACER.debugWarning("Unable to decode value %s for attribute %s "
+ + "in user entry %s: %s", v.getValue().toString(),
+ attributeType.getNameOrOID(), entry.getDN().toString(),
+ stackTraceToSingleLineString(e));
+ }
+
+ final Message message = ERR_PWPSTATE_CANNOT_DECODE_GENERALIZED_TIME
+ .get(v.getValue().toString(), attributeType.getNameOrOID(), entry
+ .getDN().toString(), String.valueOf(e));
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message, e);
+ }
+ break;
+ }
+ }
+
+ if (timeValue == -1)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugInfo("Returning -1 because attribute %s does not "
+ + "exist in user entry %s", attributeType.getNameOrOID(), entry
+ .getDN().toString());
+ }
+ }
+ // FIXME: else to be consistent...
+
+ return timeValue;
+ }
/**
- * Returns the authentication policy associated with this state.
- *
- * @return The authentication policy associated with this state.
+ * A boolean indicating whether or not the account associated with this
+ * authentication state has been administratively disabled.
*/
- public abstract AuthenticationPolicy getAuthenticationPolicy();
+ protected ConditionResult isDisabled = ConditionResult.UNDEFINED;
+
+ /**
+ * The user entry associated with this authentication policy state.
+ */
+ protected final Entry userEntry;
+
+
+
+ /**
+ * Creates a new abstract authentication policy context.
+ *
+ * @param userEntry
+ * The user's entry.
+ */
+ protected AuthenticationPolicyState(final Entry userEntry)
+ {
+ this.userEntry = userEntry;
+ }
@@ -129,6 +294,76 @@
/**
+ * Returns the authentication policy associated with this state.
+ *
+ * @return The authentication policy associated with this state.
+ */
+ public abstract AuthenticationPolicy getAuthenticationPolicy();
+
+
+
+ /**
+ * Returns {@code true} if this authentication policy state is associated with
+ * a user whose account has been administratively disabled.
+ * <p>
+ * The default implementation is use the value of the "ds-pwp-account-disable"
+ * attribute in the user's entry.
+ *
+ * @return {@code true} if this authentication policy state is associated with
+ * a user whose account has been administratively disabled.
+ */
+ public boolean isDisabled()
+ {
+ final AttributeType type = DirectoryServer.getAttributeType(
+ OP_ATTR_ACCOUNT_DISABLED, true);
+ try
+ {
+ isDisabled = getBoolean(userEntry, type);
+ }
+ catch (final Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ isDisabled = ConditionResult.TRUE;
+ if (debugEnabled())
+ {
+ TRACER.debugWarning("User %s is considered administratively "
+ + "disabled because an error occurred while "
+ + "attempting to make the determination: %s.", userEntry.getDN()
+ .toString(), stackTraceToSingleLineString(e));
+ }
+
+ return true;
+ }
+
+ if (isDisabled == ConditionResult.UNDEFINED)
+ {
+ isDisabled = ConditionResult.FALSE;
+ if (debugEnabled())
+ {
+ TRACER.debugInfo("User %s is not administratively disabled since "
+ + "the attribute \"%s\" is not present in the entry.", userEntry
+ .getDN().toString(), OP_ATTR_ACCOUNT_DISABLED);
+ }
+ return false;
+ }
+
+ if (debugEnabled())
+ {
+ TRACER.debugInfo("User %s %s administratively disabled.", userEntry
+ .getDN().toString(), ((isDisabled == ConditionResult.TRUE) ? " is"
+ : " is not"));
+ }
+
+ return isDisabled == ConditionResult.TRUE;
+ }
+
+
+
+ /**
* Returns {@code true} if this authentication policy state is associated with
* a password policy and the method {@link #getAuthenticationPolicy} will
* return a {@code PasswordPolicy}.
@@ -140,4 +375,20 @@
{
return getAuthenticationPolicy().isPasswordPolicy();
}
+
+
+
+ /**
+ * Returns {@code true} if the provided password value matches any of the
+ * user's passwords.
+ *
+ * @param password
+ * The user-provided password to verify.
+ * @return {@code true} if the provided password value matches any of the
+ * user's passwords.
+ * @throws DirectoryException
+ * If verification unexpectedly failed.
+ */
+ public abstract boolean passwordMatches(ByteString password)
+ throws DirectoryException;
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/controls/ProxiedAuthV1Control.java b/opendj-sdk/opends/src/server/org/opends/server/controls/ProxiedAuthV1Control.java
index dd4103a..f7e83d0 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/controls/ProxiedAuthV1Control.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/controls/ProxiedAuthV1Control.java
@@ -32,7 +32,7 @@
import java.util.concurrent.locks.Lock;
import java.io.IOException;
-import org.opends.server.api.AuthenticationPolicy;
+import org.opends.server.api.AuthenticationPolicyState;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.PasswordPolicyState;
import org.opends.server.protocols.asn1.*;
@@ -325,13 +325,20 @@
// FIXME -- We should provide some mechanism for enabling debug
// processing.
- AuthenticationPolicy policy = AuthenticationPolicy.forUser(userEntry,
- false);
- if (policy.isPasswordPolicy())
+ AuthenticationPolicyState state = AuthenticationPolicyState.forUser(
+ userEntry, false);
+
+ if (state.isDisabled())
{
- PasswordPolicyState pwpState = (PasswordPolicyState) policy
- .createAuthenticationPolicyState(userEntry);
- if (pwpState.isDisabled() || pwpState.isAccountExpired() ||
+ Message message = ERR_PROXYAUTH1_UNUSABLE_ACCOUNT.get(String
+ .valueOf(userEntry.getDN()));
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message);
+ }
+
+ if (state.isPasswordPolicy())
+ {
+ PasswordPolicyState pwpState = (PasswordPolicyState) state;
+ if (pwpState.isAccountExpired() ||
pwpState.lockedDueToFailures() ||
pwpState.lockedDueToIdleInterval() ||
pwpState.lockedDueToMaximumResetAge() ||
diff --git a/opendj-sdk/opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java b/opendj-sdk/opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java
index 079bceb..0a916a8 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java
@@ -33,7 +33,7 @@
import java.util.concurrent.locks.Lock;
import java.io.IOException;
-import org.opends.server.api.AuthenticationPolicy;
+import org.opends.server.api.AuthenticationPolicyState;
import org.opends.server.api.IdentityMapper;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.PasswordPolicyState;
@@ -333,13 +333,20 @@
private void checkAccountIsUsable(Entry userEntry)
throws DirectoryException
{
- AuthenticationPolicy policy = AuthenticationPolicy.forUser(userEntry,
- false);
- if (policy.isPasswordPolicy())
+ AuthenticationPolicyState state = AuthenticationPolicyState.forUser(
+ userEntry, false);
+
+ if (state.isDisabled())
{
- PasswordPolicyState pwpState = (PasswordPolicyState) policy
- .createAuthenticationPolicyState(userEntry);
- if (pwpState.isDisabled() || pwpState.isAccountExpired() ||
+ Message message = ERR_PROXYAUTH2_UNUSABLE_ACCOUNT.get(String
+ .valueOf(userEntry.getDN()));
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message);
+ }
+
+ if (state.isPasswordPolicy())
+ {
+ PasswordPolicyState pwpState = (PasswordPolicyState) state;
+ if (pwpState.isAccountExpired() ||
pwpState.lockedDueToFailures() ||
pwpState.lockedDueToIdleInterval() ||
pwpState.lockedDueToMaximumResetAge() ||
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java b/opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java
index 868a183..47cabf7 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java
@@ -77,9 +77,6 @@
- // The user entry with which this state information is associated.
- private final Entry userEntry;
-
// The string representation of the user's DN.
private final String userDNString;
@@ -169,7 +166,7 @@
*/
PasswordPolicyState(PasswordPolicy policy, Entry userEntry, long currentTime)
{
- this.userEntry = userEntry;
+ super(userEntry);
this.currentTime = currentTime;
this.userDNString = userEntry.getDN().toString();
this.passwordPolicy = policy;
@@ -225,74 +222,6 @@
/**
- * Retrieves the value of the specified attribute from the user's entry as a
- * time in generalized time format.
- *
- * @param attributeType The attribute type whose value should be parsed as a
- * generalized time value.
- *
- * @return The requested time, or -1 if it could not be determined.
- *
- * @throws DirectoryException If a problem occurs while attempting to
- * decode the value as a generalized time.
- */
- private long getGeneralizedTime(AttributeType attributeType)
- throws DirectoryException
- {
- long timeValue = -1 ;
-
- List<Attribute> attrList = userEntry.getAttribute(attributeType);
- if (attrList != null)
- {
- for (Attribute a : attrList)
- {
- if (a.isEmpty()) continue;
-
- AttributeValue v = a.iterator().next();
- try
- {
- timeValue = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(
- v.getNormalizedValue());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
-
- TRACER.debugWarning("Unable to decode value %s for attribute %s " +
- "in user entry %s: %s",
- v.getValue().toString(), attributeType.getNameOrOID(),
- userDNString, stackTraceToSingleLineString(e));
- }
-
- Message message = ERR_PWPSTATE_CANNOT_DECODE_GENERALIZED_TIME.
- get(v.getValue().toString(), attributeType.getNameOrOID(),
- userDNString, String.valueOf(e));
- throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
- message, e);
- }
- break ;
- }
- }
-
- if (timeValue == -1)
- {
- if (debugEnabled())
- {
- TRACER.debugInfo("Returning -1 because attribute %s does not " +
- "exist in user entry %s",
- attributeType.getNameOrOID(), userDNString);
- }
- }
- // FIXME: else to be consistent...
-
- return timeValue;
- }
-
-
-
- /**
* Retrieves the set of values of the specified attribute from the user's
* entry in generalized time format.
*
@@ -359,84 +288,6 @@
/**
- * Retrieves the value of the specified attribute from the user's entry as a
- * Boolean.
- *
- * @param attributeType The attribute type whose value should be parsed as a
- * Boolean.
- *
- * @return The attribute's value represented as a ConditionResult value, or
- * ConditionResult.UNDEFINED if the specified attribute does not
- * exist in the entry.
- *
- * @throws DirectoryException If the value cannot be decoded as a Boolean.
- */
- private ConditionResult getBoolean(AttributeType attributeType)
- throws DirectoryException
- {
- List<Attribute> attrList = userEntry.getAttribute(attributeType);
- if (attrList != null)
- {
- for (Attribute a : attrList)
- {
- if (a.isEmpty()) continue;
-
- String valueString
- = toLowerCase(a.iterator().next().getValue().toString());
-
- if (valueString.equals("true") || valueString.equals("yes") ||
- valueString.equals("on") || valueString.equals("1"))
- {
- if (debugEnabled())
- {
- TRACER.debugInfo("Attribute %s resolves to true for user entry " +
- "%s", attributeType.getNameOrOID(), userDNString);
- }
-
- return ConditionResult.TRUE;
- }
-
- if (valueString.equals("false") || valueString.equals("no") ||
- valueString.equals("off") || valueString.equals("0"))
- {
- if (debugEnabled())
- {
- TRACER.debugInfo("Attribute %s resolves to false for user " +
- "entry %s", attributeType.getNameOrOID(), userDNString);
- }
-
- return ConditionResult.FALSE;
- }
-
- if(debugEnabled())
- {
- TRACER.debugError("Unable to resolve value %s for attribute %s " +
- "in user entry %s as a Boolean.",
- valueString, attributeType.getNameOrOID(),
- userDNString);
- }
-
- Message message = ERR_PWPSTATE_CANNOT_DECODE_BOOLEAN.get(
- valueString, attributeType.getNameOrOID(), userDNString);
- throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
- message);
- }
- }
-
- if (debugEnabled())
- {
- TRACER.debugInfo("Returning %s because attribute %s does not exist " +
- "in user entry %s",
- ConditionResult.UNDEFINED.toString(),
- attributeType.getNameOrOID(), userDNString);
- }
-
- return ConditionResult.UNDEFINED;
- }
-
-
-
- /**
* {@inheritDoc}
*/
public PasswordPolicy getAuthenticationPolicy()
@@ -461,7 +312,7 @@
try
{
- passwordChangedTime = getGeneralizedTime(type);
+ passwordChangedTime = getGeneralizedTime(userEntry, type);
}
catch (DirectoryException e)
{
@@ -481,7 +332,7 @@
OP_ATTR_CREATE_TIMESTAMP_LC, true);
try
{
- passwordChangedTime = getGeneralizedTime(createTimeType);
+ passwordChangedTime = getGeneralizedTime(userEntry, createTimeType);
}
catch (DirectoryException e)
{
@@ -626,7 +477,7 @@
DirectoryServer.getAttributeType(OP_ATTR_CREATE_TIMESTAMP_LC, true);
try
{
- passwordChangedTime = getGeneralizedTime(createTimeType);
+ passwordChangedTime = getGeneralizedTime(userEntry, createTimeType);
if (passwordChangedTime < 0)
{
passwordChangedTime = 0;
@@ -640,81 +491,13 @@
-
- /**
- * Indicates whether the user account has been administratively disabled.
- *
- * @return <CODE>true</CODE> if the user account has been administratively
- * disabled, or <CODE>false</CODE> otherwise.
- */
- public boolean isDisabled()
- {
- if (isDisabled != ConditionResult.UNDEFINED)
- {
- if (debugEnabled())
- {
- TRACER.debugInfo("Returning stored result of %b for user %s",
- (isDisabled == ConditionResult.TRUE), userDNString);
- }
-
- return isDisabled == ConditionResult.TRUE;
- }
-
- AttributeType type =
- DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_DISABLED, true);
- try
- {
- isDisabled = getBoolean(type);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- isDisabled = ConditionResult.TRUE;
- if (debugEnabled())
- {
- TRACER.debugWarning("User %s is considered administratively " +
- "disabled because an error occurred while attempting to make " +
- "the determination: %s.",
- userDNString, stackTraceToSingleLineString(e));
- }
-
- return true;
- }
-
- if (isDisabled == ConditionResult.UNDEFINED)
- {
- isDisabled = ConditionResult.FALSE;
- if (debugEnabled())
- {
- TRACER.debugInfo("User %s is not administratively disabled since " +
- "the attribute \"%s\" is not present in the entry.",
- userDNString, OP_ATTR_ACCOUNT_DISABLED);
- }
- return false;
- }
-
- if (debugEnabled())
- {
- TRACER.debugInfo("User %s %s administratively disabled.",
- userDNString,
- ((isDisabled == ConditionResult.TRUE) ? " is" : " is not"));
- }
-
- return isDisabled == ConditionResult.TRUE;
- }
-
-
-
/**
* Updates the user entry to indicate whether user account has been
* administratively disabled.
*
- * @param isDisabled Indicates whether the user account has been
- * administratively disabled.
+ * @param isDisabled
+ * Indicates whether the user account has been administratively
+ * disabled.
*/
public void setDisabled(boolean isDisabled)
{
@@ -775,7 +558,7 @@
try
{
- accountExpirationTime = getGeneralizedTime(type);
+ accountExpirationTime = getGeneralizedTime(userEntry, type);
}
catch (Exception e)
{
@@ -1216,7 +999,7 @@
try
{
- failureLockedTime = getGeneralizedTime(type);
+ failureLockedTime = getGeneralizedTime(userEntry, type);
}
catch (Exception e)
{
@@ -1811,7 +1594,7 @@
try
{
- mustChangePassword = getBoolean(type);
+ mustChangePassword = getBoolean(userEntry, type);
}
catch (Exception e)
{
@@ -2335,7 +2118,7 @@
try
{
- requiredChangeTime = getGeneralizedTime(type);
+ requiredChangeTime = getGeneralizedTime(userEntry, type);
}
catch (Exception e)
{
@@ -2449,7 +2232,7 @@
DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_WARNED_TIME, true);
try
{
- warnedTime = getGeneralizedTime(type);
+ warnedTime = getGeneralizedTime(userEntry, type);
}
catch (Exception e)
{
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationBasis.java b/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationBasis.java
index f2e46de..ef5b606 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationBasis.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationBasis.java
@@ -37,7 +37,7 @@
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
-import org.opends.server.api.AuthenticationPolicy;
+import org.opends.server.api.AuthenticationPolicyState;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.controls.AccountUsableResponseControl;
@@ -645,15 +645,19 @@
// create it now.
if (isIncludeUsableControl())
{
+ if (controls == null)
+ {
+ controls = new ArrayList<Control>(1);
+ }
+
try
{
// FIXME -- Need a way to enable PWP debugging.
- AuthenticationPolicy policy = AuthenticationPolicy
- .forUser(entry, false);
- if (policy.isPasswordPolicy())
+ AuthenticationPolicyState state = AuthenticationPolicyState.forUser(
+ entry, false);
+ if (state.isPasswordPolicy())
{
- PasswordPolicyState pwpState = (PasswordPolicyState) policy
- .createAuthenticationPolicyState(entry);
+ PasswordPolicyState pwpState = (PasswordPolicyState) state;
boolean isInactive = pwpState.isDisabled()
|| pwpState.isAccountExpired();
@@ -667,12 +671,6 @@
{
int secondsBeforeUnlock = pwpState.getSecondsUntilUnlock();
int remainingGraceLogins = pwpState.getGraceLoginsRemaining();
-
- if (controls == null)
- {
- controls = new ArrayList<Control>(1);
- }
-
controls
.add(new AccountUsableResponseControl(isInactive, isReset,
isExpired, remainingGraceLogins, isLocked,
@@ -680,16 +678,24 @@
}
else
{
- if (controls == null)
- {
- controls = new ArrayList<Control>(1);
- }
-
int secondsBeforeExpiration = pwpState.getSecondsUntilExpiration();
controls.add(new AccountUsableResponseControl(
secondsBeforeExpiration));
}
}
+ else
+ {
+ // Another type of authentication policy (e.g. PTA).
+ if (state.isDisabled())
+ {
+ controls.add(new AccountUsableResponseControl(false, false, false,
+ -1, true, -1));
+ }
+ else
+ {
+ controls.add(new AccountUsableResponseControl(-1));
+ }
+ }
}
catch (Exception e)
{
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java
index 304f2a8..4583814 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java
@@ -73,7 +73,6 @@
// TODO: handle password policy response controls? AD?
// TODO: custom aliveness pings
- // TODO: manage account lockout
// TODO: cache password
/**
@@ -1555,14 +1554,13 @@
private final class StateImpl extends AuthenticationPolicyState
{
- private final Entry userEntry;
private ByteString cachedPassword = null;
private StateImpl(final Entry userEntry)
{
- this.userEntry = userEntry;
+ super(userEntry);
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/PlainSASLMechanismHandler.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/PlainSASLMechanismHandler.java
index aa06ad0..c835838 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/PlainSASLMechanismHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/PlainSASLMechanismHandler.java
@@ -29,6 +29,7 @@
+import static org.opends.messages.CoreMessages.*;
import static org.opends.messages.ExtensionMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.debug.DebugLogger.getTracer;
@@ -504,6 +505,17 @@
// the user's entry when the bind completes.
AuthenticationPolicyState authState = AuthenticationPolicyState.forUser(
userEntry, false);
+
+ if (authState.isDisabled())
+ {
+ // Check to see if the user is administratively disabled or locked.
+ bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
+ Message message = ERR_BIND_OPERATION_ACCOUNT_DISABLED.get(String
+ .valueOf(userEntry.getDN()));
+ bindOperation.setAuthFailureReason(message);
+ return;
+ }
+
if (!authState.passwordMatches(ByteString.valueOf(password)))
{
bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java
index 7866523..7fce649 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java
@@ -675,6 +675,14 @@
}
else
{
+ // Check to see if the user is administratively disabled or locked.
+ if (authPolicyState.isDisabled())
+ {
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
+ ERR_BIND_OPERATION_ACCOUNT_DISABLED.get(String.valueOf(userEntry
+ .getDN())));
+ }
+
// Invoke pre-operation plugins.
if (!invokePreOpPlugins())
{
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/api/AuthenticationPolicyTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/api/AuthenticationPolicyTestCase.java
index c7fcb9c..7fcdece 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/api/AuthenticationPolicyTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/api/AuthenticationPolicyTestCase.java
@@ -30,6 +30,7 @@
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import org.opends.server.TestCaseUtils;
@@ -38,6 +39,7 @@
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.types.*;
import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -54,6 +56,8 @@
*/
private final class MockPolicy extends AuthenticationPolicy
{
+ private final boolean isDisabled;
+
private boolean isPolicyFinalized = false;
private boolean isStateFinalized = false;
@@ -93,9 +97,9 @@
*
* @return The password which was tested.
*/
- public String getMatchedPassword()
+ public ByteString getMatchedPassword()
{
- return matchedPassword.toString();
+ return matchedPassword;
}
@@ -105,10 +109,13 @@
*
* @param matches
* The result to always return from {@code passwordMatches}.
+ * @param isDisabled
+ * The result to return from {@code isDisabled}.
*/
- public MockPolicy(boolean matches)
+ public MockPolicy(boolean matches, boolean isDisabled)
{
this.matches = matches;
+ this.isDisabled = isDisabled;
}
@@ -129,7 +136,7 @@
public AuthenticationPolicyState createAuthenticationPolicyState(
Entry userEntry, long time) throws DirectoryException
{
- return new AuthenticationPolicyState()
+ return new AuthenticationPolicyState(userEntry)
{
/**
@@ -147,6 +154,16 @@
/**
* {@inheritDoc}
*/
+ public boolean isDisabled()
+ {
+ return MockPolicy.this.isDisabled;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
public void finalizeStateAfterBind() throws DirectoryException
{
isStateFinalized = true;
@@ -202,29 +219,22 @@
/**
- * Test simple authentication where password validation succeeds.
+ * Returns test data for the simple/sasl tests.
*
- * @throws Exception
- * If an unexpected exception occurred.
+ * @return Test data for the simple/sasl tests.
*/
- @Test
- public void testSimpleBindAllowed() throws Exception
+ @DataProvider
+ public Object[][] testBindData()
{
- testSimpleBind(true);
- }
-
-
-
- /**
- * Test simple authentication where password validation fails.
- *
- * @throws Exception
- * If an unexpected exception occurred.
- */
- @Test
- public void testSimpleBindRefused() throws Exception
- {
- testSimpleBind(false);
+ // @formatter:off
+ return new Object[][] {
+ /* password matches, account is disabled */
+ { false, false },
+ { false, true },
+ { true, false },
+ { true, true },
+ };
+ // @formatter:on
}
@@ -232,34 +242,18 @@
/**
* Test simple authentication where password validation succeeds.
*
+ * @param matches
+ * The result to always return from {@code passwordMatches}.
+ * @param isDisabled
+ * The result to return from {@code isDisabled}.
* @throws Exception
* If an unexpected exception occurred.
*/
- @Test
- public void testSASLPLAINBindAllowed() throws Exception
+ @Test(dataProvider = "testBindData")
+ public void testSimpleBind(boolean matches, boolean isDisabled)
+ throws Exception
{
- testSASLPLAINBind(true);
- }
-
-
-
- /**
- * Test simple authentication where password validation fails.
- *
- * @throws Exception
- * If an unexpected exception occurred.
- */
- @Test
- public void testSASLPLAINBindRefused() throws Exception
- {
- testSASLPLAINBind(false);
- }
-
-
-
- private void testSimpleBind(boolean allow) throws Exception
- {
- MockPolicy policy = new MockPolicy(allow);
+ MockPolicy policy = new MockPolicy(matches, isDisabled);
DirectoryServer.registerAuthenticationPolicy(policyDN, policy);
try
{
@@ -287,13 +281,24 @@
BindOperation bind = conn.processSimpleBind(userDNString, "password");
// Check authentication result.
- assertEquals(bind.getResultCode(), allow ? ResultCode.SUCCESS
- : ResultCode.INVALID_CREDENTIALS);
+ assertEquals(bind.getResultCode(),
+ matches & !isDisabled ? ResultCode.SUCCESS
+ : ResultCode.INVALID_CREDENTIALS);
// Verify interaction with the policy/state.
assertTrue(policy.isStateFinalized());
assertFalse(policy.isPolicyFinalized());
- assertEquals(policy.getMatchedPassword(), "password");
+ if (!isDisabled)
+ {
+ assertEquals(policy.getMatchedPassword().toString(), "password");
+ }
+ else
+ {
+ // If the account is disabled then the password should not have been
+ // checked. This is important because we want to avoid potentially
+ // expensive password fetches (e.g. PTA).
+ assertNull(policy.getMatchedPassword());
+ }
}
finally
{
@@ -304,9 +309,21 @@
- private void testSASLPLAINBind(boolean allow) throws Exception
+ /**
+ * Test simple authentication where password validation succeeds.
+ *
+ * @param matches
+ * The result to always return from {@code passwordMatches}.
+ * @param isDisabled
+ * The result to return from {@code isDisabled}.
+ * @throws Exception
+ * If an unexpected exception occurred.
+ */
+ @Test(dataProvider = "testBindData")
+ public void testSASLPLAINBind(boolean matches, boolean isDisabled)
+ throws Exception
{
- MockPolicy policy = new MockPolicy(allow);
+ MockPolicy policy = new MockPolicy(matches, isDisabled);
DirectoryServer.registerAuthenticationPolicy(policyDN, policy);
try
{
@@ -342,13 +359,24 @@
credentials.toByteString());
// Check authentication result.
- assertEquals(bind.getResultCode(), allow ? ResultCode.SUCCESS
- : ResultCode.INVALID_CREDENTIALS);
+ assertEquals(bind.getResultCode(),
+ matches & !isDisabled ? ResultCode.SUCCESS
+ : ResultCode.INVALID_CREDENTIALS);
// Verify interaction with the policy/state.
assertTrue(policy.isStateFinalized());
assertFalse(policy.isPolicyFinalized());
- assertEquals(policy.getMatchedPassword(), "password");
+ if (!isDisabled)
+ {
+ assertEquals(policy.getMatchedPassword().toString(), "password");
+ }
+ else
+ {
+ // If the account is disabled then the password should not have been
+ // checked. This is important because we want to avoid potentially
+ // expensive password fetches (e.g. PTA).
+ assertNull(policy.getMatchedPassword());
+ }
}
finally
{
@@ -356,4 +384,5 @@
assertTrue(policy.isPolicyFinalized());
}
}
+
}
--
Gitblit v1.10.0