From 272e306217cfa3e394574d9a1a4e69ff9e3a9600 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Wed, 11 Apr 2007 21:07:24 +0000
Subject: [PATCH] Add a new password validator that can be used to require that passwords have a specified number of characters from various user-defined character sets. It is also possible to control whether passwords will be allowed to contain characters outside of any defined character set.
---
opends/resource/schema/02-config.ldif | 12
opends/src/server/org/opends/server/extensions/CharacterSetPasswordValidator.java | 309 ++++++++++++++++++
opends/src/admin/defn/org/opends/server/admin/std/CharacterSetPasswordValidatorConfiguration.xml | 64 +++
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CharacterSetPasswordValidatorTestCase.java | 552 ++++++++++++++++++++++++++++++++
opends/src/server/org/opends/server/util/LDIFReader.java | 60 ++-
opends/resource/config/config.ldif | 13
6 files changed, 992 insertions(+), 18 deletions(-)
diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index 12bc4f6..b219bd5 100644
--- a/opends/resource/config/config.ldif
+++ b/opends/resource/config/config.ldif
@@ -1097,6 +1097,19 @@
ds-cfg-password-validator-enabled: true
ds-cfg-test-reversed-password: true
+dn: cn=Character Set,cn=Password Validators,cn=config
+objectClass: top
+objectClass: ds-cfg-password-validator
+objectClass: ds-cfg-character-set-password-validator
+cn: Character Set
+ds-cfg-password-validator-class: org.opends.server.extensions.CharacterSetPasswordValidator
+ds-cfg-password-validator-enabled: true
+ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz
+ds-cfg-character-set: 1:ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ds-cfg-character-set: 1:0123456789
+ds-cfg-character-set: 1:~!@#$%^&*()-_=+[]{}|;:,.<>/?
+ds-cfg-allow-unclassified-characters: true
+
dn: cn=Dictionary,cn=Password Validators,cn=config
objectClass: top
objectClass: ds-cfg-password-validator
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index 2c85851..bc9bf86 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -1131,6 +1131,13 @@
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.337
NAME 'ds-cfg-test-reversed-password' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.338
+ NAME 'ds-cfg-character-set' EQUALITY caseExactMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.339
+ NAME 'ds-cfg-allow-unclassified-characters'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.340
NAME 'ds-task-rebuild-base-dn'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE
@@ -1586,9 +1593,14 @@
NAME 'ds-cfg-attribute-value-password-validator'
SUP ds-cfg-password-validator STRUCTURAL MUST ds-cfg-test-reversed-password
MAY ds-cfg-match-attribute X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.97
+ NAME 'ds-cfg-character-set-password-validator' SUP ds-cfg-password-validator
+ STRUCTURAL MUST ( ds-cfg-character-set $
+ ds-cfg-allow-unclassified-characters ) X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.98
NAME 'ds-task-rebuild' SUP ds-task
MUST ( ds-task-rebuild-base-dn $ ds-task-rebuild-index )
MAY ( ds-task-rebuild-max-threads )
X-ORIGIN 'OpenDS Directory Server' )
+
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/CharacterSetPasswordValidatorConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/CharacterSetPasswordValidatorConfiguration.xml
new file mode 100644
index 0000000..d633b04
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/CharacterSetPasswordValidatorConfiguration.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<adm:managed-object name="character-set-password-validator"
+plural-name="character-set-password-validators"
+package="org.opends.server.admin.std" extends="password-validator"
+xmlns:adm="http://www.opends.org/admin"
+xmlns:ldap="http://www.opends.org/admin-ldap">
+ <adm:synopsis>
+ The
+ <adm:user-friendly-name />
+ is used to determine whether a proposed password is acceptable by
+ determining whether it contains a sufficient number of characters from one
+ or more user-defined character sets (e.g., passwords must have at least
+ one lowercase letter, one uppercase letter, one digit, and one symbol).
+ </adm:synopsis>
+ <adm:profile name="ldap">
+ <ldap:object-class>
+ <ldap:oid>1.3.6.1.4.1.26027.1.2.97</ldap:oid>
+ <ldap:name>ds-cfg-character-set-password-validator</ldap:name>
+ <ldap:superior>ds-cfg-password-validator</ldap:superior>
+ </ldap:object-class>
+ </adm:profile>
+
+ <adm:property name="character-set" mandatory="true" multi-valued="true">
+ <adm:synopsis>
+ Specifies a character set containing characters that a password may
+ contain and a value indicating the minimum number of characters required
+ from that set. The value must be an integer (indicating the minimum
+ required characters from the set) followed by a colon and the characters
+ to include in that set (e.g., "3:abcdefghijklmnopqrstuvwxyz" indicates
+ that a user password must contain at least three characters from the set
+ of lowercase ASCII letters). Multiple character sets may be defined in
+ separate values, although no character may appear in more than one
+ character set.
+ </adm:synopsis>
+ <adm:syntax>
+ <adm:string case-insensitive="false" />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.338</ldap:oid>
+ <ldap:name>ds-cfg-character-set</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+
+ <adm:property name="allow-unclassified-characters" mandatory="true">
+ <adm:synopsis>
+ Indicates whether this password validator allows passwords to contain
+ characters outside of any of the user-defined character sets. If this is
+ "false", then only those characters in the user-defined character sets
+ may be used in passwords.
+ </adm:synopsis>
+ <adm:syntax>
+ <adm:boolean />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.339</ldap:oid>
+ <ldap:name>ds-cfg-allow-unclassified-characters</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+</adm:managed-object>
+
diff --git a/opends/src/server/org/opends/server/extensions/CharacterSetPasswordValidator.java b/opends/src/server/org/opends/server/extensions/CharacterSetPasswordValidator.java
new file mode 100644
index 0000000..ea655c7
--- /dev/null
+++ b/opends/src/server/org/opends/server/extensions/CharacterSetPasswordValidator.java
@@ -0,0 +1,309 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.server.CharacterSetPasswordValidatorCfg;
+import org.opends.server.api.PasswordValidator;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.Operation;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.DirectoryConfig;
+import org.opends.server.types.Entry;
+import org.opends.server.types.ResultCode;
+
+import static org.opends.server.messages.ExtensionsMessages.*;
+import static org.opends.server.messages.MessageHandler.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
+/**
+ * This class provides an OpenDS password validator that may be used to ensure
+ * that proposed passwords contain at least a specified number of characters
+ * from one or more user-defined character sets.
+ */
+public class CharacterSetPasswordValidator
+ extends PasswordValidator<CharacterSetPasswordValidatorCfg>
+ implements ConfigurationChangeListener<CharacterSetPasswordValidatorCfg>
+{
+ // The current configuration for this password validator.
+ private CharacterSetPasswordValidatorCfg currentConfig;
+
+ // A mapping between the character sets and the minimum number of characters
+ // required for each.
+ private HashMap<String,Integer> characterSets;
+
+
+
+ /**
+ * Creates a new instance of this character set password validator.
+ */
+ public CharacterSetPasswordValidator()
+ {
+ super();
+
+ // No implementation is required here. All initialization should be
+ // performed in the initializePasswordValidator() method.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void initializePasswordValidator(
+ CharacterSetPasswordValidatorCfg configuration)
+ throws ConfigException
+ {
+ configuration.addCharacterSetChangeListener(this);
+ currentConfig = configuration;
+
+ // Make sure that each of the character set definitions are acceptable.
+ characterSets = processCharacterSets(configuration);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void finalizePasswordValidator()
+ {
+ currentConfig.removeCharacterSetChangeListener(this);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean passwordIsAcceptable(ByteString newPassword,
+ Set<ByteString> currentPasswords,
+ Operation operation, Entry userEntry,
+ StringBuilder invalidReason)
+ {
+ // Get a handle to the current configuration.
+ CharacterSetPasswordValidatorCfg config = currentConfig;
+ HashMap<String,Integer> characterSets = this.characterSets;
+
+
+ // Process the provided password.
+ String password = newPassword.stringValue();
+ HashMap<String,Integer> counts = new HashMap<String,Integer>();
+ for (int i=0; i < password.length(); i++)
+ {
+ char c = password.charAt(i);
+ boolean found = false;
+ for (String characterSet : characterSets.keySet())
+ {
+ if (characterSet.indexOf(c) >= 0)
+ {
+ Integer count = counts.get(characterSet);
+ if (count == null)
+ {
+ counts.put(characterSet, 1);
+ }
+ else
+ {
+ counts.put(characterSet, count+1);
+ }
+
+ found = true;
+ break;
+ }
+ }
+
+ if ((! found) && (! config.isAllowUnclassifiedCharacters()))
+ {
+ int msgID = MSGID_CHARSET_VALIDATOR_ILLEGAL_CHARACTER;
+ invalidReason.append(getMessage(msgID, String.valueOf(c)));
+ return false;
+ }
+ }
+
+ for (String characterSet : characterSets.keySet())
+ {
+ int minimumCount = characterSets.get(characterSet);
+ Integer passwordCount = counts.get(characterSet);
+ if ((passwordCount == null) || (passwordCount < minimumCount))
+ {
+ int msgID = MSGID_CHARSET_VALIDATOR_TOO_FEW_CHARS_FROM_SET;
+ invalidReason.append(getMessage(msgID, characterSet, minimumCount));
+ return false;
+ }
+ }
+
+
+ // If we've gotten here, then the password is acceptable.
+ return true;
+ }
+
+
+
+ /**
+ * Parses the provided configuration and extracts the character set
+ * definitions and associated minimum counts from them.
+ *
+ * @param configuration the configuration for this password validator.
+ *
+ * @return The mapping between strings of character set values and the
+ * minimum number of characters required from those sets.
+ *
+ * @throws ConfigException If any of the character set definitions cannot be
+ * parsed, or if there are any characters present in
+ * multiple sets.
+ */
+ private HashMap<String,Integer>
+ processCharacterSets(
+ CharacterSetPasswordValidatorCfg configuration)
+ throws ConfigException
+ {
+ HashMap<String,Integer> characterSets = new HashMap<String,Integer>();
+ HashSet<Character> usedCharacters = new HashSet<Character>();
+
+ for (String definition : configuration.getCharacterSet())
+ {
+ int colonPos = definition.indexOf(':');
+ if (colonPos <= 0)
+ {
+ int msgID = MSGID_CHARSET_VALIDATOR_NO_COLON;
+ String message = getMessage(msgID, definition);
+ throw new ConfigException(msgID, message);
+ }
+ else if (colonPos == (definition.length() - 1))
+ {
+ int msgID = MSGID_CHARSET_VALIDATOR_NO_CHARS;
+ String message = getMessage(msgID, definition);
+ throw new ConfigException(msgID, message);
+ }
+
+ int minCount;
+ try
+ {
+ minCount = Integer.parseInt(definition.substring(0, colonPos));
+ }
+ catch (Exception e)
+ {
+ int msgID = MSGID_CHARSET_VALIDATOR_INVALID_COUNT;
+ String message = getMessage(msgID, definition);
+ throw new ConfigException(msgID, message);
+ }
+
+ if (minCount <= 0)
+ {
+ int msgID = MSGID_CHARSET_VALIDATOR_INVALID_COUNT;
+ String message = getMessage(msgID, definition);
+ throw new ConfigException(msgID, message);
+ }
+
+ String characterSet = definition.substring(colonPos+1);
+ for (int i=0; i < characterSet.length(); i++)
+ {
+ char c = characterSet.charAt(i);
+ if (usedCharacters.contains(c))
+ {
+ int msgID = MSGID_CHARSET_VALIDATOR_DUPLICATE_CHAR;
+ String message = getMessage(msgID, definition, String.valueOf(c));
+ throw new ConfigException(msgID, message);
+ }
+
+ usedCharacters.add(c);
+ }
+
+ characterSets.put(characterSet, minCount);
+ }
+
+ return characterSets;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isConfigurationChangeAcceptable(
+ CharacterSetPasswordValidatorCfg configuration,
+ List<String> unacceptableReasons)
+ {
+ // Make sure that we can process the defined character sets. If so, then
+ // we'll accept the new configuration.
+ try
+ {
+ processCharacterSets(configuration);
+ }
+ catch (ConfigException ce)
+ {
+ unacceptableReasons.add(ce.getMessage());
+ return false;
+ }
+
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigChangeResult applyConfigurationChange(
+ CharacterSetPasswordValidatorCfg configuration)
+ {
+ ResultCode resultCode = ResultCode.SUCCESS;
+ boolean adminActionRequired = false;
+ ArrayList<String> messages = new ArrayList<String>();
+
+
+ // Make sure that we can process the defined character sets. If so, then
+ // activate the new configuration.
+ try
+ {
+ characterSets = processCharacterSets(configuration);
+ currentConfig = configuration;
+ }
+ catch (Exception e)
+ {
+ resultCode = DirectoryConfig.getServerErrorResultCode();
+ messages.add(stackTraceToSingleLineString(e));
+ }
+
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
+}
+
diff --git a/opends/src/server/org/opends/server/util/LDIFReader.java b/opends/src/server/org/opends/server/util/LDIFReader.java
index b8eecbc..ea638f3 100644
--- a/opends/src/server/org/opends/server/util/LDIFReader.java
+++ b/opends/src/server/org/opends/server/util/LDIFReader.java
@@ -79,9 +79,6 @@
*/
public final class LDIFReader
{
-
-
-
// The reader that will be used to read the data.
private BufferedReader reader;
@@ -243,7 +240,7 @@
for (StringBuilder line : lines)
{
readAttribute(lines, line, entryDN, objectClasses, userAttributes,
- operationalAttributes);
+ operationalAttributes, checkSchema);
}
}
catch (LDIFException e)
@@ -814,6 +811,8 @@
* for the current entry.
* @param operationalAttributes The set of operational attributes decoded so
* far for the current entry.
+ * @param checkSchema Indicates whether to perform schema
+ * validation for the attribute.
*
* @throws LDIFException If a problem occurs while trying to decode the
* attribute contained in the provided entry.
@@ -822,7 +821,8 @@
StringBuilder line, DN entryDN,
HashMap<ObjectClass,String> objectClasses,
HashMap<AttributeType,List<Attribute>> userAttributes,
- HashMap<AttributeType,List<Attribute>> operationalAttributes)
+ HashMap<AttributeType,List<Attribute>> operationalAttributes,
+ boolean checkSchema)
throws LDIFException
{
// Parse the attribute type description.
@@ -934,14 +934,40 @@
LinkedHashSet<AttributeValue> valueSet = a.getValues();
if (valueSet.contains(attributeValue))
{
- int msgID = MSGID_LDIF_DUPLICATE_ATTR;
- String message = getMessage(msgID, String.valueOf(entryDN),
- lastEntryLineNumber, attrName,
- value.stringValue());
- logToRejectWriter(lines, message);
- throw new LDIFException(msgID, message, lastEntryLineNumber, true);
+ if (! checkSchema)
+ {
+ // If we're not doing schema checking, then it is possible that
+ // the attribute type should use case-sensitive matching and the
+ // values differ in capitalization. Only reject the proposed
+ // value if we find another value that is exactly the same as the
+ // one that was provided.
+ for (AttributeValue v : valueSet)
+ {
+ if (v.getValue().equals(attributeValue.getValue()))
+ {
+ int msgID = MSGID_LDIF_DUPLICATE_ATTR;
+ String message = getMessage(msgID, String.valueOf(entryDN),
+ lastEntryLineNumber, attrName,
+ value.stringValue());
+ logToRejectWriter(lines, message);
+ throw new LDIFException(msgID, message, lastEntryLineNumber,
+ true);
+ }
+ }
+ }
+ else
+ {
+ int msgID = MSGID_LDIF_DUPLICATE_ATTR;
+ String message = getMessage(msgID, String.valueOf(entryDN),
+ lastEntryLineNumber, attrName,
+ value.stringValue());
+ logToRejectWriter(lines, message);
+ throw new LDIFException(msgID, message, lastEntryLineNumber,
+ true);
+ }
}
- else if (attrType.isSingleValue() && (! valueSet.isEmpty()))
+
+ if (attrType.isSingleValue() && (! valueSet.isEmpty()) && checkSchema)
{
int msgID = MSGID_LDIF_MULTIPLE_VALUES_FOR_SINGLE_VALUED_ATTR;
String message = getMessage(msgID, String.valueOf(entryDN),
@@ -949,11 +975,9 @@
logToRejectWriter(lines, message);
throw new LDIFException(msgID, message, lastEntryLineNumber, true);
}
- else
- {
- valueSet.add(attributeValue);
- return;
- }
+
+ valueSet.add(attributeValue);
+ return;
}
}
@@ -1469,7 +1493,7 @@
for(StringBuilder line : lines)
{
readAttribute(lines, line, entryDN, objectClasses,
- attributes, attributes);
+ attributes, attributes, importConfig.validateSchema());
}
// Reconstruct the object class attribute.
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CharacterSetPasswordValidatorTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CharacterSetPasswordValidatorTestCase.java
new file mode 100644
index 0000000..db2989d
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CharacterSetPasswordValidatorTestCase.java
@@ -0,0 +1,552 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.std.meta.CharacterSetPasswordValidatorCfgDefn;
+import org.opends.server.admin.std.server.CharacterSetPasswordValidatorCfg;
+import org.opends.server.admin.server.AdminTestCaseUtils;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.Control;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of test cases for the character set password validator.
+ */
+public class CharacterSetPasswordValidatorTestCase
+ extends ExtensionsTestCase
+{
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+ }
+
+
+
+ /**
+ * Retrieves a set of valid configuration entries that may be used to
+ * initialize the validator.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "validConfigs")
+ public Object[][] getValidConfigs()
+ throws Exception
+ {
+ List<Entry> entries = TestCaseUtils.makeEntries(
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-character-set: 1:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ "ds-cfg-character-set: 1:0123456789",
+ "ds-cfg-character-set: 1:~!@#$%^&*()-_=+[]{}|;:,.<>/?",
+ "ds-cfg-allow-unclassified-characters: true",
+ "",
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-allow-unclassified-characters: true",
+ "",
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-character-set: 1:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ "ds-cfg-character-set: 1:0123456789",
+ "ds-cfg-character-set: 1:~!@#$%^&*()-_=+[]{}|;:,.<>/?",
+ "ds-cfg-allow-unclassified-characters: false");
+
+ Object[][] array = new Object[entries.size()][1];
+ for (int i=0; i < array.length; i++)
+ {
+ array[i] = new Object[] { entries.get(i) };
+ }
+
+ return array;
+ }
+
+
+
+ /**
+ * Tests the process of initializing the server with valid configurations.
+ *
+ * @param entry The configuration entry to use for the initialization.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "validConfigs", groups= { "slow" })
+ public void testInitializeWithValidConfigs(Entry e)
+ throws Exception
+ {
+ CharacterSetPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ CharacterSetPasswordValidatorCfgDefn.getInstance(), e);
+
+ CharacterSetPasswordValidator validator =
+ new CharacterSetPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+ validator.finalizePasswordValidator();
+ }
+
+
+
+ /**
+ * Retrieves a set of invalid configuration entries.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "invalidConfigs")
+ public Object[][] getInvalidConfigs()
+ throws Exception
+ {
+ List<Entry> entries = TestCaseUtils.makeEntries(
+ // Malformed character set definition -- no colon.
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: malformed",
+ "ds-cfg-allow-unclassified-characters: true",
+ "",
+ // Malformed character set definition -- colon first.
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: :malformed",
+ "ds-cfg-allow-unclassified-characters: true",
+ "",
+ // Malformed character set definition -- colon last.
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 1:",
+ "ds-cfg-allow-unclassified-characters: true",
+ "",
+ // Malformed character set definition -- non-integer count.
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: noninteger:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-allow-unclassified-characters: true",
+ "",
+ // Malformed character set definition -- zero count.
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 0:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-allow-unclassified-characters: true",
+ "",
+ // Malformed character set definition -- negative count.
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: -1:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-allow-unclassified-characters: true",
+ "",
+ // Malformed character set definition -- duplicate character in the
+ // same character set.
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyza",
+ "ds-cfg-allow-unclassified-characters: true",
+ "",
+ // Malformed character set definition -- duplicate character in
+ // different character sets.
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-character-set: 1:ABCDEFGHIJKLMNOPQRSTUVWXYz",
+ "ds-cfg-allow-unclassified-characters: true",
+ "",
+ // Malformed allow unclassified characters.
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-allow-unclassified-characters: malformed");
+
+ Object[][] array = new Object[entries.size()][1];
+ for (int i=0; i < array.length; i++)
+ {
+ array[i] = new Object[] { entries.get(i) };
+ }
+
+ return array;
+ }
+
+
+
+ /**
+ * Tests the process of initializing the server with invalid configurations.
+ *
+ * @param entry The configuration entry to use for the initialization.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "invalidConfigs",
+ expectedExceptions = { ConfigException.class,
+ InitializationException.class })
+ public void testInitializeWithInvalidConfigs(Entry e)
+ throws Exception
+ {
+ CharacterSetPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ CharacterSetPasswordValidatorCfgDefn.getInstance(), e);
+
+ CharacterSetPasswordValidator validator =
+ new CharacterSetPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ StringBuilder buffer = new StringBuilder();
+ for (StringBuilder line : e.toLDIF())
+ {
+ buffer.append(line);
+ buffer.append("\n");
+ }
+ fail(buffer.toString());
+ }
+
+
+
+ /**
+ * Retrieves a set of data to use when testing a given password with a
+ * provided configuration. Each element of the returned array should be an
+ * array of a configuration entry, a test password string, and an indication
+ * as to whether the provided password should be acceptable.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "testData")
+ public Object[][] getTestData()
+ throws Exception
+ {
+ return new Object[][]
+ {
+ // Default configuration, missing characters from multiple character sets.
+ new Object[]
+ {
+ TestCaseUtils.makeEntry(
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-character-set: 1:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ "ds-cfg-character-set: 1:0123456789",
+ "ds-cfg-character-set: 1:~!@#$%^&*()-_=+[]{}|;:,.<>/?",
+ "ds-cfg-allow-unclassified-characters: true"),
+ "password",
+ false
+ },
+
+ // Default configuration, including characters from all of multiple
+ // character sets.
+ new Object[]
+ {
+ TestCaseUtils.makeEntry(
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-character-set: 1:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ "ds-cfg-character-set: 1:0123456789",
+ "ds-cfg-character-set: 1:~!@#$%^&*()-_=+[]{}|;:,.<>/?",
+ "ds-cfg-allow-unclassified-characters: true"),
+ "PaS$w0rD",
+ true
+ },
+
+ // Default configuration, including characters from some (but not all) of
+ // multiple character sets.
+ new Object[]
+ {
+ TestCaseUtils.makeEntry(
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-character-set: 1:ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ "ds-cfg-character-set: 1:0123456789",
+ "ds-cfg-character-set: 1:~!@#$%^&*()-_=+[]{}|;:,.<>/?",
+ "ds-cfg-allow-unclassified-characters: true"),
+ "PaS$worD",
+ false
+ },
+
+ // Default configuration, including enough characters from a single
+ // character set.
+ new Object[]
+ {
+ TestCaseUtils.makeEntry(
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-allow-unclassified-characters: true"),
+ "password",
+ true
+ },
+
+ // Default configuration, including too few characters from a single
+ // character set.
+ new Object[]
+ {
+ TestCaseUtils.makeEntry(
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 6:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-allow-unclassified-characters: true"),
+ "short",
+ false
+ },
+
+ // Default configuration, allowing characters outside of any defined
+ // character set.
+ new Object[]
+ {
+ TestCaseUtils.makeEntry(
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-allow-unclassified-characters: true"),
+ "PaS$w0rD",
+ true
+ },
+
+ // Default configuration, rejecting characters outside of any defined
+ // character set.
+ new Object[]
+ {
+ TestCaseUtils.makeEntry(
+ "dn: cn=Character Set,cn=Password Validators,cn=config",
+ "objectClass: top",
+ "objectClass: ds-cfg-password-validator",
+ "objectClass: ds-cfg-character-set-password-validator",
+ "cn: Character Set",
+ "ds-cfg-password-validator-class: org.opends.server.extensions." +
+ "CharacterSetPasswordValidator",
+ "ds-cfg-password-validator-enabled: true",
+ "ds-cfg-character-set: 1:abcdefghijklmnopqrstuvwxyz",
+ "ds-cfg-allow-unclassified-characters: false"),
+ "PaS$w0rD",
+ false
+ },
+ };
+ }
+
+
+
+ /**
+ * Tests the {@code passwordIsAcceptable} method using the provided
+ * information.
+ *
+ * @param configEntry The configuration entry to use for the password
+ * validator.
+ * @param password The password to test with the validator.
+ * @param acceptable Indicates whether the provided password should be
+ * considered acceptable.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testData")
+ public void testPasswordIsAcceptable(Entry configEntry, String password,
+ boolean acceptable)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ Entry userEntry = TestCaseUtils.makeEntry(
+ "dn: uid=test.user,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: doesntmatter");
+
+ CharacterSetPasswordValidatorCfg configuration =
+ AdminTestCaseUtils.getConfiguration(
+ CharacterSetPasswordValidatorCfgDefn.getInstance(),
+ configEntry);
+
+ CharacterSetPasswordValidator validator =
+ new CharacterSetPasswordValidator();
+ validator.initializePasswordValidator(configuration);
+
+ ASN1OctetString pwOS = new ASN1OctetString(password);
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("userpassword", password)));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ new ArrayList<Control>(),
+ DN.decode("uid=test.user,o=test"), mods);
+
+ StringBuilder invalidReason = new StringBuilder();
+ assertEquals(validator.passwordIsAcceptable(pwOS,
+ new HashSet<ByteString>(0), modifyOperation,
+ userEntry, invalidReason),
+ acceptable, invalidReason.toString());
+
+ validator.finalizePasswordValidator();
+ }
+}
+
--
Gitblit v1.10.0