From 6f005d31bfdf6e0030712cceee8c9d0ce6dc13a0 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Thu, 10 Aug 2006 18:36:21 +0000
Subject: [PATCH] Update the modify processing code so that it performs the appropriate password policy processing.
---
opendj-sdk/opends/src/server/org/opends/server/core/PasswordPolicyState.java | 51 ++-
opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java | 178 ++++++++++++
opendj-sdk/opends/src/server/org/opends/server/types/Modification.java | 71 +++++
opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java | 530 +++++++++++++++++++++++++++++++++++++
4 files changed, 798 insertions(+), 32 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java
index f48d289..f0de197 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java
@@ -39,6 +39,7 @@
import org.opends.server.api.Backend;
import org.opends.server.api.ChangeNotificationListener;
import org.opends.server.api.ClientConnection;
+import org.opends.server.api.PasswordStorageScheme;
import org.opends.server.api.SynchronizationProvider;
import org.opends.server.api.plugin.PostOperationPluginResult;
import org.opends.server.api.plugin.PreOperationPluginResult;
@@ -54,6 +55,8 @@
import org.opends.server.protocols.ldap.LDAPAttribute;
import org.opends.server.protocols.ldap.LDAPException;
import org.opends.server.protocols.ldap.LDAPModification;
+import org.opends.server.schema.AuthPasswordSyntax;
+import org.opends.server.schema.UserPasswordSyntax;
import org.opends.server.types.AcceptRejectWarn;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
@@ -65,6 +68,7 @@
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
import org.opends.server.types.RDN;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
@@ -112,6 +116,12 @@
// The modified entry that will be stored in the backend.
private Entry modifiedEntry;
+ // The set of clear-text current passwords (if any were provided).
+ private List<AttributeValue> currentPasswords;
+
+ // The set of clear-text new passwords (if any were provided).
+ private List<AttributeValue> newPasswords;
+
// The set of response controls for this modify operation.
private List<Control> responseControls;
@@ -176,6 +186,9 @@
responseControls = new ArrayList<Control>();
cancelRequest = null;
changeNumber = -1;
+
+ currentPasswords = null;
+ newPasswords = null;
}
@@ -227,6 +240,9 @@
responseControls = new ArrayList<Control>();
cancelRequest = null;
changeNumber = -1;
+
+ currentPasswords = null;
+ newPasswords = null;
}
@@ -407,6 +423,45 @@
/**
+ * Retrieves the set of clear-text current passwords for the user, if
+ * available. This will only be available if the modify operation contains
+ * one or more delete elements that target the password attribute and provide
+ * the values to delete in the clear. It will not be available to pre-parse
+ * plugins.
+ *
+ * @return The set of clear-text current password values as provided in the
+ * modify request, or <CODE>null</CODE> if there were none or this
+ * information is not yet available.
+ */
+ public List<AttributeValue> getCurrentPasswords()
+ {
+ assert debugEnter(CLASS_NAME, "getCurrentPasswords");
+
+ return currentPasswords;
+ }
+
+
+
+ /**
+ * Retrieves the set of clear-text new passwords for the user, if available.
+ * This will only be available if the modify operation contains one or more
+ * add or replace elements that target the password attribute and provide the
+ * values in the clear. It will not be available to pre-parse plugins.
+ *
+ * @return The set of clear-text new passwords as provided in the modify
+ * request, or <CODE>null</CODE> if there were none or this
+ * information is not yet available.
+ */
+ public List<AttributeValue> getNewPasswords()
+ {
+ assert debugEnter(CLASS_NAME, "getNewPasswords");
+
+ return newPasswords;
+ }
+
+
+
+ /**
* Retrieves the time that processing started for this operation.
*
* @return The time that processing started for this operation.
@@ -1135,6 +1190,27 @@
}
+ // Get the password policy state object for the entry that can be used
+ // to perform any appropriate password policy processing. Also, see if
+ // the entry is being updated by the end user or an administrator.
+ PasswordPolicyState pwPolicyState;
+ boolean selfChange = entryDN.equals(getAuthorizationDN());
+ try
+ {
+ // FIXME -- Need a way to enable debug mode.
+ pwPolicyState = new PasswordPolicyState(currentEntry, false, false);
+ }
+ catch (DirectoryException de)
+ {
+ assert debugException(CLASS_NAME, "run", de);
+
+ setResultCode(de.getResultCode());
+ appendErrorMessage(de.getErrorMessage());
+
+ break modifyProcessing;
+ }
+
+
// Create a duplicate of the entry and apply the changes to it.
modifiedEntry = currentEntry.duplicate();
@@ -1170,6 +1246,75 @@
}
}
+
+ // Declare variables used for password policy state processing.
+ boolean passwordChanged = false;
+ boolean currentPasswordProvided = false;
+ int numPasswords;
+ if (currentEntry.hasAttribute(pwPolicyState.getPasswordAttribute()))
+ {
+ // It may actually have more than one, but we can't tell the
+ // difference if the values are encoded, and its enough for our
+ // purposes just to know that there is at least one.
+ numPasswords = 1;
+ }
+ else
+ {
+ numPasswords = 0;
+ }
+
+
+ // If it's not an internal or synchronization operation, then iterate
+ // through the set of modifications to see if a password is included in
+ // the changes. If so, then add the appropriate state changes to the
+ // set of modifications.
+ if (! (isInternalOperation() || isSynchronizationOperation()))
+ {
+ for (Modification m : modifications)
+ {
+ if (m.getAttribute().getAttributeType().equals(
+ pwPolicyState.getPasswordAttribute()))
+ {
+ passwordChanged = true;
+ break;
+ }
+ }
+
+ if (passwordChanged)
+ {
+ // Update the password policy state attributes in the user's entry.
+ // If the modification fails, then these changes won't be applied.
+ pwPolicyState.setPasswordChangedTime();
+ pwPolicyState.clearAuthFailureTimes();
+ pwPolicyState.clearFailureLockout();
+ pwPolicyState.clearGraceLoginTimes();
+ pwPolicyState.clearWarnedTime();
+
+ if ((! selfChange) && pwPolicyState.forceChangeOnReset())
+ {
+ pwPolicyState.setMustChangePassword(true);
+ }
+
+ if (pwPolicyState.getRequiredChangeTime() > 0)
+ {
+ pwPolicyState.setRequiredChangeTime();
+ }
+
+ modifications.addAll(pwPolicyState.getModifications());
+ }
+ else if(pwPolicyState.mustChangePassword())
+ {
+ // The user will not be allowed to do anything else before
+ // the password gets changed.
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_MUST_CHANGE_PASSWORD;
+ appendErrorMessage(getMessage(msgID));
+ break modifyProcessing;
+ }
+ }
+
+
for (Modification m : modifications)
{
Attribute a = m.getAttribute();
@@ -1181,7 +1326,8 @@
// synchronization in some way.
if (t.isNoUserModification())
{
- if (! (isInternalOperation() || isSynchronizationOperation()))
+ if (! (isInternalOperation() || isSynchronizationOperation() ||
+ m.isInternal()))
{
setResultCode(ResultCode.UNWILLING_TO_PERFORM);
appendErrorMessage(getMessage(MSGID_MODIFY_ATTR_IS_NO_USER_MOD,
@@ -1192,6 +1338,326 @@
}
+ // If the modification is updating the password attribute, then
+ // perform any necessary password policy processing. This processing
+ // should be skipped for internal and synchronization operations.
+ boolean isPassword = a.getAttributeType().equals(
+ pwPolicyState.getPasswordAttribute());
+ if (isPassword &&
+ (! (isInternalOperation() || isSynchronizationOperation())))
+ {
+ // If the attribute contains any options, then reject it. Passwords
+ // will not be allowed to have options.
+ if (a.hasOptions())
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_PASSWORDS_CANNOT_HAVE_OPTIONS;
+ appendErrorMessage(getMessage(msgID));
+ break modifyProcessing;
+ }
+
+
+ // If it's a self change, then see if that's allowed.
+ if (selfChange && (! pwPolicyState.allowUserPasswordChanges()))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_NO_USER_PW_CHANGES;
+ appendErrorMessage(getMessage(msgID));
+ break modifyProcessing;
+ }
+
+
+ // If we require secure password changes, then makes sure it's a
+ // secure communication channel.
+ if (pwPolicyState.requireSecurePasswordChanges() &&
+ (! clientConnection.isSecure()))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_REQUIRE_SECURE_CHANGES;
+ appendErrorMessage(getMessage(msgID));
+ break modifyProcessing;
+ }
+
+
+ // If it's a self change and it's not been long enough since the
+ // previous change, then reject it.
+ if (selfChange && pwPolicyState.isWithinMinimumAge())
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_WITHIN_MINIMUM_AGE;
+ appendErrorMessage(getMessage(msgID));
+ break modifyProcessing;
+ }
+
+
+ // Check to see whether this will adding, deleting, or replacing
+ // password values (increment doesn't make any sense for passwords).
+ // Then perform the appropriate type of processing for that kind of
+ // modification.
+ LinkedHashSet<AttributeValue> pwValues = a.getValues();
+ LinkedHashSet<AttributeValue> encodedValues =
+ new LinkedHashSet<AttributeValue>();
+ switch (m.getModificationType())
+ {
+ case ADD:
+ case REPLACE:
+ int passwordsToAdd = pwValues.size();
+
+ if (m.getModificationType() == ModificationType.ADD)
+ {
+ numPasswords += passwordsToAdd;
+ }
+ else
+ {
+ numPasswords = passwordsToAdd;
+ }
+
+ // If there were multiple password values provided, then make
+ // sure that's OK.
+ if ((! pwPolicyState.allowMultiplePasswordValues()) &&
+ (passwordsToAdd > 1))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_MULTIPLE_VALUES_NOT_ALLOWED;
+ appendErrorMessage(getMessage(msgID));
+ break modifyProcessing;
+ }
+
+ // Iterate through the password values and see if any of them
+ // are pre-encoded. If so, then check to see if we'll allow it.
+ // Otherwise, store the clear-text values for later validation
+ // and update the attribute with the encoded values.
+ for (AttributeValue v : pwValues)
+ {
+ if (pwPolicyState.passwordIsPreEncoded(v.getValue()))
+ {
+ if (! pwPolicyState.allowPreEncodedPasswords())
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_NO_PREENCODED_PASSWORDS;
+ appendErrorMessage(getMessage(msgID));
+ break modifyProcessing;
+ }
+ else
+ {
+ encodedValues.add(v);
+ }
+ }
+ else
+ {
+ if (newPasswords == null)
+ {
+ newPasswords = new LinkedList<AttributeValue>();
+ }
+
+ newPasswords.add(v);
+
+ try
+ {
+ for (ByteString s :
+ pwPolicyState.encodePassword(v.getValue()))
+ {
+ encodedValues.add(new AttributeValue(
+ a.getAttributeType(), s));
+ }
+ }
+ catch (DirectoryException de)
+ {
+ assert debugException(CLASS_NAME, "run", de);
+
+ setResultCode(de.getResultCode());
+ appendErrorMessage(de.getErrorMessage());
+ break modifyProcessing;
+ }
+ }
+ }
+
+ a.setValues(encodedValues);
+
+ break;
+
+ case DELETE:
+ // Iterate through the password values and see if any of them
+ // are pre-encoded. We will never allow pre-encoded passwords
+ // for user password changes, but we will allow them for
+ // administrators. For each clear-text value, verify that at
+ // least one value in the entry matches and replace the
+ // clear-text value with the appropriate encoded forms.
+ for (AttributeValue v : pwValues)
+ {
+ if (pwPolicyState.passwordIsPreEncoded(v.getValue()))
+ {
+ if (selfChange)
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_NO_PREENCODED_PASSWORDS;
+ appendErrorMessage(getMessage(msgID));
+ break modifyProcessing;
+ }
+ else
+ {
+ encodedValues.add(v);
+ }
+ }
+ else
+ {
+ List<Attribute> attrList = currentEntry.getAttribute(t);
+ if ((attrList == null) || (attrList.isEmpty()))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_NO_EXISTING_VALUES;
+ appendErrorMessage(getMessage(msgID));
+ break modifyProcessing;
+ }
+ else
+ {
+ boolean found = false;
+ for (Attribute attr : attrList)
+ {
+ for (AttributeValue av : attr.getValues())
+ {
+ if (pwPolicyState.usesAuthPasswordSyntax())
+ {
+ if (AuthPasswordSyntax.isEncoded(av.getValue()))
+ {
+ try
+ {
+ StringBuilder[] compoenents =
+ AuthPasswordSyntax.decodeAuthPassword(
+ av.getStringValue());
+ PasswordStorageScheme scheme =
+ DirectoryServer.
+ getAuthPasswordStorageScheme(
+ compoenents[0].toString());
+ if (scheme != null)
+ {
+ if (scheme.authPasswordMatches(
+ v.getValue(),
+ compoenents[1].toString(),
+ compoenents[2].toString()))
+ {
+ encodedValues.add(av);
+ found = true;
+ }
+ }
+ }
+ catch (DirectoryException de)
+ {
+ assert debugException(CLASS_NAME, "run", de);
+
+ setResultCode(de.getResultCode());
+
+ int msgID = MSGID_MODIFY_CANNOT_DECODE_PW;
+ appendErrorMessage(getMessage(msgID,
+ de.getErrorMessage()));
+ break modifyProcessing;
+ }
+ }
+ else
+ {
+ if (av.equals(v))
+ {
+ encodedValues.add(v);
+ found = true;
+ }
+ }
+ }
+ else
+ {
+ if (UserPasswordSyntax.isEncoded(av.getValue()))
+ {
+ try
+ {
+ String[] compoenents =
+ UserPasswordSyntax.decodeUserPassword(
+ av.getStringValue());
+ PasswordStorageScheme scheme =
+ DirectoryServer.getPasswordStorageScheme(
+ toLowerCase(
+ compoenents[0].toString()));
+ if (scheme != null)
+ {
+ if (scheme.passwordMatches(
+ v.getValue(),
+ new ASN1OctetString(compoenents[1])))
+ {
+ encodedValues.add(av);
+ found = true;
+ }
+ }
+ }
+ catch (DirectoryException de)
+ {
+ assert debugException(CLASS_NAME, "run", de);
+
+ setResultCode(de.getResultCode());
+
+ int msgID = MSGID_MODIFY_CANNOT_DECODE_PW;
+ appendErrorMessage(getMessage(msgID,
+ de.getErrorMessage()));
+ break modifyProcessing;
+ }
+ }
+ else
+ {
+ if (av.equals(v))
+ {
+ encodedValues.add(v);
+ found = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (found)
+ {
+ if (currentPasswords == null)
+ {
+ currentPasswords = new LinkedList<AttributeValue>();
+ }
+ currentPasswords.add(v);
+
+ numPasswords--;
+ }
+ else
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_INVALID_PASSWORD;
+ appendErrorMessage(getMessage(msgID));
+ break modifyProcessing;
+ }
+
+ currentPasswordProvided = true;
+ }
+ }
+ }
+
+ a.setValues(encodedValues);
+
+ break;
+
+ default:
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_INVALID_MOD_TYPE_FOR_PASSWORD;
+ appendErrorMessage(getMessage(msgID,
+ String.valueOf(m.getModificationType()), a.getName()));
+
+ break modifyProcessing;
+ }
+ }
+
+
switch (m.getModificationType())
{
case ADD:
@@ -1680,6 +2146,61 @@
}
+ // If there was a password change, then perform any additional checks
+ // that may be necessary.
+ if (passwordChanged)
+ {
+ // If it was a self change, then see if the current password was
+ // provided and handle accordingly.
+ if (selfChange && pwPolicyState.requireCurrentPassword() &&
+ (! currentPasswordProvided))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_PW_CHANGE_REQUIRES_CURRENT_PW;
+ appendErrorMessage(getMessage(msgID));
+ break modifyProcessing;
+ }
+
+
+ // If this change would result in multiple password values, then see
+ // if that's OK.
+ if ((numPasswords > 1) &&
+ (! pwPolicyState.allowMultiplePasswordValues()))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_MULTIPLE_PASSWORDS_NOT_ALLOWED;
+ appendErrorMessage(getMessage(msgID));
+ break modifyProcessing;
+ }
+
+
+ // If any of the password values should be validated, then do so now.
+ if (selfChange || (! pwPolicyState.skipValidationForAdministrators()))
+ {
+ if (newPasswords != null)
+ {
+ for (AttributeValue v : newPasswords)
+ {
+ StringBuilder invalidReason = new StringBuilder();
+ if (! pwPolicyState.passwordIsAcceptable(this, modifiedEntry,
+ v.getValue(),
+ invalidReason))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_MODIFY_PW_VALIDATION_FAILED;
+ appendErrorMessage(getMessage(msgID,
+ invalidReason.toString()));
+ break modifyProcessing;
+ }
+ }
+ }
+ }
+ }
+
+
// Make sure that the new entry is valid per the server schema.
if (DirectoryServer.checkSchema())
{
@@ -1695,13 +2216,6 @@
}
- // Check to see if there are any passwords included in the request and
- // if they are valid in accordance with the password policies associated
- // with the user. Also perform any encoding that might be required by
- // the password storage schemes.
- // NYI
-
-
// Check for and handle a request to cancel this operation.
if (cancelRequest != null)
{
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 f697a42..9518f50 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
@@ -1050,7 +1050,7 @@
}
else
{
- modifications.add(new Modification(ModificationType.REPLACE, a));
+ modifications.add(new Modification(ModificationType.REPLACE, a, true));
}
}
}
@@ -1192,7 +1192,7 @@
}
else
{
- modifications.add(new Modification(ModificationType.REPLACE, a));
+ modifications.add(new Modification(ModificationType.REPLACE, a, true));
}
}
else
@@ -1213,7 +1213,7 @@
else
{
modifications.add(new Modification(ModificationType.REPLACE,
- new Attribute(type)));
+ new Attribute(type), true));
}
}
}
@@ -1430,7 +1430,8 @@
if (! updateEntry)
{
- modifications.add(new Modification(ModificationType.DELETE, a));
+ modifications.add(new Modification(ModificationType.DELETE, a,
+ true));
}
}
}
@@ -1457,7 +1458,7 @@
else
{
modifications.add(new Modification(ModificationType.REPLACE,
- new Attribute(type)));
+ new Attribute(type), true));
}
}
}
@@ -1543,7 +1544,7 @@
}
else
{
- modifications.add(new Modification(ModificationType.ADD, addAttr));
+ modifications.add(new Modification(ModificationType.ADD, addAttr, true));
}
}
@@ -1587,7 +1588,7 @@
else
{
modifications.add(new Modification(ModificationType.REPLACE,
- new Attribute(type)));
+ new Attribute(type), true));
}
}
@@ -1737,7 +1738,7 @@
else
{
modifications.add(new Modification(ModificationType.REPLACE,
- new Attribute(type)));
+ new Attribute(type), true));
}
if (debug)
@@ -1832,7 +1833,7 @@
}
else
{
- modifications.add(new Modification(ModificationType.REPLACE, a));
+ modifications.add(new Modification(ModificationType.REPLACE, a, true));
}
}
@@ -1873,7 +1874,7 @@
else
{
modifications.add(new Modification(ModificationType.REPLACE,
- new Attribute(type)));
+ new Attribute(type), true));
}
}
@@ -2079,7 +2080,7 @@
}
else
{
- modifications.add(new Modification(ModificationType.REPLACE, a));
+ modifications.add(new Modification(ModificationType.REPLACE, a, true));
}
if (debug)
@@ -2403,7 +2404,7 @@
}
else
{
- modifications.add(new Modification(ModificationType.REPLACE, a));
+ modifications.add(new Modification(ModificationType.REPLACE, a, true));
}
}
else
@@ -2422,7 +2423,7 @@
else
{
modifications.add(new Modification(ModificationType.REPLACE,
- new Attribute(type)));
+ new Attribute(type), true));
}
}
}
@@ -3005,7 +3006,7 @@
}
else
{
- modifications.add(new Modification(ModificationType.REPLACE, a));
+ modifications.add(new Modification(ModificationType.REPLACE, a, true));
}
}
}
@@ -3101,7 +3102,7 @@
}
else
{
- modifications.add(new Modification(ModificationType.REPLACE, a));
+ modifications.add(new Modification(ModificationType.REPLACE, a, true));
}
if (debug)
@@ -3130,7 +3131,7 @@
else
{
Attribute a = new Attribute(type);
- modifications.add(new Modification(ModificationType.REPLACE, a));
+ modifications.add(new Modification(ModificationType.REPLACE, a, true));
}
if (debug)
@@ -3207,7 +3208,7 @@
else
{
modifications.add(new Modification(ModificationType.REPLACE,
- new Attribute(type)));
+ new Attribute(type), true));
}
}
}
@@ -3313,7 +3314,7 @@
}
else
{
- modifications.add(new Modification(ModificationType.ADD, addAttr));
+ modifications.add(new Modification(ModificationType.ADD, addAttr, true));
}
}
@@ -3355,7 +3356,7 @@
else
{
modifications.add(new Modification(ModificationType.REPLACE,
- new Attribute(type)));
+ new Attribute(type), true));
}
}
@@ -3864,7 +3865,8 @@
Attribute a = new Attribute(type, type.getNameOrOID(), removedValues);
if (! updateEntry)
{
- modifications.add(new Modification(ModificationType.DELETE, a));
+ modifications.add(new Modification(ModificationType.DELETE, a,
+ true));
}
if (! addedValues.isEmpty())
@@ -3873,7 +3875,8 @@
addedValues);
if (! updateEntry)
{
- modifications.add(new Modification(ModificationType.ADD, a2));
+ modifications.add(new Modification(ModificationType.ADD, a2,
+ true));
}
}
@@ -4042,7 +4045,8 @@
Attribute a = new Attribute(type, type.getNameOrOID(), removedValues);
if (! updateEntry)
{
- modifications.add(new Modification(ModificationType.DELETE, a));
+ modifications.add(new Modification(ModificationType.DELETE, a,
+ true));
}
if (! addedValues.isEmpty())
@@ -4051,7 +4055,8 @@
addedValues);
if (! updateEntry)
{
- modifications.add(new Modification(ModificationType.ADD, a2));
+ modifications.add(new Modification(ModificationType.ADD, a2,
+ true));
}
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
index cb90115..636b779 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -5578,6 +5578,144 @@
/**
+ * The message ID for the message that will be used if a change to the
+ * password attribute included one or more attribute options. This does not
+ * take any arguments.
+ */
+ public static final int MSGID_MODIFY_PASSWORDS_CANNOT_HAVE_OPTIONS =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 532;
+
+
+
+ /**
+ * The message ID for the message that will be used if a user password change
+ * is refused because users cannot change their own passwords. This does not
+ * take any arguments.
+ */
+ public static final int MSGID_MODIFY_NO_USER_PW_CHANGES =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 533;
+
+
+
+ /**
+ * The message ID for the message that will be used if a password change is
+ * rejected because it was not attempted over a secure channel. This does not
+ * take any arguments.
+ */
+ public static final int MSGID_MODIFY_REQUIRE_SECURE_CHANGES =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 534;
+
+
+
+ /**
+ * The message ID for the message that will be used if a password change is
+ * rejected because the password was within the minimum age. This does not
+ * take any arguments.
+ */
+ public static final int MSGID_MODIFY_WITHIN_MINIMUM_AGE =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 535;
+
+
+
+ /**
+ * The message ID for the message that will be used if a password change is
+ * rejected because multiple password values were provided. This does not
+ * take any arguments.
+ */
+ public static final int MSGID_MODIFY_MULTIPLE_VALUES_NOT_ALLOWED =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 536;
+
+
+
+ /**
+ * The message ID for the message that will be used if a password change is
+ * rejected because the password was pre-encoded. This does not take any
+ * arguments.
+ */
+ public static final int MSGID_MODIFY_NO_PREENCODED_PASSWORDS =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 537;
+
+
+
+ /**
+ * The message ID for the message that will be used if a password change is
+ * rejected because it included an invalid modification type on the password
+ * attribute. This does not take any arguments.
+ */
+ public static final int MSGID_MODIFY_INVALID_MOD_TYPE_FOR_PASSWORD =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 538;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt to delete a
+ * user password value is rejected because there are no existing passwords in
+ * the user's entry. This does not take any arguments.
+ */
+ public static final int MSGID_MODIFY_NO_EXISTING_VALUES =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 539;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * attempting to decode a user password. This takes a single argument, which
+ * is a message explaining the problem that occurred.
+ */
+ public static final int MSGID_MODIFY_CANNOT_DECODE_PW =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 540;
+
+
+
+ /**
+ * The message ID for the message that will be used if a provided password to
+ * delete does not match any passwords in the user's entry. This does not
+ * take any arguments.
+ */
+ public static final int MSGID_MODIFY_INVALID_PASSWORD =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 541;
+
+
+
+ /**
+ * The message ID for the message that will be used if the user did not
+ * provide the current password. This does not take any arguments.
+ */
+ public static final int MSGID_MODIFY_PW_CHANGE_REQUIRES_CURRENT_PW =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 542;
+
+
+
+ /**
+ * The message ID for the message that will be used if the password change
+ * would result in multiple passwords. This does not take any arguments.
+ */
+ public static final int MSGID_MODIFY_MULTIPLE_PASSWORDS_NOT_ALLOWED =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 543;
+
+
+
+ /**
+ * The message ID for the message that will be used if password validation
+ * fails. This takes a single argument, which is a message explaining the
+ * rejection.
+ */
+ public static final int MSGID_MODIFY_PW_VALIDATION_FAILED =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 544;
+
+
+
+ /**
+ * The message ID for the message that will be used if the user's password
+ * needs to be changed but the modification doesn't update the password. This
+ * does not take any arguments.
+ */
+ public static final int MSGID_MODIFY_MUST_CHANGE_PASSWORD =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 545;
+
+
+
+ /**
* Associates a set of generic messages with the message IDs defined
* in this class.
*/
@@ -6642,10 +6780,41 @@
"contained a critical control with OID %s that is not " +
"supported by the Directory Server for this type of " +
"operation.");
+ registerMessage(MSGID_MODIFY_MUST_CHANGE_PASSWORD,
+ "You must change your password before you will be " +
+ "allowed to perform any other operations.");
registerMessage(MSGID_MODIFY_ATTR_IS_NO_USER_MOD,
"Entry %s cannot be modified because the modification " +
"attempted to update attribute %s which is defined as " +
"NO-USER-MODIFICATION in the server schema.");
+ registerMessage(MSGID_MODIFY_PASSWORDS_CANNOT_HAVE_OPTIONS,
+ "Attributes used to hold user passwords are not allowed " +
+ "to have any attribute options.");
+ registerMessage(MSGID_MODIFY_NO_USER_PW_CHANGES,
+ "Users are not allowed to change their own passwords.");
+ registerMessage(MSGID_MODIFY_REQUIRE_SECURE_CHANGES,
+ "Password changes must be performed over a secure " +
+ "authentication channel.");
+ registerMessage(MSGID_MODIFY_WITHIN_MINIMUM_AGE,
+ "The password cannot be changed because it has not been " +
+ "long enough since the last password change.");
+ registerMessage(MSGID_MODIFY_MULTIPLE_VALUES_NOT_ALLOWED,
+ "Multiple password values are not allowed in user " +
+ "entries.");
+ registerMessage(MSGID_MODIFY_NO_PREENCODED_PASSWORDS,
+ "User passwords may not be provided in pre-encoded form.");
+ registerMessage(MSGID_MODIFY_NO_EXISTING_VALUES,
+ "The user entry does not have any existing passwords to " +
+ "remove.");
+ registerMessage(MSGID_MODIFY_CANNOT_DECODE_PW,
+ "An error occurred while attempting to decode an " +
+ "existing user password: %s.");
+ registerMessage(MSGID_MODIFY_INVALID_PASSWORD,
+ "The provided user password does not match any password " +
+ "in the user's entry.");
+ registerMessage(MSGID_MODIFY_INVALID_MOD_TYPE_FOR_PASSWORD,
+ "Invalid modification type %s attempted on password " +
+ "attribute %s.");
registerMessage(MSGID_MODIFY_ADD_NO_VALUES,
"Entry %s cannot be modified because the modification " +
"contained an add component for attribute %s but no " +
@@ -6696,6 +6865,15 @@
"Entry %s cannot be modified because an attempt was made " +
"to increment the value of attribute %s but that " +
"attribute did not have any values in the target entry.");
+ registerMessage(MSGID_MODIFY_PW_CHANGE_REQUIRES_CURRENT_PW,
+ "The password policy requires that user password changes " +
+ "include the current password in the request.");
+ registerMessage(MSGID_MODIFY_MULTIPLE_PASSWORDS_NOT_ALLOWED,
+ "The password change would result in multiple password " +
+ "values in the user entry, which is not allowed.");
+ registerMessage(MSGID_MODIFY_PW_VALIDATION_FAILED,
+ "The provided password value was rejected by a password " +
+ "validator: %s.");
registerMessage(MSGID_MODIFY_INCREMENT_REQUIRES_INTEGER_VALUE,
"Entry %s cannot be modified because an attempt was " +
"made to increment the value of attribute %s but the " +
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/Modification.java b/opendj-sdk/opends/src/server/org/opends/server/types/Modification.java
index d8b27da..1457bc5 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/Modification.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/Modification.java
@@ -50,6 +50,11 @@
// The attribute for this modification.
private Attribute attribute;
+ // Indicates whether this modification was generated by internal
+ // processing and therefore should not be subject to
+ // no-user-modification and related checks.
+ private boolean isInternal;
+
// The modification type for this modification.
private ModificationType modificationType;
@@ -63,7 +68,7 @@
* @param attribute The attribute for this modification.
*/
public Modification(ModificationType modificationType,
- Attribute attribute)
+ Attribute attribute)
{
assert debugConstructor(CLASS_NAME,
String.valueOf(modificationType),
@@ -71,6 +76,33 @@
this.modificationType = modificationType;
this.attribute = attribute;
+
+ isInternal = false;
+ }
+
+
+
+ /**
+ * Creates a new modification with the provided information.
+ *
+ * @param modificationType The modification type for this
+ * modification.
+ * @param attribute The attribute for this modification.
+ * @param isInternal Indicates whether this is an internal
+ * modification and therefore should not
+ * be subject to no-user-modification and
+ * related checks.
+ */
+ public Modification(ModificationType modificationType,
+ Attribute attribute, boolean isInternal)
+ {
+ assert debugConstructor(CLASS_NAME,
+ String.valueOf(modificationType),
+ String.valueOf(attribute));
+
+ this.modificationType = modificationType;
+ this.attribute = attribute;
+ this.isInternal = isInternal;
}
@@ -135,6 +167,43 @@
/**
+ * Indicates whether this is modification was created by internal
+ * processing and should not be subject to no-user-modification and
+ * related checks.
+ *
+ * @return <CODE>true</CODE> if this is an internal modification,
+ * or <CODE>false</CODE> if not.
+ */
+ public boolean isInternal()
+ {
+ assert debugEnter(CLASS_NAME, "isInternal");
+
+ return isInternal;
+ }
+
+
+
+ /**
+ * Specifies whether this modification was created by internal
+ * processing and should not be subject to no-user-modification and
+ * related checks.
+ *
+ * @param isInternal Specifies whether this modification was
+ * created by internal processing and should
+ * not be subject to no-user-modification and
+ * related checks.
+ */
+ public void setInternal(boolean isInternal)
+ {
+ assert debugEnter(CLASS_NAME, "setInternal",
+ String.valueOf(isInternal));
+
+ this.isInternal = isInternal;
+ }
+
+
+
+ /**
* Indicates whether the provided object is equal to this
* modification. It will only be considered equal if the object is
* a modification with the same modification type and an attribute
--
Gitblit v1.10.0