mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

lutoff
27.10.2007 a58e7b46dfb39b744ef3ea3970d55696ba68c646
Fix for Issue #1485 (use new admin framework for password policy configuration)
1 files added
6 files modified
2249 ■■■■ changed files
opends/src/admin/defn/org/opends/server/admin/std/PasswordPolicyConfiguration.xml 919 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml 8 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DirectoryServer.java 28 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/PasswordPolicy.java 737 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/PasswordPolicyConfig.java 363 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/PasswordPolicyConfigManager.java 185 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java 9 ●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/PasswordPolicyConfiguration.xml
New file
@@ -0,0 +1,919 @@
<?xml version="1.0" encoding="UTF-8"?>
<adm:managed-object name="password-policy"
  plural-name="password-policys" package="org.opends.server.admin.std"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    Define a number of password management rules, as well as
    requirements for authentication processing.
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.62</ldap:oid>
      <ldap:name>ds-cfg-password-policy</ldap:name>
      <ldap:superior>top</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="password-attribute" mandatory="true"
    multi-valued="false">
    <adm:synopsis>
      Specifies the attribute type used to hold user passwords.
    </adm:synopsis>
    <adm:description>
      Specifies the attribute type used to hold user passwords. This
      attribute type must be defined in the server schema. Changes to
      this configuration attribute will take effect immediately.
    </adm:description>
    <adm:syntax>
      <adm:oid />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.192</ldap:oid>
        <ldap:name>ds-cfg-password-attribute</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="default-password-storage-scheme" mandatory="true"
    multi-valued="true">
    <adm:synopsis>
      Specifies the password storage scheme (or set of schemes) that
      will be used to encode clear-text passwords.
    </adm:synopsis>
    <adm:description>
      Specifies the password storage scheme (or set of schemes) that
      will be used to encode clear-text passwords. If multiple default
      storage schemes are defined for a password policy, then the same
      password will be encoded using all of those schemes. Changes to
      this configuration attribute will take effect immediately.
    </adm:description>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.178</ldap:oid>
        <ldap:name>ds-cfg-default-password-storage-scheme</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="deprecated-password-storage-scheme"
    mandatory="false" multi-valued="true">
    <adm:synopsis>
      Specifies the password storage scheme (or set of schemes) that
      should be considered deprecated.
    </adm:synopsis>
    <adm:description>
      Specifies the password storage scheme (or set of schemes) that
      should be considered deprecated. If an authenticating user has a
      password encoded with one of these schemes, those passwords will
      be removed and replaced with passwords encoded using the default
      schemes. Changes to this configuration attribute will take effect
      immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.179</ldap:oid>
        <ldap:name>ds-cfg-deprecated-password-storage-scheme</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="password-validator-dn" mandatory="false"
    multi-valued="true">
    <adm:synopsis>
      Specifies the DN(s) of the password validator(s) that should be
      used with the associated password storage scheme.
    </adm:synopsis>
    <adm:description>
      Specifies the DN(s) of the password validator(s) that should be
      used with the associated password storage scheme. Changes to this
      configuration attribute will take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
      <adm:dn />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.195</ldap:oid>
        <ldap:name>ds-cfg-password-validator-dn</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="account-status-notification-handler-dn"
    mandatory="false" multi-valued="true">
    <adm:synopsis>
      Specifies the DN(s) of the account status notification handler(s)
      that should be used with the associated password storage scheme.
    </adm:synopsis>
    <adm:description>
      Specifies the DN(s) of the account status notification handler(s)
      that should be used with the associated password storage scheme.
      Changes to this configuration attribute will take effect
      immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
      <adm:dn />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.174</ldap:oid>
        <ldap:name>
          ds-cfg-account-status-notification-handler-dn
        </ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="allow-user-password-changes" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether users will be allowed to change their own
      passwords.
    </adm:synopsis>
    <adm:description>
      Indicates whether users will be allowed to change their own
      passwords. This check is made in addition to access control
      evaluation, and therefore both must allow the password change for
      it to occur. Changes to this configuration attribute will take
      effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>true</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.177</ldap:oid>
        <ldap:name>ds-cfg-allow-user-password-changes</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="password-change-requires-current-password"
    mandatory="false" multi-valued="false">
    <adm:synopsis>
      Indicates whether user password changes will be required to use
      the password modify extended operation and include the user's
      current password before the change will be allowed.
    </adm:synopsis>
    <adm:description>
      Indicates whether user password changes will be required to use
      the password modify extended operation and include the user's
      current password before the change will be allowed. Changes to
      this configuration attribute will take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.198</ldap:oid>
        <ldap:name>
          ds-cfg-password-change-requires-current-password
        </ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="force-change-on-add" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether users will be forced to change their passwords
      upon first authenticating to the Directory Server after their
      account has been created.
    </adm:synopsis>
    <adm:description>
      Indicates whether users will be forced to change their passwords
      upon first authenticating to the Directory Server after their
      account has been created. Changes to this configuration attribute
      will take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.208</ldap:oid>
        <ldap:name>ds-cfg-force-change-on-add</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="force-change-on-reset" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether users will be forced to change their passwords
      if they are reset by an administrator.
    </adm:synopsis>
    <adm:description>
      Indicates whether users will be forced to change their passwords
      if they are reset by an administrator. For this purpose, anyone
      with permission to change a given user's password other than that
      user will be considered an administrator. Changes to this
      configuration attribute will take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.181</ldap:oid>
        <ldap:name>ds-cfg-force-change-on-reset</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="skip-validation-for-administrators"
    mandatory="false" multi-valued="false">
    <adm:synopsis>
      Indicates whether passwords set by administrators will be allowed
      to bypass the password validation process that will be required
      for user password changes.
    </adm:synopsis>
    <adm:description>
      Indicates whether passwords set by administrators (in add, modify,
      or password modify operations) will be allowed to bypass the
      password validation process that will be required for user
      password changes. Changes to this configuration attribute will
      take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.201</ldap:oid>
        <ldap:name>ds-cfg-skip-validation-for-administrators</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="password-generator-dn" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the DN of the configuration entry that references the
      password generator for use with the associated password policy.
    </adm:synopsis>
    <adm:description>
      Specifies the DN of the configuration entry that references the
      password generator for use with the associated password policy.
      This will be used in conjunction with the password modify extended
      operation to generate a new password for a user when none was
      provided in the request. Changes to this configuration attribute
      will take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
      <adm:dn />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.194</ldap:oid>
        <ldap:name>ds-cfg-password-generator-dn</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="require-secure-authentication" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether users with the associated password policy will
      be required to authenticate in a secure manner.
    </adm:synopsis>
    <adm:description>
      Indicates whether users with the associated password policy will
      be required to authenticate in a secure manner. This could mean
      either using a secure communication channel between the client and
      the server, or using a SASL mechanism that does not expose the
      credentials. Changes to this configuration attribute will take
      effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.199</ldap:oid>
        <ldap:name>ds-cfg-require-secure-authentication</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="require-secure-password-changes" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether users with the associated password policy will
      be required to change their password in a secure manner that does
      not expose the credentials.
    </adm:synopsis>
    <adm:description>
      Indicates whether users with the associated password policy will
      be required to change their password in a secure manner that does
      not expose the credentials. Changes to this configuration
      attribute will take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.200</ldap:oid>
        <ldap:name>ds-cfg-require-secure-password-changes</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="allow-multiple-password-values" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether user entries will be allowed to have multiple
      distinct values for the password attribute.
    </adm:synopsis>
    <adm:description>
      Indicates whether user entries will be allowed to have multiple
      distinct values for the password attribute. This is potentially
      dangerous because many mechanisms used to change the password do
      not work well with such a configuration. If multiple password
      values are allowed, then any of them may be used to authenticate,
      and they will all be subject to the same policy constraints.
      Changes to this configuration attribute will take effect
      immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.209</ldap:oid>
        <ldap:name>ds-cfg-allow-multiple-password-values</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="allow-pre-encoded-passwords" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      _Indicates whether users will be allowed to change their passwords
      by providing a pre-encoded value.
    </adm:synopsis>
    <adm:description>
      Indicates whether users will be allowed to change their passwords
      by providing a pre-encoded value. This can cause a security risk
      because the clear-text version of the password is not known and
      therefore validation checks cannot be applied to it. Changes to
      this configuration attribute will take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.176</ldap:oid>
        <ldap:name>ds-cfg-allow-pre-encoded-passwords</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="minimum-password-age" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the minimum length of time that must pass after a
      password change before the user will be allowed to change the
      password again.
    </adm:synopsis>
    <adm:description>
      Specifies the minimum length of time that must pass after a
      password change before the user will be allowed to change the
      password again. The value of this attribute should be an integer
      followed by a unit of seconds, minutes, hours, days, or weeks.
      This setting can be used to prevent users from changing their
      passwords repeatedly over a short period of time to flush and old
      password from the history so that it may be re-used. Changes to
      this configuration attribute will take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0 seconds</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.191</ldap:oid>
        <ldap:name>ds-cfg-minimum-password-age</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="maximum-password-age" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum length of time that a user may continue
      using the same password before it must be changed.
    </adm:synopsis>
    <adm:description>
      Specifies the maximum length of time that a user may continue
      using the same password before it must be changed (i.e., the
      password expiration interval). The value of this attribute should
      be an integer followed by a unit of seconds, minutes, hours, days,
      or weeks. A value of 0 seconds will disable password expiration.
      Changes to this configuration attribute will take effect
      immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0 seconds</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.189</ldap:oid>
        <ldap:name>ds-cfg-maximum-password-age</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="maximum-password-reset-age" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum length of time that users have to change
      passwords after they have been reset by an administrator before
      they become locked.
    </adm:synopsis>
    <adm:description>
      Specifies the maximum length of time that users have to change
      passwords after they have been reset by an administrator before
      they become locked. The value of this attribute should be an
      integer followed by a unit of seconds, minutes, hours, days, or
      weeks. A value of 0 seconds will disable this feature. Changes to
      this configuration attribute will take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0 seconds</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.190</ldap:oid>
        <ldap:name>ds-cfg-maximum-password-reset-age</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="password-expiration-warning-interval"
    mandatory="false" multi-valued="false">
    <adm:synopsis>
      Specifies the maximum length of time before a user's password
      actually expires that the server will begin to include warning
      notifications in bind responses for that user.
    </adm:synopsis>
    <adm:description>
      Specifies the maximum length of time before a user's password
      actually expires that the server will begin to include warning
      notifications in bind responses for that user. The value of this
      attribute should be an integer followed by a unit of seconds,
      minutes, hours, days, or weeks. A value of 0 seconds will disable
      the warning interval. Changes to this configuration attribute will
      take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>5 days</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.193</ldap:oid>
        <ldap:name>
          ds-cfg-password-expiration-warning-interval
        </ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="expire-passwords-without-warning"
    mandatory="false" multi-valued="false">
    <adm:synopsis>
      Indicates whether the Directory Server should allow a user's
      password to expire even if that user has never seen an expiration
      warning notification.
    </adm:synopsis>
    <adm:description>
      Indicates whether the Directory Server should allow a user's
      password to expire even if that user has never seen an expiration
      warning notification. If this setting is enabled, then accounts
      will always be expired when the expiration time arrives. If it is
      disabled, then the user will always receive at least one warning
      notification, and the password expiration will be set to the
      warning time plus the warning interval. Changes to this
      configuration attribute will take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.180</ldap:oid>
        <ldap:name>ds-cfg-expire-passwords-without-warning</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="allow-expired-password-changes" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Indicates whether a user whose password is expired will still be
      allowed to change that password using the password modify extended
      operation.
    </adm:synopsis>
    <adm:description>
      Indicates whether a user whose password is expired will still be
      allowed to change that password using the password modify extended
      operation. Changes to this configuration attribute will take
      effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.175</ldap:oid>
        <ldap:name>ds-cfg-allow-expired-password-changes</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="grace-login-count" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the number of grace logins that a user will be allowed
      after the account has expired to allow that user to choose a new
      password.
    </adm:synopsis>
    <adm:description>
      Specifies the number of grace logins that a user will be allowed
      after the account has expired to allow that user to choose a new
      password. A value of 0 indicates that no grace logins will be
      allowed. Changes to this configuration attribute will take effect
      immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer lower-limit="0" upper-limit="2147483647" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.182</ldap:oid>
        <ldap:name>ds-cfg-grace-login-count</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="lockout-failure-count" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum number of authentication failures that a
      user should be allowed before the account is locked out.
    </adm:synopsis>
    <adm:description>
      Specifies the maximum number of authentication failures that a
      user should be allowed before the account is locked out. A value
      of 0 indicates that accounts should never be locked out due to
      failed attempts. changes to this configuration attribute will take
      effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer lower-limit="0" upper-limit="2147483647" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.187</ldap:oid>
        <ldap:name>ds-cfg-lockout-failure-count</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="lockout-duration" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the length of time that an account should be locked
      after too many authentication failures.
    </adm:synopsis>
    <adm:description>
      Specifies the length of time that an account should be locked
      after too many authentication failures. The value of this
      attribute should be an integer followed by a unit of seconds,
      minutes, hours, days, or weeks. A value of 0 seconds indicates
      that the account should remain locked until an administrator
      resets the password. Changes to this configuration attribute will
      take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0 seconds</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.186</ldap:oid>
        <ldap:name>ds-cfg-lockout-duration</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="lockout-failure-expiration-interval"
    mandatory="false" multi-valued="false">
    <adm:synopsis>
      pecifies the length of time that should pass before an
      authentication failure is no longer counted against a user for the
      purposes of account lockout.
    </adm:synopsis>
    <adm:description>
      Specifies the length of time that should pass before an
      authentication failure is no longer counted against a user for the
      purposes of account lockout. The value of this attribute should be
      an integer followed by a unit of seconds, minutes, hours, days, or
      weeks. A value of 0 seconds indicates that the authentication
      failures should never expire. The failure count will always be
      cleared upon a successful authentication. Changes to this
      configuration attribute will take effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0 seconds</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.188</ldap:oid>
        <ldap:name>
          ds-cfg-lockout-failure-expiration-interval
        </ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="require-change-by-time" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the time by which all users with the associated password
      policy must change their passwords.
    </adm:synopsis>
    <adm:description>
      Specifies the time by which all users with the associated password
      policy must change their passwords. The value should be expressed
      in a generalized time format. If this time is equal to the current
      time or is in the past, then all users will be required to change
      their passwords immediately. The behavior of the server in this
      mode will be identical to the behavior observed when users are
      forced to change their passwords after an administrative reset.
      Changes to this configuration attribute will take effect
      immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.197</ldap:oid>
        <ldap:name>ds-cfg-require-change-by-time</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="last-login-time-attribute" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the name or OID of the attribute type that should be
      used to hold the last login time for users with the associated
      password policy.
    </adm:synopsis>
    <adm:description>
      Specifies the name or OID of the attribute type that should be
      used to hold the last login time for users with the associated
      password policy. This attribute type must be defined in the
      Directory Server schema and must either be defined as an
      operational attribute or must be allowed by the set of
      objectClasses for all users with the associated password policy.
      Changes to this configuration attribute will take effect
      immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
      <adm:oid />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.184</ldap:oid>
        <ldap:name>ds-cfg-last-login-time-attribute</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="last-login-time-format" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the format string that should be used to generate the
      last login time value for users with the associated password
      policy.
    </adm:synopsis>
    <adm:description>
      Specifies the format string that should be used to generate the
      last login time value for users with the associated password
      policy. This format string should conform to the syntax described
      in the API documentation for the java.text.SimpleDateFormat class.
      Changes to this configuration attribute will take effect
      immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.185</ldap:oid>
        <ldap:name>ds-cfg-last-login-time-format</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="previous-last-login-time-format" mandatory="false"
    multi-valued="true">
    <adm:synopsis>
      Specifies the format string(s) that may have been used with the
      last login time at any point in the past for users associated with
      the password policy.
    </adm:synopsis>
    <adm:description>
      Specifies the format string(s) that may have been used with the
      last login time at any point in the past for users associated with
      the password policy. These values are used to make it possible to
      parse previous values, but will not be used to set new values.
      These format strings should conform to the syntax described in the
      API documentation for the java.text.SimpleDateFormat class.
      Changes to this configuration attribute will take effect
      immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.196</ldap:oid>
        <ldap:name>ds-cfg-previous-last-login-time-format</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="idle-lockout-interval" mandatory="false"
    multi-valued="false">
    <adm:synopsis>
      Specifies the maximum length of time that an account may remain
      idle (i.e., the associated user does notauthenticate to the
      server) before that user is locked out.
    </adm:synopsis>
    <adm:description>
      Specifies the maximum length of time that an account may remain
      idle (i.e., the associated user does notauthenticate to the
      server) before that user is locked out. The value of this
      attribute should be an integer followed by a unit of seconds,
      minutes, hours, days, or weeks. A value of 0 seconds indicates
      that idle accounts should not automatically be locked out. This
      feature will only be available if the last login time is
      maintained. Changes to this configuration attribute will take
      effect immediately.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0 seconds</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.183</ldap:oid>
        <ldap:name>ds-cfg-idle-lockout-interval</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
@@ -116,5 +116,13 @@
      </ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
  <adm:relation name="password-policy">
    <adm:one-to-many />
    <adm:profile name="ldap">
      <ldap:rdn-sequence>
        cn=Password Policies,cn=config
      </ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
  <adm:product-name>OpenDS Directory Server</adm:product-name>
</adm:root-managed-object>
opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -4932,6 +4932,24 @@
  }
  /**
   * Retrieves the password policy registered for the provided configuration
   * entry.
   *
   * @param  configEntryDN  The DN of the configuration entry for which to
   *                        retrieve the associated password policy.
   *
   * @return  The password policy config registered for the provided
   *          configuration entry, or <CODE>null</CODE> if there is
   *          no such policy.
   */
  public static PasswordPolicyConfig getPasswordPolicyConfig(DN configEntryDN)
  {
    Validator.ensureNotNull(configEntryDN);
    return directoryServer.passwordPolicies.get(configEntryDN);
  }
  /**
   * Registers the provided password policy with the Directory Server.  If a
@@ -4940,14 +4958,13 @@
   *
   * @param  configEntryDN  The DN of the configuration entry that defines the
   *                        password policy.
   * @param  policy         The password policy to register with the server.
   * @param  config         The password policy config to register with the
   *                        server.
   */
  public static void registerPasswordPolicy(DN configEntryDN,
                                            PasswordPolicy policy)
                                            PasswordPolicyConfig config)
  {
    Validator.ensureNotNull(configEntryDN, policy);
    PasswordPolicyConfig config = new PasswordPolicyConfig(policy);
    Validator.ensureNotNull(configEntryDN, config);
    directoryServer.passwordPolicies.put(configEntryDN, config);
  }
@@ -4972,7 +4989,6 @@
    PasswordPolicyConfig config
            = directoryServer.passwordPolicies.remove(configEntryDN);
    if (null != config) config.finalizePasswordPolicyConfig();
  }
opends/src/server/org/opends/server/core/PasswordPolicy.java
@@ -33,24 +33,19 @@
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.SortedSet;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import org.opends.server.admin.std.server.PasswordPolicyCfg;
import org.opends.server.admin.std.server.PasswordValidatorCfg;
import org.opends.server.api.AccountStatusNotificationHandler;
import org.opends.server.api.PasswordGenerator;
import org.opends.server.api.PasswordStorageScheme;
import org.opends.server.api.PasswordValidator;
import org.opends.server.config.BooleanConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.DNConfigAttribute;
import org.opends.server.config.IntegerConfigAttribute;
import org.opends.server.config.IntegerWithUnitConfigAttribute;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.schema.GeneralizedTimeSyntax;
import org.opends.server.types.AttributeType;
@@ -222,7 +217,7 @@
   * provided configuration entry.  Any parameters not included in the provided
   * configuration entry will be assigned server-wide default values.
   *
   * @param  configEntry  The configuration entry with the information to use to
   * @param  configuration  The configuration with the information to use to
   *                      initialize this password policy.
   *
   * @throws  ConfigException  If the provided entry does not contain a valid
@@ -232,7 +227,7 @@
   *                                   password policy that is not related to
   *                                   the server configuration.
   */
  public PasswordPolicy(ConfigEntry configEntry)
  public PasswordPolicy(PasswordPolicyCfg configuration)
         throws ConfigException, InitializationException
  {
    // Create a list of units and values that we can use to represent time
@@ -250,19 +245,15 @@
    timeUnits.put(TIME_UNIT_WEEKS_FULL, (double) (60 * 60 * 24 * 7));
    this.configEntryDN = configEntry.getDN();
    this.configEntryDN = configuration.dn();
    int msgID;
    // Get the password attribute.  If specified, it must have either the
    // user password or auth password syntax.
    int msgID = MSGID_PWPOLICY_DESCRIPTION_PW_ATTR;
    StringConfigAttribute pwAttrStub =
         new StringConfigAttribute(ATTR_PWPOLICY_PASSWORD_ATTRIBUTE,
                                   getMessage(msgID), false, false, false);
    String passwordAttr = configuration.getPasswordAttribute();
    try
    {
      StringConfigAttribute pwAttrAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(pwAttrStub);
      if (pwAttrAttr == null)
      if (passwordAttr == null)
      {
        this.passwordAttribute  = null;
        this.authPasswordSyntax = false;
@@ -273,13 +264,13 @@
      }
      else
      {
        String lowerName = toLowerCase(pwAttrAttr.pendingValue());
        String lowerName = toLowerCase(passwordAttr);
        AttributeType pwAttrType = DirectoryServer.getAttributeType(lowerName);
        if (pwAttrType == null)
        {
          msgID = MSGID_PWPOLICY_UNDEFINED_PASSWORD_ATTRIBUTE;
          String message = getMessage(msgID, String.valueOf(configEntryDN),
                                String.valueOf(pwAttrAttr.pendingValue()));
                                String.valueOf(passwordAttr));
          throw new ConfigException(msgID, message);
        }
@@ -304,7 +295,7 @@
          msgID = MSGID_PWPOLICY_INVALID_PASSWORD_ATTRIBUTE_SYNTAX;
          String message = getMessage(msgID, String.valueOf(configEntryDN),
                                      String.valueOf(pwAttrAttr.pendingValue()),
                                      String.valueOf(passwordAttr),
                                      String.valueOf(syntax));
          throw new ConfigException(msgID, message);
        }
@@ -330,16 +321,11 @@
    // Get the default storage schemes.  They must all reference valid storage
    // schemes that support the syntax for the specified password attribute.
    msgID = MSGID_PWPOLICY_DESCRIPTION_DEFAULT_STORAGE_SCHEMES;
    StringConfigAttribute defaultSchemeStub =
           new StringConfigAttribute(ATTR_PWPOLICY_DEFAULT_SCHEME,
                                     getMessage(msgID), false, true, false);
    SortedSet<String> storageSchemes =
      configuration.getDefaultPasswordStorageScheme();
    try
    {
      StringConfigAttribute defaultSchemeAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(defaultSchemeStub);
      if (defaultSchemeAttr == null)
      if (storageSchemes == null)
      {
        msgID = MSGID_PWPOLICY_NO_DEFAULT_STORAGE_SCHEMES;
        String message = getMessage(msgID, String.valueOf(configEntryDN));
@@ -349,7 +335,7 @@
      {
        LinkedList<PasswordStorageScheme> schemes =
             new LinkedList<PasswordStorageScheme>();
        for (String schemeName : defaultSchemeAttr.pendingValues())
        for (String schemeName : storageSchemes)
        {
          PasswordStorageScheme scheme;
          if (this.authPasswordSyntax)
@@ -398,20 +384,14 @@
    // Get the names of the deprecated storage schemes.
    msgID = MSGID_PWPOLICY_DESCRIPTION_DEPRECATED_STORAGE_SCHEMES;
    StringConfigAttribute deprecatedSchemeStub =
         new StringConfigAttribute(ATTR_PWPOLICY_DEPRECATED_SCHEME,
                                   getMessage(msgID), false, true, false);
    SortedSet<String> deprecatedStorageSchemes =
      configuration.getDeprecatedPasswordStorageScheme();
    try
    {
      StringConfigAttribute deprecatedSchemeAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(deprecatedSchemeStub);
      if (deprecatedSchemeAttr != null)
      if (deprecatedStorageSchemes != null)
      {
        this.deprecatedStorageSchemes =
             new CopyOnWriteArraySet<String>(
                      deprecatedSchemeAttr.pendingValues());
             new CopyOnWriteArraySet<String>(deprecatedStorageSchemes);
      }
    }
    catch (Exception e)
@@ -429,15 +409,11 @@
    // Get the password validators.
    msgID = MSGID_PWPOLICY_DESCRIPTION_PASSWORD_VALIDATORS;
    DNConfigAttribute validatorStub =
         new DNConfigAttribute(ATTR_PWPOLICY_PASSWORD_VALIDATOR,
                               getMessage(msgID), false, true, false);
    SortedSet<DN> passwordValidators =
      configuration.getPasswordValidatorDN();
    try
    {
      DNConfigAttribute validatorAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(validatorStub);
      if (validatorAttr != null)
      if (passwordValidators != null)
      {
        ConcurrentHashMap<DN,
             PasswordValidator<? extends PasswordValidatorCfg>>
@@ -445,7 +421,7 @@
                  new ConcurrentHashMap<DN,
                       PasswordValidator<? extends
                            PasswordValidatorCfg>>();
        for (DN validatorDN : validatorAttr.pendingValues())
        for (DN validatorDN : passwordValidators)
        {
          PasswordValidator<? extends PasswordValidatorCfg>
               validator = DirectoryServer.getPasswordValidator(validatorDN);
@@ -482,19 +458,15 @@
    // Get the status notification handlers.
    msgID = MSGID_PWPOLICY_DESCRIPTION_NOTIFICATION_HANDLERS;
    DNConfigAttribute notificationStub =
         new DNConfigAttribute(ATTR_PWPOLICY_NOTIFICATION_HANDLER,
                               getMessage(msgID), false, true, false);
    SortedSet<DN> statusNotificationHandlers =
      configuration.getAccountStatusNotificationHandlerDN();
    try
    {
      DNConfigAttribute notificationAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(notificationStub);
      if (notificationAttr != null)
      if (statusNotificationHandlers != null)
      {
        ConcurrentHashMap<DN,AccountStatusNotificationHandler> handlers =
             new ConcurrentHashMap<DN,AccountStatusNotificationHandler>();
        for (DN handlerDN : notificationAttr.pendingValues())
        for (DN handlerDN : statusNotificationHandlers)
        {
          AccountStatusNotificationHandler handler =
               DirectoryServer.getAccountStatusNotificationHandler(handlerDN);
@@ -531,173 +503,39 @@
    // Determine whether to allow user password changes.
    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_USER_PW_CHANGES;
    BooleanConfigAttribute userChangeStub =
         new BooleanConfigAttribute(ATTR_PWPOLICY_ALLOW_USER_CHANGE,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute userChangeAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(userChangeStub);
      if (userChangeAttr != null)
      {
        this.allowUserPasswordChanges = userChangeAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_ALLOW_USER_PW_CHANGES;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.allowUserPasswordChanges = configuration.isAllowUserPasswordChanges();
    // Determine whether to require the current password for user changes.
    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_CURRENT_PW;
    BooleanConfigAttribute requirePWStub =
         new BooleanConfigAttribute(ATTR_PWPOLICY_REQUIRE_CURRENT_PASSWORD,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute requirePWAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(requirePWStub);
      if (requirePWAttr != null)
      {
        this.requireCurrentPassword = requirePWAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_REQUIRE_CURRENT_PW;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.requireCurrentPassword =
      configuration.isPasswordChangeRequiresCurrentPassword();
    // Determine whether to force password changes on add.
    msgID = MSGID_PWPOLICY_DESCRIPTION_FORCE_CHANGE_ON_ADD;
    BooleanConfigAttribute forceChangeOnAddStub =
         new BooleanConfigAttribute(ATTR_PWPOLICY_FORCE_CHANGE_ON_ADD,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute forceChangeOnAddAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(forceChangeOnAddStub);
      if (forceChangeOnAddAttr != null)
      {
        this.forceChangeOnAdd = forceChangeOnAddAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_FORCE_CHANGE_ON_ADD;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.forceChangeOnAdd = configuration.isForceChangeOnAdd();
    // Determine whether to force password changes on reset.
    msgID = MSGID_PWPOLICY_DESCRIPTION_FORCE_CHANGE_ON_RESET;
    BooleanConfigAttribute forceChangeOnResetStub =
      new BooleanConfigAttribute(ATTR_PWPOLICY_FORCE_CHANGE_ON_RESET,
                                 getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute forceChangeAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(forceChangeOnResetStub);
      if (forceChangeAttr != null)
      {
        this.forceChangeOnReset = forceChangeAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_FORCE_CHANGE_ON_RESET;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.forceChangeOnReset = configuration.isForceChangeOnReset();
    // Determine whether to validate reset passwords.
    msgID = MSGID_PWPOLICY_DESCRIPTION_SKIP_ADMIN_VALIDATION;
    BooleanConfigAttribute validateResetStub =
         new BooleanConfigAttribute(ATTR_PWPOLICY_SKIP_ADMIN_VALIDATION,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute validateResetAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(validateResetStub);
      if (validateResetAttr != null)
      {
        this.skipValidationForAdministrators =
             validateResetAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_SKIP_ADMIN_VALIDATION;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.skipValidationForAdministrators =
      configuration.isSkipValidationForAdministrators();
    // Get the password generator.
    msgID = MSGID_PWPOLICY_DESCRIPTION_PASSWORD_GENERATOR;
    DNConfigAttribute generatorStub =
         new DNConfigAttribute(ATTR_PWPOLICY_PASSWORD_GENERATOR,
                               getMessage(msgID), false, false, false);
    DN passGenDN = configuration.getPasswordGeneratorDN() ;
    try
    {
      DNConfigAttribute generatorAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(generatorStub);
      if (generatorAttr != null)
      if (passGenDN != null)
      {
        PasswordGenerator generator =
             DirectoryServer.getPasswordGenerator(generatorAttr.pendingValue());
             DirectoryServer.getPasswordGenerator(passGenDN);
        if (generator == null)
        {
          msgID = MSGID_PWPOLICY_NO_SUCH_GENERATOR;
          String message = getMessage(msgID, String.valueOf(configEntryDN),
                                String.valueOf(generatorAttr.pendingValue()));
                                String.valueOf(passGenDN));
          throw new ConfigException(msgID, message);
        }
        this.passwordGeneratorDN = generatorAttr.pendingValue();
        this.passwordGeneratorDN = passGenDN;
        this.passwordGenerator   = generator;
      }
    }
@@ -720,272 +558,37 @@
    // Determine whether to require secure authentication.
    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_SECURE_AUTH;
    BooleanConfigAttribute secureAuthStub =
         new BooleanConfigAttribute(ATTR_PWPOLICY_REQUIRE_SECURE_AUTHENTICATION,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute secureAuthAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(secureAuthStub);
      if (secureAuthAttr != null)
      {
        this.requireSecureAuthentication = secureAuthAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_REQUIRE_SECURE_AUTH;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.requireSecureAuthentication =
      configuration.isRequireSecureAuthentication();
    // Determine whether to require secure password changes.
    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_SECURE_CHANGES;
    BooleanConfigAttribute secureChangeStub =
         new BooleanConfigAttribute(
                  ATTR_PWPOLICY_REQUIRE_SECURE_PASSWORD_CHANGES,
                  getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute secureChangeAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(secureChangeStub);
      if (secureChangeAttr != null)
      {
        this.requireSecurePasswordChanges = secureChangeAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_REQUIRE_SECURE_CHANGES;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.requireSecurePasswordChanges =
      configuration.isRequireSecurePasswordChanges() ;
    // Determine whether to allow multiple password values.
    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_MULTIPLE_PW_VALUES;
    BooleanConfigAttribute allowMultiplePWStub =
         new BooleanConfigAttribute(ATTR_PWPOLICY_ALLOW_MULTIPLE_PW_VALUES,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute allowMultiplePWAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(allowMultiplePWStub);
      if (allowMultiplePWAttr != null)
      {
        this.allowMultiplePasswordValues = allowMultiplePWAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_ALLOW_MULTIPLE_PW_VALUES;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.allowMultiplePasswordValues =
      configuration.isAllowMultiplePasswordValues();
    // Determine whether to allow pre-encoded passwords.
    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_PREENCODED;
    BooleanConfigAttribute preEncodedStub =
         new BooleanConfigAttribute(ATTR_PWPOLICY_ALLOW_PRE_ENCODED_PASSWORDS,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute preEncodedAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(preEncodedStub);
      if (preEncodedAttr != null)
      {
        this.allowPreEncodedPasswords = preEncodedAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_ALLOW_PREENCODED;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.allowPreEncodedPasswords = configuration.isAllowPreEncodedPasswords();
    // Get the minimum password age.
    msgID = MSGID_PWPOLICY_DESCRIPTION_MIN_AGE;
    IntegerWithUnitConfigAttribute minAgeStub =
         new IntegerWithUnitConfigAttribute(ATTR_PWPOLICY_MINIMUM_PASSWORD_AGE,
                                            getMessage(msgID), false, timeUnits,
                                            true, 0, true, Integer.MAX_VALUE);
    try
    {
      IntegerWithUnitConfigAttribute minAgeAttr =
           (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(minAgeStub);
      if (minAgeAttr != null)
      {
        this.minimumPasswordAge = (int) minAgeAttr.pendingCalculatedValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_MIN_AGE;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.minimumPasswordAge = (int) configuration.getMinimumPasswordAge();
    // Get the maximum password age.
    msgID = MSGID_PWPOLICY_DESCRIPTION_MAX_AGE;
    IntegerWithUnitConfigAttribute maxAgeStub =
         new IntegerWithUnitConfigAttribute(ATTR_PWPOLICY_MAXIMUM_PASSWORD_AGE,
                                            getMessage(msgID), false, timeUnits,
                                            true, 0, true, Integer.MAX_VALUE);
    try
    {
      IntegerWithUnitConfigAttribute maxAgeAttr =
           (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(maxAgeStub);
      if (maxAgeAttr != null)
      {
        this.maximumPasswordAge = (int) maxAgeAttr.pendingCalculatedValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_MAX_AGE;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.maximumPasswordAge = (int) configuration.getMaximumPasswordAge();
    // Get the maximum password reset age.
    msgID = MSGID_PWPOLICY_DESCRIPTION_MAX_RESET_AGE;
    IntegerWithUnitConfigAttribute maxResetStub =
         new IntegerWithUnitConfigAttribute(
                  ATTR_PWPOLICY_MAXIMUM_PASSWORD_RESET_AGE, getMessage(msgID),
                  false, timeUnits, true, 0, true, Integer.MAX_VALUE);
    try
    {
      IntegerWithUnitConfigAttribute maxResetAttr =
           (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(maxResetStub);
      if (maxResetAttr != null)
      {
        this.maximumPasswordResetAge =
             (int) maxResetAttr.pendingCalculatedValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_MAX_RESET_AGE;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.maximumPasswordResetAge = (int) configuration
        .getMaximumPasswordResetAge();
    // Get the warning interval.
    msgID = MSGID_PWPOLICY_DESCRIPTION_WARNING_INTERVAL;
    IntegerWithUnitConfigAttribute warningStub =
         new IntegerWithUnitConfigAttribute(ATTR_PWPOLICY_WARNING_INTERVAL,
                                            getMessage(msgID), false, timeUnits,
                                            true, 0, true, Integer.MAX_VALUE);
    try
    {
      IntegerWithUnitConfigAttribute warningAttr =
           (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(warningStub);
      if (warningAttr != null)
      {
        this.warningInterval = (int) warningAttr.pendingCalculatedValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_WARNING_INTERVAL;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.warningInterval = (int) configuration
        .getPasswordExpirationWarningInterval();
    // Determine whether to expire passwords without warning.
    msgID = MSGID_PWPOLICY_DESCRIPTION_EXPIRE_WITHOUT_WARNING;
    BooleanConfigAttribute expireWithoutWarningStub =
         new BooleanConfigAttribute(ATTR_PWPOLICY_EXPIRE_WITHOUT_WARNING,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute expireWithoutWarningAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(expireWithoutWarningStub);
      if (expireWithoutWarningAttr != null)
      {
        this.expirePasswordsWithoutWarning =
             expireWithoutWarningAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_EXPIRE_WITHOUT_WARNING;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.expirePasswordsWithoutWarning = configuration
        .isExpirePasswordsWithoutWarning();
    // If the expire without warning option is disabled, then there must be a
    // warning interval.
@@ -997,173 +600,30 @@
      throw new ConfigException(msgID, message);
    }
    // Determine whether to allow user changes for expired passwords.
    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_EXPIRED_CHANGES;
    BooleanConfigAttribute allowExpiredChangesStub =
         new BooleanConfigAttribute(ATTR_PWPOLICY_ALLOW_EXPIRED_CHANGES,
                                    getMessage(msgID), false);
    try
    {
      BooleanConfigAttribute allowExpiredChangesAttr =
           (BooleanConfigAttribute)
           configEntry.getConfigAttribute(allowExpiredChangesStub);
      if (allowExpiredChangesAttr != null)
      {
        this.allowExpiredPasswordChanges =
             allowExpiredChangesAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_ALLOW_EXPIRED_CHANGES;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.allowExpiredPasswordChanges = configuration
        .isAllowExpiredPasswordChanges();
    // Get the grace login count.
    msgID = MSGID_PWPOLICY_DESCRIPTION_GRACE_LOGIN_COUNT;
    IntegerConfigAttribute graceStub =
         new IntegerConfigAttribute(ATTR_PWPOLICY_GRACE_LOGIN_COUNT,
                                    getMessage(msgID), false, false, false,
                                    true, 0, true, Integer.MAX_VALUE);
    try
    {
      IntegerConfigAttribute graceAttr =
           (IntegerConfigAttribute) configEntry.getConfigAttribute(graceStub);
      if (graceAttr != null)
      {
        this.graceLoginCount = graceAttr.pendingIntValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_GRACE_LOGIN_COUNT;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.graceLoginCount = configuration.getGraceLoginCount();
    // Get the lockout failure count.
    msgID = MSGID_PWPOLICY_DESCRIPTION_LOCKOUT_FAILURE_COUNT;
    IntegerConfigAttribute failureCountStub =
         new IntegerConfigAttribute(ATTR_PWPOLICY_LOCKOUT_FAILURE_COUNT,
                                    getMessage(msgID), false, false, false,
                                    true, 0, true, Integer.MAX_VALUE);
    try
    {
      IntegerConfigAttribute failureCountAttr =
           (IntegerConfigAttribute)
           configEntry.getConfigAttribute(failureCountStub);
      if (failureCountAttr != null)
      {
        this.lockoutFailureCount = failureCountAttr.pendingIntValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_LOCKOUT_FAILURE_COUNT;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.lockoutFailureCount = configuration.getLockoutFailureCount();
    // Get the lockout duration.
    msgID = MSGID_PWPOLICY_DESCRIPTION_LOCKOUT_DURATION;
    IntegerWithUnitConfigAttribute lockoutDurationStub =
         new IntegerWithUnitConfigAttribute(ATTR_PWPOLICY_LOCKOUT_DURATION,
                                            getMessage(msgID), false, timeUnits,
                                            true, 0, true, Integer.MAX_VALUE);
    try
    {
      IntegerWithUnitConfigAttribute lockoutDurationAttr =
           (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(lockoutDurationStub);
      if (lockoutDurationAttr != null)
      {
        this.lockoutDuration =
             (int) lockoutDurationAttr.pendingCalculatedValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_LOCKOUT_DURATION;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.lockoutDuration = (int) configuration.getLockoutDuration();
    // Get the lockout failure expiration interval.
    msgID = MSGID_PWPOLICY_DESCRIPTION_FAILURE_EXPIRATION;
    IntegerWithUnitConfigAttribute failureExpirationStub =
         new IntegerWithUnitConfigAttribute(
                  ATTR_PWPOLICY_LOCKOUT_FAILURE_EXPIRATION_INTERVAL,
                  getMessage(msgID), false, timeUnits, true, 0, true,
                  Integer.MAX_VALUE);
    try
    {
      IntegerWithUnitConfigAttribute failureExpirationAttr =
           (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(failureExpirationStub);
      if (failureExpirationAttr != null)
      {
        this.lockoutFailureExpirationInterval =
             (int) failureExpirationAttr.pendingCalculatedValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_FAILURE_EXPIRATION;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.lockoutFailureExpirationInterval = (int) configuration
        .getLockoutFailureExpirationInterval();
    // Get the required change time.
    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_CHANGE_BY_TIME;
    StringConfigAttribute requireChangeByStub =
         new StringConfigAttribute(ATTR_PWPOLICY_REQUIRE_CHANGE_BY_TIME,
                                   getMessage(msgID), false, false, false);
    String requireChangeBy = configuration.getRequireChangeByTime();
    try
    {
      StringConfigAttribute requireChangeByAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(requireChangeByStub);
      if (requireChangeByAttr != null)
      if (requireChangeBy != null)
      {
        ByteString valueString = new
             ASN1OctetString(requireChangeByAttr.pendingValue());
        ByteString valueString = new ASN1OctetString(requireChangeBy);
        GeneralizedTimeSyntax syntax =
             (GeneralizedTimeSyntax)
@@ -1202,25 +662,19 @@
    // the server schema.  It does not need to have a generalized time syntax
    // because the value that it will store will not necessarily conform to this
    // format.
    msgID = MSGID_PWPOLICY_DESCRIPTION_LAST_LOGIN_TIME_ATTR;
    StringConfigAttribute lastLoginAttrStub =
         new StringConfigAttribute(ATTR_PWPOLICY_LAST_LOGIN_TIME_ATTRIBUTE,
                                   getMessage(msgID), false, false, false);
    String lastLoginTimeAtt = configuration.getLastLoginTimeAttribute();
    try
    {
      StringConfigAttribute lastLoginAttrAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(lastLoginAttrStub);
      if (lastLoginAttrAttr != null)
      if (lastLoginTimeAtt != null)
      {
        String lowerName = toLowerCase(lastLoginAttrAttr.pendingValue());
        String lowerName = toLowerCase(lastLoginTimeAtt);
        AttributeType attrType = DirectoryServer.getAttributeType(lowerName);
        if (attrType == null)
        {
          msgID = MSGID_PWPOLICY_UNDEFINED_LAST_LOGIN_TIME_ATTRIBUTE;
          String message =
               getMessage(msgID, String.valueOf(configEntryDN),
                          String.valueOf(lastLoginAttrAttr.pendingValue()));
                          String.valueOf(lastLoginTimeAtt));
          throw new ConfigException(msgID, message);
        }
@@ -1246,19 +700,11 @@
    // Get the last login time format.  If specified, it must be a valid format
    // string.
    msgID = MSGID_PWPOLICY_DESCRIPTION_LAST_LOGIN_TIME_FORMAT;
    StringConfigAttribute lastLoginFormatStub =
         new StringConfigAttribute(ATTR_PWPOLICY_LAST_LOGIN_TIME_FORMAT,
                                   getMessage(msgID), false, false, false);
    String formatString = configuration.getLastLoginTimeFormat();
    try
    {
      StringConfigAttribute lastLoginFormatAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(lastLoginFormatStub);
      if (lastLoginFormatAttr != null)
      if (formatString != null)
      {
        String formatString = lastLoginFormatAttr.pendingValue();
        try
        {
          new SimpleDateFormat(formatString);
@@ -1299,19 +745,12 @@
    // Get the previous last login time formats.  If specified, they must all
    // be valid format strings.
    msgID = MSGID_PWPOLICY_DESCRIPTION_PREVIOUS_LAST_LOGIN_TIME_FORMAT;
    StringConfigAttribute previousFormatStub =
         new StringConfigAttribute(
                  ATTR_PWPOLICY_PREVIOUS_LAST_LOGIN_TIME_FORMAT,
                  getMessage(msgID), false, true, false);
    SortedSet<String> formatStrings =
      configuration.getPreviousLastLoginTimeFormat() ;
    try
    {
      StringConfigAttribute previousFormatAttr =
             (StringConfigAttribute)
             configEntry.getConfigAttribute(previousFormatStub);
      if (previousFormatAttr != null)
      if (formatStrings != null)
      {
        List<String> formatStrings = previousFormatAttr.pendingValues();
        for (String s : formatStrings)
        {
          try
@@ -1355,35 +794,7 @@
    // Get the idle lockout duration.
    msgID = MSGID_PWPOLICY_DESCRIPTION_IDLE_LOCKOUT_INTERVAL;
    IntegerWithUnitConfigAttribute idleIntervalStub =
         new IntegerWithUnitConfigAttribute(ATTR_PWPOLICY_IDLE_LOCKOUT_INTERVAL,
                                            getMessage(msgID), false, timeUnits,
                                            true, 0, true, Integer.MAX_VALUE);
    try
    {
      IntegerWithUnitConfigAttribute idleIntervalAttr =
           (IntegerWithUnitConfigAttribute)
           configEntry.getConfigAttribute(idleIntervalStub);
      if (idleIntervalAttr != null)
      {
        this.idleLockoutInterval =
             (int) idleIntervalAttr.pendingCalculatedValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_PWPOLICY_CANNOT_DETERMINE_IDLE_LOCKOUT_INTERVAL;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  getExceptionMessage(e));
      throw new InitializationException(msgID, message, e);
    }
    this.idleLockoutInterval = (int) configuration.getIdleLockoutInterval();
    /*
     *  Holistic validation.
opends/src/server/org/opends/server/core/PasswordPolicyConfig.java
@@ -27,33 +27,20 @@
package org.opends.server.core;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.api.PasswordStorageScheme;
import org.opends.server.config.BooleanConfigAttribute;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.PasswordPolicyCfg;
import org.opends.server.config.ConfigException;
import org.opends.server.config.DNConfigAttribute;
import org.opends.server.config.IntegerConfigAttribute;
import org.opends.server.config.IntegerWithUnitConfigAttribute;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.schema.GeneralizedTimeSyntax;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import org.opends.server.types.DebugLogLevel;
import static org.opends.server.messages.CoreMessages.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.util.ServerConstants.*;
/**
 This class is the interface between the password policy configurable component
@@ -63,7 +50,7 @@
 valiadating any proposed modification and applying an accepted modification.
 */
public class PasswordPolicyConfig
        implements ConfigurableComponent
        implements ConfigurationChangeListener<PasswordPolicyCfg>
{
  /**
@@ -85,325 +72,22 @@
  public PasswordPolicyConfig(PasswordPolicy policy)
  {
    this.currentPolicy = policy;
    DirectoryServer.registerConfigurableComponent(this);
  }
  /**
   * Finalize a password policy configuration handler.
   * {@inheritDoc}
   */
  public void finalizePasswordPolicyConfig()
  public boolean isConfigurationChangeAcceptable(
      PasswordPolicyCfg configuration, List<String> unacceptableReasons)
  {
    DirectoryServer.deregisterConfigurableComponent(this);
  }
  /**
   * Retrieves the DN of the configuration entry with which this component is
   * associated.
   *
   * @return  The DN of the configuration entry with which this component is
   *          associated.
   */
  public DN getConfigurableComponentEntryDN()
  {
    return currentPolicy.getConfigEntryDN();
  }
  /**
   * Retrieves the set of configuration attributes that are associated with this
   * configurable component.
   *
   * @return  The set of configuration attributes that are associated with this
   *          configurable component.
   */
  public List<ConfigAttribute> getConfigurationAttributes()
  {
    // Create a list of units and values that we can use to represent time
    // periods.
    LinkedHashMap<String,Double> timeUnits = new LinkedHashMap<String,Double>();
    timeUnits.put(TIME_UNIT_SECONDS_ABBR, 1D);
    timeUnits.put(TIME_UNIT_SECONDS_FULL, 1D);
    timeUnits.put(TIME_UNIT_MINUTES_ABBR, 60D);
    timeUnits.put(TIME_UNIT_MINUTES_FULL, 60D);
    timeUnits.put(TIME_UNIT_HOURS_ABBR, (double) (60 * 60));
    timeUnits.put(TIME_UNIT_HOURS_FULL, (double) (60 * 60));
    timeUnits.put(TIME_UNIT_DAYS_ABBR, (double) (60 * 60 * 24));
    timeUnits.put(TIME_UNIT_DAYS_FULL, (double) (60 * 60 * 24));
    timeUnits.put(TIME_UNIT_WEEKS_ABBR, (double) (60 * 60 * 24 * 7));
    timeUnits.put(TIME_UNIT_WEEKS_FULL, (double) (60 * 60 * 24 * 7));
    PasswordPolicy policy = this.currentPolicy; // this field is volatile
    LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>();
    int msgID = MSGID_PWPOLICY_DESCRIPTION_PW_ATTR;
    String pwAttr = (policy.getPasswordAttribute() == null)
                    ? null
                    : policy.getPasswordAttribute().getNameOrOID();
    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_PASSWORD_ATTRIBUTE,
                                           getMessage(msgID), false, false,
                                           false, pwAttr));
    msgID = MSGID_PWPOLICY_DESCRIPTION_DEFAULT_STORAGE_SCHEMES;
    ArrayList<String> schemes = new ArrayList<String>();
    for (PasswordStorageScheme s : policy.getDefaultStorageSchemes())
    {
      schemes.add(s.getStorageSchemeName());
    }
    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_DEFAULT_SCHEME,
                                           getMessage(msgID), false, true,
                                           false, schemes));
    msgID = MSGID_PWPOLICY_DESCRIPTION_DEPRECATED_STORAGE_SCHEMES;
    ArrayList<String> deprecatedSchemes = new ArrayList<String>();
    deprecatedSchemes.addAll(policy.getDeprecatedStorageSchemes());
    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_DEPRECATED_SCHEME,
                                           getMessage(msgID), false, true,
                                           false, deprecatedSchemes));
    msgID = MSGID_PWPOLICY_DESCRIPTION_PASSWORD_VALIDATORS;
    ArrayList<DN> validatorDNs = new ArrayList<DN>();
    validatorDNs.addAll(policy.getPasswordValidators().keySet());
    attrList.add(new DNConfigAttribute(ATTR_PWPOLICY_PASSWORD_VALIDATOR,
                                       getMessage(msgID), false, true, false,
                                       validatorDNs));
    msgID = MSGID_PWPOLICY_DESCRIPTION_NOTIFICATION_HANDLERS;
    ArrayList<DN> handlerDNs = new ArrayList<DN>();
    handlerDNs.addAll(policy.getAccountStatusNotificationHandlers().keySet());
    attrList.add(new DNConfigAttribute(ATTR_PWPOLICY_NOTIFICATION_HANDLER,
                                       getMessage(msgID), false, true, false,
                                       handlerDNs));
    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_USER_PW_CHANGES;
    attrList.add(new BooleanConfigAttribute(ATTR_PWPOLICY_ALLOW_USER_CHANGE,
                                            getMessage(msgID), false,
                                            policy.allowUserPasswordChanges()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_CURRENT_PW;
    attrList.add(new BooleanConfigAttribute(
                             ATTR_PWPOLICY_REQUIRE_CURRENT_PASSWORD,
                             getMessage(msgID), false,
                             policy.requireCurrentPassword()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_FORCE_CHANGE_ON_ADD;
    attrList.add(new BooleanConfigAttribute(ATTR_PWPOLICY_FORCE_CHANGE_ON_ADD,
                                            getMessage(msgID), false,
                                            policy.forceChangeOnAdd()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_FORCE_CHANGE_ON_RESET;
    attrList.add(new BooleanConfigAttribute(ATTR_PWPOLICY_FORCE_CHANGE_ON_RESET,
                                            getMessage(msgID), false,
                                            policy.forceChangeOnReset()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_SKIP_ADMIN_VALIDATION;
    attrList.add(new BooleanConfigAttribute(
                             ATTR_PWPOLICY_SKIP_ADMIN_VALIDATION,
                             getMessage(msgID), false,
                             policy.skipValidationForAdministrators()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_PASSWORD_GENERATOR;
    attrList.add(new DNConfigAttribute(ATTR_PWPOLICY_PASSWORD_GENERATOR,
                                       getMessage(msgID), false, false, false,
                                       policy.getPasswordGeneratorDN()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_SECURE_AUTH;
    attrList.add(new BooleanConfigAttribute(
                             ATTR_PWPOLICY_REQUIRE_SECURE_AUTHENTICATION,
                             getMessage(msgID), false,
                             policy.requireSecureAuthentication()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_SECURE_CHANGES;
    attrList.add(new BooleanConfigAttribute(
                             ATTR_PWPOLICY_REQUIRE_SECURE_PASSWORD_CHANGES,
                             getMessage(msgID), false,
                             policy.requireSecurePasswordChanges()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_MULTIPLE_PW_VALUES;
    attrList.add(new BooleanConfigAttribute(
                             ATTR_PWPOLICY_ALLOW_MULTIPLE_PW_VALUES,
                             getMessage(msgID), false,
                             policy.allowMultiplePasswordValues()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_PREENCODED;
    attrList.add(new BooleanConfigAttribute(
                             ATTR_PWPOLICY_ALLOW_PRE_ENCODED_PASSWORDS,
                             getMessage(msgID), false,
                             policy.allowPreEncodedPasswords()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_MIN_AGE;
    attrList.add(new IntegerWithUnitConfigAttribute(
                          ATTR_PWPOLICY_MINIMUM_PASSWORD_AGE,
                          getMessage(msgID), false, timeUnits, true, 0, true,
                          Integer.MAX_VALUE, policy.getMinimumPasswordAge(),
                          TIME_UNIT_SECONDS_FULL));
    msgID = MSGID_PWPOLICY_DESCRIPTION_MAX_AGE;
    attrList.add(new IntegerWithUnitConfigAttribute(
                          ATTR_PWPOLICY_MAXIMUM_PASSWORD_AGE,
                          getMessage(msgID), false, timeUnits, true, 0, true,
                          Integer.MAX_VALUE, policy.getMaximumPasswordAge(),
                          TIME_UNIT_SECONDS_FULL));
    msgID = MSGID_PWPOLICY_DESCRIPTION_MAX_RESET_AGE;
    attrList.add(new IntegerWithUnitConfigAttribute(
                          ATTR_PWPOLICY_MAXIMUM_PASSWORD_RESET_AGE,
                          getMessage(msgID), false, timeUnits, true, 0, true,
                          Integer.MAX_VALUE,
                          policy.getMaximumPasswordResetAge(),
                          TIME_UNIT_SECONDS_FULL));
    msgID = MSGID_PWPOLICY_DESCRIPTION_WARNING_INTERVAL;
    attrList.add(new IntegerWithUnitConfigAttribute(
                          ATTR_PWPOLICY_WARNING_INTERVAL, getMessage(msgID),
                          false, timeUnits, true, 0, true, Integer.MAX_VALUE,
                          policy.getWarningInterval(), TIME_UNIT_SECONDS_FULL));
    msgID = MSGID_PWPOLICY_DESCRIPTION_EXPIRE_WITHOUT_WARNING;
    attrList.add(new BooleanConfigAttribute(
                          ATTR_PWPOLICY_EXPIRE_WITHOUT_WARNING,
                          getMessage(msgID), false,
                          policy.expirePasswordsWithoutWarning()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_ALLOW_EXPIRED_CHANGES;
    attrList.add(new BooleanConfigAttribute(
                          ATTR_PWPOLICY_ALLOW_EXPIRED_CHANGES,
                          getMessage(msgID), false,
                          policy.allowExpiredPasswordChanges()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_GRACE_LOGIN_COUNT;
    attrList.add(new IntegerConfigAttribute(ATTR_PWPOLICY_GRACE_LOGIN_COUNT,
                                            getMessage(msgID), false, false,
                                            false, true, 0, true,
                                            Integer.MAX_VALUE,
                                            policy.getGraceLoginCount()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_LOCKOUT_FAILURE_COUNT;
    attrList.add(new IntegerConfigAttribute(ATTR_PWPOLICY_LOCKOUT_FAILURE_COUNT,
                                            getMessage(msgID), false, false,
                                            false, true, 0, true,
                                            Integer.MAX_VALUE,
                                            policy.getLockoutFailureCount()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_LOCKOUT_DURATION;
    attrList.add(new IntegerWithUnitConfigAttribute(
                          ATTR_PWPOLICY_LOCKOUT_DURATION, getMessage(msgID),
                          false, timeUnits, true, 0, true, Integer.MAX_VALUE,
                          policy.getLockoutDuration(), TIME_UNIT_SECONDS_FULL));
    msgID = MSGID_PWPOLICY_DESCRIPTION_FAILURE_EXPIRATION;
    attrList.add(new IntegerWithUnitConfigAttribute(
                          ATTR_PWPOLICY_LOCKOUT_FAILURE_EXPIRATION_INTERVAL,
                          getMessage(msgID), false, timeUnits, true, 0, true,
                          Integer.MAX_VALUE,
                          policy.getLockoutFailureExpirationInterval(),
                          TIME_UNIT_SECONDS_FULL));
    msgID = MSGID_PWPOLICY_DESCRIPTION_REQUIRE_CHANGE_BY_TIME;
    String timeStr = null;
    if (policy.getRequireChangeByTime() > 0)
    {
      timeStr = GeneralizedTimeSyntax.createGeneralizedTimeValue(
                     policy.getRequireChangeByTime()).getStringValue();
    }
    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_REQUIRE_CHANGE_BY_TIME,
                                           getMessage(msgID), false, false,
                                           false, timeStr));
    msgID = MSGID_PWPOLICY_DESCRIPTION_LAST_LOGIN_TIME_ATTR;
    String loginTimeAttr = (policy.getLastLoginTimeAttribute() == null)
                           ? null
                           : policy.getLastLoginTimeAttribute().getNameOrOID();
    attrList.add(new StringConfigAttribute(
                          ATTR_PWPOLICY_LAST_LOGIN_TIME_ATTRIBUTE,
                          getMessage(msgID), false, false, false,
                          loginTimeAttr));
    msgID = MSGID_PWPOLICY_DESCRIPTION_LAST_LOGIN_TIME_FORMAT;
    attrList.add(new StringConfigAttribute(ATTR_PWPOLICY_LAST_LOGIN_TIME_FORMAT,
                                           getMessage(msgID), false, false,
                                           false,
                                           policy.getLastLoginTimeFormat()));
    msgID = MSGID_PWPOLICY_DESCRIPTION_PREVIOUS_LAST_LOGIN_TIME_FORMAT;
    ArrayList<String> previousFormats = new ArrayList<String>();
    previousFormats.addAll(policy.getPreviousLastLoginTimeFormats());
    attrList.add(new StringConfigAttribute(
                          ATTR_PWPOLICY_PREVIOUS_LAST_LOGIN_TIME_FORMAT,
                          getMessage(msgID), false, false, false,
                          previousFormats));
    msgID = MSGID_PWPOLICY_DESCRIPTION_IDLE_LOCKOUT_INTERVAL;
    attrList.add(new IntegerWithUnitConfigAttribute(
                          ATTR_PWPOLICY_IDLE_LOCKOUT_INTERVAL,
                          getMessage(msgID), false, timeUnits, true, 0, true,
                          Integer.MAX_VALUE,  policy.getIdleLockoutInterval(),
                          TIME_UNIT_SECONDS_FULL));
    return attrList;
  }
  /**
   * Indicates whether the provided configuration entry has an acceptable
   * configuration for this component.  If it does not, then detailed
   * information about the problem(s) should be added to the provided list.
   *
   * @param  configEntry          The configuration entry for which to make the
   *                              determination.
   * @param  unacceptableReasons  A list that can be used to hold messages about
   *                              why the provided entry does not have an
   *                              acceptable configuration.
   *
   * @return  <CODE>true</CODE> if the provided entry has an acceptable
   *          configuration for this component, or <CODE>false</CODE> if not.
   */
  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
                                            List<String> unacceptableReasons)
  {
    assert configEntry.getDN().equals(this.currentPolicy.getConfigEntryDN() )
    assert configuration.dn().equals(this.currentPolicy.getConfigEntryDN() )
            : "Internal Error: mismatch between DN of configuration entry and"
              + "DN of current password policy." ;
    try
    {
      new PasswordPolicy(configEntry);
      new PasswordPolicy(configuration);
    }
    catch (ConfigException ce)
    {
@@ -433,25 +117,12 @@
  /**
   * Makes a best-effort attempt to apply the configuration contained in the
   * provided entry.  Information about the result of this processing should be
   * added to the provided message list.  Information should always be added to
   * this list if a configuration change could not be applied.  If detailed
   * results are requested, then information about the changes applied
   * successfully (and optionally about parameters that were not changed) should
   * also be included.
   *
   * @param  configEntry      The entry containing the new configuration to
   *                          apply for this component.
   * @param  detailedResults  Indicates whether detailed information about the
   *                          processing should be added to the list.
   *
   * @return  Information about the result of the configuration update.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
                                                  boolean detailedResults)
  public ConfigChangeResult applyConfigurationChange(
      PasswordPolicyCfg configuration)
  {
    assert configEntry.getDN().equals(this.currentPolicy.getConfigEntryDN() )
    assert configuration.dn().equals(this.currentPolicy.getConfigEntryDN() )
            : "Internal Error: mismatch between DN of configuration entry and"
              + "DN of current password policy." ;
@@ -459,7 +130,7 @@
    try
    {
      p = new PasswordPolicy(configEntry);
      p = new PasswordPolicy(configuration);
    }
    catch (ConfigException ce)
    {
@@ -489,11 +160,8 @@
    // If we've made it here, then everything is acceptable.  Apply the new
    // configuration.
    ArrayList<String> messages = new ArrayList<String>();
    if (detailedResults)
    {
      int msgID = MSGID_PWPOLICY_UPDATED_POLICY;
      messages.add(getMessage(msgID, String.valueOf(p.getConfigEntryDN())));
    }
    int msgID = MSGID_PWPOLICY_UPDATED_POLICY;
    messages.add(getMessage(msgID, String.valueOf(p.getConfigEntryDN())));
    this.currentPolicy = p;
@@ -501,7 +169,6 @@
                                  /*adminActionRequired*/ false, messages);
  }
  /**
   * Retrieves the PasswordPolicy object representing the configuration entry
   * managed by this object.
opends/src/server/org/opends/server/core/PasswordPolicyConfigManager.java
@@ -29,20 +29,19 @@
import java.util.ArrayList;
import java.util.List;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.api.ConfigDeleteListener;
import org.opends.server.config.ConfigEntry;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.server.ServerManagementContext;
import org.opends.server.admin.std.server.PasswordPolicyCfg;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.config.ConfigException;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import org.opends.server.types.DebugLogLevel;
import static org.opends.server.messages.ConfigMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.StaticUtils.*;
@@ -56,7 +55,8 @@
 * the server is running.
 */
public class PasswordPolicyConfigManager
       implements ConfigAddListener, ConfigDeleteListener
       implements ConfigurationAddListener<PasswordPolicyCfg>,
       ConfigurationDeleteListener<PasswordPolicyCfg>
{
@@ -85,44 +85,23 @@
  public void initializePasswordPolicies()
         throws ConfigException, InitializationException
  {
    // Get the root configuration object.
    ServerManagementContext managementContext =
         ServerManagementContext.getInstance();
    RootCfg rootConfiguration =
         managementContext.getRootConfiguration();
    // Register as an add and delete listener with the root configuration so we
    // can be notified if any password ploicies entries are added or removed.
    rootConfiguration.addPasswordPolicyAddListener(this);
    rootConfiguration.addPasswordPolicyDeleteListener(this);
    // First, get the configuration base entry.
    ConfigEntry baseEntry;
    try
    {
      DN policyBase = DN.decode(DN_PWPOLICY_CONFIG_BASE);
      baseEntry = DirectoryServer.getConfigHandler().getConfigEntry(policyBase);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int    msgID   = MSGID_CONFIG_PWPOLICY_CANNOT_GET_BASE;
      String message = getMessage(msgID, String.valueOf(e));
      throw new ConfigException(msgID, message, e);
    }
    if (baseEntry == null)
    {
      // The password policy base entry does not exist.  This is not
      // acceptable, so throw an exception.
      int    msgID   = MSGID_CONFIG_PWPOLICY_BASE_DOES_NOT_EXIST;
      String message = getMessage(msgID);
      throw new ConfigException(msgID, message);
    }
    // Register add and delete listeners with the policy base entry.  We
    // don't care about modifications to it.
    baseEntry.registerAddListener(this);
    baseEntry.registerDeleteListener(this);
    String[] passwordPoliciesName = rootConfiguration.listPasswordPolicys() ;
    // See if the base entry has any children.  If not, then that means that
    // there are no policies defined, so that's a problem.
    if (! baseEntry.hasChildren())
    if (passwordPoliciesName.length == 0)
    {
      int    msgID   = MSGID_CONFIG_PWPOLICY_NO_POLICIES;
      String message = getMessage(msgID);
@@ -141,32 +120,39 @@
    // Iterate through the child entries and process them as password policy
    // configuration entries.
    for (ConfigEntry childEntry : baseEntry.getChildren().values())
    for (String passwordPolicyName : passwordPoliciesName)
    {
      PasswordPolicyCfg passwordPolicyConfiguration =
        rootConfiguration.getPasswordPolicy(passwordPolicyName);
      try
      {
        PasswordPolicy policy = new PasswordPolicy(childEntry);
        DirectoryServer.registerPasswordPolicy(childEntry.getDN(), policy);
        PasswordPolicy policy = new PasswordPolicy(passwordPolicyConfiguration);
        PasswordPolicyConfig config = new PasswordPolicyConfig(policy);
        DirectoryServer.registerPasswordPolicy(
            passwordPolicyConfiguration.dn(), config);
        passwordPolicyConfiguration.addChangeListener(config);
      }
      catch (ConfigException ce)
      {
        int    msgID   = MSGID_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG;
        String message = getMessage(msgID, String.valueOf(childEntry.getDN()),
                                    ce.getMessage());
        int msgID = MSGID_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG;
        String message = getMessage(msgID, String
            .valueOf(passwordPolicyConfiguration.dn()), ce.getMessage());
        throw new ConfigException(msgID, message, ce);
      }
      catch (InitializationException ie)
      {
        int    msgID   = MSGID_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG;
        String message = getMessage(msgID, String.valueOf(childEntry.getDN()),
                                    ie.getMessage());
        String message = getMessage(msgID, String
            .valueOf(passwordPolicyConfiguration.dn()), ie.getMessage());
        throw new InitializationException(msgID, message, ie);
      }
      catch (Exception e)
      {
        int    msgID   = MSGID_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG;
        String message = getMessage(msgID, String.valueOf(childEntry.getDN()),
                                    stackTraceToSingleLineString(e));
        String message = getMessage(msgID, String
            .valueOf(passwordPolicyConfiguration.dn()),
            stackTraceToSingleLineString(e));
        throw new InitializationException(msgID, message, e);
      }
    }
@@ -186,49 +172,39 @@
  /**
   * Indicates whether the configuration entry that will result from a proposed
   * add is acceptable to this add listener.
   *
   * @param  configEntry         The configuration entry that will result from
   *                             the requested add.
   * @param  unacceptableReason  A buffer to which this method can append a
   *                             human-readable message explaining why the
   *                             proposed entry is not acceptable.
   *
   * @return  <CODE>true</CODE> if the proposed entry contains an acceptable
   *          configuration, or <CODE>false</CODE> if it does not.
   * {@inheritDoc}
   */
  public boolean configAddIsAcceptable(ConfigEntry configEntry,
                                       StringBuilder unacceptableReason)
  public boolean isConfigurationAddAcceptable(PasswordPolicyCfg configuration,
                                       List<String> unacceptableReason)
  {
    // See if we can create a password policy from the provided configuration
    // entry.  If so, then it's acceptable.
    try
    {
      new PasswordPolicy(configEntry);
      new PasswordPolicy(configuration);
    }
    catch (ConfigException ce)
    {
      int    msgID   = MSGID_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG;
      String message = getMessage(msgID, String.valueOf(configEntry.getDN()),
      String message = getMessage(msgID, String.valueOf(configuration.dn()),
                                  ce.getMessage());
      unacceptableReason.append(message);
      unacceptableReason.add(message);
      return false;
    }
    catch (InitializationException ie)
    {
      int    msgID   = MSGID_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG;
      String message = getMessage(msgID, String.valueOf(configEntry.getDN()),
      String message = getMessage(msgID, String.valueOf(configuration.dn()),
                                  ie.getMessage());
      unacceptableReason.append(message);
      unacceptableReason.add(message);
      return false;
    }
    catch (Exception e)
    {
      int    msgID   = MSGID_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG;
      String message = getMessage(msgID, String.valueOf(configEntry.getDN()),
      String message = getMessage(msgID, String.valueOf(configuration.dn()),
                                  stackTraceToSingleLineString(e));
      unacceptableReason.append(message);
      unacceptableReason.add(message);
      return false;
    }
@@ -240,17 +216,12 @@
  /**
   * Attempts to apply a new configuration based on the provided added entry.
   *
   * @param  configEntry  The new configuration entry that contains the
   *                      configuration to apply.
   *
   * @return  Information about the result of processing the configuration
   *          change.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry)
  public ConfigChangeResult applyConfigurationAdd(
      PasswordPolicyCfg configuration)
  {
    DN                configEntryDN       = configEntry.getDN();
    DN                configEntryDN       = configuration.dn();
    ArrayList<String> messages            = new ArrayList<String>();
@@ -258,14 +229,17 @@
    // entry.  If so, then register it with the Directory Server.
    try
    {
      PasswordPolicy policy = new PasswordPolicy(configEntry);
      DirectoryServer.registerPasswordPolicy(configEntryDN, policy);
      PasswordPolicy policy = new PasswordPolicy(configuration);
      PasswordPolicyConfig config = new PasswordPolicyConfig(policy);
      DirectoryServer.registerPasswordPolicy(configEntryDN, config);
      configuration.addChangeListener(config);
      return new ConfigChangeResult(ResultCode.SUCCESS, false, messages);
    }
    catch (ConfigException ce)
    {
      int msgID = MSGID_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG;
      messages.add(getMessage(msgID, String.valueOf(configEntry.getDN()),
      messages.add(getMessage(msgID, String.valueOf(configuration.dn()),
                              ce.getMessage()));
      return new ConfigChangeResult(ResultCode.CONSTRAINT_VIOLATION, false,
@@ -274,7 +248,7 @@
    catch (InitializationException ie)
    {
      int msgID = MSGID_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG;
      messages.add(getMessage(msgID, String.valueOf(configEntry.getDN()),
      messages.add(getMessage(msgID, String.valueOf(configuration.dn()),
                              ie.getMessage()));
      return new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
@@ -283,7 +257,7 @@
    catch (Exception e)
    {
      int msgID = MSGID_CONFIG_PWPOLICY_INVALID_POLICY_CONFIG;
      messages.add(getMessage(msgID, String.valueOf(configEntry.getDN()),
      messages.add(getMessage(msgID, String.valueOf(configuration.dn()),
                              stackTraceToSingleLineString(e)));
      return new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
@@ -294,20 +268,10 @@
  /**
   * Indicates whether it is acceptable to remove the provided configuration
   * entry.
   *
   * @param  configEntry         The configuration entry that will be removed
   *                             from the configuration.
   * @param  unacceptableReason  A buffer to which this method can append a
   *                             human-readable message explaining why the
   *                             proposed delete is not acceptable.
   *
   * @return  <CODE>true</CODE> if the proposed entry may be removed from the
   *          configuration, or <CODE>false</CODE> if not.
   * {@inheritDoc}
   */
  public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
                                          StringBuilder unacceptableReason)
  public boolean isConfigurationDeleteAcceptable(
      PasswordPolicyCfg configuration, List<String> unacceptableReason)
  {
    // We'll allow the policy to be removed as long as it isn't the default.
    // FIXME: something like a referential integrity check is needed to ensure
@@ -315,11 +279,11 @@
    // directly or via a virtual attribute).
    DN defaultPolicyDN = DirectoryServer.getDefaultPasswordPolicyDN();
    if ((defaultPolicyDN != null) &&
        defaultPolicyDN.equals(configEntry.getDN()))
        defaultPolicyDN.equals(configuration.dn()))
    {
      int msgID = MSGID_CONFIG_PWPOLICY_CANNOT_DELETE_DEFAULT_POLICY;
      String message = getMessage(msgID, String.valueOf(defaultPolicyDN));
      unacceptableReason.append(message);
      unacceptableReason.add(message);
      return false;
    }
    else
@@ -331,21 +295,17 @@
  /**
   * Attempts to apply a new configuration based on the provided deleted entry.
   *
   * @param  configEntry  The new configuration entry that has been deleted.
   *
   * @return  Information about the result of processing the configuration
   *          change.
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry)
  public ConfigChangeResult applyConfigurationDelete(
      PasswordPolicyCfg configuration)
  {
    // We'll allow the policy to be removed as long as it isn't the default.
    // FIXME: something like a referential integrity check is needed to ensure
    //  a policy is not removed when referenced by a user entry (either
    // directly or via a virtual attribute).
    ArrayList<String> messages = new ArrayList<String>(1);
    DN policyDN = configEntry.getDN();
    DN policyDN = configuration.dn();
    DN defaultPolicyDN = DirectoryServer.getDefaultPasswordPolicyDN();
    if ((defaultPolicyDN != null) && defaultPolicyDN.equals(policyDN))
    {
@@ -355,8 +315,13 @@
      return new ConfigChangeResult(ResultCode.CONSTRAINT_VIOLATION, false,
                                    messages);
    }
    DirectoryServer.deregisterPasswordPolicy(policyDN);
    PasswordPolicyConfig config =
      DirectoryServer.getPasswordPolicyConfig(policyDN);
    if (config != null)
    {
      configuration.removeChangeListener(config);
    }
    int msgID = MSGID_CONFIG_PWPOLICY_REMOVED_POLICY;
    messages.add(getMessage(msgID, String.valueOf(policyDN)));
opends/tests/unit-tests-testng/src/server/org/opends/server/core/PasswordPolicyTestCase.java
@@ -38,6 +38,9 @@
import org.testng.annotations.Test;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.server.AdminTestCaseUtils;
import org.opends.server.admin.std.meta.PasswordPolicyCfgDefn;
import org.opends.server.admin.std.server.PasswordPolicyCfg;
import org.opends.server.api.PasswordStorageScheme;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
@@ -2111,7 +2114,11 @@
    ConfigEntry parentEntry = DirectoryServer.getConfigEntry(parentDN);
    ConfigEntry configEntry = new ConfigEntry(e, parentEntry);
    new PasswordPolicy(configEntry);
    PasswordPolicyCfg configuration =
      AdminTestCaseUtils.getConfiguration(PasswordPolicyCfgDefn.getInstance(),
          configEntry.getEntry());
    new PasswordPolicy(configuration);
  }