You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2011/10/15 17:26:44 UTC
svn commit: r1183660 [1/2] - in /directory/apacheds/trunk:
core-integ/src/test/java/org/apache/directory/server/core/authn/ppolicy/
core-shared/src/main/java/org/apache/directory/server/core/shared/authn/
core-shared/src/main/java/org/apache/directory/...
Author: elecharny
Date: Sat Oct 15 15:26:42 2011
New Revision: 1183660
URL: http://svn.apache.org/viewvc?rev=1183660&view=rev
Log:
Remoevd some cross-dependencies between inteceptors
Added:
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/EncryptionMethod.java
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/PasswordUtil.java
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/DefaultPasswordValidator.java
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordPolicyConfiguration.java
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordPolicyException.java
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordValidator.java
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/ExpressionEvaluator.java
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/LeafEvaluator.java
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/ScopeEvaluator.java
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/SubstringEvaluator.java
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/normalization/
directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/normalization/FilterNormalizingVisitor.java
Removed:
directory/apacheds/trunk/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/ppolicy/DefaultPasswordValidator.java
directory/apacheds/trunk/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/ppolicy/PasswordPolicyConfiguration.java
directory/apacheds/trunk/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/ppolicy/PasswordPolicyException.java
directory/apacheds/trunk/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/ppolicy/PasswordValidator.java
directory/apacheds/trunk/interceptors/event/src/main/java/org/apache/directory/server/core/event/ExpressionEvaluator.java
directory/apacheds/trunk/interceptors/event/src/main/java/org/apache/directory/server/core/event/LeafEvaluator.java
directory/apacheds/trunk/interceptors/event/src/main/java/org/apache/directory/server/core/event/ScopeEvaluator.java
directory/apacheds/trunk/interceptors/event/src/main/java/org/apache/directory/server/core/event/SubstringEvaluator.java
directory/apacheds/trunk/interceptors/normalization/src/main/java/org/apache/directory/server/core/normalization/FilterNormalizingVisitor.java
Modified:
directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/authn/ppolicy/PasswordPolicyTest.java
directory/apacheds/trunk/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/AbstractAuthenticator.java
directory/apacheds/trunk/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/AuthenticationInterceptor.java
directory/apacheds/trunk/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/Authenticator.java
directory/apacheds/trunk/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/PasswordUtil.java
directory/apacheds/trunk/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/ppolicy/PpolicyConfigContainer.java
directory/apacheds/trunk/interceptors/authz/pom.xml
directory/apacheds/trunk/interceptors/authz/src/main/java/org/apache/directory/server/core/authz/support/ACDFEngine.java
directory/apacheds/trunk/interceptors/authz/src/test/java/org/apache/directory/server/core/authz/support/RelatedProtectedItemFilterTest.java
directory/apacheds/trunk/interceptors/event/pom.xml
directory/apacheds/trunk/interceptors/event/src/main/java/org/apache/directory/server/core/event/DefaultEventService.java
directory/apacheds/trunk/interceptors/event/src/main/java/org/apache/directory/server/core/event/EventInterceptor.java
directory/apacheds/trunk/interceptors/hash/pom.xml
directory/apacheds/trunk/interceptors/hash/src/main/java/org/apache/directory/server/core/hash/PasswordHashingInterceptor.java
directory/apacheds/trunk/interceptors/normalization/pom.xml
directory/apacheds/trunk/interceptors/normalization/src/main/java/org/apache/directory/server/core/normalization/NormalizationInterceptor.java
directory/apacheds/trunk/interceptors/normalization/src/test/java/org/apache/directory/server/core/normalization/NormalizationVisitorTest.java
directory/apacheds/trunk/interceptors/schema/ (props changed)
directory/apacheds/trunk/interceptors/schema/pom.xml
directory/apacheds/trunk/interceptors/schema/src/main/java/org/apache/directory/server/core/schema/PartitionSchemaLoader.java
directory/apacheds/trunk/interceptors/subtree/pom.xml
directory/apacheds/trunk/interceptors/subtree/src/main/java/org/apache/directory/server/core/subtree/SubtreeEvaluator.java
directory/apacheds/trunk/interceptors/subtree/src/test/java/org/apache/directory/server/core/subtree/SubtreeEvaluatorTest.java
directory/apacheds/trunk/ldif-partition/src/test/java/org/apache/directory/server/core/partition/ldif/LdifPartitionTest.java
directory/apacheds/trunk/ldif-partition/src/test/java/org/apache/directory/server/core/partition/ldif/SingleFileLdifPartitionTest.java
directory/apacheds/trunk/service-builder/src/main/java/org/apache/directory/server/config/builder/ServiceBuilder.java
Modified: directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/authn/ppolicy/PasswordPolicyTest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/authn/ppolicy/PasswordPolicyTest.java?rev=1183660&r1=1183659&r2=1183660&view=diff
==============================================================================
--- directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/authn/ppolicy/PasswordPolicyTest.java (original)
+++ directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/authn/ppolicy/PasswordPolicyTest.java Sat Oct 15 15:26:42 2011
@@ -43,6 +43,7 @@ import org.apache.directory.server.core.
import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
import org.apache.directory.server.core.integ.FrameworkRunner;
import org.apache.directory.server.core.integ.IntegrationUtils;
+import org.apache.directory.server.core.shared.authn.ppolicy.PasswordPolicyConfiguration;
import org.apache.directory.shared.ldap.codec.api.LdapApiService;
import org.apache.directory.shared.ldap.codec.api.LdapApiServiceFactory;
import org.apache.directory.shared.ldap.extras.controls.ppolicy.PasswordPolicy;
Added: directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/EncryptionMethod.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/EncryptionMethod.java?rev=1183660&view=auto
==============================================================================
--- directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/EncryptionMethod.java (added)
+++ directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/EncryptionMethod.java Sat Oct 15 15:26:42 2011
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.directory.server.core.shared.authn;
+
+
+import org.apache.directory.shared.ldap.model.constants.LdapSecurityConstants;
+import org.apache.directory.shared.util.Strings;
+
+
+/**
+ * A class to store all informations about the existing
+ * password found in the cache or get from the backend.
+ *
+ * This is necessary as we have to compute :
+ * - the used algorithm
+ * - the salt if any
+ * - the password itself.
+ *
+ * If we have a on-way encrypted password, it is stored using this
+ * format :
+ * {<algorithm>}<encrypted password>
+ * where the encrypted password format can be :
+ * - MD5/SHA : base64(<password>)
+ * - SMD5/SSH : base64(<salted-password-digest><salt (4 or 8 bytes)>)
+ * - crypt : <salt (2 btytes)><password>
+ *
+ * Algorithm are currently MD5, SMD5, SHA, SSHA, SHA2, SSHA-2 (except SHA-224), CRYPT and empty
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class EncryptionMethod
+{
+ private byte[] salt;
+ private LdapSecurityConstants algorithm;
+
+
+ /** package protected */EncryptionMethod( LdapSecurityConstants algorithm, byte[] salt )
+ {
+ this.algorithm = algorithm;
+ this.salt = salt;
+ }
+
+
+ public LdapSecurityConstants getAlgorithm()
+ {
+ return algorithm;
+ }
+
+
+ public byte[] getSalt()
+ {
+ return salt;
+ }
+
+
+ /** package protected */ void setSalt( byte[] salt )
+ {
+ // just to make this class immutable, though we have a setter
+ if ( this.salt != null )
+ {
+ throw new IllegalStateException( "salt will only be allowed to set once" );
+ }
+
+ this.salt = salt;
+ }
+
+
+ @Override
+ public String toString()
+ {
+ return "EncryptionMethod [algorithm=" + algorithm.getName().toUpperCase() + ", salt=" + Strings.dumpBytes(salt) + "]";
+ }
+
+
+}
Added: directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/PasswordUtil.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/PasswordUtil.java?rev=1183660&view=auto
==============================================================================
--- directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/PasswordUtil.java (added)
+++ directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/PasswordUtil.java Sat Oct 15 15:26:42 2011
@@ -0,0 +1,550 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.directory.server.core.shared.authn;
+
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.directory.server.core.shared.authn.ppolicy.PasswordPolicyConfiguration;
+import org.apache.directory.shared.ldap.model.constants.LdapSecurityConstants;
+import org.apache.directory.shared.ldap.model.entry.Attribute;
+import org.apache.directory.shared.ldap.model.entry.Value;
+import org.apache.directory.shared.util.Base64;
+import org.apache.directory.shared.util.DateUtils;
+import org.apache.directory.shared.util.Strings;
+import org.apache.directory.shared.util.UnixCrypt;
+
+
+/**
+ * A utility class containing methods related to processing passwords.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class PasswordUtil
+{
+
+ /** The SHA1 hash length */
+ public static final int SHA1_LENGTH = 20;
+
+ /** The SHA256 hash length */
+ public static final int SHA256_LENGTH = 32;
+
+ /** The SHA384 hash length */
+ public static final int SHA384_LENGTH = 48;
+
+ /** The SHA512 hash length */
+ public static final int SHA512_LENGTH = 64;
+
+ /** The MD5 hash length */
+ public static final int MD5_LENGTH = 16;
+
+
+ /**
+ * Get the algorithm from the stored password.
+ * It can be found on the beginning of the stored password, between
+ * curly brackets.
+ * @param credentials the credentials of the user
+ * @return the name of the algorithm to use
+ */
+ public static LdapSecurityConstants findAlgorithm( byte[] credentials )
+ {
+ if ( ( credentials == null ) || ( credentials.length == 0 ) )
+ {
+ return null;
+ }
+
+ if ( credentials[0] == '{' )
+ {
+ // get the algorithm
+ int pos = 1;
+
+ while ( pos < credentials.length )
+ {
+ if ( credentials[pos] == '}' )
+ {
+ break;
+ }
+
+ pos++;
+ }
+
+ if ( pos < credentials.length )
+ {
+ if ( pos == 1 )
+ {
+ // We don't have an algorithm : return the credentials as is
+ return null;
+ }
+
+ String algorithm = Strings.toLowerCase( new String( credentials, 1, pos - 1 ) );
+
+ return LdapSecurityConstants.getAlgorithm( algorithm );
+ }
+ else
+ {
+ // We don't have an algorithm
+ return null;
+ }
+ }
+ else
+ {
+ // No '{algo}' part
+ return null;
+ }
+ }
+
+
+ /**
+ * @see #createStoragePassword(byte[], LdapSecurityConstants)
+ */
+ public static byte[] createStoragePassword( String credentials, LdapSecurityConstants algorithm )
+ {
+ return createStoragePassword( Strings.getBytesUtf8(credentials), algorithm );
+ }
+
+
+ /**
+ * create a hashed password in a format that can be stored in the server.
+ * If the specified algorithm requires a salt then a random salt of 8 byte size is used
+ *
+ * @param credentials the plain text password
+ * @param algorithm the hashing algorithm to be applied
+ * @return the password after hashing with the given algorithm
+ */
+ public static byte[] createStoragePassword( byte[] credentials, LdapSecurityConstants algorithm )
+ {
+ byte[] salt;
+
+ switch( algorithm )
+ {
+ case HASH_METHOD_SSHA:
+ case HASH_METHOD_SSHA256:
+ case HASH_METHOD_SSHA384:
+ case HASH_METHOD_SSHA512:
+ case HASH_METHOD_SMD5:
+ salt = new byte[8]; // we use 8 byte salt always except for "crypt" which needs 2 byte salt
+ new SecureRandom().nextBytes( salt );
+ break;
+
+ case HASH_METHOD_CRYPT:
+ salt = new byte[2];
+ SecureRandom sr = new SecureRandom();
+ int i1 = sr.nextInt( 64 );
+ int i2 = sr.nextInt( 64 );
+
+ salt[0] = ( byte ) ( i1 < 12 ? ( i1 + '.' ) : i1 < 38 ? ( i1 + 'A' - 12 ) : ( i1 + 'a' - 38 ) );
+ salt[1] = ( byte ) ( i2 < 12 ? ( i2 + '.' ) : i2 < 38 ? ( i2 + 'A' - 12 ) : ( i2 + 'a' - 38 ) );
+ break;
+
+ default:
+ salt = null;
+ }
+
+ byte[] hashedPassword = encryptPassword( credentials, algorithm, salt );
+ StringBuffer sb = new StringBuffer();
+
+ if ( algorithm != null )
+ {
+ sb.append( '{' ).append( algorithm.getName().toUpperCase() ).append( '}' );
+
+ if ( algorithm == LdapSecurityConstants.HASH_METHOD_CRYPT )
+ {
+ sb.append( Strings.utf8ToString(salt) );
+ sb.append( Strings.utf8ToString(hashedPassword) );
+ }
+ else if ( salt != null )
+ {
+ byte[] hashedPasswordWithSaltBytes = new byte[hashedPassword.length + salt.length];
+ merge( hashedPasswordWithSaltBytes, hashedPassword, salt );
+ sb.append( String.valueOf( Base64.encode( hashedPasswordWithSaltBytes ) ) );
+ }
+ else
+ {
+ sb.append( String.valueOf( Base64.encode(hashedPassword) ) );
+ }
+ }
+ else
+ {
+ sb.append( Strings.utf8ToString(hashedPassword) );
+ }
+
+ return Strings.getBytesUtf8(sb.toString());
+ }
+
+
+ /**
+ *
+ * Compare the credentials.
+ * We have at least 6 algorithms to encrypt the password :
+ * <ul>
+ * <li>- SHA</li>
+ * <li>- SSHA (salted SHA)</li>
+ * <li>- SHA-2(256, 384 and 512 and their salted versions)</li>
+ * <li>- MD5</li>
+ * <li>- SMD5 (slated MD5)</li>
+ * <li>- crypt (unix crypt)</li>
+ * <li>- plain text, ie no encryption.</li>
+ * </ul>
+ * <p>
+ * If we get an encrypted password, it is prefixed by the used algorithm, between
+ * brackets : {SSHA}password ...
+ * </p>
+ * If the password is using SSHA, SMD5 or crypt, some 'salt' is added to the password :
+ * <ul>
+ * <li>- length(password) - 20, starting at 21th position for SSHA</li>
+ * <li>- length(password) - 16, starting at 16th position for SMD5</li>
+ * <li>- length(password) - 2, starting at 3rd position for crypt</li>
+ * </ul>
+ * <p>
+ * For (S)SHA, SHA-256 and (S)MD5, we have to transform the password from Base64 encoded text
+ * to a byte[] before comparing the password with the stored one.
+ * </p>
+ * <p>
+ * For crypt, we only have to remove the salt.
+ * </p>
+ * <p>
+ * At the end, we use the digest() method for (S)SHA and (S)MD5, the crypt() method for
+ * the CRYPT algorithm and a straight comparison for PLAIN TEXT passwords.
+ * </p>
+ * <p>
+ * The stored password is always using the unsalted form, and is stored as a bytes array.
+ * </p>
+ *
+ * @param receivedCredentials the credentials provided by user
+ * @param storedCredentials the credentials stored in the server
+ * @return true if they are equal, false otherwise
+ */
+ public static boolean compareCredentials( byte[] receivedCredentials, byte[] storedCredentials )
+ {
+ LdapSecurityConstants algorithm = findAlgorithm( storedCredentials );
+
+ if ( algorithm != null )
+ {
+ EncryptionMethod encryptionMethod = new EncryptionMethod( algorithm, null );
+
+ // Let's get the encrypted part of the stored password
+ // We should just keep the password, excluding the algorithm
+ // and the salt, if any.
+ // But we should also get the algorithm and salt to
+ // be able to encrypt the submitted user password in the next step
+ byte[] encryptedStored = PasswordUtil.splitCredentials( storedCredentials, encryptionMethod );
+
+ // Reuse the saltedPassword informations to construct the encrypted
+ // password given by the user.
+ byte[] userPassword = PasswordUtil.encryptPassword( receivedCredentials, encryptionMethod.getAlgorithm(), encryptionMethod.getSalt() );
+
+ // Now, compare the two passwords.
+ return Arrays.equals( userPassword, encryptedStored );
+ }
+ else
+ {
+ return Arrays.equals( storedCredentials, receivedCredentials );
+ }
+ }
+
+
+ /**
+ * encrypts the given credentials based on the algorithm name and optional salt
+ *
+ * @param credentials the credentials to be encrypted
+ * @param algorithm the algorithm to be used for encrypting the credentials
+ * @param salt value to be used as salt (optional)
+ * @return the encrypted credentials
+ */
+ public static byte[] encryptPassword( byte[] credentials, LdapSecurityConstants algorithm, byte[] salt )
+ {
+ switch ( algorithm )
+ {
+ case HASH_METHOD_SHA:
+ case HASH_METHOD_SSHA:
+ return digest( LdapSecurityConstants.HASH_METHOD_SHA, credentials, salt );
+
+ case HASH_METHOD_SHA256:
+ case HASH_METHOD_SSHA256:
+ return digest( LdapSecurityConstants.HASH_METHOD_SHA256, credentials, salt );
+
+ case HASH_METHOD_SHA384:
+ case HASH_METHOD_SSHA384:
+ return digest( LdapSecurityConstants.HASH_METHOD_SHA384, credentials, salt );
+
+ case HASH_METHOD_SHA512:
+ case HASH_METHOD_SSHA512:
+ return digest( LdapSecurityConstants.HASH_METHOD_SHA512, credentials, salt );
+
+ case HASH_METHOD_MD5:
+ case HASH_METHOD_SMD5:
+ return digest( LdapSecurityConstants.HASH_METHOD_MD5, credentials, salt );
+
+ case HASH_METHOD_CRYPT:
+ String saltWithCrypted = UnixCrypt.crypt( Strings.utf8ToString(credentials), Strings
+ .utf8ToString(salt) );
+ String crypted = saltWithCrypted.substring( 2 );
+
+ return Strings.getBytesUtf8(crypted);
+
+ default:
+ return credentials;
+ }
+ }
+
+
+ /**
+ * Compute the hashed password given an algorithm, the credentials and
+ * an optional salt.
+ *
+ * @param algorithm the algorithm to use
+ * @param password the credentials
+ * @param salt the optional salt
+ * @return the digested credentials
+ */
+ private static byte[] digest( LdapSecurityConstants algorithm, byte[] password, byte[] salt )
+ {
+ MessageDigest digest;
+
+ try
+ {
+ digest = MessageDigest.getInstance( algorithm.getName() );
+ }
+ catch ( NoSuchAlgorithmException e1 )
+ {
+ return null;
+ }
+
+ if ( salt != null )
+ {
+ digest.update( password );
+ digest.update( salt );
+ return digest.digest();
+ }
+ else
+ {
+ return digest.digest( password );
+ }
+ }
+
+
+ /**
+ * Decompose the stored password in an algorithm, an eventual salt
+ * and the password itself.
+ *
+ * If the algorithm is SHA, SSHA, MD5 or SMD5, the part following the algorithm
+ * is base64 encoded
+ *
+ * @param encryptionMethod The structure to feed
+ * @return The password
+ * @param credentials the credentials to split
+ */
+ public static byte[] splitCredentials( byte[] credentials, EncryptionMethod encryptionMethod )
+ {
+ int algoLength = encryptionMethod.getAlgorithm().getName().length() + 2;
+
+ int hashLen = 0;
+
+ switch ( encryptionMethod.getAlgorithm() )
+ {
+ case HASH_METHOD_MD5:
+ case HASH_METHOD_SHA:
+ try
+ {
+ // We just have the password just after the algorithm, base64 encoded.
+ // Just decode the password and return it.
+ return Base64
+ .decode( new String( credentials, algoLength, credentials.length - algoLength, "UTF-8" )
+ .toCharArray() );
+ }
+ catch ( UnsupportedEncodingException uee )
+ {
+ // do nothing
+ return credentials;
+ }
+
+ case HASH_METHOD_SMD5:
+ try
+ {
+ // The password is associated with a salt. Decompose it
+ // in two parts, after having decoded the password.
+ // The salt will be stored into the EncryptionMethod structure
+ // The salt is at the end of the credentials, and is 8 bytes long
+ byte[] passwordAndSalt = Base64.decode( new String( credentials, algoLength, credentials.length
+ - algoLength, "UTF-8" ).toCharArray() );
+
+ int saltLength = passwordAndSalt.length - MD5_LENGTH;
+ encryptionMethod.setSalt( new byte[saltLength] );
+ byte[] password = new byte[MD5_LENGTH];
+ split( passwordAndSalt, 0, password, encryptionMethod.getSalt() );
+
+ return password;
+ }
+ catch ( UnsupportedEncodingException uee )
+ {
+ // do nothing
+ return credentials;
+ }
+
+ case HASH_METHOD_SSHA:
+ hashLen = SHA1_LENGTH;
+
+ case HASH_METHOD_SHA256:
+ case HASH_METHOD_SSHA256:
+ if ( hashLen == 0 )
+ {
+ hashLen = SHA256_LENGTH;
+ }
+
+ case HASH_METHOD_SHA384:
+ case HASH_METHOD_SSHA384:
+ if ( hashLen == 0 )
+ {
+ hashLen = SHA384_LENGTH;
+ }
+
+ case HASH_METHOD_SHA512:
+ case HASH_METHOD_SSHA512:
+ if ( hashLen == 0 )
+ {
+ hashLen = SHA512_LENGTH;
+ }
+
+ try
+ {
+ // The password is associated with a salt. Decompose it
+ // in two parts, after having decoded the password.
+ // The salt will be stored into the EncryptionMethod structure
+ // The salt is at the end of the credentials, and is 8 bytes long
+ byte[] passwordAndSalt = Base64.decode( new String( credentials, algoLength, credentials.length
+ - algoLength, "UTF-8" ).toCharArray() );
+
+ int saltLength = passwordAndSalt.length - hashLen;
+ encryptionMethod.setSalt( new byte[saltLength] );
+ byte[] password = new byte[hashLen];
+ split( passwordAndSalt, 0, password, encryptionMethod.getSalt() );
+
+ return password;
+ }
+ catch ( UnsupportedEncodingException uee )
+ {
+ // do nothing
+ return credentials;
+ }
+
+ case HASH_METHOD_CRYPT:
+ // The password is associated with a salt. Decompose it
+ // in two parts, storing the salt into the EncryptionMethod structure.
+ // The salt comes first, not like for SSHA and SMD5, and is 2 bytes long
+ encryptionMethod.setSalt( new byte[2] );
+ byte[] password = new byte[credentials.length - encryptionMethod.getSalt().length - algoLength];
+ split( credentials, algoLength, encryptionMethod.getSalt(), password );
+
+ return password;
+
+ default:
+ // unknown method
+ return credentials;
+
+ }
+ }
+
+
+ private static void split( byte[] all, int offset, byte[] left, byte[] right )
+ {
+ System.arraycopy( all, offset, left, 0, left.length );
+ System.arraycopy( all, offset + left.length, right, 0, right.length );
+ }
+
+
+ private static void merge( byte[] all, byte[] left, byte[] right )
+ {
+ System.arraycopy( left, 0, all, 0, left.length );
+ System.arraycopy( right, 0, all, left.length, right.length );
+ }
+
+
+ /**
+ * checks if the given password's change time is older than the max age
+ *
+ * @param pwdChangedZtime time when the password was last changed
+ * @param pwdMaxAgeSec the max age value in seconds
+ * @return true if expired, false otherwise
+ */
+ public static boolean isPwdExpired( String pwdChangedZtime, int pwdMaxAgeSec )
+ {
+ Date pwdChangeDate = DateUtils.getDate( pwdChangedZtime );
+
+ long time = pwdMaxAgeSec * 1000;
+ time += pwdChangeDate.getTime();
+
+ Date expiryDate = DateUtils.getDate( DateUtils.getGeneralizedTime( time ) );
+ Date now = DateUtils.getDate( DateUtils.getGeneralizedTime() );
+
+ boolean expired = false;
+
+ if ( expiryDate.equals( now ) || expiryDate.before( now ) )
+ {
+ expired = true;
+ }
+
+ return expired;
+ }
+
+
+ /**
+ * purges failure timestamps which are older than the configured interval
+ * (section 7.6 in the draft)
+ */
+ public static void purgeFailureTimes( PasswordPolicyConfiguration config, Attribute pwdFailTimeAt )
+ {
+ long interval = config.getPwdFailureCountInterval();
+
+ if ( interval == 0 )
+ {
+ return;
+ }
+
+ interval *= 1000;
+
+ long currentTime = DateUtils.getDate( DateUtils.getGeneralizedTime() ).getTime();
+ List<Value<?>> valList = new ArrayList<Value<?>>();
+
+ for ( Value<?> value : pwdFailTimeAt )
+ {
+ String failureTime = value.getString();
+ long time = DateUtils.getDate( failureTime ).getTime();
+ time += interval;
+
+ if ( currentTime > time )
+ {
+ valList.add( value );
+ }
+ }
+
+ for ( Value<?> val : valList )
+ {
+ pwdFailTimeAt.remove( val );
+ }
+ }
+}
Added: directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/DefaultPasswordValidator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/DefaultPasswordValidator.java?rev=1183660&view=auto
==============================================================================
--- directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/DefaultPasswordValidator.java (added)
+++ directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/DefaultPasswordValidator.java Sat Oct 15 15:26:42 2011
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.directory.server.core.shared.authn.ppolicy;
+
+
+/**
+ * The default password validator.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DefaultPasswordValidator implements PasswordValidator
+{
+
+ /** the default validator's instance */
+ public final static DefaultPasswordValidator INSTANCE = new DefaultPasswordValidator();
+
+
+ /**
+ * Creates a new instance of DefaultPasswordValidator.
+ */
+ public DefaultPasswordValidator()
+ {
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void validate( String password, String entryRdnVal ) throws PasswordPolicyException
+ {
+ checkUsernameSubstring( password, entryRdnVal );
+ //TODO add more checks
+ }
+
+
+ /**
+ * The password does not contain three letter (or more) tokens from the user's account name.
+ *
+ * If the account name is less than three characters long, this check is not performed
+ * because the rate at which passwords would be rejected is too high. For each token that is
+ * three or more characters long, that token is searched for in the password; if it is present,
+ * the password change is rejected. For example, the name "First M. Last" would be split into
+ * three tokens: "First", "M", and "Last". Because the second token is only one character long,
+ * it would be ignored. Therefore, this user could not have a password that included either
+ * "first" or "last" as a substring anywhere in the password. All of these checks are
+ * case-insensitive.
+ */
+ private void checkUsernameSubstring( String password, String username ) throws PasswordPolicyException
+ {
+ if ( username == null || username.trim().length() == 0 )
+ {
+ return;
+ }
+
+ String[] tokens = username.split( "[^a-zA-Z]" );
+
+ for ( int ii = 0; ii < tokens.length; ii++ )
+ {
+ if ( password.matches( "(?i).*" + tokens[ii] + ".*" ) )
+ {
+ throw new PasswordPolicyException( "Password shouldn't contain parts of the username", 5 );// 5 == PasswordPolicyErrorEnum.INSUFFICIENT_PASSWORD_QUALITY
+ }
+ }
+ }
+
+}
Added: directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordPolicyConfiguration.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordPolicyConfiguration.java?rev=1183660&view=auto
==============================================================================
--- directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordPolicyConfiguration.java (added)
+++ directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordPolicyConfiguration.java Sat Oct 15 15:26:42 2011
@@ -0,0 +1,529 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.directory.server.core.shared.authn.ppolicy;
+
+
+import org.apache.directory.shared.ldap.model.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+
+
+/**
+ * A simple pojo holding the password policy configuration base on
+ * <a href="http://tools.ietf.org/html/draft-behera-ldap-password-policy-10">this draft</a>.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class PasswordPolicyConfiguration
+{
+ /** the name of the attribute to which the password policy is applied.
+ * Currently only "userPassword" attribute is supported
+ */
+ private String pwdAttribute = SchemaConstants.USER_PASSWORD_AT;
+
+ /**
+ * holds the number of seconds that must elapse between modifications to the password.
+ * Default value is 0
+ */
+ private int pwdMinAge = 0;
+
+ /**
+ * holds the number of seconds after which a modified password will expire.
+ * Default value is 0, does not expire. If not 0, the value must be greater than or equal
+ * to the value of the pwdMinAge.
+ */
+ private int pwdMaxAge = 0;
+
+ /**
+ * specifies the maximum number of used passwords stored in the pwdHistory attribute.
+ * Default value is 0, no password history maintained
+ */
+ private int pwdInHistory = 0;
+
+ /** indicates how the password quality will be verified while being modified or added.
+ * Default value 0, do not check
+ */
+ private int pwdCheckQuality = 0;
+
+ /** this attribute holds the minimum number of characters that must be used in a password.
+ * Default value 0, no minimum length enforced
+ */
+ private int pwdMinLength = 0;
+
+ /**
+ * this attribute holds the maximum number of characters that may be used in a password.
+ * Default value 0, no maximum length enforced
+ */
+ private int pwdMaxLength = 0;
+
+ /**
+ * the maximum number of seconds before a password is due to expire that expiration warning
+ * messages will be returned to an authenticating user.
+ * Default value is 0, never send a warning message.
+ */
+ private int pwdExpireWarning = 0;
+
+ /**
+ * the number of times an expired password can be used to authenticate.
+ * Default value is 0, do not allow a expired password for authentication.
+ */
+ private int pwdGraceAuthNLimit = 0;
+
+ /**
+ * specifies the number of seconds the grace authentications are valid
+ * Default value is 0, no limit.
+ */
+ private int pwdGraceExpire = 0;
+
+ /**
+ * flag to indicate if the account needs to be locked after a specified number of
+ * consecutive failed bind attempts. The maximum number of consecutive
+ * failed bind attempts is specified in {@link #pwdMaxFailure}
+ */
+ private boolean pwdLockout = false;
+
+ /**
+ * the number of seconds that the password cannot be used to authenticate due to
+ * too many failed bind attempts.
+ * Default value is 300 seconds.
+ */
+ private int pwdLockoutDuration = 300;
+
+ /**
+ * the number of consecutive failed bind attempts after which the password may not
+ * be used to authenticate.
+ * Default value is 0, no limit on the number of authentication failures
+ */
+ private int pwdMaxFailure = 0;
+
+ /**
+ * the number of seconds after which the password failures are purged from the failure counter.
+ * Default value is 0, reset all pwdFailureTimes after a successful authentication.
+ */
+ private int pwdFailureCountInterval = 0;
+
+ /**
+ * flag to indicate if the password must be changed by the user after they bind to the
+ * directory after a password is set or reset by a password administrator.
+ * Default value is false, no need to change the password by user.
+ */
+ private boolean pwdMustChange = false;
+
+ /** indicates whether users can change their own passwords. Default value is true, allow change */
+ private boolean pwdAllowUserChange = true;
+
+ /**
+ * flag to specify whether or not the existing password must be sent along with the
+ * new password when being changed.
+ * Default value is false.
+ */
+ private boolean pwdSafeModify = false;
+
+ /**
+ * the number of seconds to delay responding to the first failed authentication attempt
+ * Default value 0, no delay.
+ */
+ private int pwdMinDelay = 0;
+
+ /** the maximum number of seconds to delay when responding to a failed authentication attempt.*/
+ private int pwdMaxDelay = 0;
+
+ /**
+ * the number of seconds an account may remain unused before it becomes locked
+ * Default value is 0, no check for idle time.
+ */
+ private int pwdMaxIdle = 0;
+
+ /** validator used for checking the quality of password */
+ //TODO to be injected from config
+ private PasswordValidator pwdValidator = DefaultPasswordValidator.INSTANCE;
+
+ public String getPwdAttribute()
+ {
+ return pwdAttribute;
+ }
+
+
+ public void setPwdAttribute( String pwdAttribute )
+ {
+ this.pwdAttribute = pwdAttribute;
+ }
+
+
+ public int getPwdMinAge()
+ {
+ return pwdMinAge;
+ }
+
+
+ public void setPwdMinAge( int pwdMinAge )
+ {
+ this.pwdMinAge = pwdMinAge;
+ }
+
+
+ public int getPwdMaxAge()
+ {
+ return pwdMaxAge;
+ }
+
+
+ public void setPwdMaxAge( int pwdMaxAge )
+ {
+ this.pwdMaxAge = pwdMaxAge;
+ }
+
+
+ public int getPwdInHistory()
+ {
+ return pwdInHistory;
+ }
+
+
+ public void setPwdInHistory( int pwdInHistory )
+ {
+ this.pwdInHistory = pwdInHistory;
+ }
+
+
+ public int getPwdCheckQuality()
+ {
+ return pwdCheckQuality;
+ }
+
+
+ public void setPwdCheckQuality( int pwdCheckQuality )
+ {
+ this.pwdCheckQuality = pwdCheckQuality;
+ }
+
+
+ public int getPwdMinLength()
+ {
+ return pwdMinLength;
+ }
+
+
+ public void setPwdMinLength( int pwdMinLength )
+ {
+ this.pwdMinLength = pwdMinLength;
+ }
+
+
+ public int getPwdMaxLength()
+ {
+ return pwdMaxLength;
+ }
+
+
+ public void setPwdMaxLength( int pwdMaxLength )
+ {
+ this.pwdMaxLength = pwdMaxLength;
+ }
+
+
+ public int getPwdExpireWarning()
+ {
+ return pwdExpireWarning;
+ }
+
+
+ public void setPwdExpireWarning( int pwdExpireWarning )
+ {
+ this.pwdExpireWarning = pwdExpireWarning;
+ }
+
+
+ public int getPwdGraceAuthNLimit()
+ {
+ return pwdGraceAuthNLimit;
+ }
+
+
+ public void setPwdGraceAuthNLimit( int pwdGraceAuthNLimit )
+ {
+ this.pwdGraceAuthNLimit = pwdGraceAuthNLimit;
+ }
+
+
+ public int getPwdGraceExpire()
+ {
+ return pwdGraceExpire;
+ }
+
+
+ public void setPwdGraceExpire( int pwdGraceExpire )
+ {
+ this.pwdGraceExpire = pwdGraceExpire;
+ }
+
+
+ public boolean isPwdLockout()
+ {
+ return pwdLockout;
+ }
+
+
+ public void setPwdLockout( boolean pwdLockout )
+ {
+ this.pwdLockout = pwdLockout;
+ }
+
+
+ public int getPwdLockoutDuration()
+ {
+ return pwdLockoutDuration;
+ }
+
+
+ public void setPwdLockoutDuration( int pwdLockoutDuration )
+ {
+ this.pwdLockoutDuration = pwdLockoutDuration;
+ }
+
+
+ public int getPwdMaxFailure()
+ {
+ return pwdMaxFailure;
+ }
+
+
+ public void setPwdMaxFailure( int pwdMaxFailure )
+ {
+ this.pwdMaxFailure = pwdMaxFailure;
+ }
+
+
+ public int getPwdFailureCountInterval()
+ {
+ return pwdFailureCountInterval;
+ }
+
+
+ public void setPwdFailureCountInterval( int pwdFailureCountInterval )
+ {
+ this.pwdFailureCountInterval = pwdFailureCountInterval;
+ }
+
+
+ public boolean isPwdMustChange()
+ {
+ return pwdMustChange;
+ }
+
+
+ public void setPwdMustChange( boolean pwdMustChange )
+ {
+ this.pwdMustChange = pwdMustChange;
+ }
+
+
+ public boolean isPwdAllowUserChange()
+ {
+ return pwdAllowUserChange;
+ }
+
+
+ public void setPwdAllowUserChange( boolean pwdAllowUserChange )
+ {
+ this.pwdAllowUserChange = pwdAllowUserChange;
+ }
+
+
+ public boolean isPwdSafeModify()
+ {
+ return pwdSafeModify;
+ }
+
+
+ public void setPwdSafeModify( boolean pwdSafeModify )
+ {
+ this.pwdSafeModify = pwdSafeModify;
+ }
+
+
+ public int getPwdMinDelay()
+ {
+ return pwdMinDelay;
+ }
+
+
+ public void setPwdMinDelay( int pwdMinDelay )
+ {
+ this.pwdMinDelay = pwdMinDelay;
+ }
+
+
+ public int getPwdMaxDelay()
+ {
+ return pwdMaxDelay;
+ }
+
+
+ public void setPwdMaxDelay( int pwdMaxDelay )
+ {
+ this.pwdMaxDelay = pwdMaxDelay;
+ }
+
+
+ public int getPwdMaxIdle()
+ {
+ return pwdMaxIdle;
+ }
+
+
+ public void setPwdMaxIdle( int pwdMaxIdle )
+ {
+ this.pwdMaxIdle = pwdMaxIdle;
+ }
+
+
+ /**
+ * @return the pwdValidator
+ */
+ public PasswordValidator getPwdValidator()
+ {
+ return pwdValidator;
+ }
+
+
+ /**
+ * @param pwdValidator the pwdValidator to set
+ */
+ public void setPwdValidator( PasswordValidator pwdValidator )
+ {
+ this.pwdValidator = pwdValidator;
+ }
+
+
+ /**
+ * validates the policy configuration and throws a LdapException if there are any errors
+ *
+ * @throws LdapException if there are any errors in the configuration
+ */
+ public void validate() throws LdapException
+ {
+ StringBuilder sb = new StringBuilder();
+
+ int errCount = 0;
+
+ if ( pwdMinAge < 0 )
+ {
+ sb.append( ++errCount ).append( ". password minimum age cannot be negative\n" );
+ }
+
+ if ( pwdMaxAge < 0 )
+ {
+ sb.append( ++errCount ).append( ". password maximum age cannot be negative\n" );
+ }
+
+ if ( ( pwdMaxAge > 0 ) && ( pwdMaxAge < pwdMinAge ) )
+ {
+ sb.append( ++errCount ).append( ". password maximum age should be greater than the minimum age\n" );
+ }
+
+ if ( pwdInHistory < 0 )
+ {
+ sb.append( ++errCount ).append( ". password history count cannot be negative\n" );
+ }
+
+ if ( ( pwdCheckQuality < 0 ) || ( pwdCheckQuality > 2 ) )
+ {
+ sb.append( ++errCount ).append( ". invalid password quality check value, valid values are 0, 1 and 2 \n" );
+ }
+
+ if ( pwdMinLength < 0 )
+ {
+ sb.append( ++errCount ).append( ". password minimum length cannot be negative\n" );
+ }
+
+ if ( pwdMaxLength < 0 )
+ {
+ sb.append( ++errCount ).append( ". password maximum length cannot be negative\n" );
+ }
+
+ if ( ( pwdMaxLength > 0 ) && ( pwdMaxLength < pwdMinLength ) )
+ {
+ sb.append( ++errCount ).append( ". password maximum length should be greater than minimum length\n" );
+ }
+
+ if ( pwdExpireWarning < 0 )
+ {
+ sb.append( ++errCount ).append( ". password expire warning time cannot be negative\n" );
+ }
+
+ if ( pwdGraceAuthNLimit < 0 )
+ {
+ sb.append( ++errCount ).append( ". password grace authentication limits cannot be negative\n" );
+ }
+
+ if ( pwdGraceExpire < 0 )
+ {
+ sb.append( ++errCount ).append( ". password grace expiration time cannot be negative\n" );
+ }
+
+ if ( pwdLockoutDuration < 0 )
+ {
+ sb.append( ++errCount ).append( ". password lockout duration time cannot be negative\n" );
+ }
+
+ if ( pwdMaxFailure < 0 )
+ {
+ sb.append( ++errCount ).append( ". password maximum failure count cannot be negative\n" );
+ }
+
+ if ( pwdFailureCountInterval < 0 )
+ {
+ sb.append( ++errCount ).append( ". password failure count interval time cannot be negative\n" );
+ }
+
+ if ( ( ( pwdMinDelay > 0 ) && ( pwdMaxDelay <= 0 ) )
+ || ( ( pwdMaxDelay > 0 ) && ( pwdMinDelay <= 0 ) ) )
+ {
+ sb
+ .append( ++errCount )
+ .append(
+ ". if password minimum or maximum delay time is specified then the correspomding maximu or minimum delay time should also be specified\n" );
+ }
+ else
+ // just to avoid both warnings
+ {
+ if ( pwdMinDelay < 0 )
+ {
+ sb.append( ++errCount ).append( ". password minimum delay time cannot be negative\n" );
+ }
+
+ if ( pwdMaxDelay < 0 )
+ {
+ sb.append( ++errCount ).append( ". password maximum delay time cannot be negative\n" );
+ }
+ }
+
+ if ( pwdMaxIdle < 0 )
+ {
+ sb.append( ++errCount ).append( ". password maximum idle time cannot be negative\n" );
+ }
+
+ if ( errCount > 0 )
+ {
+ throw new LdapException( "There are errors in password policy configuration\n" + sb.toString() );
+ }
+ }
+}
Added: directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordPolicyException.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordPolicyException.java?rev=1183660&view=auto
==============================================================================
--- directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordPolicyException.java (added)
+++ directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordPolicyException.java Sat Oct 15 15:26:42 2011
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.directory.server.core.shared.authn.ppolicy;
+
+
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+
+
+/**
+ * A exception class defined for PasswordPolicy related errors.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class PasswordPolicyException extends LdapException
+{
+ private static final long serialVersionUID = -9158126177779964262L;
+
+ /** password policy error code */
+ private int errorCode;
+
+ /** the array of valid error codes representing password policy errors */
+ private static final int[] VALID_CODES = {0, 1, 2, 3, 4, 5, 6, 7, 8};
+
+ public PasswordPolicyException( Throwable cause )
+ {
+ super( cause );
+ }
+
+
+ public PasswordPolicyException( String message )
+ {
+ super( message );
+ }
+
+
+ public PasswordPolicyException( String message, int errorCode )
+ {
+ super( message );
+ validateErrorCode( errorCode );
+ this.errorCode = errorCode;
+ }
+
+
+ public PasswordPolicyException( int errorCode )
+ {
+ validateErrorCode( errorCode );
+ this.errorCode = errorCode;
+ }
+
+
+ public int getErrorCode()
+ {
+ return errorCode;
+ }
+
+
+ /**
+ * this method checks if the given error code is valid or not.
+ * This method was created cause using PasswordPolicyErrorEnum class creates some
+ * unwanted dependency issues on core-api
+ *
+ * @param errorCode the error code of password policy
+ */
+ private void validateErrorCode( int errorCode )
+ {
+ for ( int i : VALID_CODES )
+ {
+ if ( i == errorCode )
+ {
+ return;
+ }
+ }
+
+ throw new IllegalArgumentException( "Unknown password policy response error code " + errorCode );
+ }
+}
Added: directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordValidator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordValidator.java?rev=1183660&view=auto
==============================================================================
--- directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordValidator.java (added)
+++ directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/authn/ppolicy/PasswordValidator.java Sat Oct 15 15:26:42 2011
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.directory.server.core.shared.authn.ppolicy;
+
+/**
+ * An interface for implementing password quality verifiers.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface PasswordValidator
+{
+ /**
+ * checks if the given password meets the required quality contraints.<br>
+ * <p>Note: the length based validations are already done before calling this method<br>
+ *
+ * so the implementor should concentrate on the content checking.</p>
+ *
+ * @param password the password value
+ * @param entryRdnVal the value of entry's RDN(typically this is the username) e.x 'admin' if the entry's DN is {uid/cn/etc..}=admin,ou=system
+ * @throws PasswordPolicyException if the password doesn't meet the quality contraints
+ */
+ void validate( String password, String entryRdnVal ) throws PasswordPolicyException;
+}
Added: directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/ExpressionEvaluator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/ExpressionEvaluator.java?rev=1183660&view=auto
==============================================================================
--- directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/ExpressionEvaluator.java (added)
+++ directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/ExpressionEvaluator.java Sat Oct 15 15:26:42 2011
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.directory.server.core.shared.event;
+
+
+import org.apache.directory.server.core.api.event.Evaluator;
+import org.apache.directory.server.i18n.I18n;
+import org.apache.directory.shared.ldap.model.entry.Entry;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+import org.apache.directory.shared.ldap.model.exception.LdapInvalidSearchFilterException;
+import org.apache.directory.shared.ldap.model.filter.AndNode;
+import org.apache.directory.shared.ldap.model.filter.BranchNode;
+import org.apache.directory.shared.ldap.model.filter.ExprNode;
+import org.apache.directory.shared.ldap.model.filter.NotNode;
+import org.apache.directory.shared.ldap.model.filter.OrNode;
+import org.apache.directory.shared.ldap.model.name.Dn;
+import org.apache.directory.shared.ldap.model.schema.SchemaManager;
+
+
+
+/**
+ * Top level filter expression evaluator implementation.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class ExpressionEvaluator implements Evaluator
+{
+ /** Leaf Evaluator flyweight use for leaf filter assertions */
+ private LeafEvaluator leafEvaluator;
+
+
+ // ------------------------------------------------------------------------
+ // C O N S T R U C T O R S
+ // ------------------------------------------------------------------------
+ /**
+ * Creates a top level Evaluator where leaves are delegated to a leaf node
+ * evaluator which is already provided.
+ *
+ * @param leafEvaluator handles leaf node evaluation.
+ */
+ public ExpressionEvaluator( LeafEvaluator leafEvaluator )
+ {
+ this.leafEvaluator = leafEvaluator;
+ }
+
+
+ /**
+ * Creates a top level Evaluator where leaves are delegated to a leaf node
+ * evaluator which will be created.
+ *
+ * @param schemaManager The server schemaManager
+ */
+ public ExpressionEvaluator( SchemaManager schemaManager )
+ {
+ SubstringEvaluator substringEvaluator = null;
+ substringEvaluator = new SubstringEvaluator();
+// leafEvaluator = new LeafEvaluator( schemaManager, substringEvaluator );
+ leafEvaluator = new LeafEvaluator( substringEvaluator );
+ }
+
+
+ /**
+ * Gets the leaf evaluator used by this top level expression evaluator.
+ *
+ * @return the leaf evaluator used by this top level expression evaluator
+ */
+ public LeafEvaluator getLeafEvaluator()
+ {
+ return leafEvaluator;
+ }
+
+
+ // ------------------------------------------------------------------------
+ // Evaluator.evaluate() implementation
+ // ------------------------------------------------------------------------
+ /**
+ * {@inheritDoc}
+ */
+ public boolean evaluate( ExprNode node, Dn dn, Entry entry ) throws LdapException
+ {
+ if ( node.isLeaf() )
+ {
+ return leafEvaluator.evaluate( node, dn, entry );
+ }
+
+ BranchNode bnode = ( BranchNode ) node;
+
+ if ( bnode instanceof OrNode )
+ {
+ for ( ExprNode child: bnode.getChildren() )
+ {
+ if ( evaluate( child, dn, entry ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ else if ( bnode instanceof AndNode)
+ {
+ for ( ExprNode child: bnode.getChildren() )
+ {
+ boolean res = evaluate( child, dn, entry );
+
+ if ( !res )
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ else if ( bnode instanceof NotNode)
+ {
+ if ( null != bnode.getFirstChild() )
+ {
+ return !evaluate( bnode.getFirstChild(), dn, entry );
+ }
+
+ throw new LdapInvalidSearchFilterException( I18n.err( I18n.ERR_243, node ) );
+ }
+ else
+ {
+ throw new LdapInvalidSearchFilterException( I18n.err( I18n.ERR_244, bnode ) );
+ }
+ }
+}
Added: directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/LeafEvaluator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/LeafEvaluator.java?rev=1183660&view=auto
==============================================================================
--- directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/LeafEvaluator.java (added)
+++ directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/LeafEvaluator.java Sat Oct 15 15:26:42 2011
@@ -0,0 +1,392 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.directory.server.core.shared.event;
+
+
+import java.util.Comparator;
+
+import org.apache.directory.server.core.api.event.Evaluator;
+import org.apache.directory.server.i18n.I18n;
+import org.apache.directory.shared.ldap.model.entry.Attribute;
+import org.apache.directory.shared.ldap.model.entry.Entry;
+import org.apache.directory.shared.ldap.model.entry.Value;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+import org.apache.directory.shared.ldap.model.exception.LdapInvalidSearchFilterException;
+import org.apache.directory.shared.ldap.model.filter.ApproximateNode;
+import org.apache.directory.shared.ldap.model.filter.EqualityNode;
+import org.apache.directory.shared.ldap.model.filter.ExprNode;
+import org.apache.directory.shared.ldap.model.filter.ExtensibleNode;
+import org.apache.directory.shared.ldap.model.filter.GreaterEqNode;
+import org.apache.directory.shared.ldap.model.filter.LessEqNode;
+import org.apache.directory.shared.ldap.model.filter.PresenceNode;
+import org.apache.directory.shared.ldap.model.filter.ScopeNode;
+import org.apache.directory.shared.ldap.model.filter.SimpleNode;
+import org.apache.directory.shared.ldap.model.filter.SubstringNode;
+import org.apache.directory.shared.ldap.model.name.Dn;
+import org.apache.directory.shared.ldap.model.schema.AttributeType;
+import org.apache.directory.shared.ldap.model.schema.LdapComparator;
+import org.apache.directory.shared.ldap.model.schema.MatchingRule;
+import org.apache.directory.shared.ldap.model.schema.Normalizer;
+import org.apache.directory.shared.util.exception.NotImplementedException;
+
+
+/**
+ * Evaluates LeafNode assertions on candidates using a database.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class LeafEvaluator implements Evaluator
+{
+ /** equality matching type constant */
+ private static final int EQUALITY_MATCH = 0;
+
+ /** ordering matching type constant */
+ private static final int ORDERING_MATCH = 1;
+
+ /** substring matching type constant */
+ private static final int SUBSTRING_MATCH = 3;
+
+// /** SchemaManager needed for normalizing and comparing values */
+// private SchemaManager schemaManager;
+
+ /** Substring node evaluator we depend on */
+ private SubstringEvaluator substringEvaluator;
+
+ /** ScopeNode evaluator we depend on */
+ private ScopeEvaluator scopeEvaluator;
+
+ /** Constants used for comparisons */
+ private static final boolean COMPARE_GREATER = true;
+ private static final boolean COMPARE_LESSER = false;
+
+
+ /**
+ * Creates a leaf expression node evaluator.
+ *
+ * @param schemaManager The server schemaManager
+ */
+ public LeafEvaluator( SubstringEvaluator substringEvaluator )
+ {
+ this.scopeEvaluator = new ScopeEvaluator();
+ this.substringEvaluator = substringEvaluator;
+ }
+
+
+// /**
+// * Creates a leaf expression node evaluator.
+// *
+// * @param schemaManager The server schemaManager
+// */
+// public LeafEvaluator( SchemaManager schemaManager,
+// SubstringEvaluator substringEvaluator )
+// {
+// this.schemaManager = schemaManager;
+// this.scopeEvaluator = new ScopeEvaluator();
+// this.substringEvaluator = substringEvaluator;
+// }
+
+
+ public ScopeEvaluator getScopeEvaluator()
+ {
+ return scopeEvaluator;
+ }
+
+
+ public SubstringEvaluator getSubstringEvaluator()
+ {
+ return substringEvaluator;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean evaluate( ExprNode node, Dn dn, Entry entry ) throws LdapException
+ {
+ if ( node instanceof ScopeNode )
+ {
+ return scopeEvaluator.evaluate( node, dn, entry );
+ }
+
+ if ( node instanceof PresenceNode )
+ {
+ return evalPresence( ( ( PresenceNode ) node ).getAttributeType(), entry );
+ }
+ else if ( ( node instanceof EqualityNode ) || ( node instanceof ApproximateNode ) )
+ {
+ return evalEquality( ( EqualityNode<?> ) node, entry );
+ }
+ else if ( node instanceof GreaterEqNode )
+ {
+ return evalGreaterOrLesser( ( GreaterEqNode<?> ) node, entry, COMPARE_GREATER );
+ }
+ else if ( node instanceof LessEqNode)
+ {
+ return evalGreaterOrLesser( ( LessEqNode<?> ) node, entry, COMPARE_LESSER );
+ }
+ else if ( node instanceof SubstringNode )
+ {
+ return substringEvaluator.evaluate( node, dn, entry );
+ }
+ else if ( node instanceof ExtensibleNode )
+ {
+ throw new NotImplementedException();
+ }
+ else
+ {
+ throw new LdapInvalidSearchFilterException( I18n.err( I18n.ERR_245, node ) );
+ }
+ }
+
+
+ /**
+ * Evaluates a simple greater than or less than attribute value assertion on
+ * a perspective candidate.
+ *
+ * @param node the greater than or less than node to evaluate
+ * @param entry the perspective candidate
+ * @param isGreater true if it is a greater than or equal to comparison,
+ * false if it is a less than or equal to comparison.
+ * @return the ava evaluation on the perspective candidate
+ * @throws LdapException if there is a database access failure
+ */
+ @SuppressWarnings("unchecked")
+ private boolean evalGreaterOrLesser( SimpleNode<?> node, Entry entry, boolean isGreaterOrLesser )
+ throws LdapException
+ {
+ AttributeType attributeType = node.getAttributeType();
+
+ // get the attribute associated with the node
+ Attribute attr = entry.get( node.getAttribute() );
+
+ // If we do not have the attribute just return false
+ if ( null == attr )
+ {
+ return false;
+ }
+
+ /*
+ * We need to iterate through all values and for each value we normalize
+ * and use the comparator to determine if a match exists.
+ */
+ Normalizer normalizer = getNormalizer( attributeType );
+ Comparator comparator = getComparator( attributeType );
+ Object filterValue = normalizer.normalize( node.getValue() );
+
+ /*
+ * Cheaper to not check isGreater in one loop - better to separate
+ * out into two loops which you choose to execute based on isGreater
+ */
+ if ( isGreaterOrLesser == COMPARE_GREATER )
+ {
+ for ( Value<?> value : attr )
+ {
+ Object normValue = normalizer.normalize( value );
+
+ // Found a value that is greater than or equal to the ava value
+ if ( comparator.compare( normValue, filterValue ) >= 0 )
+ {
+ return true;
+ }
+ }
+ }
+ else
+ {
+ for ( Value<?> value : attr )
+ {
+ Object normValue = normalizer.normalize( value );
+
+ // Found a value that is less than or equal to the ava value
+ if ( comparator.compare( normValue, filterValue ) <= 0 )
+ {
+ return true;
+ }
+ }
+ }
+
+ // no match so return false
+ return false;
+ }
+
+
+ /**
+ * Evaluates a simple presence attribute value assertion on a perspective
+ * candidate.
+ *
+ * @param attrId the name of the attribute tested for presence
+ * @param entry the perspective candidate
+ * @return the ava evaluation on the perspective candidate
+ */
+ private boolean evalPresence( AttributeType attributeType, Entry entry ) throws LdapException
+ {
+ if ( entry == null )
+ {
+ return false;
+ }
+
+ return null != entry.get( attributeType );
+ }
+
+
+ /**
+ * Evaluates a simple equality attribute value assertion on a perspective
+ * candidate.
+ *
+ * @param node the equality node to evaluate
+ * @param entry the perspective candidate
+ * @return the ava evaluation on the perspective candidate
+ * @throws org.apache.directory.shared.ldap.model.exception.LdapException if there is a database access failure
+ */
+ @SuppressWarnings("unchecked")
+ private boolean evalEquality( EqualityNode<?> node, Entry entry ) throws LdapException
+ {
+ Normalizer normalizer = getNormalizer( node.getAttributeType() );
+ Comparator comparator = getComparator( node.getAttributeType() );
+
+ // get the attribute associated with the node
+ Attribute attr = entry.get( node.getAttribute() );
+
+ // If we do not have the attribute just return false
+ if ( null == attr )
+ {
+ return false;
+ }
+
+ // check if Ava value exists in attribute
+ AttributeType attributeType = node.getAttributeType();
+ Value<?> value = null;
+
+ if ( attributeType.getSyntax().isHumanReadable() )
+ {
+ if ( node.getValue().isHumanReadable() )
+ {
+ value = node.getValue();
+ }
+ else
+ {
+ value = new org.apache.directory.shared.ldap.model.entry.StringValue( node.getValue().getString() );
+ }
+ }
+ else
+ {
+ value = node.getValue();
+ }
+
+ if ( attr.contains( value ) )
+ {
+ return true;
+ }
+
+ // get the normalized Ava filter value
+ Value<?> filterValue = normalizer.normalize( value );
+
+ // check if the normalized value is present
+ if ( attr.contains( filterValue ) )
+ {
+ return true;
+ }
+
+ /*
+ * We need to now iterate through all values because we could not get
+ * a lookup to work. For each value we normalize and use the comparator
+ * to determine if a match exists.
+ */
+ for ( Value<?> val : attr )
+ {
+ Value<?> normValue = normalizer.normalize( val );
+
+ if ( 0 == comparator.compare( normValue.getValue(), filterValue.getValue() ) )
+ {
+ return true;
+ }
+ }
+
+ // no match so return false
+ return false;
+ }
+
+
+ /**
+ * Gets the comparator for equality matching.
+ *
+ * @param attributeType the attributeType
+ * @return the comparator for equality matching
+ * @throws LdapException if there is a failure
+ */
+ private LdapComparator<? super Object> getComparator( AttributeType attributeType ) throws LdapException
+ {
+ MatchingRule mrule = getMatchingRule( attributeType, EQUALITY_MATCH );
+
+ return mrule.getLdapComparator();
+ }
+
+
+ /**
+ * Gets the normalizer for equality matching.
+ *
+ * @param attributeType the attributeType
+ * @return the normalizer for equality matching
+ * @throws LdapException if there is a failure
+ */
+ private Normalizer getNormalizer( AttributeType attributeType ) throws LdapException
+ {
+ MatchingRule mrule = getMatchingRule( attributeType, EQUALITY_MATCH );
+
+ return mrule.getNormalizer();
+ }
+
+
+ /**
+ * Gets the matching rule for an attributeType.
+ *
+ * @param attributeType the attributeType
+ * @return the matching rule
+ * @throws LdapException if there is a failure
+ */
+ private MatchingRule getMatchingRule( AttributeType attributeType, int matchType ) throws LdapException
+ {
+ MatchingRule mrule = null;
+
+ switch ( matchType )
+ {
+ case ( EQUALITY_MATCH ):
+ mrule = attributeType.getEquality();
+ break;
+
+ case ( SUBSTRING_MATCH ):
+ mrule = attributeType.getSubstring();
+ break;
+
+ case ( ORDERING_MATCH ):
+ mrule = attributeType.getOrdering();
+ break;
+
+ default:
+ throw new LdapException( I18n.err( I18n.ERR_246, matchType ) );
+ }
+
+ if ( ( mrule == null ) && ( matchType != EQUALITY_MATCH ) )
+ {
+ mrule = attributeType.getEquality();
+ }
+
+ return mrule;
+ }
+}
Added: directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/ScopeEvaluator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/ScopeEvaluator.java?rev=1183660&view=auto
==============================================================================
--- directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/ScopeEvaluator.java (added)
+++ directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/ScopeEvaluator.java Sat Oct 15 15:26:42 2011
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.directory.server.core.shared.event;
+
+
+import org.apache.directory.server.core.api.event.Evaluator;
+import org.apache.directory.server.i18n.I18n;
+import org.apache.directory.shared.ldap.model.entry.Entry;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+import org.apache.directory.shared.ldap.model.exception.LdapInvalidSearchFilterException;
+import org.apache.directory.shared.ldap.model.filter.ExprNode;
+import org.apache.directory.shared.ldap.model.filter.ScopeNode;
+import org.apache.directory.shared.ldap.model.name.Dn;
+
+
+/**
+ * Evaluates ScopeNode assertions on candidates.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class ScopeEvaluator implements Evaluator
+{
+ public ScopeEvaluator()
+ {
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean evaluate( ExprNode node, Dn dn, Entry record ) throws LdapException
+ {
+ ScopeNode snode = ( ScopeNode ) node;
+
+ switch ( snode.getScope() )
+ {
+ case OBJECT:
+ return dn.equals( snode.getBaseDn() );
+
+ case ONELEVEL:
+ if ( dn.isDescendantOf( snode.getBaseDn() ) )
+ {
+ return ( snode.getBaseDn().size() + 1 ) == dn.size();
+ }
+
+ case SUBTREE:
+ return dn.isDescendantOf( snode.getBaseDn() );
+
+ default:
+ throw new LdapInvalidSearchFilterException( I18n.err( I18n.ERR_247 ) );
+ }
+ }
+}
Added: directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/SubstringEvaluator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/SubstringEvaluator.java?rev=1183660&view=auto
==============================================================================
--- directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/SubstringEvaluator.java (added)
+++ directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/event/SubstringEvaluator.java Sat Oct 15 15:26:42 2011
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.directory.server.core.shared.event;
+
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.directory.server.core.api.event.Evaluator;
+import org.apache.directory.server.i18n.I18n;
+import org.apache.directory.shared.ldap.model.entry.Entry;
+import org.apache.directory.shared.ldap.model.entry.Attribute;
+import org.apache.directory.shared.ldap.model.entry.Value;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+import org.apache.directory.shared.ldap.model.exception.LdapInvalidSearchFilterException;
+import org.apache.directory.shared.ldap.model.filter.ExprNode;
+import org.apache.directory.shared.ldap.model.filter.SubstringNode;
+import org.apache.directory.shared.ldap.model.name.Dn;
+import org.apache.directory.shared.ldap.model.schema.AttributeType;
+import org.apache.directory.shared.ldap.model.schema.MatchingRule;
+import org.apache.directory.shared.ldap.model.schema.Normalizer;
+
+
+/**
+ * Evaluates substring filter assertions on an entry.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class SubstringEvaluator implements Evaluator
+{
+ /**
+ * Creates a new SubstringEvaluator for substring expressions.
+ */
+ public SubstringEvaluator()
+ {
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean evaluate( ExprNode node, Dn dn, Entry entry ) throws LdapException
+ {
+ Pattern regex = null;
+ SubstringNode snode = (SubstringNode)node;
+ AttributeType attributeType = snode.getAttributeType();
+ MatchingRule matchingRule = attributeType.getSubstring();
+
+ if ( matchingRule == null )
+ {
+ matchingRule = attributeType.getEquality();
+ }
+
+ Normalizer normalizer = matchingRule.getNormalizer();
+
+
+ // get the attribute
+ Attribute attr = entry.get( snode.getAttribute() );
+
+ // if the attribute does not exist just return false
+ if ( null == attr )
+ {
+ return false;
+ }
+
+ // compile the regular expression to search for a matching attribute
+ try
+ {
+ regex = snode.getRegex( normalizer );
+ }
+ catch ( PatternSyntaxException pse )
+ {
+ LdapInvalidSearchFilterException ne = new LdapInvalidSearchFilterException( I18n.err( I18n.ERR_248, node ) );
+ ne.initCause( pse );
+ throw ne;
+ }
+
+ /*
+ * Cycle through the attribute values testing normalized version
+ * obtained from using the substring matching rule's normalizer.
+ * The test uses the comparator obtained from the appropriate
+ * substring matching rule.
+ */
+
+ for ( Value<?> value: attr )
+ {
+ String normValue = normalizer.normalize( value.getString() );
+
+ // Once match is found cleanup and return true
+
+ if ( regex.matcher( normValue ).matches() )
+ {
+ return true;
+ }
+ }
+
+ // we fell through so a match was not found - assertion was false.
+ return false;
+ }
+}