From b73ca0732b8d1da28ff9efd45d1fb14016860107 Mon Sep 17 00:00:00 2001 From: Neil Wilson Date: Thu, 17 Aug 2023 17:10:39 -0500 Subject: [PATCH] Update ds-pwp-state-json for non-current PW enc Updated support for the Ping Identity Directory Server's ds-pwp-state-json virtual attribute to include fields pertaining to support for passwords encoded in a manner that doesn't reflect the current configuration for the associated password storage scheme. --- docs/release-notes.html | 8 ++ .../unboundidds/PasswordPolicyStateJSON.java | 98 +++++++++++++++++ .../PasswordPolicyStateJSONField.java | 26 ++++- .../PasswordPolicyStateJSONTestCase.java | 103 ++++++++++++++++++ 4 files changed, 232 insertions(+), 3 deletions(-) diff --git a/docs/release-notes.html b/docs/release-notes.html index ff7090cca..d06579747 100644 --- a/docs/release-notes.html +++ b/docs/release-notes.html @@ -78,6 +78,14 @@

Version 6.0.10

control.

+ +
  • + Updated support for the Ping Identity Directory Server's ds-pwp-state-json + virtual attribute to include fields pertaining to support for passwords encoded + in a manner that doesn't reflect the current configuration for the associated + password storage scheme. +

    +
  • diff --git a/src/com/unboundid/ldap/sdk/unboundidds/PasswordPolicyStateJSON.java b/src/com/unboundid/ldap/sdk/unboundidds/PasswordPolicyStateJSON.java index eb0eeab77..a29576550 100644 --- a/src/com/unboundid/ldap/sdk/unboundidds/PasswordPolicyStateJSON.java +++ b/src/com/unboundid/ldap/sdk/unboundidds/PasswordPolicyStateJSON.java @@ -218,6 +218,24 @@ public final class PasswordPolicyStateJSON + /** + * The name of the field that will be used to hold the name of a password + * storage scheme for a password that is encoded with non-current settings. + */ + @NotNull private static final String NON_CURRENT_ENCODING_FIELD_SCHEME = + "scheme"; + + + + /** + * The name of the field that will be used to hold the set of explanations for + * a password that is encoded with non-current settings. + */ + @NotNull private static final String NON_CURRENT_ENCODING_FIELD_EXPLANATIONS = + "explanations"; + + + /** * The serial version UID for this serializable class. */ @@ -2194,6 +2212,86 @@ private List getPasswordQualityRequirements( + /** + * Indicates whether the user has a static password that is encoded with + * settings that don't match the current configuration for the associated + * password storage scheme. + * + * @return {@code Boolean.TRUE} if the account has a static password that + * is encoded with non-current settings, {@code Boolean.FALSE} if the + * account does not have a static password that is encoded with + * non-current settings, or {@code null} if this flag was not + * included in the password policy state JSON object. + */ + @Nullable() + public Boolean hasPasswordEncodedWithNonCurrentSettings() + { + return passwordPolicyStateObject.getFieldAsBoolean( + HAS_PASSWORD_ENCODED_WITH_NON_CURRENT_SETTINGS.getFieldName()); + } + + + + /** + * Retrieves a map with information about the reasons that a password may not + * be encoded with the current settings for the associated password storage + * scheme. The keys of the map will the name of the storage scheme, and the + * values will be a possibly-empty list of explanations that describe why a + * password encoded with that scheme is encoded with non-current settings. + * + * @return A map with information about the reasons that a password may not + * be encoded with the current settings for the associated password + * storage scheme, or an empty map if this was not included in the + * password policy state JSON object. + */ + @NotNull() + public Map> + getNonCurrentPasswordStorageSchemeSettingsExplanations() + { + final Map> explanationsMap = new LinkedHashMap<>(); + + final List values = passwordPolicyStateObject.getFieldAsArray( + NON_CURRENT_PASSWORD_STORAGE_SCHEME_SETTINGS_EXPLANATIONS. + getFieldName()); + if (values != null) + { + for (final JSONValue value : values) + { + if (value instanceof JSONObject) + { + final JSONObject valueObject = (JSONObject) value; + final String schemeName = valueObject.getFieldAsString( + NON_CURRENT_ENCODING_FIELD_SCHEME); + if (schemeName == null) + { + continue; + } + + final List explanationStrings = new ArrayList<>(); + final List explanationValues = valueObject.getFieldAsArray( + NON_CURRENT_ENCODING_FIELD_EXPLANATIONS); + if (explanationValues != null) + { + for (final JSONValue explanationValue : explanationValues) + { + if (explanationValue instanceof JSONString) + { + explanationStrings.add( + ((JSONString) explanationValue).stringValue()); + } + } + } + + explanationsMap.put(schemeName, explanationStrings); + } + } + } + + return Collections.unmodifiableMap(explanationsMap); + } + + + /** * Retrieves the value of the specified field as a {@code Date}. * diff --git a/src/com/unboundid/ldap/sdk/unboundidds/PasswordPolicyStateJSONField.java b/src/com/unboundid/ldap/sdk/unboundidds/PasswordPolicyStateJSONField.java index a19c08f91..191f2bdea 100644 --- a/src/com/unboundid/ldap/sdk/unboundidds/PasswordPolicyStateJSONField.java +++ b/src/com/unboundid/ldap/sdk/unboundidds/PasswordPolicyStateJSONField.java @@ -774,10 +774,30 @@ public enum PasswordPolicyStateJSONField /** - * The field used to hold information about the requirements that passwords - * will be required to satisfy. + * The field (password-quality-requirements) used to hold information about + * the requirements that passwords will be required to satisfy. */ - PASSWORD_QUALITY_REQUIREMENTS("password-quality-requirements"); + PASSWORD_QUALITY_REQUIREMENTS("password-quality-requirements"), + + + + /** + * The field (has-password-encoded-with-non-current-settings) used to indicate + * whether the user has a password that is encoded with settings that don't + * match the current configuration for the associated password storage scheme. + */ + HAS_PASSWORD_ENCODED_WITH_NON_CURRENT_SETTINGS( + "has-password-encoded-with-non-current-settings"), + + + + /** + * The field (non-current-password-storage-scheme-settings-explanations) used + * to hold information about the reasons that a password may not be encoded + * with the current settings for the associated password storage scheme. + */ + NON_CURRENT_PASSWORD_STORAGE_SCHEME_SETTINGS_EXPLANATIONS( + "non-current-password-storage-scheme-settings-explanations"); diff --git a/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/PasswordPolicyStateJSONTestCase.java b/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/PasswordPolicyStateJSONTestCase.java index dc63ec722..b6741a479 100644 --- a/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/PasswordPolicyStateJSONTestCase.java +++ b/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/PasswordPolicyStateJSONTestCase.java @@ -2358,6 +2358,109 @@ public void testGetPasswordQualityRequirementsPropertyMissingValue() + /** + * Tests the behavior for fields related to passwords encoded with non-current + * settings. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testPasswordEncodedWithNonCurrentSettings() + throws Exception + { + // Test with a state that represents a user in which the password is encoded + // with current settings. + PasswordPolicyStateJSON state = createState(StaticUtils.mapOf( + HAS_PASSWORD_ENCODED_WITH_NON_CURRENT_SETTINGS, JSONBoolean.FALSE)); + assertEquals(state.hasPasswordEncodedWithNonCurrentSettings(), + Boolean.FALSE); + assertNotNull( + state.getNonCurrentPasswordStorageSchemeSettingsExplanations()); + assertTrue(state. + getNonCurrentPasswordStorageSchemeSettingsExplanations().isEmpty()); + + + // Test with a state that represents a user in which the password is + // encoded with non-current settings but no explanations. + state = createState(StaticUtils.mapOf( + HAS_PASSWORD_ENCODED_WITH_NON_CURRENT_SETTINGS, JSONBoolean.TRUE, + NON_CURRENT_PASSWORD_STORAGE_SCHEME_SETTINGS_EXPLANATIONS, + new JSONArray( + createNonCurrentSchemeSettingsExplanationsObject( + "SSHA256")))); + assertEquals(state.hasPasswordEncodedWithNonCurrentSettings(), + Boolean.TRUE); + assertNotNull( + state.getNonCurrentPasswordStorageSchemeSettingsExplanations()); + assertEquals( + state.getNonCurrentPasswordStorageSchemeSettingsExplanations().size(), + 1); + assertTrue(state.getNonCurrentPasswordStorageSchemeSettingsExplanations(). + containsKey("SSHA256")); + assertTrue(state.getNonCurrentPasswordStorageSchemeSettingsExplanations(). + get("SSHA256").isEmpty()); + + + // Test with a state that represents a user in which the password is + // encoded with non-current settings and has explanations. + state = createState(StaticUtils.mapOf( + HAS_PASSWORD_ENCODED_WITH_NON_CURRENT_SETTINGS, JSONBoolean.TRUE, + NON_CURRENT_PASSWORD_STORAGE_SCHEME_SETTINGS_EXPLANATIONS, + new JSONArray( + createNonCurrentSchemeSettingsExplanationsObject( + "SSHA256", "explanation1", "explanation2")))); + assertEquals(state.hasPasswordEncodedWithNonCurrentSettings(), + Boolean.TRUE); + assertNotNull( + state.getNonCurrentPasswordStorageSchemeSettingsExplanations()); + assertEquals( + state.getNonCurrentPasswordStorageSchemeSettingsExplanations().size(), + 1); + assertTrue(state.getNonCurrentPasswordStorageSchemeSettingsExplanations(). + containsKey("SSHA256")); + assertEquals( + state.getNonCurrentPasswordStorageSchemeSettingsExplanations().get( + "SSHA256"), + Arrays.asList("explanation1", "explanation2")); + } + + + + /** + * Creates a JSON object with information about a password that is encoded + * with non-current scheme settings. + * + * @param schemeName The name of the associated password storage scheme. + * It must not be {@code null}. + * @param explanations An optional set of explanation values. This may be + * empty but must not be {@code null}. + * + * @return The JSON object that was created. + */ + private static JSONObject createNonCurrentSchemeSettingsExplanationsObject( + final String schemeName, + final String... explanations) + { + final Map fields = new LinkedHashMap<>(); + fields.put("scheme", new JSONString(schemeName)); + + if (explanations.length > 0) + { + final List explanationValues = + new ArrayList<>(explanations.length); + for (final String explanation : explanations) + { + explanationValues.add(new JSONString(explanation)); + } + + fields.put("explanations", new JSONArray(explanationValues)); + } + + return new JSONObject(fields); + } + + + /** * Creates a password policy state JSON object with the provided fields. *