| | |
| | | |
| | | import static org.opends.messages.CoreMessages.*; |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.protocols.internal.InternalClientConnection.getRootConnection; |
| | | import static org.opends.server.schema.SchemaConstants.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | |
| | | /** Indicates whether the user's password is expired. */ |
| | | private ConditionResult isPasswordExpired = ConditionResult.UNDEFINED; |
| | | |
| | | /** |
| | | * Indicates whether the warning to send to the client would be the first |
| | | * warning for the user. |
| | | */ |
| | | /** Indicates whether the warning to send to the client would be the first warning for the user. */ |
| | | private ConditionResult isFirstWarning = ConditionResult.UNDEFINED; |
| | | |
| | | /** Indicates whether the user's account is locked by the idle lockout. */ |
| | | private ConditionResult isIdleLocked = ConditionResult.UNDEFINED; |
| | | |
| | | /** |
| | | * Indicates whether the user may use a grace login if the password is expired |
| | | * and there are one or more grace logins remaining. |
| | | * Indicates whether the user may use a grace login if the password is expiredand there are one or |
| | | * more grace logins remaining. |
| | | */ |
| | | private ConditionResult mayUseGraceLogin = ConditionResult.UNDEFINED; |
| | | |
| | |
| | | /** The time that the user's account should expire (or did expire). */ |
| | | private long accountExpirationTime = Long.MIN_VALUE; |
| | | |
| | | /** |
| | | * The time that the user's entry was locked due to too many authentication |
| | | * failures. |
| | | */ |
| | | /** The time that the user's entry was locked due to too many authentication failures. */ |
| | | private long failureLockedTime = Long.MIN_VALUE; |
| | | |
| | | /** The time that the user last authenticated to the Directory Server. */ |
| | |
| | | |
| | | /** |
| | | * Creates a new password policy state object with the provided information. |
| | | * Note that this version of the constructor should only be used for testing |
| | | * purposes when the tests should be evaluated with a fixed time rather than |
| | | * the actual current time. For all other purposes, the other constructor |
| | | * Note that this version of the constructor should only be used for testing purposes when the tests should be |
| | | * evaluated with a fixed time rather than the actual current time. For all other purposes, the other constructor |
| | | * should be used. |
| | | * |
| | | * @param policy |
| | | * The password policy associated with the state. |
| | | * @param userEntry |
| | | * The entry with the user account. |
| | | * @param currentTime |
| | | * The time to use as the current time for all time-related |
| | | * determinations. |
| | | * @param policy The password policy associated with the state. |
| | | * @param userEntry The entry with the user account. |
| | | * @param currentTime The time to use as the current time for all time-related determinations. |
| | | */ |
| | | PasswordPolicyState(PasswordPolicy policy, Entry userEntry, long currentTime) |
| | | { |
| | | super(userEntry); |
| | | this.currentTime = currentTime; |
| | | this.userDNString = userEntry.getName().toString(); |
| | | this.passwordPolicy = policy; |
| | | this.userDNString = userEntry.getName().toString(); |
| | | this.passwordPolicy = policy; |
| | | } |
| | | |
| | | |
| | |
| | | * |
| | | * @param attributeType The attribute type whose value should be retrieved. |
| | | * |
| | | * @return The value of the specified attribute as a string, or |
| | | * <CODE>null</CODE> if there is no such value. |
| | | * @return The value of the specified attribute as a string, or <CODE>null</CODE> if there is no such value. |
| | | */ |
| | | private String getValue(AttributeType attributeType) |
| | | { |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning null because attribute %s does not " + |
| | | "exist in user entry %s", |
| | | logger.trace("Returning null because attribute %s does not exist in user entry %s", |
| | | attributeType.getNameOrOID(), userDNString); |
| | | } |
| | | } |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning value %s for user %s", |
| | | stringValue, userDNString); |
| | | logger.trace("Returning value %s for user %s", stringValue, userDNString); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of values of the specified attribute from the user's |
| | | * entry in generalized time format. |
| | | * Retrieves the set of values of the specified attribute from the user's entry in generalized time format. |
| | | * |
| | | * @param attributeType The attribute type whose values should be parsed as |
| | | * generalized time values. |
| | | * @param attributeType The attribute type whose values should be parsed as generalized time values. |
| | | * |
| | | * @return The set of generalized time values, or an empty list if there are |
| | | * none. |
| | | * @return The set of generalized time values, or an empty list if there are none. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting to |
| | | * decode a value as a generalized time. |
| | | * @throws DirectoryException If a problem occurs while attempting to decode a value as a generalized time. |
| | | */ |
| | | private List<Long> getGeneralizedTimes(AttributeType attributeType) |
| | | throws DirectoryException |
| | |
| | | logger.traceException(e, "Unable to decode value %s for attribute %s in user entry %s", |
| | | v, attributeType.getNameOrOID(), userDNString); |
| | | |
| | | LocalizableMessage message = ERR_PWPSTATE_CANNOT_DECODE_GENERALIZED_TIME. |
| | | get(v, attributeType.getNameOrOID(), userDNString, e); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, e); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | ERR_PWPSTATE_CANNOT_DECODE_GENERALIZED_TIME.get(v, attributeType.getNameOrOID(), userDNString, e), |
| | | e); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | if (timeValues.isEmpty()) |
| | | { |
| | | logger.trace("Returning an empty list because attribute %s " + |
| | | "does not exist in user entry %s", |
| | | logger.trace("Returning an empty list because attribute %s does not exist in user entry %s", |
| | | attributeType.getNameOrOID(), userDNString); |
| | | } |
| | | return timeValues; |
| | |
| | | if (passwordChangedTime < 0) |
| | | { |
| | | // Get the password changed time for the user. |
| | | AttributeType type = DirectoryServer.getAttributeType( |
| | | OP_ATTR_PWPOLICY_CHANGED_TIME_LC, true); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_CHANGED_TIME_LC, true); |
| | | |
| | | try |
| | | { |
| | |
| | | catch (DirectoryException e) |
| | | { |
| | | /* |
| | | * The password change time could not be parsed (but has been logged in |
| | | * the debug log). The best effort we can do from here is to a) use the |
| | | * current time, b) use the start of the epoch (1/1/1970), or c) use the |
| | | * create time stamp. Lets treat this problem as if the change time |
| | | * The password change time could not be parsed (but has been logged in the debug log). |
| | | * The best effort we can do from here is to a) use the current time, b) use the start |
| | | * of the epoch (1/1/1970), or c) use the create time stamp. Lets treat this problem as if the change time |
| | | * attribute did not exist and resort to the create time stamp. |
| | | */ |
| | | } |
| | |
| | | if (passwordChangedTime < 0) |
| | | { |
| | | // Get the time that the user's account was created. |
| | | AttributeType createTimeType = DirectoryServer.getAttributeType( |
| | | OP_ATTR_CREATE_TIMESTAMP_LC, true); |
| | | AttributeType createTimeType = DirectoryServer.getAttributeType(OP_ATTR_CREATE_TIMESTAMP_LC, true); |
| | | try |
| | | { |
| | | passwordChangedTime = getGeneralizedTime(userEntry, createTimeType); |
| | |
| | | catch (DirectoryException e) |
| | | { |
| | | /* |
| | | * The create time stamp could not be parsed (but has been logged in |
| | | * the debug log). The best effort we can do from here is to a) use |
| | | * the current time, or b) use the start of the epoch (1/1/1970). Lets |
| | | * treat this problem as if the change time attribute did not exist |
| | | * and use the start of the epoch. Doing so stands a greater chance of |
| | | * forcing a password change. |
| | | * The create time stamp could not be parsed (but has been logged in the debug log). |
| | | * The best effort we can do from here is to a) use the current time, or b) use the start of |
| | | * the epoch (1/1/1970). Lets treat this problem as if the change time attribute did not exist |
| | | * and use the start of the epoch. Doing so stands a greater chance of forcing a password change. |
| | | */ |
| | | } |
| | | |
| | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace( |
| | | "Could not determine password changed time for " + "user %s.", |
| | | userDNString); |
| | | "Could not determine password changed time for " + "user %s.", userDNString); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the unmodifiable set of values for the password |
| | | * attribute from the user entry. |
| | | * Retrieves the unmodifiable set of values for the password attribute from the user entry. |
| | | * |
| | | * @return The unmodifiable set of values for the password attribute |
| | | * from the user entry. |
| | | * @return The unmodifiable set of values for the password attribute from the user entry. |
| | | */ |
| | | public Set<ByteString> getPasswordValues() |
| | | { |
| | | List<Attribute> attrList = userEntry.getAttribute(passwordPolicy |
| | | .getPasswordAttribute()); |
| | | List<Attribute> attrList = userEntry.getAttribute(passwordPolicy.getPasswordAttribute()); |
| | | if (attrList != null) |
| | | { |
| | | for (Attribute a : attrList) |
| | |
| | | |
| | | |
| | | /** |
| | | * Sets a new value for the password changed time equal to the |
| | | * current time. |
| | | * Sets a new value for the password changed time equal to the current time. |
| | | */ |
| | | public void setPasswordChangedTime() |
| | | { |
| | |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Sets a new value for the password changed time equal to the specified time. |
| | | * This method should generally only be used for testing purposes, since the |
| | | * variant that uses the current time is preferred almost everywhere else. |
| | | * This method should generally only be used for testing purposes, since the variant that uses |
| | | * the current time is preferred almost everywhere else. |
| | | * |
| | | * @param passwordChangedTime The time to use |
| | | */ |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Setting password changed time for user %s to " + |
| | | "current time of %d", userDNString, currentTime); |
| | | logger.trace("Setting password changed time for user %s to current time of %d", userDNString, currentTime); |
| | | } |
| | | |
| | | // passwordChangedTime is computed in the constructor from values in the |
| | | // entry. |
| | | // passwordChangedTime is computed in the constructor from values in the entry. |
| | | if (getPasswordChangedTime() != passwordChangedTime) |
| | | { |
| | | this.passwordChangedTime = passwordChangedTime; |
| | | |
| | | String timeValue = GeneralizedTimeSyntax.format(passwordChangedTime); |
| | | Attribute a = Attributes.create(OP_ATTR_PWPOLICY_CHANGED_TIME, |
| | | timeValue); |
| | | Attribute a = Attributes.create(OP_ATTR_PWPOLICY_CHANGED_TIME, timeValue); |
| | | |
| | | modifications.add(new Modification(ModificationType.REPLACE, a, true)); |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Removes the password changed time value from the user's entry. This should |
| | | * only be used for testing purposes, as it can really mess things up if you |
| | | * don't know what you're doing. |
| | | * Removes the password changed time value from the user's entry. This should only be used for testing |
| | | * purposes, as it can really mess things up if you don't know what you're doing. |
| | | */ |
| | | public void clearPasswordChangedTime() |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Clearing password changed time for user %s", |
| | | userDNString); |
| | | logger.trace("Clearing password changed time for user %s", userDNString); |
| | | } |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_CHANGED_TIME_LC, |
| | | true); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_CHANGED_TIME_LC, true); |
| | | Attribute a = Attributes.empty(type); |
| | | modifications.add(new Modification(ModificationType.REPLACE, a, true)); |
| | | |
| | | |
| | | // Fall back to using the entry creation time as the password changed time, |
| | | // if it's defined. Otherwise, use a value of zero. |
| | | AttributeType createTimeType = |
| | | DirectoryServer.getAttributeType(OP_ATTR_CREATE_TIMESTAMP_LC, true); |
| | | // Fall back to using the entry creation time as the password changed time, if it's defined. |
| | | // Otherwise, use a value of zero. |
| | | AttributeType createTimeType = DirectoryServer.getAttributeType(OP_ATTR_CREATE_TIMESTAMP_LC, true); |
| | | try |
| | | { |
| | | passwordChangedTime = getGeneralizedTime(userEntry, createTimeType); |
| | |
| | | |
| | | |
| | | /** |
| | | * Updates the user entry to indicate whether user account has been |
| | | * administratively disabled. |
| | | * Updates the user entry to indicate whether user account has been administratively disabled. |
| | | * |
| | | * @param isDisabled |
| | | * Indicates whether the user account has been administratively |
| | | * disabled. |
| | | * Indicates whether the user account has been administratively disabled. |
| | | */ |
| | | public void setDisabled(boolean isDisabled) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Updating user %s to set the disabled flag to %b", |
| | | userDNString, isDisabled); |
| | | logger.trace("Updating user %s to set the disabled flag to %b", userDNString, isDisabled); |
| | | } |
| | | |
| | | |
| | |
| | | |
| | | this.isDisabled = ConditionResult.not(this.isDisabled); |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_DISABLED, true); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_DISABLED, true); |
| | | |
| | | if (isDisabled) |
| | | { |
| | |
| | | else |
| | | { |
| | | // erase |
| | | modifications.add(new Modification(ModificationType.REPLACE, |
| | | Attributes.empty(type), true)); |
| | | modifications.add(new Modification(ModificationType.REPLACE, Attributes.empty(type), true)); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the user's account is currently expired. |
| | | * |
| | | * @return <CODE>true</CODE> if the user's account is expired, or |
| | | * <CODE>false</CODE> if not. |
| | | * @return <CODE>true</CODE> if the user's account is expired, or <CODE>false</CODE> if not. |
| | | */ |
| | | public boolean isAccountExpired() |
| | | { |
| | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning stored result of %b for user %s", |
| | | (isAccountExpired == ConditionResult.TRUE), userDNString); |
| | | isAccountExpired == ConditionResult.TRUE, userDNString); |
| | | } |
| | | |
| | | return isAccountExpired == ConditionResult.TRUE; |
| | | } |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_EXPIRATION_TIME, |
| | | true); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_EXPIRATION_TIME, true); |
| | | |
| | | try |
| | | { |
| | | try { |
| | | accountExpirationTime = getGeneralizedTime(userEntry, type); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e, "User %s is considered to have an expired " + |
| | | "account because an error occurred while attempting to make " + |
| | | "the determination.", userDNString); |
| | | logger.traceException(e, "User %s is considered to have an expired account because an error occurred " + |
| | | "while attempting to make the determination.", userDNString); |
| | | |
| | | isAccountExpired = ConditionResult.TRUE; |
| | | return true; |
| | |
| | | { |
| | | // The user does have an expiration time, but it hasn't arrived yet. |
| | | isAccountExpired = ConditionResult.FALSE; |
| | | logger.trace("The account for user %s is not expired because " + |
| | | "the expiration time has not yet arrived.", userDNString); |
| | | logger.trace("The account for user %s is not expired because the expiration time has not yet arrived.", |
| | | userDNString); |
| | | } |
| | | else if (accountExpirationTime >= 0) |
| | | { |
| | | // The user does have an expiration time, and it is in the past. |
| | | isAccountExpired = ConditionResult.TRUE; |
| | | logger.trace("The account for user %s is expired because the " + |
| | | "expiration time in that account has passed.", userDNString); |
| | | logger.trace("The account for user %s is expired because the expiration time in that account has passed.", |
| | | userDNString); |
| | | } |
| | | else |
| | | { |
| | | // The user doesn't have an expiration time in their entry, so it |
| | | // can't be expired. |
| | | // The user doesn't have an expiration time in their entry, so it can't be expired. |
| | | isAccountExpired = ConditionResult.FALSE; |
| | | logger.trace("The account for user %s is not expired because " + |
| | | "there is no expiration time in the user's entry.", |
| | | logger.trace("The account for user %s is not expired because there is no expiration time in the user's entry.", |
| | | userDNString); |
| | | } |
| | | |
| | |
| | | /** |
| | | * Retrieves the time at which the user's account will expire. |
| | | * |
| | | * @return The time at which the user's account will expire, or -1 if it is |
| | | * not configured with an expiration time. |
| | | * @return The time at which the user's account will expire, or -1 if it is not configured with an expiration time. |
| | | */ |
| | | public long getAccountExpirationTime() |
| | | { |
| | |
| | | /** |
| | | * Sets the user's account expiration time to the specified value. |
| | | * |
| | | * @param accountExpirationTime The time that the user's account should |
| | | * expire. |
| | | * @param accountExpirationTime The time that the user's account should expire. |
| | | */ |
| | | public void setAccountExpirationTime(long accountExpirationTime) |
| | | { |
| | |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Setting account expiration time for user %s to %s", |
| | | userDNString, timeStr); |
| | | logger.trace("Setting account expiration time for user %s to %s", userDNString, timeStr); |
| | | } |
| | | |
| | | this.accountExpirationTime = accountExpirationTime; |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_EXPIRATION_TIME, |
| | | true); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_EXPIRATION_TIME, true); |
| | | |
| | | Attribute a = Attributes.create(type, timeStr); |
| | | modifications.add(new Modification(ModificationType.REPLACE, a, true)); |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Clearing account expiration time for user %s", |
| | | userDNString); |
| | | logger.trace("Clearing account expiration time for user %s", userDNString); |
| | | } |
| | | |
| | | accountExpirationTime = -1; |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_EXPIRATION_TIME, |
| | | true); |
| | | |
| | | modifications.add(new Modification(ModificationType.REPLACE, |
| | | Attributes.empty(type), true)); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_EXPIRATION_TIME, true); |
| | | modifications.add(new Modification(ModificationType.REPLACE, Attributes.empty(type), true)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of times of failed authentication attempts for |
| | | * the user. If authentication failure time expiration is enabled, |
| | | * and there are expired times in the entry, these times are removed |
| | | * from the instance field and an update is provided to delete those |
| | | * values from the entry. |
| | | * Retrieves the set of times of failed authentication attempts for the user. If authentication failure |
| | | * time expiration is enabled, and there are expired times in the entry, these times are removed |
| | | * from the instance field and an update is provided to delete those values from the entry. |
| | | * |
| | | * @return The set of times of failed authentication attempts for |
| | | * the user, which will be an empty list in the case of no |
| | | * valid (unexpired) times in the entry. |
| | | * @return The set of times of failed authentication attempts for the user, which will be an empty list |
| | | * in the case of no valid (unexpired) times in the entry. |
| | | */ |
| | | public List<Long> getAuthFailureTimes() |
| | | { |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning stored auth failure time list of %d " + |
| | | "elements for user %s", |
| | | logger.trace("Returning stored auth failure time list of %d elements for user %s", |
| | | authFailureTimes.size(), userDNString); |
| | | } |
| | | |
| | | return authFailureTimes; |
| | | } |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_FAILURE_TIME_LC); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_FAILURE_TIME_LC); |
| | | if (type == null) |
| | | { |
| | | type = DirectoryServer.getDefaultAttributeType( |
| | | OP_ATTR_PWPOLICY_FAILURE_TIME); |
| | | type = DirectoryServer.getDefaultAttributeType(OP_ATTR_PWPOLICY_FAILURE_TIME); |
| | | } |
| | | |
| | | try |
| | |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e, "Error while processing auth failure times for user %s", |
| | | userDNString); |
| | | logger.traceException(e, "Error while processing auth failure times for user %s", userDNString); |
| | | |
| | | authFailureTimes = new ArrayList<Long>(); |
| | | |
| | | modifications.add(new Modification(ModificationType.REPLACE, |
| | | Attributes.empty(type), true)); |
| | | |
| | | modifications.add(new Modification(ModificationType.REPLACE, Attributes.empty(type), true)); |
| | | return authFailureTimes; |
| | | } |
| | | |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning an empty auth failure time list for " + |
| | | "user %s because the attribute is absent from the entry.", |
| | | userDNString); |
| | | logger.trace("Returning an empty auth failure time list for user %s because the attribute" + |
| | | " is absent from the entry.", userDNString); |
| | | } |
| | | |
| | | return authFailureTimes; |
| | |
| | | { |
| | | LinkedHashSet<ByteString> valuesToRemove = null; |
| | | |
| | | long expirationTime = currentTime - |
| | | (passwordPolicy.getLockoutFailureExpirationInterval() * 1000L); |
| | | long expirationTime = currentTime - passwordPolicy.getLockoutFailureExpirationInterval() * 1000L; |
| | | Iterator<Long> iterator = authFailureTimes.iterator(); |
| | | while (iterator.hasNext()) |
| | | { |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Removing expired auth failure time %d for " + |
| | | "user %s", l, userDNString); |
| | | logger.trace("Removing expired auth failure time %d for user %s", l, userDNString); |
| | | } |
| | | |
| | | iterator.remove(); |
| | |
| | | AttributeBuilder builder = new AttributeBuilder(type); |
| | | builder.addAll(valuesToRemove); |
| | | Attribute a = builder.toAttribute(); |
| | | modifications.add(new Modification(ModificationType.DELETE, a, |
| | | true)); |
| | | modifications.add(new Modification(ModificationType.DELETE, a, true)); |
| | | } |
| | | } |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning auth failure time list of %d elements " + |
| | | "for user %s", authFailureTimes.size(), userDNString); |
| | | logger.trace("Returning auth failure time list of %d elements for user %s", |
| | | authFailureTimes.size(), userDNString); |
| | | } |
| | | |
| | | return authFailureTimes; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Updates the set of authentication failure times to include the current |
| | | * time. If the number of failures reaches the policy configuration limit, |
| | | * lock the account. |
| | | * Updates the set of authentication failure times to include the current time. |
| | | * If the number of failures reaches the policy configuration limit, lock the account. |
| | | */ |
| | | public void updateAuthFailureTimes() |
| | | { |
| | |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Updating authentication failure times for user %s", |
| | | userDNString); |
| | | logger.trace("Updating authentication failure times for user %s", userDNString); |
| | | } |
| | | |
| | | |
| | |
| | | failureTimes.add(highestFailureTime); |
| | | |
| | | // And the attribute in the user entry |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_FAILURE_TIME_LC); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_FAILURE_TIME_LC); |
| | | if (type == null) |
| | | { |
| | | type = DirectoryServer.getDefaultAttributeType( |
| | | OP_ATTR_PWPOLICY_FAILURE_TIME); |
| | | type = DirectoryServer.getDefaultAttributeType(OP_ATTR_PWPOLICY_FAILURE_TIME); |
| | | } |
| | | |
| | | Attribute addAttr = Attributes.create(type, GeneralizedTimeSyntax.format(highestFailureTime)); |
| | | modifications.add(new Modification(ModificationType.ADD, addAttr, true)); |
| | | |
| | | // Now check to see if there have been sufficient failures to lock the |
| | | // account. |
| | | // Now check to see if there have been sufficient failures to lock the account. |
| | | int lockoutCount = passwordPolicy.getLockoutFailureCount(); |
| | | if ((lockoutCount > 0) && (lockoutCount <= authFailureTimes.size())) |
| | | if (lockoutCount > 0 && lockoutCount <= authFailureTimes.size()) |
| | | { |
| | | setFailureLockedTime(highestFailureTime); |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Locking user account %s due to too many failures.", |
| | | userDNString); |
| | | logger.trace("Locking user account %s due to too many failures.", userDNString); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Explicitly specifies the auth failure times for the associated user. This |
| | | * should generally only be used for testing purposes. Note that it will also |
| | | * set or clear the locked time as appropriate. |
| | | * Explicitly specifies the auth failure times for the associated user. This should generally only be used |
| | | * for testing purposes. Note that it will also set or clear the locked time as appropriate. |
| | | * |
| | | * @param authFailureTimes The set of auth failure times to use for the |
| | | * account. An empty list or {@code null} will |
| | | * clear the account of any existing failures. |
| | | * @param authFailureTimes The set of auth failure times to use for the account. An empty list or |
| | | * {@code null} will clear the account of any existing failures. |
| | | */ |
| | | public void setAuthFailureTimes(List<Long> authFailureTimes) |
| | | { |
| | | if ((authFailureTimes == null) || authFailureTimes.isEmpty()) |
| | | if (authFailureTimes == null || authFailureTimes.isEmpty()) |
| | | { |
| | | clearAuthFailureTimes(); |
| | | clearFailureLockedTime(); |
| | | return; |
| | | } |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_FAILURE_TIME_LC, |
| | | true); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_FAILURE_TIME_LC, true); |
| | | this.authFailureTimes = authFailureTimes; |
| | | |
| | | AttributeBuilder builder = new AttributeBuilder(type); |
| | |
| | | |
| | | modifications.add(new Modification(ModificationType.REPLACE, a, true)); |
| | | |
| | | // Now check to see if there have been sufficient failures to lock the |
| | | // account. |
| | | // Now check to see if there have been sufficient failures to lock the account. |
| | | int lockoutCount = passwordPolicy.getLockoutFailureCount(); |
| | | if ((lockoutCount > 0) && (lockoutCount <= authFailureTimes.size())) |
| | | if (lockoutCount > 0 && lockoutCount <= authFailureTimes.size()) |
| | | { |
| | | setFailureLockedTime(highestFailureTime); |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Locking user account %s due to too many failures.", |
| | | userDNString); |
| | | logger.trace("Locking user account %s due to too many failures.", userDNString); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Updates the user entry to remove any record of previous authentication |
| | | * failure times. |
| | | * Updates the user entry to remove any record of previous authentication failure times. |
| | | */ |
| | | private void clearAuthFailureTimes() |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Clearing authentication failure times for user %s", |
| | | userDNString); |
| | | logger.trace("Clearing authentication failure times for user %s", userDNString); |
| | | } |
| | | |
| | | List<Long> failureTimes = getAuthFailureTimes(); |
| | |
| | | return; |
| | | } |
| | | |
| | | failureTimes.clear(); // Note: failureTimes == this.authFailureTimes |
| | | failureTimes.clear(); // Note: failureTimes != this.authFailureTimes |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_FAILURE_TIME_LC); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_FAILURE_TIME_LC); |
| | | if (type == null) |
| | | { |
| | | type = DirectoryServer.getDefaultAttributeType( |
| | | OP_ATTR_PWPOLICY_FAILURE_TIME); |
| | | type = DirectoryServer.getDefaultAttributeType(OP_ATTR_PWPOLICY_FAILURE_TIME); |
| | | } |
| | | |
| | | modifications.add(new Modification(ModificationType.REPLACE, |
| | | Attributes.empty(type), true)); |
| | | modifications.add(new Modification(ModificationType.REPLACE, Attributes.empty(type), true)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Retrieves the time of an authentication failure lockout for the user. |
| | | * |
| | | * @return The time of an authentication failure lockout for the user, or -1 |
| | | * if no such time is present in the entry. |
| | | * @return The time of an authentication failure lockout for the user, or -1 if no such time is present in the entry. |
| | | */ |
| | | private long getFailureLockedTime() |
| | | { |
| | |
| | | return failureLockedTime; |
| | | } |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_LOCKED_TIME_LC); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_LOCKED_TIME_LC); |
| | | if (type == null) |
| | | { |
| | | type = DirectoryServer.getDefaultAttributeType( |
| | | OP_ATTR_PWPOLICY_LOCKED_TIME); |
| | | type = DirectoryServer.getDefaultAttributeType(OP_ATTR_PWPOLICY_LOCKED_TIME); |
| | | } |
| | | |
| | | try |
| | |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e, |
| | | "Returning current time for user %s because an error occurred", userDNString); |
| | | logger.traceException(e, "Returning current time for user %s because an error occurred", userDNString); |
| | | |
| | | failureLockedTime = currentTime; |
| | | return failureLockedTime; |
| | |
| | | |
| | | failureLockedTime = time; |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_LOCKED_TIME_LC); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_LOCKED_TIME_LC); |
| | | if (type == null) |
| | | { |
| | | type = DirectoryServer.getDefaultAttributeType( |
| | | OP_ATTR_PWPOLICY_LOCKED_TIME); |
| | | type = DirectoryServer.getDefaultAttributeType(OP_ATTR_PWPOLICY_LOCKED_TIME); |
| | | } |
| | | |
| | | Attribute a = Attributes.create(type, GeneralizedTimeSyntax.format(failureLockedTime)); |
| | |
| | | |
| | | |
| | | /** |
| | | * Updates the user entry to remove any record of previous authentication |
| | | * failure lockout. |
| | | * Updates the user entry to remove any record of previous authentication failure lockout. |
| | | */ |
| | | private void clearFailureLockedTime() |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Clearing failure lockout time for user %s.", |
| | | userDNString); |
| | | logger.trace("Clearing failure lockout time for user %s.", userDNString); |
| | | } |
| | | |
| | | if (-1L == getFailureLockedTime()) |
| | |
| | | |
| | | failureLockedTime = -1L; |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_LOCKED_TIME_LC); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_LOCKED_TIME_LC); |
| | | if (type == null) |
| | | { |
| | | type = DirectoryServer.getDefaultAttributeType( |
| | | OP_ATTR_PWPOLICY_LOCKED_TIME); |
| | | type = DirectoryServer.getDefaultAttributeType(OP_ATTR_PWPOLICY_LOCKED_TIME); |
| | | } |
| | | |
| | | modifications.add(new Modification(ModificationType.REPLACE, |
| | | Attributes.empty(type), true)); |
| | | modifications.add(new Modification(ModificationType.REPLACE, Attributes.empty(type), true)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the associated user should be considered locked out as a |
| | | * result of too many authentication failures. In the case of an expired |
| | | * lock-out, this routine produces the update to clear the lock-out attribute |
| | | * and the authentication failure timestamps. |
| | | * In case the failure lockout time is absent from the entry, but sufficient |
| | | * authentication failure timestamps are present in the entry, this routine |
| | | * produces the update to set the lock-out attribute. |
| | | * Indicates whether the associated user should be considered locked out as a result of too many |
| | | * authentication failures. In the case of an expired lock-out, this routine produces the update |
| | | * to clear the lock-out attribute and the authentication failure timestamps. |
| | | * In case the failure lockout time is absent from the entry, but sufficient authentication failure |
| | | * timestamps are present in the entry, this routine produces the update to set the lock-out attribute. |
| | | * |
| | | * @return <CODE>true</CODE> if the user is currently locked out due to too |
| | | * many authentication failures, or <CODE>false</CODE> if not. |
| | | * @return <CODE>true</CODE> if the user is currently locked out due to too many authentication failures, |
| | | * or <CODE>false</CODE> if not. |
| | | */ |
| | | public boolean lockedDueToFailures() |
| | | { |
| | | // FIXME: Introduce a state field to cache the computed value of this |
| | | // method. Note that only a cached "locked" status can be returned due to |
| | | // the possibility of intervening updates to this.failureLockedTime by |
| | | // updateAuthFailureTimes. |
| | | // FIXME: Introduce a state field to cache the computed value of this method. |
| | | // Note that only a cached "locked" status can be returned due to the possibility of intervening updates to |
| | | // this.failureLockedTime by updateAuthFailureTimes. |
| | | |
| | | // Check if the feature is enabled in the policy. |
| | | final int maxFailures = passwordPolicy.getLockoutFailureCount(); |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false for user %s because lockout due " + |
| | | "to failures is not enabled.", userDNString); |
| | | logger.trace("Returning false for user %s because lockout due to failures is not enabled.", userDNString); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | // Get the locked time from the user's entry. If it is present and not |
| | | // expired, the account is locked. If it is absent, the failure timestamps |
| | | // must be checked, since failure timestamps sufficient to lock the |
| | | // account could be produced across the synchronization topology within the |
| | | // synchronization latency. Also, note that IETF |
| | | // draft-behera-ldap-password-policy-09 specifies "19700101000000Z" as |
| | | // the value to be set under a "locked until reset" regime; however, this |
| | | // implementation accepts the value as a locked entry, but observes the |
| | | // lockout expiration policy for all values including this one. |
| | | // FIXME: This "getter" is unusual in that it might produce an update to the |
| | | // entry in two cases. Does it make sense to factor the methods so that, |
| | | // e.g., an expired lockout is reported, and clearing the lockout is left to |
| | | // the caller? |
| | | // Get the locked time from the user's entry. If it is present and not expired, the account is locked. |
| | | // If it is absent, the failure timestamps must be checked, since failure timestamps sufficient to lock the |
| | | // account could be produced across the synchronization topology within the synchronization latency. |
| | | // Also, note that IETF draft-behera-ldap-password-policy-09 specifies "19700101000000Z" as the value to be set |
| | | // under a "locked until reset" regime; however, this implementation accepts the value as a locked entry, |
| | | // but observes the lockout expiration policy for all values including this one. |
| | | // FIXME: This "getter" is unusual in that it might produce an update to the entry in two cases. |
| | | // Does it make sense to factor the methods so that, e.g., an expired lockout is reported, and clearing |
| | | // the lockout is left to the caller? |
| | | if (getFailureLockedTime() < 0L) |
| | | { |
| | | // There was no locked time present in the entry; however, sufficient |
| | | // failure times might have accumulated to trigger a lockout. |
| | | // There was no locked time present in the entry; however, sufficient failure times might have accumulated |
| | | // to trigger a lockout. |
| | | if (getAuthFailureTimes().size() < maxFailures) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false for user %s because there is " + |
| | | "no locked time.", userDNString); |
| | | logger.trace("Returning false for user %s because there is no locked time.", userDNString); |
| | | } |
| | | |
| | | return false; |
| | |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Locking user %s because there were enough " + |
| | | "existing failures even though there was no account locked time.", |
| | | userDNString); |
| | | logger.trace("Locking user %s because there were enough existing failures even though there was" + |
| | | " no account locked time.", userDNString); |
| | | } |
| | | // Fall through... |
| | | } |
| | |
| | | // There is a failure locked time, but it may be expired. |
| | | if (passwordPolicy.getLockoutDuration() > 0) |
| | | { |
| | | final long unlockTime = getFailureLockedTime() + |
| | | (1000L * passwordPolicy.getLockoutDuration()); |
| | | final long unlockTime = getFailureLockedTime() + 1000L * passwordPolicy.getLockoutDuration(); |
| | | if (unlockTime > currentTime) |
| | | { |
| | | secondsUntilUnlock = (int) ((unlockTime - currentTime) / 1000); |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning true for user %s because there is a " + |
| | | "locked time and the lockout duration has not been reached.", |
| | | userDNString); |
| | | logger.trace("Returning true for user %s because there is a locked time and the lockout duration has" + |
| | | " not been reached.", userDNString); |
| | | } |
| | | |
| | | return true; |
| | |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false for user %s " + |
| | | "because the existing lockout has expired.", userDNString); |
| | | logger.trace("Returning false for user %s because the existing lockout has expired.", userDNString); |
| | | } |
| | | |
| | | assert -1L == getFailureLockedTime(); |
| | |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning true for user %s " + |
| | | "because there is a locked time and no lockout duration.", |
| | | userDNString); |
| | | logger.trace("Returning true for user %s because there is a locked time and no lockout duration.", userDNString); |
| | | } |
| | | |
| | | assert -1L <= getFailureLockedTime(); |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the length of time in seconds until the user's account is |
| | | * automatically unlocked. This should only be called after calling |
| | | * <CODE>lockedDueToFailures</CODE>. |
| | | * Retrieves the length of time in seconds until the user's account is automatically unlocked. |
| | | * This should only be called after calling <CODE>lockedDueToFailures</CODE>. |
| | | * |
| | | * @return The length of time in seconds until the user's account is |
| | | * automatically unlocked, or -1 if the account is not locked or the |
| | | * lockout requires administrative action to clear. |
| | | * @return The length of time in seconds until the user's account is automatically unlocked, or -1 if the account |
| | | * is not locked or the lockout requires administrative action to clear. |
| | | */ |
| | | public int getSecondsUntilUnlock() |
| | | { |
| | | // secondsUntilUnlock is only set when failureLockedTime is present and |
| | | // PasswordPolicy.getLockoutDuration is enabled; hence it is not |
| | | // unreasonable to find secondsUntilUnlock uninitialized. |
| | | // secondsUntilUnlock is only set when failureLockedTime is present and PasswordPolicy.getLockoutDuration |
| | | // is enabled; hence it is not unreasonable to find secondsUntilUnlock uninitialized. |
| | | assert failureLockedTime != Long.MIN_VALUE; |
| | | |
| | | return (secondsUntilUnlock < 0) ? -1 : secondsUntilUnlock; |
| | | return secondsUntilUnlock < 0 ? -1 : secondsUntilUnlock; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Updates the user account to remove any record of a previous lockout due to |
| | | * failed authentications. |
| | | * Updates the user account to remove any record of a previous lockout due to failed authentications. |
| | | */ |
| | | public void clearFailureLockout() |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the time that the user last authenticated to the Directory |
| | | * Server. |
| | | * Retrieves the time that the user last authenticated to the Directory Server. |
| | | * |
| | | * @return The time that the user last authenticated to the Directory Server, |
| | | * or -1 if it cannot be determined. |
| | | * @return The time that the user last authenticated to the Directory Server, or -1 if it cannot be determined. |
| | | */ |
| | | public long getLastLoginTime() |
| | | { |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning stored last login time of %d for " + |
| | | "user %s.", lastLoginTime, userDNString); |
| | | logger.trace("Returning stored last login time of %d for user %s.", lastLoginTime, userDNString); |
| | | } |
| | | |
| | | return lastLoginTime; |
| | | } |
| | | |
| | | // The policy configuration must be checked since the entry cannot be |
| | | // evaluated without both an attribute name and timestamp format. |
| | | // The policy configuration must be checked since the entry cannot be evaluated without both an attribute |
| | | // name and timestamp format. |
| | | AttributeType type = passwordPolicy.getLastLoginTimeAttribute(); |
| | | String format = passwordPolicy.getLastLoginTimeFormat(); |
| | | |
| | | if ((type == null) || (format == null)) |
| | | if (type == null || format == null) |
| | | { |
| | | lastLoginTime = -1; |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning -1 for user %s because no last login " + |
| | | "time will be maintained.", userDNString); |
| | | logger.trace("Returning -1 for user %s because no last login time will be maintained.", userDNString); |
| | | } |
| | | |
| | | return lastLoginTime; |
| | | } |
| | | |
| | | boolean isGeneralizedTime = |
| | | type.getSyntax().getName().equals(SYNTAX_GENERALIZED_TIME_NAME); |
| | | boolean isGeneralizedTime = type.getSyntax().getName().equals(SYNTAX_GENERALIZED_TIME_NAME); |
| | | lastLoginTime = -1; |
| | | List<Attribute> attrList = userEntry.getAttribute(type); |
| | | |
| | |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning last login time of %d for user %s" + |
| | | "decoded using current last login time format.", |
| | | logger.trace("Returning last login time of %d for user %s, decoded using current last login time format.", |
| | | lastLoginTime, userDNString); |
| | | } |
| | | |
| | |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | // This could mean that the last login time was encoded using a |
| | | // previous format. |
| | | // This could mean that the last login time was encoded using a previous format. |
| | | for (String f : passwordPolicy.getPreviousLastLoginTimeFormats()) |
| | | { |
| | | try |
| | |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning last login time of %d for " + |
| | | "user %s decoded using previous last login time format " + |
| | | "of %s.", lastLoginTime, userDNString, f); |
| | | logger.trace("Returning last login time of %d for user %s decoded using previous last login time " + |
| | | "format of %s.", lastLoginTime, userDNString, f); |
| | | } |
| | | |
| | | return lastLoginTime; |
| | |
| | | assert lastLoginTime == -1; |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning -1 for user %s because the " + |
| | | "last login time value %s could not be parsed using any " + |
| | | "known format.", userDNString, valueString); |
| | | logger.trace("Returning -1 for user %s because the last login time value %s could not be parsed " + |
| | | "using any known format.", userDNString, valueString); |
| | | } |
| | | |
| | | return lastLoginTime; |
| | |
| | | assert lastLoginTime == -1; |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning %d for user %s because no last " + |
| | | "login time value exists.", lastLoginTime, userDNString); |
| | | logger.trace("Returning %d for user %s because no last login time value exists.", lastLoginTime, userDNString); |
| | | } |
| | | |
| | | return lastLoginTime; |
| | |
| | | |
| | | |
| | | /** |
| | | * Updates the user entry to use the specified last login time. This should |
| | | * be used primarily for testing purposes, as the variant that uses the |
| | | * current time should be used most of the time. |
| | | * Updates the user entry to use the specified last login time. This should be used primarily for testing purposes, |
| | | * as the variant that uses the current time should be used most of the time. |
| | | * |
| | | * @param lastLoginTime The last login time to set in the user entry. |
| | | */ |
| | |
| | | AttributeType type = passwordPolicy.getLastLoginTimeAttribute(); |
| | | String format = passwordPolicy.getLastLoginTimeFormat(); |
| | | |
| | | if ((type == null) || (format == null)) |
| | | if (type == null || format == null) |
| | | { |
| | | return; |
| | | } |
| | |
| | | { |
| | | SimpleDateFormat dateFormat = new SimpleDateFormat(format); |
| | | // If the attribute has a Generalized Time syntax, make it UTC time. |
| | | if (type.getSyntax().getName() |
| | | .equals(SYNTAX_GENERALIZED_TIME_NAME)) |
| | | if (type.getSyntax().getName().equals(SYNTAX_GENERALIZED_TIME_NAME)) |
| | | { |
| | | dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); |
| | | } |
| | |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e, "Unable to set last login time for user %s " + |
| | | "because an error occurred", userDNString); |
| | | logger.traceException(e, "Unable to set last login time for user %s because an error occurred", userDNString); |
| | | return; |
| | | } |
| | | |
| | | |
| | | String existingTimestamp = getValue(type); |
| | | if ((existingTimestamp != null) && timestamp.equals(existingTimestamp)) |
| | | if (existingTimestamp != null && timestamp.equals(existingTimestamp)) |
| | | { |
| | | logger.trace("Not updating last login time for user %s " + |
| | | "because the new value matches the existing value.", userDNString); |
| | | logger.trace("Not updating last login time for user %s because the new value matches the existing value.", |
| | | userDNString); |
| | | return; |
| | | } |
| | | |
| | |
| | | Attribute a = Attributes.create(type, timestamp); |
| | | modifications.add(new Modification(ModificationType.REPLACE, a, true)); |
| | | |
| | | logger.trace("Updated the last login time for user %s to %s", |
| | | userDNString, timestamp); |
| | | logger.trace("Updated the last login time for user %s to %s", userDNString, timestamp); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Clears the last login time from the user's entry. This should generally be |
| | | * used only for testing purposes. |
| | | * Clears the last login time from the user's entry. This should generally be used only for testing purposes. |
| | | */ |
| | | public void clearLastLoginTime() |
| | | { |
| | |
| | | |
| | | lastLoginTime = -1; |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_LAST_LOGIN_TIME, true); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_LAST_LOGIN_TIME, true); |
| | | |
| | | modifications.add(new Modification(ModificationType.REPLACE, |
| | | Attributes.empty(type), true)); |
| | | modifications.add(new Modification(ModificationType.REPLACE, Attributes.empty(type), true)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the user's account is currently locked because it has |
| | | * been idle for too long. |
| | | * Indicates whether the user's account is currently locked because it has been idle for too long. |
| | | * |
| | | * @return <CODE>true</CODE> if the user's account is locked because it has |
| | | * been idle for too long, or <CODE>false</CODE> if not. |
| | | * @return <CODE>true</CODE> if the user's account is locked because it has been idle for too long, |
| | | * or <CODE>false</CODE> if not. |
| | | */ |
| | | public boolean lockedDueToIdleInterval() |
| | | { |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning stored result of %b for user %s", |
| | | (isIdleLocked == ConditionResult.TRUE), userDNString); |
| | | logger.trace("Returning stored result of %b for user %s", isIdleLocked == ConditionResult.TRUE, userDNString); |
| | | } |
| | | |
| | | return isIdleLocked == ConditionResult.TRUE; |
| | | } |
| | | |
| | | // Return immediately if this feature is disabled, since the feature is not |
| | | // responsible for any state attribute in the entry. |
| | | // Return immediately if this feature is disabled, since the feature is not responsible for any state attribute |
| | | // in the entry. |
| | | if (passwordPolicy.getIdleLockoutInterval() <= 0) |
| | | { |
| | | isIdleLocked = ConditionResult.FALSE; |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false for user %s because no idle " + |
| | | "lockout interval is defined.", userDNString); |
| | | logger.trace("Returning false for user %s because no idle lockout interval is defined.", userDNString); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | long lockTime = currentTime - |
| | | (1000L * passwordPolicy.getIdleLockoutInterval()); |
| | | long lockTime = currentTime - 1000L * passwordPolicy.getIdleLockoutInterval(); |
| | | if(lockTime < 0) lockTime = 0; |
| | | |
| | | long theLastLoginTime = getLastLoginTime(); |
| | |
| | | isIdleLocked = ConditionResult.TRUE; |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | String reason = (theLastLoginTime < 0) |
| | | ? "there is no last login time and the password " + |
| | | "changed time is not in an acceptable window" |
| | | : "neither last login time nor password " + |
| | | "changed time are in an acceptable window"; |
| | | logger.trace("Returning true for user %s because %s.", |
| | | userDNString, reason); |
| | | String reason = theLastLoginTime < 0 |
| | | ? "there is no last login time and the password changed time is not in an acceptable window" |
| | | : "neither last login time nor password changed time are in an acceptable window"; |
| | | logger.trace("Returning true for user %s because %s.", userDNString, reason); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the user's password must be changed before any other |
| | | * operation can be performed. |
| | | * Indicates whether the user's password must be changed before any other operation can be performed. |
| | | * |
| | | * @return <CODE>true</CODE> if the user's password must be changed before |
| | | * any other operation can be performed. |
| | | * @return <CODE>true</CODE> if the user's password must be changed before any other operation can be performed. |
| | | */ |
| | | public boolean mustChangePassword() |
| | | { |
| | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning stored result of %b for user %s.", |
| | | (mustChangePassword == ConditionResult.TRUE), userDNString); |
| | | mustChangePassword == ConditionResult.TRUE, userDNString); |
| | | } |
| | | |
| | | return mustChangePassword == ConditionResult.TRUE; |
| | | } |
| | | |
| | | // If the password policy doesn't use force change on add or force change on |
| | | // reset, or if it forbids the user from changing his password, then return |
| | | // false. |
| | | // FIXME: the only getter responsible for a state attribute (pwdReset) that |
| | | // considers the policy before checking the entry for the presence of the |
| | | // attribute. |
| | | // If the password policy doesn't use force change on add or force change on reset, or if it forbids the user |
| | | // from changing his password, then return false. |
| | | // FIXME: the only getter responsible for a state attribute (pwdReset) that considers the policy before |
| | | // checking the entry for the presence of the attribute. |
| | | if (! (passwordPolicy.isAllowUserPasswordChanges() |
| | | && (passwordPolicy.isForceChangeOnAdd() |
| | | || passwordPolicy.isForceChangeOnReset()))) |
| | | && (passwordPolicy.isForceChangeOnAdd() || passwordPolicy.isForceChangeOnReset()))) |
| | | { |
| | | mustChangePassword = ConditionResult.FALSE; |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false for user %s because neither " + |
| | | "force change on add nor force change on reset is enabled, " + |
| | | "or users are not allowed to self-modify passwords.", |
| | | userDNString); |
| | | |
| | | logger.trace("Returning false for user %s because neither force change on add nor force change on reset" + |
| | | " is enabled, or users are not allowed to self-modify passwords.", userDNString); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_RESET_REQUIRED_LC); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_RESET_REQUIRED_LC); |
| | | if (type == null) |
| | | { |
| | | type = DirectoryServer.getDefaultAttributeType( |
| | | OP_ATTR_PWPOLICY_RESET_REQUIRED); |
| | | type = DirectoryServer.getDefaultAttributeType(OP_ATTR_PWPOLICY_RESET_REQUIRED); |
| | | } |
| | | |
| | | try |
| | |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e, "Returning true for user %s because an error occurred", |
| | | userDNString); |
| | | logger.traceException(e, "Returning true for user %s because an error occurred", userDNString); |
| | | |
| | | mustChangePassword = ConditionResult.TRUE; |
| | | |
| | |
| | | if(mustChangePassword == ConditionResult.UNDEFINED) |
| | | { |
| | | mustChangePassword = ConditionResult.FALSE; |
| | | logger.trace("Returning %b for user since the attribute \"%s\"" + |
| | | " is not present in the entry.", |
| | | logger.trace("Returning %b for user since the attribute \"%s\" is not present in the entry.", |
| | | false, userDNString, OP_ATTR_PWPOLICY_RESET_REQUIRED); |
| | | |
| | | return false; |
| | |
| | | |
| | | |
| | | /** |
| | | * Updates the user entry to indicate whether the user's password must be |
| | | * changed. |
| | | * Updates the user entry to indicate whether the user's password must be changed. |
| | | * |
| | | * @param mustChangePassword Indicates whether the user's password must be |
| | | * changed. |
| | | * @param mustChangePassword Indicates whether the user's password must be changed. |
| | | */ |
| | | public void setMustChangePassword(boolean mustChangePassword) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Updating user %s to set the reset flag to %b", |
| | | userDNString, mustChangePassword); |
| | | logger.trace("Updating user %s to set the reset flag to %b", userDNString, mustChangePassword); |
| | | } |
| | | |
| | | if (mustChangePassword == mustChangePassword()) |
| | |
| | | |
| | | this.mustChangePassword = ConditionResult.not(this.mustChangePassword); |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_RESET_REQUIRED_LC); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_RESET_REQUIRED_LC); |
| | | if (type == null) |
| | | { |
| | | type = DirectoryServer.getDefaultAttributeType( |
| | | OP_ATTR_PWPOLICY_RESET_REQUIRED); |
| | | type = DirectoryServer.getDefaultAttributeType(OP_ATTR_PWPOLICY_RESET_REQUIRED); |
| | | } |
| | | |
| | | if (mustChangePassword) |
| | |
| | | } |
| | | else |
| | | { |
| | | modifications.add(new Modification(ModificationType.REPLACE, |
| | | Attributes.empty(type), true)); |
| | | modifications.add(new Modification(ModificationType.REPLACE, Attributes.empty(type), true)); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the user's account is locked because the password has |
| | | * been reset by an administrator but the user did not change the password in |
| | | * a timely manner. |
| | | * Indicates whether the user's account is locked because the password has been reset by an administrator |
| | | * but the user did not change the password in a timely manner. |
| | | * |
| | | * @return <CODE>true</CODE> if the user's account is locked because of the |
| | | * maximum reset age, or <CODE>false</CODE> if not. |
| | | * @return <CODE>true</CODE> if the user's account is locked because of the maximum reset age, |
| | | * or <CODE>false</CODE> if not. |
| | | */ |
| | | public boolean lockedDueToMaximumResetAge() |
| | | { |
| | | // This feature is reponsible for neither a state field nor an entry state |
| | | // attribute. |
| | | // This feature is responsible for neither a state field nor an entry state attribute. |
| | | if (passwordPolicy.getMaxPasswordResetAge() <= 0L) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false for user %s because there is no " + |
| | | "maximum reset age.", userDNString); |
| | | logger.trace("Returning false for user %s because there is no maximum reset age.", userDNString); |
| | | } |
| | | |
| | | return false; |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false for user %s because the user's " + |
| | | "password has not been reset.", userDNString); |
| | | logger.trace("Returning false for user %s because the user's password has not been reset.", userDNString); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | long maxResetTime = getPasswordChangedTime() + |
| | | (1000L * passwordPolicy.getMaxPasswordResetAge()); |
| | | boolean locked = (maxResetTime < currentTime); |
| | | long maxResetTime = getPasswordChangedTime() + 1000L * passwordPolicy.getMaxPasswordResetAge(); |
| | | boolean locked = maxResetTime < currentTime; |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning %b for user %s after comparing the " + |
| | | "current and max reset times.", locked, userDNString); |
| | | logger.trace("Returning %b for user %s after comparing the current and max reset times.", locked, userDNString); |
| | | } |
| | | |
| | | return locked; |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the time that the user's password should expire (if the |
| | | * expiration is in the future) or did expire (if the expiration was in the |
| | | * past). Note that this method should be called after the |
| | | * <CODE>lockedDueToMaximumResetAge</CODE> method because grace logins will |
| | | * not be allowed in the case that the maximum reset age has passed whereas |
| | | * they may be used for expiration due to maximum password age or forced |
| | | * change time. |
| | | * Retrieves the time that the user's password should expire (if the expiration is in the future) or |
| | | * did expire (if the expiration was in the past). Note that this method should be called after the |
| | | * <CODE>lockedDueToMaximumResetAge</CODE> method because grace logins will not be allowed in the case |
| | | * that the maximum reset age has passed whereas they may be used for expiration due to maximum password |
| | | * age or forced change time. |
| | | * |
| | | * @return The time that the user's password should/did expire, or -1 if it |
| | | * should not expire. |
| | | * @return The time that the user's password should/did expire, or -1 if it should not expire. |
| | | */ |
| | | public long getPasswordExpirationTime() |
| | | { |
| | |
| | | long maxAge = passwordPolicy.getMaxPasswordAge(); |
| | | if (maxAge > 0L) |
| | | { |
| | | long expTime = getPasswordChangedTime() + (1000L*maxAge); |
| | | long expTime = getPasswordChangedTime() + 1000L * maxAge; |
| | | if (expTime < passwordExpirationTime) |
| | | { |
| | | passwordExpirationTime = expTime; |
| | |
| | | } |
| | | |
| | | long maxResetAge = passwordPolicy.getMaxPasswordResetAge(); |
| | | if (mustChangePassword() && (maxResetAge > 0L)) |
| | | if (mustChangePassword() && maxResetAge > 0L) |
| | | { |
| | | long expTime = getPasswordChangedTime() + (1000L*maxResetAge); |
| | | long expTime = getPasswordChangedTime() + 1000L * maxResetAge; |
| | | if (expTime < passwordExpirationTime) |
| | | { |
| | | passwordExpirationTime = expTime; |
| | |
| | | if (mustChangeTime > 0) |
| | | { |
| | | long reqChangeTime = getRequiredChangeTime(); |
| | | if ((reqChangeTime != mustChangeTime) && |
| | | (mustChangeTime < passwordExpirationTime)) |
| | | if (reqChangeTime != mustChangeTime && mustChangeTime < passwordExpirationTime) |
| | | { |
| | | passwordExpirationTime = mustChangeTime; |
| | | checkWarning = true; |
| | |
| | | { |
| | | mayUseGraceLogin = ConditionResult.TRUE; |
| | | |
| | | long warningInterval = passwordPolicy |
| | | .getPasswordExpirationWarningInterval(); |
| | | long warningInterval = passwordPolicy.getPasswordExpirationWarningInterval(); |
| | | if (warningInterval > 0L) |
| | | { |
| | | long shouldWarnTime = |
| | | passwordExpirationTime - (warningInterval*1000L); |
| | | long shouldWarnTime = passwordExpirationTime - warningInterval * 1000L; |
| | | if (shouldWarnTime > currentTime) |
| | | { |
| | | // The warning time is in the future, so we know the password isn't |
| | | // expired. |
| | | // The warning time is in the future, so we know the password isn't expired. |
| | | shouldWarn = ConditionResult.FALSE; |
| | | isFirstWarning = ConditionResult.FALSE; |
| | | isPasswordExpired = ConditionResult.FALSE; |
| | | } |
| | | else |
| | | { |
| | | // We're at least in the warning period, but the password may be |
| | | // expired. |
| | | // We're at least in the warning period, but the password may be expired. |
| | | long theWarnedTime = getWarnedTime(); |
| | | |
| | | if (passwordExpirationTime > currentTime) |
| | |
| | | |
| | | if (! passwordPolicy.isExpirePasswordsWithoutWarning()) |
| | | { |
| | | passwordExpirationTime = |
| | | currentTime + (warningInterval*1000L); |
| | | passwordExpirationTime = currentTime + warningInterval * 1000L; |
| | | } |
| | | } |
| | | else |
| | |
| | | |
| | | if (! passwordPolicy.isExpirePasswordsWithoutWarning()) |
| | | { |
| | | passwordExpirationTime = |
| | | theWarnedTime + (warningInterval*1000L); |
| | | passwordExpirationTime = theWarnedTime + warningInterval * 1000L; |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // The expiration time has passed, but we may not actually be |
| | | // expired if the user has not yet seen a warning. |
| | | // The expiration time has passed, but we may not actually be expired if the user has not |
| | | // yet seen a warning. |
| | | if (passwordPolicy.isExpirePasswordsWithoutWarning()) |
| | | { |
| | | shouldWarn = ConditionResult.FALSE; |
| | |
| | | } |
| | | else if (theWarnedTime > 0) |
| | | { |
| | | passwordExpirationTime = |
| | | theWarnedTime + (warningInterval*1000L); |
| | | passwordExpirationTime = theWarnedTime + warningInterval*1000L; |
| | | if (passwordExpirationTime > currentTime) |
| | | { |
| | | shouldWarn = ConditionResult.TRUE; |
| | |
| | | shouldWarn = ConditionResult.TRUE; |
| | | isFirstWarning = ConditionResult.TRUE; |
| | | isPasswordExpired = ConditionResult.FALSE; |
| | | passwordExpirationTime = currentTime + (warningInterval*1000L); |
| | | passwordExpirationTime = currentTime + warningInterval*1000L; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // There will never be a warning, and the user's password may be |
| | | // expired. |
| | | // There will never be a warning, and the user's password may be expired. |
| | | shouldWarn = ConditionResult.FALSE; |
| | | isFirstWarning = ConditionResult.FALSE; |
| | | |
| | |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning password expiration time of %d for user " + |
| | | "%s.", passwordExpirationTime, userDNString); |
| | | logger.trace("Returning password expiration time of %d for user %s.", passwordExpirationTime, userDNString); |
| | | } |
| | | |
| | | return passwordExpirationTime; |
| | |
| | | /** |
| | | * Indicates whether the user's password is currently expired. |
| | | * |
| | | * @return <CODE>true</CODE> if the user's password is currently expired, or |
| | | * <CODE>false</CODE> if not. |
| | | * @return <CODE>true</CODE> if the user's password is currently expired, or <CODE>false</CODE> if not. |
| | | */ |
| | | public boolean isPasswordExpired() |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Indicates whether the user's last password change was within the minimum |
| | | * password age. |
| | | * Indicates whether the user's last password change was within the minimum password age. |
| | | * |
| | | * @return <CODE>true</CODE> if the password minimum age is nonzero, the |
| | | * account is not in force-change mode, and the last password change |
| | | * was within the minimum age, or <CODE>false</CODE> otherwise. |
| | | * @return <CODE>true</CODE> if the password minimum age is nonzero, the account is not in force-change mode, |
| | | * and the last password change was within the minimum age, or <CODE>false</CODE> otherwise. |
| | | */ |
| | | public boolean isWithinMinimumAge() |
| | | { |
| | | // This feature is reponsible for neither a state field nor entry state |
| | | // attribute. |
| | | // This feature is responsible for neither a state field nor entry state attribute. |
| | | long minAge = passwordPolicy.getMinPasswordAge(); |
| | | if (minAge <= 0L) |
| | | { |
| | |
| | | |
| | | return false; |
| | | } |
| | | else if ((getPasswordChangedTime() + (minAge*1000L)) < currentTime) |
| | | else if (getPasswordChangedTime() + minAge * 1000L < currentTime) |
| | | { |
| | | // It's been long enough since the user changed their password. |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false because the minimum age has " + |
| | | "expired."); |
| | | logger.trace("Returning false because the minimum age has expired."); |
| | | } |
| | | |
| | | return false; |
| | |
| | | // The user is in a must-change mode, so the minimum age doesn't apply. |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false because the account is in a " + |
| | | "must-change state."); |
| | | logger.trace("Returning false because the account is in a must-change state."); |
| | | } |
| | | |
| | | return false; |
| | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the user may use a grace login if the password is expired |
| | | * and there is at least one grace login remaining. Note that this does not |
| | | * check to see if the user's password is expired, does not verify that there |
| | | * are any remaining grace logins, and does not update the set of grace login |
| | | * times. |
| | | * Indicates whether the user may use a grace login if the password is expired and there is at least one |
| | | * grace login remaining. Note that this does not check to see if the user's password is expired, does not |
| | | * verify that there are any remaining grace logins, and does not update the set of grace login times. |
| | | * |
| | | * @return <CODE>true</CODE> if the user may use a grace login if the |
| | | * password is expired and there is at least one grace login |
| | | * remaining, or <CODE>false</CODE> if the user may not use a grace |
| | | * @return <CODE>true</CODE> if the user may use a grace login if the password is expired and there is |
| | | * at least one grace login remaining, or <CODE>false</CODE> if the user may not use a grace |
| | | * login for some reason. |
| | | */ |
| | | public boolean mayUseGraceLogin() |
| | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the user should receive a warning notification that the |
| | | * password is about to expire. |
| | | * Indicates whether the user should receive a warning notification that the password is about to expire. |
| | | * |
| | | * @return <CODE>true</CODE> if the user should receive a warning |
| | | * notification that the password is about to expire, or |
| | | * <CODE>false</CODE> if not. |
| | | * @return <CODE>true</CODE> if the user should receive a warning notification that the password is about to expire, |
| | | * or <CODE>false</CODE> if not. |
| | | */ |
| | | public boolean shouldWarn() |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the warning that the user should receive would be the |
| | | * first warning for the user. |
| | | * Indicates whether the warning that the user should receive would be the first warning for the user. |
| | | * |
| | | * @return <CODE>true</CODE> if the warning that should be sent to the user |
| | | * would be the first warning, or <CODE>false</CODE> if not. |
| | | * @return <CODE>true</CODE> if the warning that should be sent to the user would be the first warning, |
| | | * or <CODE>false</CODE> if not. |
| | | */ |
| | | public boolean isFirstWarning() |
| | | { |
| | |
| | | * Retrieves the length of time in seconds until the user's password expires. |
| | | * |
| | | * @return The length of time in seconds until the user's password expires, |
| | | * 0 if the password is currently expired, or -1 if the password |
| | | * should not expire. |
| | | * 0 if the password is currently expired, or -1 if the password should not expire. |
| | | */ |
| | | public int getSecondsUntilExpiration() |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the timestamp for the last required change time that the user |
| | | * complied with. |
| | | * Retrieves the timestamp for the last required change time that the user complied with. |
| | | * |
| | | * @return The timestamp for the last required change time that the user |
| | | * complied with, or -1 if the user's password has not been changed |
| | | * in compliance with this configuration. |
| | | * @return The timestamp for the last required change time that the user complied with, |
| | | * or -1 if the user's password has not been changed in compliance with this configuration. |
| | | */ |
| | | public long getRequiredChangeTime() |
| | | { |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning stored required change time of %d for " + |
| | | "user %s", requiredChangeTime, userDNString); |
| | | logger.trace("Returning stored required change time of %d for user %s", requiredChangeTime, userDNString); |
| | | } |
| | | |
| | | return requiredChangeTime; |
| | | } |
| | | |
| | | AttributeType type = DirectoryServer.getAttributeType( |
| | | OP_ATTR_PWPOLICY_CHANGED_BY_REQUIRED_TIME, true); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_CHANGED_BY_REQUIRED_TIME, true); |
| | | |
| | | try |
| | | { |
| | |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e, "Returning %d for user %s because an error occurred", |
| | | requiredChangeTime, userDNString); |
| | | logger.traceException(e, "Returning %d for user %s because an error occurred", requiredChangeTime, userDNString); |
| | | |
| | | requiredChangeTime = -1; |
| | | return requiredChangeTime; |
| | | } |
| | | |
| | | logger.trace("Returning required change time of %d for user %s", |
| | | requiredChangeTime, userDNString); |
| | | logger.trace("Returning required change time of %d for user %s", requiredChangeTime, userDNString); |
| | | |
| | | return requiredChangeTime; |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Updates the user entry with a timestamp indicating that the password has |
| | | * been changed in accordance with the require change time. |
| | | * Updates the user entry with a timestamp indicating that the password has been changed in accordance |
| | | * with the require change time. |
| | | */ |
| | | public void setRequiredChangeTime() |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Updates the user entry with a timestamp indicating that the password has |
| | | * been changed in accordance with the require change time. |
| | | * Updates the user entry with a timestamp indicating that the password has been changed in accordance |
| | | * with the require change time. |
| | | * |
| | | * @param requiredChangeTime The timestamp to use for the required change |
| | | * time value. |
| | | * @param requiredChangeTime The timestamp to use for the required change time value. |
| | | */ |
| | | public void setRequiredChangeTime(long requiredChangeTime) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Updating required change time for user %s", |
| | | userDNString); |
| | | logger.trace("Updating required change time for user %s", userDNString); |
| | | } |
| | | |
| | | if (getRequiredChangeTime() != requiredChangeTime) |
| | | { |
| | | this.requiredChangeTime = requiredChangeTime; |
| | | |
| | | AttributeType type = DirectoryServer.getAttributeType( |
| | | OP_ATTR_PWPOLICY_CHANGED_BY_REQUIRED_TIME, true); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_CHANGED_BY_REQUIRED_TIME, true); |
| | | |
| | | String timeValue = GeneralizedTimeSyntax.format(requiredChangeTime); |
| | | Attribute a = Attributes.create(type, timeValue); |
| | |
| | | |
| | | |
| | | /** |
| | | * Updates the user entry to remove any timestamp indicating that the password |
| | | * has been changed in accordance with the required change time. |
| | | * Updates the user entry to remove any timestamp indicating that the password has been changed in accordance |
| | | * with the required change time. |
| | | */ |
| | | public void clearRequiredChangeTime() |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Clearing required change time for user %s", |
| | | userDNString); |
| | | logger.trace("Clearing required change time for user %s", userDNString); |
| | | } |
| | | |
| | | this.requiredChangeTime = Long.MIN_VALUE; |
| | | |
| | | AttributeType type = DirectoryServer.getAttributeType( |
| | | OP_ATTR_PWPOLICY_CHANGED_BY_REQUIRED_TIME, true); |
| | | modifications.add(new Modification(ModificationType.REPLACE, |
| | | Attributes.empty(type), true)); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_CHANGED_BY_REQUIRED_TIME, true); |
| | | modifications.add(new Modification(ModificationType.REPLACE, Attributes.empty(type), true)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the time that the user was first warned about an upcoming |
| | | * expiration. |
| | | * Retrieves the time that the user was first warned about an upcoming expiration. |
| | | * |
| | | * @return The time that the user was first warned about an upcoming |
| | | * expiration, or -1 if the user has not been warned. |
| | | * @return The time that the user was first warned about an upcoming expiration, or -1 if the user has |
| | | * not been warned. |
| | | */ |
| | | public long getWarnedTime() |
| | | { |
| | | if (warnedTime == Long.MIN_VALUE) |
| | | { |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_WARNED_TIME, true); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_WARNED_TIME, true); |
| | | try |
| | | { |
| | | warnedTime = getGeneralizedTime(userEntry, type); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e, "Unable to decode the warned time for user %s", |
| | | userDNString); |
| | | logger.traceException(e, "Unable to decode the warned time for user %s", userDNString); |
| | | warnedTime = -1; |
| | | } |
| | | } |
| | | |
| | | logger.trace("Returning a warned time of %d for user %s", |
| | | warnedTime, userDNString); |
| | | logger.trace("Returning a warned time of %d for user %s", warnedTime, userDNString); |
| | | return warnedTime; |
| | | } |
| | | |
| | |
| | | |
| | | |
| | | /** |
| | | * Updates the user entry to set the warned time to the specified time. This |
| | | * method should generally only be used for testing purposes, since the |
| | | * variant that uses the current time is preferred almost everywhere else. |
| | | * Updates the user entry to set the warned time to the specified time. This method should generally |
| | | * only be used for testing purposes, since the variant that uses the current time is preferred almost |
| | | * everywhere else. |
| | | * |
| | | * @param warnedTime The value to use for the warned time. |
| | | */ |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Not updating warned time for user %s because " + |
| | | "the warned time is the same as the specified time.", |
| | | logger.trace("Not updating warned time for user %s because the warned time is the same as the specified time.", |
| | | userDNString); |
| | | } |
| | | |
| | |
| | | |
| | | this.warnedTime = warnedTime; |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_WARNED_TIME, true); |
| | | Attribute a = Attributes.create(type, GeneralizedTimeSyntax |
| | | .createGeneralizedTimeValue(currentTime)); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_WARNED_TIME, true); |
| | | Attribute a = Attributes.create(type, GeneralizedTimeSyntax.createGeneralizedTimeValue(currentTime)); |
| | | |
| | | modifications.add(new Modification(ModificationType.REPLACE, a, true)); |
| | | |
| | |
| | | } |
| | | warnedTime = -1; |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_WARNED_TIME, true); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_WARNED_TIME, true); |
| | | Attribute a = Attributes.empty(type); |
| | | modifications.add(new Modification(ModificationType.REPLACE, a, true)); |
| | | |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the times that the user has authenticated to the server using a |
| | | * grace login. |
| | | * Retrieves the times that the user has authenticated to the server using a grace login. |
| | | * |
| | | * @return The times that the user has authenticated to the server using a |
| | | * grace login. |
| | | * @return The times that the user has authenticated to the server using a grace login. |
| | | */ |
| | | public List<Long> getGraceLoginTimes() |
| | | { |
| | | if (graceLoginTimes == null) |
| | | { |
| | | AttributeType type = DirectoryServer.getAttributeType( |
| | | OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME_LC); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME_LC); |
| | | if (type == null) |
| | | { |
| | | type = DirectoryServer.getDefaultAttributeType( |
| | | OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME); |
| | | type = DirectoryServer.getDefaultAttributeType(OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME); |
| | | } |
| | | |
| | | try |
| | |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e, "Error while processing grace login times for user %s", |
| | | userDNString); |
| | | logger.traceException(e, "Error while processing grace login times for user %s", userDNString); |
| | | |
| | | graceLoginTimes = new ArrayList<Long>(); |
| | | |
| | | modifications.add(new Modification(ModificationType.REPLACE, |
| | | Attributes.empty(type), true)); |
| | | modifications.add(new Modification(ModificationType.REPLACE, Attributes.empty(type), true)); |
| | | } |
| | | } |
| | | |
| | |
| | | /** |
| | | * Retrieves the number of grace logins that the user has left. |
| | | * |
| | | * @return The number of grace logins that the user has left, or -1 if grace |
| | | * logins are not allowed. |
| | | * @return The number of grace logins that the user has left, or -1 if grace logins are not allowed. |
| | | */ |
| | | public int getGraceLoginsRemaining() |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Updates the set of grace login times for the user to include the current |
| | | * time. |
| | | * Updates the set of grace login times for the user to include the current time. |
| | | */ |
| | | public void updateGraceLoginTimes() |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Updating grace login times for user %s", |
| | | userDNString); |
| | | logger.trace("Updating grace login times for user %s", userDNString); |
| | | } |
| | | |
| | | List<Long> graceTimes = getGraceLoginTimes(); |
| | |
| | | } |
| | | graceTimes.add(highestGraceTime); // graceTimes == this.graceLoginTimes |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME_LC); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME_LC); |
| | | if (type == null) |
| | | { |
| | | type = DirectoryServer.getDefaultAttributeType( |
| | | OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME); |
| | | type = DirectoryServer.getDefaultAttributeType(OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME); |
| | | } |
| | | |
| | | Attribute addAttr = Attributes.create(type, GeneralizedTimeSyntax.format(highestGraceTime)); |
| | |
| | | |
| | | |
| | | /** |
| | | * Specifies the set of grace login use times for the associated user. If |
| | | * the provided list is empty or {@code null}, then the set will be cleared. |
| | | * Specifies the set of grace login use times for the associated user. If the provided list is empty |
| | | * or {@code null}, then the set will be cleared. |
| | | * |
| | | * @param graceLoginTimes The grace login use times for the associated user. |
| | | */ |
| | | public void setGraceLoginTimes(List<Long> graceLoginTimes) |
| | | { |
| | | if ((graceLoginTimes == null) || graceLoginTimes.isEmpty()) |
| | | if (graceLoginTimes == null || graceLoginTimes.isEmpty()) |
| | | { |
| | | clearGraceLoginTimes(); |
| | | return; |
| | |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Updating grace login times for user %s", |
| | | userDNString); |
| | | logger.trace("Updating grace login times for user %s", userDNString); |
| | | } |
| | | |
| | | this.graceLoginTimes = graceLoginTimes; |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME_LC, |
| | | true); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME_LC, true); |
| | | AttributeBuilder builder = new AttributeBuilder(type); |
| | | for (Long l : graceLoginTimes) |
| | | { |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Clearing grace login times for user %s", |
| | | userDNString); |
| | | logger.trace("Clearing grace login times for user %s", userDNString); |
| | | } |
| | | |
| | | List<Long> graceTimes = getGraceLoginTimes(); |
| | |
| | | } |
| | | graceTimes.clear(); // graceTimes == this.graceLoginTimes |
| | | |
| | | AttributeType type = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME_LC); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME_LC); |
| | | if (type == null) |
| | | { |
| | | type = DirectoryServer.getDefaultAttributeType( |
| | | OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME); |
| | | type = DirectoryServer.getDefaultAttributeType(OP_ATTR_PWPOLICY_GRACE_LOGIN_TIME); |
| | | } |
| | | |
| | | modifications.add(new Modification(ModificationType.REPLACE, |
| | | Attributes.empty(type), true)); |
| | | modifications.add(new Modification(ModificationType.REPLACE, Attributes.empty(type), true)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves a list of the clear-text passwords for the user. If the user |
| | | * does not have any passwords in the clear, then the list will be empty. |
| | | * Retrieves a list of the clear-text passwords for the user. If the user does not have any passwords |
| | | * in the clear, then the list will be empty. |
| | | * |
| | | * @return A list of the clear-text passwords for the user. |
| | | */ |
| | |
| | | { |
| | | LinkedList<ByteString> clearPasswords = new LinkedList<ByteString>(); |
| | | |
| | | List<Attribute> attrList = |
| | | userEntry.getAttribute(passwordPolicy.getPasswordAttribute()); |
| | | List<Attribute> attrList = userEntry.getAttribute(passwordPolicy.getPasswordAttribute()); |
| | | |
| | | if (attrList == null) |
| | | { |
| | |
| | | { |
| | | try |
| | | { |
| | | StringBuilder[] pwComponents = |
| | | getPwComponents(usesAuthPasswordSyntax, v); |
| | | StringBuilder[] pwComponents = getPwComponents(usesAuthPasswordSyntax, v); |
| | | |
| | | String schemeName = pwComponents[0].toString(); |
| | | PasswordStorageScheme<?> scheme = (usesAuthPasswordSyntax) |
| | | ? DirectoryServer.getAuthPasswordStorageScheme(schemeName) |
| | | : DirectoryServer.getPasswordStorageScheme(schemeName); |
| | | PasswordStorageScheme<?> scheme = usesAuthPasswordSyntax |
| | | ? DirectoryServer.getAuthPasswordStorageScheme(schemeName) |
| | | : DirectoryServer.getPasswordStorageScheme(schemeName); |
| | | if (scheme == null) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("User entry %s contains a password with " + |
| | | "scheme %s that is not defined in the server.", |
| | | userDNString, schemeName); |
| | | logger.trace("User entry %s contains a password with scheme %s that is not defined in the server.", |
| | | userDNString, schemeName); |
| | | } |
| | | |
| | | continue; |
| | |
| | | |
| | | if (scheme.isReversible()) |
| | | { |
| | | ByteString clearValue = (usesAuthPasswordSyntax) |
| | | ? scheme.getAuthPasswordPlaintextValue( |
| | | pwComponents[1].toString(), |
| | | pwComponents[2].toString()) |
| | | : scheme.getPlaintextValue( |
| | | ByteString.valueOf(pwComponents[1].toString())); |
| | | ByteString clearValue = usesAuthPasswordSyntax |
| | | ? scheme.getAuthPasswordPlaintextValue(pwComponents[1].toString(), pwComponents[2].toString()) |
| | | : scheme.getPlaintextValue(ByteString.valueOf(pwComponents[1].toString())); |
| | | clearPasswords.add(clearValue); |
| | | } |
| | | } |
| | |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Cannot get clear password value foruser %s: " + |
| | | "%s", userDNString, e); |
| | | logger.trace("Cannot get clear password value for user %s: %s", userDNString, e); |
| | | } |
| | | } |
| | | } |
| | |
| | | @Override |
| | | public boolean passwordMatches(ByteString password) |
| | | { |
| | | List<Attribute> attrList = |
| | | userEntry.getAttribute(passwordPolicy.getPasswordAttribute()); |
| | | if ((attrList == null) || attrList.isEmpty()) |
| | | List<Attribute> attrList = userEntry.getAttribute(passwordPolicy.getPasswordAttribute()); |
| | | if (attrList == null || attrList.isEmpty()) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false because user %s does not have " + |
| | | "any values for password attribute %s", userDNString, |
| | | passwordPolicy.getPasswordAttribute().getNameOrOID()); |
| | | logger.trace("Returning false because user %s does not have any values for password attribute %s", |
| | | userDNString, passwordPolicy.getPasswordAttribute().getNameOrOID()); |
| | | } |
| | | |
| | | return false; |
| | |
| | | { |
| | | try |
| | | { |
| | | StringBuilder[] pwComponents = |
| | | getPwComponents(usesAuthPasswordSyntax, v); |
| | | |
| | | StringBuilder[] pwComponents = getPwComponents(usesAuthPasswordSyntax, v); |
| | | String schemeName = pwComponents[0].toString(); |
| | | PasswordStorageScheme<?> scheme = (usesAuthPasswordSyntax) |
| | | PasswordStorageScheme<?> scheme = usesAuthPasswordSyntax |
| | | ? DirectoryServer.getAuthPasswordStorageScheme(schemeName) |
| | | : DirectoryServer.getPasswordStorageScheme(schemeName); |
| | | if (scheme == null) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("User entry %s contains a password with " + |
| | | "scheme %s that is not defined in the server.", |
| | | logger.trace("User entry %s contains a password with scheme %s that is not defined in the server.", |
| | | userDNString, schemeName); |
| | | } |
| | | |
| | | continue; |
| | | } |
| | | |
| | | boolean passwordMatches = (usesAuthPasswordSyntax) |
| | | ? scheme.authPasswordMatches(password, |
| | | pwComponents[1].toString(), |
| | | pwComponents[2].toString()) |
| | | : scheme.passwordMatches(password, |
| | | ByteString.valueOf(pwComponents[1].toString())); |
| | | boolean passwordMatches = usesAuthPasswordSyntax |
| | | ? scheme.authPasswordMatches(password, pwComponents[1].toString(), pwComponents[2].toString()) |
| | | : scheme.passwordMatches(password, ByteString.valueOf(pwComponents[1].toString())); |
| | | if (passwordMatches) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning true for user %s because the " + |
| | | "provided password matches a value encoded with scheme %s", |
| | | userDNString, schemeName); |
| | | logger.trace("Returning true for user %s because the provided password matches a value " + |
| | | "encoded with scheme %s", userDNString, schemeName); |
| | | } |
| | | |
| | | return true; |
| | |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e, "An error occurred while attempting to " + |
| | | "process a password value for user %s", userDNString); |
| | | logger.traceException(e, "An error occurred while attempting to process a password value for user %s", |
| | | userDNString); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // If we've gotten here, then we couldn't find a match. |
| | | logger.trace("Returning false because the provided password does " + |
| | | "not match any of the stored password values for user %s", |
| | | userDNString); |
| | | logger.trace("Returning false because the provided password does not match any of the stored password " + |
| | | "values for user %s", userDNString); |
| | | |
| | | return false; |
| | | } |
| | |
| | | * |
| | | * @return An array of components. |
| | | */ |
| | | private StringBuilder[] getPwComponents(boolean usesAuthPasswordSyntax, |
| | | ByteString v) throws DirectoryException |
| | | private StringBuilder[] getPwComponents(boolean usesAuthPasswordSyntax, ByteString v) throws DirectoryException |
| | | { |
| | | if (usesAuthPasswordSyntax) |
| | | { |
| | |
| | | * |
| | | * @param passwordValue The value for which to make the determination. |
| | | * |
| | | * @return <CODE>true</CODE> if the provided password value is pre-encoded, |
| | | * or <CODE>false</CODE> if it is not. |
| | | * @return <CODE>true</CODE> if the provided password value is pre-encoded, or <CODE>false</CODE> if it is not. |
| | | */ |
| | | public boolean passwordIsPreEncoded(ByteString passwordValue) |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Encodes the provided password using the default storage schemes (using the |
| | | * appropriate syntax for the password attribute). |
| | | * Encodes the provided password using the default storage schemes (using the appropriate syntax for the |
| | | * password attribute). |
| | | * |
| | | * @param password The password to be encoded. |
| | | * |
| | | * @return The password encoded using the default schemes. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting to encode |
| | | * the password. |
| | | * @throws DirectoryException If a problem occurs while attempting to encode the password. |
| | | */ |
| | | public List<ByteString> encodePassword(ByteString password) |
| | | throws DirectoryException |
| | | { |
| | | List<PasswordStorageScheme<?>> schemes = |
| | | passwordPolicy.getDefaultPasswordStorageSchemes(); |
| | | List<ByteString> encodedPasswords = |
| | | new ArrayList<ByteString>(schemes.size()); |
| | | List<PasswordStorageScheme<?>> schemes = passwordPolicy.getDefaultPasswordStorageSchemes(); |
| | | List<ByteString> encodedPasswords = new ArrayList<ByteString>(schemes.size()); |
| | | |
| | | if (passwordPolicy.isAuthPasswordSyntax()) |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the provided password appears to be acceptable according |
| | | * to the password validators. |
| | | * Indicates whether the provided password appears to be acceptable according to the password validators. |
| | | * |
| | | * @param operation The operation that provided the password. |
| | | * @param userEntry The user entry in which the password is used. |
| | | * @param newPassword The password to be validated. |
| | | * @param currentPasswords The set of clear-text current passwords for the |
| | | * user (this may be a subset if not all of them are |
| | | * available in the clear, or empty if none of them |
| | | * @param currentPasswords The set of clear-text current passwords for the user (this may be a subset |
| | | * if not all of them are available in the clear, or empty if none of them |
| | | * are available in the clear). |
| | | * @param invalidReason A buffer that may be used to hold the invalid |
| | | * reason if the password is rejected. |
| | | * @param invalidReason A buffer that may be used to hold the invalid reason if the password is rejected. |
| | | * |
| | | * @return <CODE>true</CODE> if the password is acceptable for use, or |
| | | * <CODE>false</CODE> if it is not. |
| | | * @return <CODE>true</CODE> if the password is acceptable for use, or <CODE>false</CODE> if it is not. |
| | | */ |
| | | public boolean passwordIsAcceptable(Operation operation, Entry userEntry, |
| | | ByteString newPassword, Set<ByteString> currentPasswords, |
| | | LocalizableMessageBuilder invalidReason) |
| | | public boolean passwordIsAcceptable(Operation operation, Entry userEntry, ByteString newPassword, |
| | | Set<ByteString> currentPasswords, LocalizableMessageBuilder invalidReason) |
| | | { |
| | | for (PasswordValidator<?> validator : passwordPolicy |
| | | .getPasswordValidators()) |
| | | for (PasswordValidator<?> validator : passwordPolicy.getPasswordValidators()) |
| | | { |
| | | if (!validator.passwordIsAcceptable(newPassword, currentPasswords, |
| | | operation, userEntry, invalidReason)) |
| | | if (!validator.passwordIsAcceptable(newPassword, currentPasswords, operation, userEntry, invalidReason)) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("The password provided for user %s failed validation: %s", |
| | | userDNString, invalidReason); |
| | | logger.trace("The password provided for user %s failed validation: %s", userDNString, invalidReason); |
| | | } |
| | | return false; |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Performs any processing that may be necessary to remove deprecated storage |
| | | * schemes from the user's entry that match the provided password and |
| | | * re-encodes them using the default schemes. |
| | | * Performs any processing that may be necessary to remove deprecated storage schemes from the user's entry |
| | | * that match the provided password and re-encodes them using the default schemes. |
| | | * |
| | | * @param password The clear-text password provided by the user. |
| | | */ |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Doing nothing for user %s because no " + |
| | | "deprecated storage schemes have been defined.", userDNString); |
| | | logger.trace("Doing nothing for user %s because no deprecated storage schemes have been defined.", |
| | | userDNString); |
| | | } |
| | | |
| | | return; |
| | |
| | | |
| | | AttributeType type = passwordPolicy.getPasswordAttribute(); |
| | | List<Attribute> attrList = userEntry.getAttribute(type); |
| | | if ((attrList == null) || attrList.isEmpty()) |
| | | if (attrList == null || attrList.isEmpty()) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Doing nothing for entry %s because no password " + |
| | | "values were found.", userDNString); |
| | | logger.trace("Doing nothing for entry %s because no password values were found.", userDNString); |
| | | } |
| | | |
| | | return; |
| | |
| | | |
| | | for (Attribute a : attrList) |
| | | { |
| | | Iterator<ByteString> iterator = a.iterator(); |
| | | while (iterator.hasNext()) |
| | | { |
| | | ByteString v = iterator.next(); |
| | | |
| | | try |
| | | { |
| | | StringBuilder[] pwComponents = |
| | | getPwComponents(usesAuthPasswordSyntax, v); |
| | | for (ByteString v : a) { |
| | | try { |
| | | StringBuilder[] pwComponents = getPwComponents(usesAuthPasswordSyntax, v); |
| | | |
| | | String schemeName = pwComponents[0].toString(); |
| | | PasswordStorageScheme<?> scheme = (usesAuthPasswordSyntax) |
| | | ? DirectoryServer.getAuthPasswordStorageScheme(schemeName) |
| | | : DirectoryServer.getPasswordStorageScheme(schemeName); |
| | | if (scheme == null) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Skipping password value for user %s " + |
| | | "because the associated storage scheme %s is not " + |
| | | "configured for use.", userDNString, schemeName); |
| | | PasswordStorageScheme<?> scheme = usesAuthPasswordSyntax |
| | | ? DirectoryServer.getAuthPasswordStorageScheme(schemeName) |
| | | : DirectoryServer.getPasswordStorageScheme(schemeName); |
| | | if (scheme == null) { |
| | | if (logger.isTraceEnabled()) { |
| | | logger.trace("Skipping password value for user %s because the associated storage scheme %s " + |
| | | "is not configured for use.", userDNString, schemeName); |
| | | } |
| | | |
| | | continue; |
| | | } |
| | | |
| | | boolean passwordMatches = (usesAuthPasswordSyntax) |
| | | ? scheme.authPasswordMatches(password, |
| | | pwComponents[1].toString(), |
| | | pwComponents[2].toString()) |
| | | : scheme.passwordMatches(password, |
| | | ByteString.valueOf(pwComponents[1].toString())); |
| | | if (passwordMatches) |
| | | { |
| | | if (passwordPolicy.isDefaultPasswordStorageScheme(schemeName)) |
| | | { |
| | | boolean passwordMatches = usesAuthPasswordSyntax |
| | | ? scheme.authPasswordMatches(password, pwComponents[1].toString(), pwComponents[2].toString()) |
| | | : scheme.passwordMatches(password, ByteString.valueOf(pwComponents[1].toString())); |
| | | |
| | | if (passwordMatches) { |
| | | if (passwordPolicy.isDefaultPasswordStorageScheme(schemeName)) { |
| | | existingDefaultSchemes.add(schemeName); |
| | | updatedValues.add(v); |
| | | } |
| | | else if (passwordPolicy |
| | | .isDeprecatedPasswordStorageScheme(schemeName)) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Marking password with scheme %s for " + |
| | | "removal from user entry %s.", schemeName, userDNString); |
| | | } else if (passwordPolicy.isDeprecatedPasswordStorageScheme(schemeName)) { |
| | | if (logger.isTraceEnabled()) { |
| | | logger.trace("Marking password with scheme %s for removal from user entry %s.", |
| | | schemeName, userDNString); |
| | | } |
| | | |
| | | removedValues.add(v); |
| | | } |
| | | else |
| | | { |
| | | } else { |
| | | updatedValues.add(v); |
| | | } |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e, "Skipping password value for user %s because " + |
| | | "an error occurred while attempting to decode it based on " + |
| | | "the user password syntax", userDNString); |
| | | } catch (Exception e) { |
| | | logger.traceException(e, "Skipping password value for user %s because an error occurred while attempting " + |
| | | "to decode it based on the user password syntax", userDNString); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (removedValues.isEmpty()) |
| | | { |
| | | logger.trace("User entry %s does not have any password values " + |
| | | "encoded using deprecated schemes.", userDNString); |
| | | logger.trace("User entry %s does not have any password values encoded using deprecated schemes.", userDNString); |
| | | return; |
| | | } |
| | | |
| | | LinkedHashSet<ByteString> addedValues = new LinkedHashSet<ByteString>(); |
| | | for (PasswordStorageScheme<?> s : |
| | | passwordPolicy.getDefaultPasswordStorageSchemes()) |
| | | for (PasswordStorageScheme<?> s : passwordPolicy.getDefaultPasswordStorageSchemes()) |
| | | { |
| | | if (! existingDefaultSchemes.contains( |
| | | toLowerCase(s.getStorageSchemeName()))) |
| | | if (! existingDefaultSchemes.contains(toLowerCase(s.getStorageSchemeName()))) |
| | | { |
| | | try |
| | | { |
| | | ByteString encodedPassword = (usesAuthPasswordSyntax) |
| | | ? s.encodeAuthPassword(password) |
| | | : s.encodePasswordWithScheme(password); |
| | | ByteString encodedPassword = |
| | | usesAuthPasswordSyntax ? s.encodeAuthPassword(password) : s.encodePasswordWithScheme(password); |
| | | addedValues.add(encodedPassword); |
| | | updatedValues.add(encodedPassword); |
| | | } |
| | |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.traceException(e, "Unable to encode password for user %s using " + |
| | | "default scheme %s", userDNString, s.getStorageSchemeName()); |
| | | logger.traceException(e, "Unable to encode password for user %s using default scheme %s", |
| | | userDNString, s.getStorageSchemeName()); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | if (updatedValues.isEmpty()) |
| | | { |
| | | logger.trace("Not updating user entry %s because removing " + |
| | | "deprecated schemes would leave the user without a password.", |
| | | logger.trace( |
| | | "Not updating user entry %s because removing deprecated schemes would leave the user without a password.", |
| | | userDNString); |
| | | return; |
| | | } |
| | |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Updating user entry %s to replace password values " + |
| | | "encoded with deprecated schemes with values encoded " + |
| | | "with the default schemes.", userDNString); |
| | | logger.trace("Updating user entry %s to replace password values encoded with deprecated schemes " + |
| | | "with values encoded with the default schemes.", userDNString); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether password history information should be matained for this |
| | | * user. |
| | | * Indicates whether password history information should be maintained for this user. |
| | | * |
| | | * @return {@code true} if password history information should be maintained |
| | | * for this user, or {@code false} if not. |
| | | * @return {@code true} if password history information should be maintained for this user, or {@code false} if not. |
| | | */ |
| | | public boolean maintainHistory() |
| | | { |
| | | return ((passwordPolicy.getPasswordHistoryCount() > 0) || |
| | | (passwordPolicy.getPasswordHistoryDuration() > 0)); |
| | | return passwordPolicy.getPasswordHistoryCount() > 0 |
| | | || passwordPolicy.getPasswordHistoryDuration() > 0; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the provided password is equal to any of the current |
| | | * passwords, or any of the passwords in the history. |
| | | * Indicates whether the provided password is equal to any of the current passwords, |
| | | * or any of the passwords in the history. |
| | | * |
| | | * @param password The password for which to make the determination. |
| | | * |
| | | * @return {@code true} if the provided password is equal to any of the |
| | | * current passwords or any of the passwords in the history, or |
| | | * {@code false} if not. |
| | | * @return {@code true} if the provided password is equal to any of the current passwords or any of the passwords |
| | | * in the history, or {@code false} if not. |
| | | */ |
| | | public boolean isPasswordInHistory(ByteString password) |
| | | { |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false because password history " + |
| | | "checking is disabled."); |
| | | logger.trace("Returning false because password history checking is disabled."); |
| | | } |
| | | |
| | | // Password history checking is disabled, so we don't care if it is in the |
| | | // list or not. |
| | | return false; |
| | | } |
| | | |
| | | // Check to see if the provided password is equal to any of the current |
| | | // passwords. If so, then we'll consider it to be in the history. |
| | | // Check to see if the provided password is equal to any of the current passwords. |
| | | // If so, then we'll consider it to be in the history. |
| | | if (passwordMatches(password)) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning true because the provided password " + |
| | | "is currently in use."); |
| | | logger.trace("Returning true because the provided password is currently in use."); |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | |
| | | // Get the attribute containing the history and check to see if any of the |
| | | // values is equal to the provided password. However, first prune the list |
| | | // by size and duration if necessary. |
| | | // Get the attribute containing the history and check to see if any of the values is equal to the provided password. |
| | | // However, first prune the list by size and duration if necessary. |
| | | TreeMap<Long, ByteString> historyMap = getSortedHistoryValues(null); |
| | | |
| | | int historyCount = passwordPolicy.getPasswordHistoryCount(); |
| | | if ((historyCount > 0) && (historyMap.size() > historyCount)) |
| | | if (historyCount > 0 && historyMap.size() > historyCount) |
| | | { |
| | | int numToDelete = historyMap.size() - historyCount; |
| | | Iterator<Long> iterator = historyMap.keySet().iterator(); |
| | | while ((iterator.hasNext()) && (numToDelete > 0)) |
| | | while (iterator.hasNext() && numToDelete > 0) |
| | | { |
| | | iterator.next(); |
| | | iterator.remove(); |
| | |
| | | long historyDuration = passwordPolicy.getPasswordHistoryDuration(); |
| | | if (historyDuration > 0L) |
| | | { |
| | | long retainDate = currentTime - (1000 * historyDuration); |
| | | long retainDate = currentTime - 1000 * historyDuration; |
| | | Iterator<Long> iterator = historyMap.keySet().iterator(); |
| | | while (iterator.hasNext()) |
| | | { |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning true because the password is in " + |
| | | "the history."); |
| | | logger.trace("Returning true because the password is in the history."); |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | |
| | | // If we've gotten here, then the password isn't in the history. |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false because the password isn't in the " + |
| | | "history."); |
| | | logger.trace("Returning false because the password isn't in the history."); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Gets a sorted list of the password history values contained in the user's |
| | | * entry. The values will be sorted by timestamp. |
| | | * Gets a sorted list of the password history values contained in the user's entry. |
| | | * The values will be sorted by timestamp. |
| | | * |
| | | * @param removeAttrs A list into which any values will be placed that could |
| | | * not be properly decoded. It may be {@code null} if |
| | | * this is not needed. |
| | | * @param removeAttrs A list into which any values will be placed that could not be properly decoded. |
| | | * It may be {@code null} if this is not needed. |
| | | */ |
| | | private TreeMap<Long,ByteString> getSortedHistoryValues(List<Attribute> |
| | | removeAttrs) |
| | | private TreeMap<Long,ByteString> getSortedHistoryValues(List<Attribute> removeAttrs) |
| | | { |
| | | TreeMap<Long, ByteString> historyMap = new TreeMap<Long, ByteString>(); |
| | | AttributeType historyType = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_HISTORY_LC, true); |
| | | AttributeType historyType = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_HISTORY_LC, true); |
| | | List<Attribute> attrList = userEntry.getAttribute(historyType); |
| | | if (attrList != null) |
| | | { |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Found value " + histStr + " in the " + |
| | | "history with no timestamp. Marking it " + |
| | | "for removal."); |
| | | logger.trace("Found value " + histStr + " in the history with no timestamp. Marking it for removal."); |
| | | } |
| | | |
| | | if (removeAttrs != null) |
| | |
| | | try |
| | | { |
| | | long timestamp = |
| | | GeneralizedTimeSyntax.decodeGeneralizedTimeValue( |
| | | ByteString.valueOf(histStr.substring(0, hashPos))); |
| | | GeneralizedTimeSyntax.decodeGeneralizedTimeValue(ByteString.valueOf(histStr.substring(0, hashPos))); |
| | | historyMap.put(timestamp, v); |
| | | } |
| | | catch (Exception e) |
| | |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | logger.trace("Could not decode the timestamp in " + |
| | | "history value " + histStr + " -- " + e + |
| | | logger.trace("Could not decode the timestamp in history value " + histStr + " -- " + e + |
| | | ". Marking it for removal."); |
| | | } |
| | | |
| | | if (removeAttrs != null) |
| | | { |
| | | removeAttrs.add(Attributes |
| | | .create(a.getAttributeType(), v)); |
| | | removeAttrs.add(Attributes.create(a.getAttributeType(), v)); |
| | | } |
| | | } |
| | | } |
| | |
| | | /** |
| | | * Indicates whether the provided password matches the given history value. |
| | | * |
| | | * @param password The clear-text password for which to make the |
| | | * determination. |
| | | * @param historyValue The encoded history value to compare against the |
| | | * clear-text password. |
| | | * @param password The clear-text password for which to make the determination. |
| | | * @param historyValue The encoded history value to compare against the clear-text password. |
| | | * |
| | | * @return {@code true} if the provided password matches the history value, |
| | | * or {@code false} if not. |
| | | * @return {@code true} if the provided password matches the history value, or {@code false} if not. |
| | | */ |
| | | private boolean historyValueMatches(ByteString password, |
| | | ByteString historyValue) |
| | | { |
| | | // According to draft-behera-ldap-password-policy, password history values |
| | | // should be in the format time#syntaxoid#encodedvalue. In this method, |
| | | // we only care about the syntax OID and encoded password. |
| | | private boolean historyValueMatches(ByteString password, ByteString historyValue) { |
| | | // According to draft-behera-ldap-password-policy, password history values should be in the format |
| | | // time#syntaxoid#encodedvalue. In this method, we only care about the syntax OID and encoded password. |
| | | try |
| | | { |
| | | String histStr = historyValue.toString(); |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false because the password history " + |
| | | "value didn't include any hash characters."); |
| | | logger.trace("Returning false because the password history value didn't include any hash characters."); |
| | | } |
| | | |
| | | return false; |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false because the password history " + |
| | | "value only had one hash character."); |
| | | logger.trace("Returning false because the password history value only had one hash character."); |
| | | } |
| | | |
| | | return false; |
| | |
| | | String syntaxOID = toLowerCase(histStr.substring(hashPos1+1, hashPos2)); |
| | | if (syntaxOID.equals(SYNTAX_AUTH_PASSWORD_OID)) |
| | | { |
| | | StringBuilder[] authPWComponents = |
| | | AuthPasswordSyntax.decodeAuthPassword( |
| | | histStr.substring(hashPos2+1)); |
| | | PasswordStorageScheme<?> scheme = |
| | | DirectoryServer.getAuthPasswordStorageScheme( |
| | | authPWComponents[0].toString()); |
| | | if (scheme.authPasswordMatches(password, authPWComponents[1].toString(), |
| | | authPWComponents[2].toString())) |
| | | StringBuilder[] authPWComponents = AuthPasswordSyntax.decodeAuthPassword(histStr.substring(hashPos2+1)); |
| | | PasswordStorageScheme<?> scheme = DirectoryServer.getAuthPasswordStorageScheme(authPWComponents[0].toString()); |
| | | if (scheme.authPasswordMatches(password, authPWComponents[1].toString(), authPWComponents[2].toString())) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning true because the auth password " + |
| | | "history value matched."); |
| | | logger.trace("Returning true because the auth password history value matched."); |
| | | } |
| | | |
| | | return true; |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false because the auth password " + |
| | | "history value did not match."); |
| | | logger.trace("Returning false because the auth password history value did not match."); |
| | | } |
| | | |
| | | return false; |
| | |
| | | } |
| | | else if (syntaxOID.equals(SYNTAX_USER_PASSWORD_OID)) |
| | | { |
| | | String[] userPWComponents = |
| | | UserPasswordSyntax.decodeUserPassword( |
| | | histStr.substring(hashPos2+1)); |
| | | PasswordStorageScheme<?> scheme = |
| | | DirectoryServer.getPasswordStorageScheme(userPWComponents[0]); |
| | | if (scheme.passwordMatches(password, |
| | | ByteString.valueOf(userPWComponents[1]))) |
| | | String[] userPWComponents = UserPasswordSyntax.decodeUserPassword(histStr.substring(hashPos2+1)); |
| | | PasswordStorageScheme<?> scheme = DirectoryServer.getPasswordStorageScheme(userPWComponents[0]); |
| | | if (scheme.passwordMatches(password, ByteString.valueOf(userPWComponents[1]))) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning true because the user password " + |
| | | "history value matched."); |
| | | logger.trace("Returning true because the user password history value matched."); |
| | | } |
| | | |
| | | return true; |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false because the user password " + |
| | | "history value did not match."); |
| | | logger.trace("Returning false because the user password history value did not match."); |
| | | } |
| | | |
| | | return false; |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false because the syntax OID " + |
| | | syntaxOID + " didn't match for either the auth " + |
| | | "or user password syntax."); |
| | | logger.trace("Returning false because the syntax OID " + syntaxOID + |
| | | " didn't match for either the auth or user password syntax."); |
| | | } |
| | | |
| | | return false; |
| | |
| | | |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Returning false because of an exception: " + |
| | | stackTraceToSingleLineString(e)); |
| | | logger.trace("Returning false because of an exception: " + stackTraceToSingleLineString(e)); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | |
| | | /** |
| | | * Updates the password history information for this user by adding one of |
| | | * the passwords to it. It will choose the first password encoded using a |
| | | * secure storage scheme, and will fall back to a password encoded using an |
| | | * insecure storage scheme if necessary. |
| | | * Updates the password history information for this user by adding one of the passwords to it. |
| | | * It will choose the first password encoded using a secure storage scheme, and will fall back to |
| | | * a password encoded using an insecure storage scheme if necessary. |
| | | */ |
| | | public void updatePasswordHistory() |
| | | { |
| | | List<Attribute> attrList = |
| | | userEntry.getAttribute(passwordPolicy.getPasswordAttribute()); |
| | | List<Attribute> attrList = userEntry.getAttribute(passwordPolicy.getPasswordAttribute()); |
| | | if (attrList != null) |
| | | { |
| | | for (Attribute a : attrList) |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Encoded password " + v + |
| | | " cannot be decoded and cannot be added to history."); |
| | | logger.trace("Encoded password " + v + " cannot be decoded and cannot be added to history."); |
| | | } |
| | | } |
| | | } |
| | | // If we get here we haven't found a password encoded securely, so we |
| | | // have to use one of the other values. |
| | | // If we get here we haven't found a password encoded securely, so we have to use one of the other values. |
| | | if (insecurePassword != null) |
| | | { |
| | | addPasswordToHistory(insecurePassword.toString()); |
| | |
| | | |
| | | |
| | | /** |
| | | * Adds the provided password to the password history. If appropriate, one or |
| | | * more old passwords may be evicted from the list if the total size would |
| | | * exceed the configured count, or if passwords are older than the configured |
| | | * duration. |
| | | * Adds the provided password to the password history. If appropriate, one or more old passwords may be |
| | | * evicted from the list if the total size would exceed the configured count, or if passwords are older |
| | | * than the configured duration. |
| | | * |
| | | * @param encodedPassword The encoded password (in either user password or |
| | | * auth password format) to be added to the history. |
| | | * @param encodedPassword The encoded password (in either user password or auth password format) |
| | | * to be added to the history. |
| | | */ |
| | | private void addPasswordToHistory(String encodedPassword) |
| | | { |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Not doing anything because password history " + |
| | | "maintenance is disabled."); |
| | | logger.trace("Not doing anything because password history maintenance is disabled."); |
| | | } |
| | | |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Get a sorted list of the existing values to see if there are any that |
| | | // should be removed. |
| | | // Get a sorted list of the existing values to see if there are any that should be removed. |
| | | LinkedList<Attribute> removeAttrs = new LinkedList<Attribute>(); |
| | | TreeMap<Long, ByteString> historyMap = getSortedHistoryValues(removeAttrs); |
| | | |
| | | |
| | | // If there is a maximum number of values to retain and we would be over the |
| | | // limit with the new value, then get rid of enough values (oldest first) |
| | | // to satisfy the count. |
| | | AttributeType historyType = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_HISTORY_LC, true); |
| | | // If there is a maximum number of values to retain and we would be over the limit with the new value, |
| | | // then get rid of enough values (oldest first) to satisfy the count. |
| | | AttributeType historyType = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_HISTORY_LC, true); |
| | | int historyCount = passwordPolicy.getPasswordHistoryCount(); |
| | | if ((historyCount > 0) && (historyMap.size() >= historyCount)) |
| | | if (historyCount > 0 && historyMap.size() >= historyCount) |
| | | { |
| | | int numToDelete = (historyMap.size() - historyCount) + 1; |
| | | LinkedHashSet<ByteString> removeValues = |
| | | new LinkedHashSet<ByteString>(numToDelete); |
| | | int numToDelete = historyMap.size() - historyCount + 1; |
| | | LinkedHashSet<ByteString> removeValues = new LinkedHashSet<ByteString>(numToDelete); |
| | | Iterator<ByteString> iterator = historyMap.values().iterator(); |
| | | while (iterator.hasNext() && (numToDelete > 0)) |
| | | while (iterator.hasNext() && numToDelete > 0) |
| | | { |
| | | ByteString v = iterator.next(); |
| | | removeValues.add(v); |
| | |
| | | } |
| | | |
| | | |
| | | // If there is a maximum duration, then get rid of any values that would be |
| | | // over the duration. |
| | | // If there is a maximum duration, then get rid of any values that would be over the duration. |
| | | long historyDuration = passwordPolicy.getPasswordHistoryDuration(); |
| | | if (historyDuration > 0L) |
| | | { |
| | | long minAgeToKeep = currentTime - (1000L * historyDuration); |
| | | long minAgeToKeep = currentTime - 1000L * historyDuration; |
| | | Iterator<Long> iterator = historyMap.keySet().iterator(); |
| | | LinkedHashSet<ByteString> removeValues = new LinkedHashSet<ByteString>(); |
| | | while (iterator.hasNext()) |
| | |
| | | } |
| | | |
| | | |
| | | // At this point, we can add the new value. However, we want to make sure |
| | | // that its timestamp (which is the current time) doesn't conflict with any |
| | | // value already in the list. If there is a conflict, then simply add one |
| | | // to it until we don't have any more conflicts. |
| | | // At this point, we can add the new value. However, we want to make sure that its timestamp |
| | | // (which is the current time) doesn't conflict with any value already in the list. If there is a conflict, |
| | | // then simply add one to it until we don't have any more conflicts. |
| | | long newTimestamp = currentTime; |
| | | while (historyMap.containsKey(newTimestamp)) |
| | | { |
| | | newTimestamp++; |
| | | } |
| | | String newHistStr = GeneralizedTimeSyntax.format(newTimestamp) + "#" + |
| | | passwordPolicy.getPasswordAttribute().getSyntax().getOID() + |
| | | "#" + encodedPassword; |
| | | passwordPolicy.getPasswordAttribute().getSyntax().getOID() + "#" + encodedPassword; |
| | | Attribute newHistAttr = Attributes.create(historyType, newHistStr); |
| | | |
| | | if (logger.isTraceEnabled()) |
| | |
| | | } |
| | | |
| | | |
| | | // Apply the changes, either by adding modifications or by directly updating |
| | | // the entry. |
| | | // Apply the changes, either by adding modifications or by directly updating the entry. |
| | | for (Attribute a : removeAttrs) |
| | | { |
| | | modifications.add(new Modification(ModificationType.DELETE, a, true)); |
| | | } |
| | | |
| | | modifications.add(new Modification(ModificationType.ADD, newHistAttr, |
| | | true)); |
| | | modifications.add(new Modification(ModificationType.ADD, newHistAttr, true)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the password history state values for the user. This is only |
| | | * intended for testing purposes. |
| | | * Retrieves the password history state values for the user. This is only intended for testing purposes. |
| | | * |
| | | * @return The password history state values for the user. |
| | | */ |
| | | public String[] getPasswordHistoryValues() |
| | | { |
| | | ArrayList<String> historyValues = new ArrayList<String>(); |
| | | AttributeType historyType = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_HISTORY_LC, true); |
| | | AttributeType historyType = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_HISTORY_LC, true); |
| | | List<Attribute> attrList = userEntry.getAttribute(historyType); |
| | | if (attrList != null) |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Clears the password history state information for the user. This is only |
| | | * intended for testing purposes. |
| | | * Clears the password history state information for the user. This is only intended for testing purposes. |
| | | */ |
| | | public void clearPasswordHistory() |
| | | { |
| | |
| | | logger.trace("Clearing password history for user %s", userDNString); |
| | | } |
| | | |
| | | AttributeType type = DirectoryServer.getAttributeType( |
| | | OP_ATTR_PWPOLICY_HISTORY_LC, true); |
| | | modifications.add(new Modification(ModificationType.REPLACE, |
| | | Attributes.empty(type), true)); |
| | | AttributeType type = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_HISTORY_LC, true); |
| | | modifications.add(new Modification(ModificationType.REPLACE, Attributes.empty(type), true)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Generates a new password for the user. |
| | | * |
| | | * @return The new password that has been generated, or <CODE>null</CODE> if |
| | | * no password generator has been defined. |
| | | * @return The new password that has been generated, or <CODE>null</CODE> if no password generator has been defined. |
| | | * |
| | | * @throws DirectoryException If an error occurs while attempting to |
| | | * generate the new password. |
| | | * @throws DirectoryException If an error occurs while attempting to generate the new password. |
| | | */ |
| | | public ByteString generatePassword() |
| | | throws DirectoryException |
| | |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Unable to generate a new password for user " + |
| | | "%s because no password generator has been defined in the " + |
| | | "associated password policy.", userDNString); |
| | | logger.trace("Unable to generate a new password for user %s because no password generator has been defined" + |
| | | "in the associated password policy.", userDNString); |
| | | } |
| | | |
| | | return null; |
| | |
| | | /** |
| | | * Generates an account status notification for this user. |
| | | * |
| | | * @param notificationType The type for the account status |
| | | * notification. |
| | | * @param userEntry The entry for the user to which this |
| | | * notification applies. |
| | | * @param message The human-readable message for the |
| | | * notification. |
| | | * @param notificationType The type for the account status notification. |
| | | * @param userEntry The entry for the user to which this notification applies. |
| | | * @param message The human-readable message for the notification. |
| | | * @param notificationProperties The set of properties for the notification. |
| | | */ |
| | | public void generateAccountStatusNotification( |
| | | AccountStatusNotificationType notificationType, |
| | | Entry userEntry, LocalizableMessage message, |
| | | Map<AccountStatusNotificationProperty,List<String>> |
| | | notificationProperties) |
| | | AccountStatusNotificationType notificationType, |
| | | Entry userEntry, LocalizableMessage message, |
| | | Map<AccountStatusNotificationProperty,List<String>> notificationProperties) |
| | | { |
| | | generateAccountStatusNotification(new AccountStatusNotification( |
| | | notificationType, userEntry, message, notificationProperties)); |
| | | generateAccountStatusNotification( |
| | | new AccountStatusNotification(notificationType, userEntry, message, notificationProperties)); |
| | | } |
| | | |
| | | |
| | |
| | | /** |
| | | * Generates an account status notification for this user. |
| | | * |
| | | * @param notification The account status notification that should be |
| | | * generated. |
| | | * @param notification The account status notification that should be generated. |
| | | */ |
| | | public void generateAccountStatusNotification( |
| | | AccountStatusNotification notification) |
| | | public void generateAccountStatusNotification(AccountStatusNotification notification) |
| | | { |
| | | Collection<AccountStatusNotificationHandler<?>> handlers = |
| | | passwordPolicy.getAccountStatusNotificationHandlers(); |
| | | Collection<AccountStatusNotificationHandler<?>> handlers = passwordPolicy.getAccountStatusNotificationHandlers(); |
| | | for (AccountStatusNotificationHandler<?> handler : handlers) |
| | | { |
| | | handler.handleStatusNotification(notification); |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of modifications that correspond to changes made in |
| | | * password policy processing that may need to be applied to the user entry. |
| | | * Retrieves the set of modifications that correspond to changes made in password policy processing |
| | | * that may need to be applied to the user entry. |
| | | * |
| | | * @return The set of modifications that correspond to changes made in |
| | | * password policy processing that may need to be applied to the user |
| | | * entry. |
| | | * @return The set of modifications that correspond to changes made in password policy processing |
| | | * that may need to be applied to the user entry. |
| | | */ |
| | | public List<Modification> getModifications() |
| | | { |
| | |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Convert the set of modifications to a set of LDAP modifications. |
| | | ArrayList<RawModification> modList = new ArrayList<RawModification>(); |
| | | for (Modification m : modifications) |
| | | { |
| | | modList.add(RawModification.create(m.getModificationType(), |
| | | new LDAPAttribute(m.getAttribute()))); |
| | | modList.add(RawModification.create(m.getModificationType(), new LDAPAttribute(m.getAttribute()))); |
| | | } |
| | | |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | ModifyOperation internalModify = |
| | | conn.processModify(ByteString.valueOf(userDNString), modList); |
| | | InternalClientConnection conn = getRootConnection(); |
| | | ModifyOperation internalModify = conn.processModify(ByteString.valueOf(userDNString), modList); |
| | | |
| | | ResultCode resultCode = internalModify.getResultCode(); |
| | | if (resultCode != ResultCode.SUCCESS) |
| | | { |
| | | LocalizableMessage message = ERR_PWPSTATE_CANNOT_UPDATE_USER_ENTRY.get(userDNString, |
| | | internalModify.getErrorMessage()); |
| | | LocalizableMessage message = ERR_PWPSTATE_CANNOT_UPDATE_USER_ENTRY.get( |
| | | userDNString, internalModify.getErrorMessage()); |
| | | |
| | | // If this is a root user, or if the password policy says that we should |
| | | // ignore these problems, then log a warning message. Otherwise, cause |
| | | // the bind to fail. |
| | | if ((DirectoryServer.isRootDN(userEntry.getName()) || |
| | | (passwordPolicy.getStateUpdateFailurePolicy() == |
| | | PasswordPolicyCfgDefn.StateUpdateFailurePolicy.IGNORE))) |
| | | // If this is a root user, or if the password policy says that we should ignore these problems, |
| | | // then log a warning message. Otherwise, cause the bind to fail. |
| | | if (DirectoryServer.isRootDN(userEntry.getName()) |
| | | || passwordPolicy.getStateUpdateFailurePolicy() == PasswordPolicyCfgDefn.StateUpdateFailurePolicy.IGNORE) |
| | | { |
| | | logger.error(message); |
| | | } |