From 16a4c18b4c101e8e3dc7b8be756de1807970065f Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Thu, 19 Jul 2007 16:12:32 +0000
Subject: [PATCH] Update the server to provide more complete support for the password policy control as described in draft-behera-ldap-password-policy. In particular, improved support has been provided for all operations for the case in which a user must change his/her password before performing any other types of operations. These changes also provide enhanced support for add and modify operations that are rejected because a password change is not acceptable for some reason.
---
opends/src/server/org/opends/server/core/DirectoryServer.java | 26
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java | 84 ++
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestCfg.java | 2
opends/tests/unit-tests-testng/src/server/org/opends/server/controls/PasswordPolicyControlTestCase.java | 1530 +++++++++++++++++++++++++++++++++++++++++++++++++
opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java | 96 ++
opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java | 52 +
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java | 37 +
7 files changed, 1,795 insertions(+), 32 deletions(-)
diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index 6b7c19c..d1ec034 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -67,6 +67,8 @@
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.JMXMBean;
+import org.opends.server.controls.PasswordPolicyErrorType;
+import org.opends.server.controls.PasswordPolicyResponseControl;
import org.opends.server.extensions.ConfigFileHandler;
import org.opends.server.extensions.JMXAlertHandler;
import org.opends.server.loggers.TextErrorLogPublisher;
@@ -7203,6 +7205,18 @@
case DELETE:
case MODIFY_DN:
case SEARCH:
+ // See if the request included the password policy request control.
+ // If it did, then add a corresponding response control.
+ for (Control c : operation.getRequestControls())
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ operation.addResponseControl(new PasswordPolicyResponseControl(
+ null, 0, PasswordPolicyErrorType.CHANGE_AFTER_RESET));
+ break;
+ }
+ }
+
int msgID = MSGID_ENQUEUE_MUST_CHANGE_PASSWORD;
String message = getMessage(msgID);
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
@@ -7217,6 +7231,18 @@
((! requestOID.equals(OID_PASSWORD_MODIFY_REQUEST)) &&
(! requestOID.equals(OID_START_TLS_REQUEST))))
{
+ // See if the request included the password policy request control.
+ // If it did, then add a corresponding response control.
+ for (Control c : operation.getRequestControls())
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ operation.addResponseControl(new PasswordPolicyResponseControl(
+ null, 0, PasswordPolicyErrorType.CHANGE_AFTER_RESET));
+ break;
+ }
+ }
+
msgID = MSGID_ENQUEUE_MUST_CHANGE_PASSWORD;
message = getMessage(msgID);
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
diff --git a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
index 6226046..4cf5799 100644
--- a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
+++ b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -1898,8 +1898,18 @@
new AddResponseProtocolOp(de.getResultCode().getIntValue(),
de.getErrorMessage(), de.getMatchedDN(),
de.getReferralURLs());
+
+ List<Control> responseControls = addOp.getResponseControls();
+ ArrayList<LDAPControl> responseLDAPControls =
+ new ArrayList<LDAPControl>(responseControls.size());
+ for (Control c : responseControls)
+ {
+ responseLDAPControls.add(new LDAPControl(c));
+ }
+
sendLDAPMessage(securityProvider,
- new LDAPMessage(message.getMessageID(), responseOp));
+ new LDAPMessage(message.getMessageID(), responseOp,
+ responseLDAPControls));
}
@@ -2014,8 +2024,18 @@
new BindResponseProtocolOp(de.getResultCode().getIntValue(),
de.getErrorMessage(), de.getMatchedDN(),
de.getReferralURLs());
+
+ List<Control> responseControls = bindOp.getResponseControls();
+ ArrayList<LDAPControl> responseLDAPControls =
+ new ArrayList<LDAPControl>(responseControls.size());
+ for (Control c : responseControls)
+ {
+ responseLDAPControls.add(new LDAPControl(c));
+ }
+
sendLDAPMessage(securityProvider,
- new LDAPMessage(message.getMessageID(), responseOp));
+ new LDAPMessage(message.getMessageID(), responseOp,
+ responseLDAPControls));
// If it was a protocol error, then terminate the connection.
if (de.getResultCode() == ResultCode.PROTOCOL_ERROR)
@@ -2086,8 +2106,18 @@
de.getErrorMessage(),
de.getMatchedDN(),
de.getReferralURLs());
+
+ List<Control> responseControls = compareOp.getResponseControls();
+ ArrayList<LDAPControl> responseLDAPControls =
+ new ArrayList<LDAPControl>(responseControls.size());
+ for (Control c : responseControls)
+ {
+ responseLDAPControls.add(new LDAPControl(c));
+ }
+
sendLDAPMessage(securityProvider,
- new LDAPMessage(message.getMessageID(), responseOp));
+ new LDAPMessage(message.getMessageID(), responseOp,
+ responseLDAPControls));
}
@@ -2147,8 +2177,18 @@
new DeleteResponseProtocolOp(de.getResultCode().getIntValue(),
de.getErrorMessage(), de.getMatchedDN(),
de.getReferralURLs());
+
+ List<Control> responseControls = deleteOp.getResponseControls();
+ ArrayList<LDAPControl> responseLDAPControls =
+ new ArrayList<LDAPControl>(responseControls.size());
+ for (Control c : responseControls)
+ {
+ responseLDAPControls.add(new LDAPControl(c));
+ }
+
sendLDAPMessage(securityProvider,
- new LDAPMessage(message.getMessageID(), responseOp));
+ new LDAPMessage(message.getMessageID(), responseOp,
+ responseLDAPControls));
}
@@ -2217,8 +2257,18 @@
de.getErrorMessage(),
de.getMatchedDN(),
de.getReferralURLs());
+
+ List<Control> responseControls = extendedOp.getResponseControls();
+ ArrayList<LDAPControl> responseLDAPControls =
+ new ArrayList<LDAPControl>(responseControls.size());
+ for (Control c : responseControls)
+ {
+ responseLDAPControls.add(new LDAPControl(c));
+ }
+
sendLDAPMessage(securityProvider,
- new LDAPMessage(message.getMessageID(), responseOp));
+ new LDAPMessage(message.getMessageID(), responseOp,
+ responseLDAPControls));
}
@@ -2278,8 +2328,18 @@
new ModifyResponseProtocolOp(de.getResultCode().getIntValue(),
de.getErrorMessage(), de.getMatchedDN(),
de.getReferralURLs());
+
+ List<Control> responseControls = modifyOp.getResponseControls();
+ ArrayList<LDAPControl> responseLDAPControls =
+ new ArrayList<LDAPControl>(responseControls.size());
+ for (Control c : responseControls)
+ {
+ responseLDAPControls.add(new LDAPControl(c));
+ }
+
sendLDAPMessage(securityProvider,
- new LDAPMessage(message.getMessageID(), responseOp));
+ new LDAPMessage(message.getMessageID(), responseOp,
+ responseLDAPControls));
}
@@ -2343,8 +2403,18 @@
de.getErrorMessage(),
de.getMatchedDN(),
de.getReferralURLs());
+
+ List<Control> responseControls = modifyDNOp.getResponseControls();
+ ArrayList<LDAPControl> responseLDAPControls =
+ new ArrayList<LDAPControl>(responseControls.size());
+ for (Control c : responseControls)
+ {
+ responseLDAPControls.add(new LDAPControl(c));
+ }
+
sendLDAPMessage(securityProvider,
- new LDAPMessage(message.getMessageID(), responseOp));
+ new LDAPMessage(message.getMessageID(), responseOp,
+ responseLDAPControls));
}
@@ -2410,8 +2480,18 @@
de.getErrorMessage(),
de.getMatchedDN(),
de.getReferralURLs());
+
+ List<Control> responseControls = searchOp.getResponseControls();
+ ArrayList<LDAPControl> responseLDAPControls =
+ new ArrayList<LDAPControl>(responseControls.size());
+ for (Control c : responseControls)
+ {
+ responseLDAPControls.add(new LDAPControl(c));
+ }
+
sendLDAPMessage(securityProvider,
- new LDAPMessage(message.getMessageID(), responseOp));
+ new LDAPMessage(message.getMessageID(), responseOp,
+ responseLDAPControls));
}
diff --git a/opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java b/opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java
index 21b4065..69ea295 100644
--- a/opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java
+++ b/opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java
@@ -94,25 +94,49 @@
* program.
*/
public static void main(String[] args) {
- DSConfig app = new DSConfig(System.in, System.out, System.err,
- new LDAPManagementContextFactory());
- // Only initialize the client environment when run as a standalone
- // application.
- try {
- app.initializeClientEnvironment();
- } catch (InitializationException e) {
- // TODO: is this ok as an error message?
- System.err.println(wrapText(e.getMessage(), MAX_LINE_WIDTH));
- System.exit(1);
- }
-
- // Run the application.
- int exitCode = app.run(args);
+ int exitCode = main(args, true, System.out, System.err);
if (exitCode != 0) {
System.exit(filterExitCode(exitCode));
}
}
+ /**
+ * Provides the command-line arguments to the main application for
+ * processing and returns the exit code as an integer.
+ *
+ * @param args The set of command-line arguments provided to
+ * this program.
+ * @param initializeServer Indicates whether to perform basic initialization
+ * (which should not be done if the tool is running
+ * in the same JVM as the server).
+ * @param outStream The output stream for standard output.
+ * @param errStream The output stream for standard error.
+ *
+ * @return Zero to indicate that the program completed successfully, or
+ * non-zero to indicate that an error occurred.
+ */
+ public static int main(String[] args, boolean initializeServer,
+ OutputStream outStream, OutputStream errStream)
+ {
+ DSConfig app = new DSConfig(System.in, outStream, errStream,
+ new LDAPManagementContextFactory());
+ // Only initialize the client environment when run as a standalone
+ // application.
+ if (initializeServer)
+ {
+ try {
+ app.initializeClientEnvironment();
+ } catch (InitializationException e) {
+ // TODO: is this ok as an error message?
+ System.err.println(wrapText(e.getMessage(), MAX_LINE_WIDTH));
+ return 1;
+ }
+ }
+
+ // Run the application.
+ return app.run(args);
+ }
+
// Flag indicating whether or not the application environment has
// already been initialized.
private boolean environmentInitialized = false;
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
index 681aff6..211629a 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
@@ -29,6 +29,7 @@
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.messages.CoreMessages.*;
import static org.opends.server.messages.MessageHandler.getMessage;
+import static org.opends.server.util.ServerConstants.*;
import java.util.ArrayList;
import java.util.HashSet;
@@ -39,6 +40,8 @@
import org.opends.server.api.PasswordStorageScheme;
import org.opends.server.api.PasswordValidator;
+import org.opends.server.controls.PasswordPolicyErrorType;
+import org.opends.server.controls.PasswordPolicyResponseControl;
import org.opends.server.core.AddOperation;
import org.opends.server.core.AddOperationWrapper;
import org.opends.server.core.DirectoryServer;
@@ -51,6 +54,7 @@
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteString;
+import org.opends.server.types.Control;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.ObjectClass;
@@ -121,7 +125,7 @@
* policy processing for the add operation.
*/
public final void handlePasswordPolicy(PasswordPolicy passwordPolicy,
- Entry userEntry)
+ Entry userEntry)
throws DirectoryException
{
// See if a password was specified.
@@ -161,6 +165,8 @@
if ((! passwordPolicy.allowMultiplePasswordValues()) && (values.size() > 1))
{
// FIXME -- What if they're pre-encoded and might all be the same?
+ addPWPolicyControl(PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED);
+
int msgID = MSGID_PWPOLICY_MULTIPLE_PW_VALUES_NOT_ALLOWED;
String message = getMessage(msgID, passwordAttribute.getNameOrOID());
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
@@ -187,6 +193,9 @@
}
else
{
+ addPWPolicyControl(
+ PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY);
+
int msgID = MSGID_PWPOLICY_PREENCODED_NOT_ALLOWED;
String message = getMessage(msgID,
passwordAttribute.getNameOrOID());
@@ -206,6 +215,9 @@
}
else
{
+ addPWPolicyControl(
+ PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY);
+
int msgID = MSGID_PWPOLICY_PREENCODED_NOT_ALLOWED;
String message = getMessage(msgID,
passwordAttribute.getNameOrOID());
@@ -229,6 +241,9 @@
if (! validator.passwordIsAcceptable(value, currentPasswords, this,
userEntry, invalidReason))
{
+ addPWPolicyControl(
+ PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY);
+
int msgID = MSGID_PWPOLICY_VALIDATION_FAILED;
String message = getMessage(msgID, passwordAttribute.getNameOrOID(),
String.valueOf(invalidReason));
@@ -289,6 +304,8 @@
// If we should force change on add, then set the appropriate flag.
if (passwordPolicy.forceChangeOnAdd())
{
+ addPWPolicyControl(PasswordPolicyErrorType.CHANGE_AFTER_RESET);
+
AttributeType resetType =
DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_RESET_REQUIRED_LC);
if (resetType == null)
@@ -309,6 +326,24 @@
}
/**
+ * Adds a password policy response control if the corresponding request
+ * control was included.
+ *
+ * @param errorType The error type to use for the response control.
+ */
+ private void addPWPolicyControl(PasswordPolicyErrorType errorType)
+ {
+ for (Control c : getRequestControls())
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ addResponseControl(new PasswordPolicyResponseControl(null, 0,
+ errorType));
+ }
+ }
+ }
+
+ /**
* Adds the provided objectClass to the entry, along with its superior classes
* if appropriate.
*
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
index d7470cf..ee3a804 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -227,6 +227,8 @@
// Create a labeled block of code that we can break out of if a problem is
// detected.
+ boolean pwPolicyControlRequested = false;
+ PasswordPolicyErrorType pwpErrorType = null;
modifyProcessing:
{
DN entryDN = localOp.getEntryDN();
@@ -261,8 +263,20 @@
DN authzDN = localOp.getAuthorizationDN();
if ((authzDN != null) && (! authzDN.equals(entryDN)))
{
- // The user will not be allowed to do anything else before
- // the password gets changed.
+ // The user will not be allowed to do anything else before the
+ // password gets changed. Also note that we haven't yet checked the
+ // request controls so we need to do that now to see if the password
+ // policy request control was provided.
+ for (Control c : localOp.getRequestControls())
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ pwPolicyControlRequested = true;
+ pwpErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET;
+ break;
+ }
+ }
+
localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
int msgID = MSGID_MODIFY_MUST_CHANGE_PASSWORD;
@@ -658,6 +672,10 @@
localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
}
}
+ else if (oid.equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ pwPolicyControlRequested = true;
+ }
// NYI -- Add support for additional controls.
else if (c.isCritical())
@@ -783,6 +801,9 @@
Privilege.PASSWORD_RESET,
localOp))
{
+ pwpErrorType =
+ PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
+
int msgID = MSGID_MODIFY_PWRESET_INSUFFICIENT_PRIVILEGES;
localOp.appendErrorMessage(getMessage(msgID));
localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
@@ -885,6 +906,8 @@
if (selfChange &&
(! pwPolicyState.getPolicy().allowUserPasswordChanges()))
{
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
+
localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
int msgID = MSGID_MODIFY_NO_USER_PW_CHANGES;
@@ -898,6 +921,8 @@
if (pwPolicyState.getPolicy().requireSecurePasswordChanges() &&
(! clientConnection.isSecure()))
{
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
+
localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
int msgID = MSGID_MODIFY_REQUIRE_SECURE_CHANGES;
@@ -910,6 +935,8 @@
// previous change, then reject it.
if (selfChange && pwPolicyState.isWithinMinimumAge())
{
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_TOO_YOUNG;
+
localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
int msgID = MSGID_MODIFY_WITHIN_MINIMUM_AGE;
@@ -944,10 +971,12 @@
// If there were multiple password values provided, then make
// sure that's OK.
- if (! localOp.isInternalOperation() &&
- ! pwPolicyState.getPolicy().allowExpiredPasswordChanges() &&
+ if ((! localOp.isInternalOperation()) &&
+ (! pwPolicyState.getPolicy().allowMultiplePasswordValues()) &&
(passwordsToAdd > 1))
{
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
+
localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
int msgID = MSGID_MODIFY_MULTIPLE_VALUES_NOT_ALLOWED;
@@ -966,6 +995,9 @@
if ((!localOp.isInternalOperation()) &&
! pwPolicyState.getPolicy().allowPreEncodedPasswords())
{
+ pwpErrorType =
+ PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY;
+
localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
int msgID = MSGID_MODIFY_NO_PREENCODED_PASSWORDS;
@@ -985,6 +1017,9 @@
// exist.
if (pwPolicyState.passwordMatches(v.getValue()))
{
+ pwpErrorType =
+ PasswordPolicyErrorType.PASSWORD_IN_HISTORY;
+
localOp.setResultCode(
ResultCode.ATTRIBUTE_OR_VALUE_EXISTS);
@@ -1044,6 +1079,9 @@
{
if ((!localOp.isInternalOperation()) && selfChange)
{
+ pwpErrorType =
+ PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY;
+
localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
int msgID = MSGID_MODIFY_NO_PREENCODED_PASSWORDS;
@@ -1774,6 +1812,8 @@
pwPolicyState.getPolicy().requireCurrentPassword() &&
(! currentPasswordProvided))
{
+ pwpErrorType = PasswordPolicyErrorType.MUST_SUPPLY_OLD_PASSWORD;
+
localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
int msgID = MSGID_MODIFY_PW_CHANGE_REQUIRES_CURRENT_PW;
@@ -1787,6 +1827,8 @@
if ((numPasswords > 1) &&
(! pwPolicyState.getPolicy().allowMultiplePasswordValues()))
{
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
+
localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
int msgID = MSGID_MODIFY_MULTIPLE_PASSWORDS_NOT_ALLOWED;
@@ -1856,6 +1898,9 @@
clearPasswords,
invalidReason))
{
+ pwpErrorType =
+ PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY;
+
localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
int msgID = MSGID_MODIFY_PW_VALIDATION_FAILED;
@@ -1881,6 +1926,8 @@
if (selfChange || (! pwPolicyState.getPolicy().
skipValidationForAdministrators()))
{
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_IN_HISTORY;
+
localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
int msgID = MSGID_MODIFY_PW_IN_HISTORY;
@@ -1924,8 +1971,8 @@
{
// See if the account was locked for any reason.
wasLocked = pwPolicyState.lockedDueToIdleInterval() ||
- pwPolicyState.lockedDueToMaximumResetAge() ||
- pwPolicyState.lockedDueToFailures();
+ pwPolicyState.lockedDueToMaximumResetAge() ||
+ pwPolicyState.lockedDueToFailures();
// Update the password policy state attributes in the user's entry.
// If the modification fails, then these changes won't be applied.
@@ -1943,6 +1990,12 @@
}
else
{
+ if ((pwpErrorType == null) &&
+ pwPolicyState.getPolicy().forceChangeOnReset())
+ {
+ pwpErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET;
+ }
+
pwPolicyState.setMustChangePassword(
pwPolicyState.getPolicy().forceChangeOnReset());
}
@@ -1971,6 +2024,8 @@
{
// The user will not be allowed to do anything else before
// the password gets changed.
+ pwpErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET;
+
localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
int msgID = MSGID_MODIFY_MUST_CHANGE_PASSWORD;
@@ -2378,6 +2433,15 @@
}
+ // If the password policy request control was included, then make sure we
+ // send the corresponding response control.
+ if (pwPolicyControlRequested)
+ {
+ localOp.addResponseControl(
+ new PasswordPolicyResponseControl(null, 0, pwpErrorType));
+ }
+
+
// Indicate that it is now too late to attempt to cancel the operation.
localOp.setCancelResult(CancelResult.TOO_LATE);
@@ -5222,8 +5286,7 @@
}
}
- // Check to see if there are any controls in the request. If so,
- // then
+ // Check to see if there are any controls in the request. If so, then
// see if there is any special processing required.
boolean noOp = false;
LDAPPostReadRequestControl postReadRequest = null;
@@ -5487,6 +5550,11 @@
localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
}
}
+ else if (oid.equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ // We don't need to do anything here because it's already handled
+ // in LocalBackendAddOperation.handlePasswordPolicy().
+ }
// NYI -- Add support for additional controls.
else if (c.isCritical())
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestCfg.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestCfg.java
index 86db1df..d1daf3d 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestCfg.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestCfg.java
@@ -73,7 +73,7 @@
RootCfgDefn.getInstance().deregisterRelationDefinition(
RD_TEST_ONE_TO_MANY_PARENT);
RootCfgDefn.getInstance().deregisterRelationDefinition(
- RD_TEST_ONE_TO_MANY_PARENT);
+ RD_TEST_ONE_TO_ZERO_OR_ONE_PARENT);
}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/PasswordPolicyControlTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/PasswordPolicyControlTestCase.java
new file mode 100644
index 0000000..f75804b
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/controls/PasswordPolicyControlTestCase.java
@@ -0,0 +1,1530 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.controls;
+
+
+
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.protocols.asn1.*;
+import org.opends.server.protocols.ldap.*;
+import org.opends.server.types.*;
+import org.opends.server.tools.dsconfig.DSConfig;
+
+import static org.testng.Assert.*;
+
+import static org.opends.server.util.ServerConstants.*;
+
+
+
+/**
+ * This class contains test cases that verify the appropriate handling of the
+ * password policy control as defined in draft-behera-ldap-password-policy.
+ */
+public class PasswordPolicyControlTestCase
+ extends ControlsTestCase
+{
+ /**
+ * Make sure that the server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+ }
+
+
+
+ /**
+ * Tests that an appropriate password policy response control is returned for
+ * an add operation when the user's password is in a "must change" state.
+ * This test will also ensure that the bind response is also capable of
+ * including the password policy response control with the "change after
+ * reset" error type set.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddMustChange()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "force-change-on-add:true"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "ds-privilege-name: bypass-acl");
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+
+ try
+ {
+ ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ boolean found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.CHANGE_AFTER_RESET);
+ found = true;
+ }
+ }
+ assertTrue(found);
+
+
+ ArrayList<RawAttribute> rawAttrs = new ArrayList<RawAttribute>();
+ rawAttrs.add(RawAttribute.create("objectClass", "organizationalUnit"));
+ rawAttrs.add(RawAttribute.create("ou", "People"));
+
+ AddRequestProtocolOp addRequest = new AddRequestProtocolOp(
+ new ASN1OctetString("ou=People,o=test"), rawAttrs);
+
+ controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ message = new LDAPMessage(2, addRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ AddResponseProtocolOp addResponse = message.getAddResponseProtocolOp();
+ assertFalse(addResponse.getResultCode() == LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.CHANGE_AFTER_RESET);
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally
+ {
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "force-change-on-add:false"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+
+
+
+ /**
+ * Tests that an appropriate password policy response control is returned for
+ * an add operation in which the proposed password is pre-encoded.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddPreEncodedPassword()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+
+ try
+ {
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("cn=Directory Manager"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+ ArrayList<RawAttribute> rawAttrs = new ArrayList<RawAttribute>();
+ rawAttrs.add(RawAttribute.create("objectClass", "inetOrgPerson"));
+ rawAttrs.add(RawAttribute.create("uid", "test.user"));
+ rawAttrs.add(RawAttribute.create("givenName", "Test"));
+ rawAttrs.add(RawAttribute.create("sn", "User"));
+ rawAttrs.add(RawAttribute.create("cn", "Test User"));
+ rawAttrs.add(RawAttribute.create("userPassword",
+ "{SSHA}0pZPpMIm6xSBIW4hGvR/72fjO4M9p3Ff1g7QFw=="));
+
+ AddRequestProtocolOp addRequest = new AddRequestProtocolOp(
+ new ASN1OctetString("ou=uid=test.user,o=test"), rawAttrs);
+
+ ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ message = new LDAPMessage(2, addRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ AddResponseProtocolOp addResponse = message.getAddResponseProtocolOp();
+ assertFalse(addResponse.getResultCode() == LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ boolean found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY);
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally
+ {
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+
+
+
+ /**
+ * Tests that an appropriate password policy response control is returned for
+ * an add operation in which the proposed password fails validation.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddPasswordFailsValidation()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--add", "password-validator-dn:cn=Length-Based Password Validator," +
+ "cn=Password Validators,cn=config"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+
+ try
+ {
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("cn=Directory Manager"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+ ArrayList<RawAttribute> rawAttrs = new ArrayList<RawAttribute>();
+ rawAttrs.add(RawAttribute.create("objectClass", "inetOrgPerson"));
+ rawAttrs.add(RawAttribute.create("uid", "test.user"));
+ rawAttrs.add(RawAttribute.create("givenName", "Test"));
+ rawAttrs.add(RawAttribute.create("sn", "User"));
+ rawAttrs.add(RawAttribute.create("cn", "Test User"));
+ rawAttrs.add(RawAttribute.create("userPassword", "short"));
+
+ AddRequestProtocolOp addRequest = new AddRequestProtocolOp(
+ new ASN1OctetString("ou=uid=test.user,o=test"), rawAttrs);
+
+ ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ message = new LDAPMessage(2, addRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ AddResponseProtocolOp addResponse = message.getAddResponseProtocolOp();
+ assertFalse(addResponse.getResultCode() == LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ boolean found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY);
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally
+ {
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--remove", "password-validator-dn:cn=Length-Based Password " +
+ "Validator,cn=Password Validators,cn=config"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+
+
+
+ /**
+ * Tests that an appropriate password policy response control is returned for
+ * a bind operation in which the user's account is locked due to
+ * authentication failures.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testBindLockedDueToFailures()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "lockout-failure-count:3"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "ds-privilege-name: bypass-acl");
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+
+ try
+ {
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("wrong"));
+
+ for (int i=1; i <= 3; i++)
+ {
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertFalse(bindResponse.getResultCode() == LDAPResultCode.SUCCESS);
+ }
+
+ bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("password"));
+
+ ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ LDAPMessage message = new LDAPMessage(4, bindRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertFalse(bindResponse.getResultCode() == LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ boolean found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.ACCOUNT_LOCKED);
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally
+ {
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "lockout-failure-count:0"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+
+
+
+ /**
+ * Tests that an appropriate password policy response control is returned for
+ * a compare operation when the user's password is in a "must change" state.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testCompareMustChange()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "force-change-on-add:true"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "ds-privilege-name: bypass-acl");
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+
+ try
+ {
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+ CompareRequestProtocolOp compareRequest =
+ new CompareRequestProtocolOp(new ASN1OctetString("o=test"), "o",
+ new ASN1OctetString("test"));
+
+ ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ message = new LDAPMessage(2, compareRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ CompareResponseProtocolOp compareResponse =
+ message.getCompareResponseProtocolOp();
+ assertFalse(compareResponse.getResultCode() == LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ boolean found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.CHANGE_AFTER_RESET);
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally
+ {
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "force-change-on-add:false"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+
+
+
+ /**
+ * Tests that an appropriate password policy response control is returned for
+ * a delete operation when the user's password is in a "must change" state.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testDeleteMustChange()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "force-change-on-add:true"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ TestCaseUtils.addEntries(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "ds-privilege-name: bypass-acl",
+ "",
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People");
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+
+ try
+ {
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+ DeleteRequestProtocolOp deleteRequest =
+ new DeleteRequestProtocolOp(new ASN1OctetString("ou=People,o=test"));
+
+ ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ message = new LDAPMessage(2, deleteRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ DeleteResponseProtocolOp deleteResponse =
+ message.getDeleteResponseProtocolOp();
+ assertFalse(deleteResponse.getResultCode() == LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ boolean found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.CHANGE_AFTER_RESET);
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally
+ {
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "force-change-on-add:false"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+
+
+
+ /**
+ * Tests that an appropriate password policy response control is returned for
+ * a modify operation when the user's password is in a "must change" state.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testModifyMustChange()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "force-change-on-add:true"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "ds-privilege-name: bypass-acl");
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+
+ try
+ {
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+ ArrayList<RawModification> mods = new ArrayList<RawModification>();
+ mods.add(RawModification.create(ModificationType.REPLACE, "description",
+ "foo"));
+
+ ModifyRequestProtocolOp modifyRequest =
+ new ModifyRequestProtocolOp(new ASN1OctetString("o=test"), mods);
+
+ ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ message = new LDAPMessage(2, modifyRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ModifyResponseProtocolOp modifyResponse =
+ message.getModifyResponseProtocolOp();
+ assertFalse(modifyResponse.getResultCode() == LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ boolean found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.CHANGE_AFTER_RESET);
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally
+ {
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "force-change-on-add:false"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+
+
+
+ /**
+ * Tests that an appropriate password policy response control is returned for
+ * a modify operation when users do not have permission to change their own
+ * passwords.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testModifyCannotChange()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "allow-user-password-changes:false"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "ds-privilege-name: bypass-acl");
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+
+ try
+ {
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+ ArrayList<RawModification> mods = new ArrayList<RawModification>();
+ mods.add(RawModification.create(ModificationType.REPLACE, "userPassword",
+ "newpassword"));
+
+ ModifyRequestProtocolOp modifyRequest =
+ new ModifyRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), mods);
+
+ ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ message = new LDAPMessage(2, modifyRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ModifyResponseProtocolOp modifyResponse =
+ message.getModifyResponseProtocolOp();
+ assertFalse(modifyResponse.getResultCode() == LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ boolean found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED);
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally
+ {
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "allow-user-password-changes:true"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+
+
+
+ /**
+ * Tests that an appropriate password policy response control is returned for
+ * a modify operation when the proposed password is in the user's password
+ * history.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testModifyPasswordInHistory()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "password-history-count:5"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "ds-privilege-name: bypass-acl");
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+
+ try
+ {
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+ ArrayList<RawModification> mods = new ArrayList<RawModification>();
+ mods.add(RawModification.create(ModificationType.REPLACE, "userPassword",
+ "password"));
+
+ ModifyRequestProtocolOp modifyRequest =
+ new ModifyRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), mods);
+
+ ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ message = new LDAPMessage(2, modifyRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ModifyResponseProtocolOp modifyResponse =
+ message.getModifyResponseProtocolOp();
+ assertFalse(modifyResponse.getResultCode() == LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ boolean found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.PASSWORD_IN_HISTORY);
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally
+ {
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "password-history-count:0"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+
+
+
+ /**
+ * Tests that an appropriate password policy response control is returned for
+ * a modify operation when the user didn't provide their current password when
+ * it was required.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testModifyMissingCurrentPassword()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "password-change-requires-current-password:true"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "ds-privilege-name: bypass-acl");
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+
+ try
+ {
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+ ArrayList<RawModification> mods = new ArrayList<RawModification>();
+ mods.add(RawModification.create(ModificationType.REPLACE, "userPassword",
+ "newpassword"));
+
+ ModifyRequestProtocolOp modifyRequest =
+ new ModifyRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), mods);
+
+ ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ message = new LDAPMessage(2, modifyRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ModifyResponseProtocolOp modifyResponse =
+ message.getModifyResponseProtocolOp();
+ assertFalse(modifyResponse.getResultCode() == LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ boolean found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.MUST_SUPPLY_OLD_PASSWORD);
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally
+ {
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "password-change-requires-current-password:false"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+
+
+
+ /**
+ * Tests that an appropriate password policy response control is returned for
+ * a modify operation when the user tried to perform multiple password changes
+ * without respecting the minimum age.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testModifyMinimumPasswordAge()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "minimum-password-age:24 hours"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "ds-privilege-name: bypass-acl");
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+
+ try
+ {
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+ ArrayList<RawModification> mods = new ArrayList<RawModification>();
+ mods.add(RawModification.create(ModificationType.REPLACE, "userPassword",
+ "newpassword"));
+
+ ModifyRequestProtocolOp modifyRequest =
+ new ModifyRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), mods);
+
+ ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ message = new LDAPMessage(2, modifyRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ModifyResponseProtocolOp modifyResponse =
+ message.getModifyResponseProtocolOp();
+ assertFalse(modifyResponse.getResultCode() == LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ boolean found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.PASSWORD_TOO_YOUNG);
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally
+ {
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "minimum-password-age:0 seconds"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+
+
+
+ /**
+ * Tests that an appropriate password policy response control is returned for
+ * a modify DN operation when the user's password is in a "must change" state.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testModifyDNMustChange()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "force-change-on-add:true"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ TestCaseUtils.addEntries(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "ds-privilege-name: bypass-acl",
+ "",
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People");
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+
+ try
+ {
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+ ModifyDNRequestProtocolOp modifyDNRequest =
+ new ModifyDNRequestProtocolOp(
+ new ASN1OctetString("ou=People,o=test"),
+ new ASN1OctetString("ou=Users"), true);
+
+ ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ message = new LDAPMessage(2, modifyDNRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ ModifyDNResponseProtocolOp modifyDNResponse =
+ message.getModifyDNResponseProtocolOp();
+ assertFalse(modifyDNResponse.getResultCode() == LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ boolean found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.CHANGE_AFTER_RESET);
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally
+ {
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "force-change-on-add:false"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+
+
+
+ /**
+ * Tests that an appropriate password policy response control is returned for
+ * a search operation when the user's password is in a "must change" state.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSearchMustChange()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "force-change-on-add:true"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ TestCaseUtils.addEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "ds-privilege-name: bypass-acl");
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ ASN1Reader r = new ASN1Reader(s);
+ ASN1Writer w = new ASN1Writer(s);
+
+ try
+ {
+ BindRequestProtocolOp bindRequest = new BindRequestProtocolOp(
+ new ASN1OctetString("uid=test.user,o=test"), 3,
+ new ASN1OctetString("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+ SearchRequestProtocolOp searchRequest =
+ new SearchRequestProtocolOp(new ASN1OctetString("o=test"),
+ SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0,
+ 0, false,
+ LDAPFilter.decode("(objectClass=*)"),
+ new LinkedHashSet<String>());
+
+ ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+ controls.add(new LDAPControl(OID_PASSWORD_POLICY_CONTROL, true));
+
+ message = new LDAPMessage(2, searchRequest, controls);
+ w.writeElement(message.encode());
+
+ message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ SearchResultDoneProtocolOp searchDone =
+ message.getSearchResultDoneProtocolOp();
+ assertFalse(searchDone.getResultCode() == LDAPResultCode.SUCCESS);
+
+ controls = message.getControls();
+ assertNotNull(controls);
+ assertFalse(controls.isEmpty());
+
+ boolean found = false;
+ for (LDAPControl c : controls)
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ PasswordPolicyResponseControl pwpControl =
+ PasswordPolicyResponseControl.decodeControl(c.getControl());
+ assertEquals(pwpControl.getErrorType(),
+ PasswordPolicyErrorType.CHANGE_AFTER_RESET);
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally
+ {
+ args = new String[]
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "set-password-policy-prop",
+ "--policy-name", "Default Password Policy",
+ "--set", "force-change-on-add:false"
+ };
+ assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+ try
+ {
+ s.close();
+ } catch (Exception e) {}
+ }
+ }
+}
+
--
Gitblit v1.10.0