directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From elecha...@apache.org
Subject svn commit: r1185571 [1/2] - in /directory/apacheds/trunk: core-api/src/main/java/org/apache/directory/server/core/api/authn/ core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/ core-api/src/main/java/org/apache/directory/server/...
Date Tue, 18 Oct 2011 11:14:42 GMT
Author: elecharny
Date: Tue Oct 18 11:14:40 2011
New Revision: 1185571

URL: http://svn.apache.org/viewvc?rev=1185571&view=rev
Log:
Moved interceptor helper classes from core-shared to core-api

Added:
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/EncryptionMethod.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/PasswordUtil.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/DefaultPasswordValidator.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordPolicyConfiguration.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordPolicyException.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordValidator.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/normalization/
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/normalization/FilterNormalizingVisitor.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/LdapClassLoader.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/StoredProcEngine.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/StoredProcEngineConfig.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/StoredProcExecutionManager.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/StoredProcUtils.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/java/
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/java/JavaStoredProcEngine.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/java/JavaStoredProcEngineConfig.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/java/LdapJavaStoredProcClassLoader.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/subtree/RefinementEvaluator.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/subtree/RefinementLeafEvaluator.java
    directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/subtree/SubentryUtils.java
    directory/apacheds/trunk/core-api/src/test/java/org/apache/directory/server/core/api/sp/
    directory/apacheds/trunk/core-api/src/test/java/org/apache/directory/server/core/api/sp/StoredProcUtilsTest.java
Removed:
    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/event/
    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/sp/LdapClassLoader.java
    directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/sp/StoredProcEngine.java
    directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/sp/StoredProcEngineConfig.java
    directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/sp/StoredProcExecutionManager.java
    directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/sp/StoredProcUtils.java
    directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/sp/java/
    directory/apacheds/trunk/core-shared/src/main/java/org/apache/directory/server/core/shared/subtree/
    directory/apacheds/trunk/core-shared/src/test/java/org/apache/directory/server/core/shared/sp/
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/DefaultCoreSession.java
Modified:
    directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/authn/ppolicy/PasswordPolicyTest.java
    directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/operations/lookup/LookupPerfIT.java
    directory/apacheds/trunk/core-integ/src/test/java/org/apache/directory/server/core/sp/LdapClassLoaderIT.java
    directory/apacheds/trunk/core-jndi/src/main/java/org/apache/directory/server/core/jndi/ServerContext.java
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/DefaultDirectoryService.java
    directory/apacheds/trunk/core/src/test/java/org/apache/directory/server/core/interceptor/InterceptorChainTest.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/src/main/java/org/apache/directory/server/core/authz/AciAuthorizationInterceptor.java
    directory/apacheds/trunk/interceptors/authz/src/main/java/org/apache/directory/server/core/authz/support/ACDFEngine.java
    directory/apacheds/trunk/interceptors/authz/src/main/java/org/apache/directory/server/core/authz/support/RelatedProtectedItemFilter.java
    directory/apacheds/trunk/interceptors/authz/src/test/java/org/apache/directory/server/core/authz/support/RelatedProtectedItemFilterTest.java
    directory/apacheds/trunk/interceptors/event/src/main/java/org/apache/directory/server/core/event/DefaultEventService.java
    directory/apacheds/trunk/interceptors/hash/src/main/java/org/apache/directory/server/core/hash/PasswordHashingInterceptor.java
    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/src/main/java/org/apache/directory/server/core/schema/PartitionSchemaLoader.java
    directory/apacheds/trunk/interceptors/subtree/src/test/java/org/apache/directory/server/core/subtree/RefinementEvaluatorTest.java
    directory/apacheds/trunk/interceptors/subtree/src/test/java/org/apache/directory/server/core/subtree/RefinementLeafEvaluatorTest.java
    directory/apacheds/trunk/interceptors/subtree/src/test/java/org/apache/directory/server/core/subtree/SubtreeEvaluatorTest.java
    directory/apacheds/trunk/interceptors/trigger/src/main/java/org/apache/directory/server/core/trigger/TriggerInterceptor.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/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/extended/StoredProcedureExtendedOperationHandler.java
    directory/apacheds/trunk/service-builder/src/main/java/org/apache/directory/server/config/builder/ServiceBuilder.java

Added: directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/EncryptionMethod.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/EncryptionMethod.java?rev=1185571&view=auto
==============================================================================
--- directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/EncryptionMethod.java (added)
+++ directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/EncryptionMethod.java Tue Oct 18 11:14:40 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.api.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-api/src/main/java/org/apache/directory/server/core/api/authn/PasswordUtil.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/PasswordUtil.java?rev=1185571&view=auto
==============================================================================
--- directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/PasswordUtil.java (added)
+++ directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/PasswordUtil.java Tue Oct 18 11:14:40 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.api.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.api.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-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/DefaultPasswordValidator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/DefaultPasswordValidator.java?rev=1185571&view=auto
==============================================================================
--- directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/DefaultPasswordValidator.java (added)
+++ directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/DefaultPasswordValidator.java Tue Oct 18 11:14:40 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.api.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-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordPolicyConfiguration.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordPolicyConfiguration.java?rev=1185571&view=auto
==============================================================================
--- directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordPolicyConfiguration.java (added)
+++ directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordPolicyConfiguration.java Tue Oct 18 11:14:40 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.api.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-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordPolicyException.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordPolicyException.java?rev=1185571&view=auto
==============================================================================
--- directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordPolicyException.java (added)
+++ directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordPolicyException.java Tue Oct 18 11:14:40 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.api.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-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordValidator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordValidator.java?rev=1185571&view=auto
==============================================================================
--- directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordValidator.java (added)
+++ directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/authn/ppolicy/PasswordValidator.java Tue Oct 18 11:14:40 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.api.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>
+     *       &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+     *       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-api/src/main/java/org/apache/directory/server/core/api/normalization/FilterNormalizingVisitor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/normalization/FilterNormalizingVisitor.java?rev=1185571&view=auto
==============================================================================
--- directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/normalization/FilterNormalizingVisitor.java (added)
+++ directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/normalization/FilterNormalizingVisitor.java Tue Oct 18 11:14:40 2011
@@ -0,0 +1,511 @@
+/*
+ *  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.api.normalization;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.directory.shared.ldap.model.entry.StringValue;
+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.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.ExtensibleNode;
+import org.apache.directory.shared.ldap.model.filter.FilterVisitor;
+import org.apache.directory.shared.ldap.model.filter.LeafNode;
+import org.apache.directory.shared.ldap.model.filter.NotNode;
+import org.apache.directory.shared.ldap.model.filter.PresenceNode;
+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.schema.AttributeType;
+import org.apache.directory.shared.ldap.model.schema.SchemaManager;
+import org.apache.directory.shared.ldap.model.schema.normalizers.NameComponentNormalizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A filter visitor which normalizes leaf node values as it visits them.  It also removes
+ * leaf nodes from branches whose attributeType is undefined.  It obviously cannot remove
+ * a leaf node from a filter which is only a leaf node.  Checks to see if a filter is a
+ * leaf node with undefined attributeTypes should be done outside this visitor.
+ *
+ * Since this visitor may remove filter nodes it may produce negative results on filters,
+ * like NOT branch nodes without a child or AND and OR nodes with one or less children.  This
+ * might make some partition implementations choke.  To avoid this problem we clean up branch
+ * nodes that don't make sense.  For example all BranchNodes without children are just
+ * removed.  An AND and OR BranchNode with a single child is replaced with it's child for
+ * all but the topmost branch node which we cannot replace.  So again the top most branch
+ * node must be inspected by code outside of this visitor.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class FilterNormalizingVisitor implements FilterVisitor
+{
+    /** logger used by this class */
+    private static final Logger log = LoggerFactory.getLogger( FilterNormalizingVisitor.class );
+
+    /** the name component normalizer used by this visitor */
+    private final NameComponentNormalizer ncn;
+
+    /** the SchemaManager instance used to resolve OIDs for attributeType ids */
+    private final SchemaManager schemaManager;
+
+    /**
+     * Chars which need to be escaped in a filter
+     * '\0' | '(' | ')' | '*' | '\'
+     */
+    private static final boolean[] FILTER_CHAR =
+        { true, false, false, false, false, false, false, false, // 00 -> 07 NULL
+            false, false, false, false, false, false, false, false, // 08 -> 0F
+            false, false, false, false, false, false, false, false, // 10 -> 17
+            false, false, false, false, false, false, false, false, // 18 -> 1F
+            false, false, false, false, false, false, false, false, // 20 -> 27
+            true, true, true, false, false, false, false, false, // 28 -> 2F '(', ')', '*'
+            false, false, false, false, false, false, false, false, // 30 -> 37
+            false, false, false, false, false, false, false, false, // 38 -> 3F 
+            false, false, false, false, false, false, false, false, // 40 -> 47
+            false, false, false, false, false, false, false, false, // 48 -> 4F
+            false, false, false, false, false, false, false, false, // 50 -> 57
+            false, false, false, false, true, false, false, false, // 58 -> 5F '\'
+            false, false, false, false, false, false, false, false, // 60 -> 67
+            false, false, false, false, false, false, false, false, // 68 -> 6F
+            false, false, false, false, false, false, false, false, // 70 -> 77
+            false, false, false, false, false, false, false, false // 78 -> 7F
+        };
+
+
+    /**
+     * Check if the given char is a filter escaped char
+     * &lt;filterEscapedChars&gt; ::= '\0' | '(' | ')' | '*' | '\'
+     *
+     * @param c the char we want to test
+     * @return true if the char is a pair char only
+     */
+    public static boolean isFilterChar( char c )
+    {
+        return ( ( ( c | 0x7F ) == 0x7F ) && FILTER_CHAR[c & 0x7f] );
+    }
+
+
+    /**
+     * 
+     * Creates a new instance of NormalizingVisitor.
+     *
+     * @param ncn The name component normalizer to use
+     * @param schemaManager The schemaManager
+     */
+    public FilterNormalizingVisitor( NameComponentNormalizer ncn, SchemaManager schemaManager )
+    {
+        this.ncn = ncn;
+        this.schemaManager = schemaManager;
+    }
+
+
+    /**
+     * A private method used to normalize a value. At this point, the value
+     * is a Value<byte[]>, we have to translate it to a Value<String> if its
+     * AttributeType is H-R. Then we have to normalize the value accordingly
+     * to the AttributeType Normalizer.
+     * 
+     * @param attribute The attribute's ID
+     * @param value The value to normalize
+     * @return the normalized value
+     */
+    private Value<?> normalizeValue( AttributeType attributeType, Value<?> value )
+    {
+        try
+        {
+            Value<?> normalized = null;
+
+            if ( attributeType.getSyntax().isHumanReadable() )
+            {
+                normalized = new StringValue( ( String ) ncn.normalizeByName( attributeType.getOid(), value.getString() ) );
+            }
+            else
+            {
+                normalized = ( Value<?> ) ncn.normalizeByName( attributeType.getOid(), value.getBytes() );
+            }
+
+            return normalized;
+        }
+        catch ( LdapException ne )
+        {
+            log.warn( "Failed to normalize filter value: {}", ne.getLocalizedMessage(), ne );
+            return null;
+        }
+    }
+
+
+    /**
+     * Visit a PresenceNode. If the attribute exists, the node is returned, otherwise
+     * null is returned.
+     * 
+     * @param node the node to visit
+     * @return The visited node
+     */
+    private ExprNode visitPresenceNode( PresenceNode node ) throws LdapException
+    {
+        // still need this check here in case the top level is a leaf node
+        // with an undefined attributeType for its attribute
+        if ( !ncn.isDefined( node.getAttribute() ) )
+        {
+            return null;
+        }
+
+        node.setAttributeType( schemaManager.lookupAttributeTypeRegistry( node.getAttribute() ) );
+
+        return node;
+    }
+
+
+    /**
+     * Visit a SimpleNode. If the attribute exists, the node is returned, otherwise
+     * null is returned. SimpleNodes are :
+     *  - ApproximateNode
+     *  - EqualityNode
+     *  - GreaterEqNode
+     *  - LesserEqNode
+     *  
+     * @param node the node to visit
+     * @return the visited node
+     */
+    private ExprNode visitSimpleNode( SimpleNode node ) throws LdapException
+    {
+        if ( node.getAttributeType() == null )
+        {
+            // still need this check here in case the top level is a leaf node
+            // with an undefined attributeType for its attribute
+            if ( !ncn.isDefined( node.getAttribute() ) )
+            {
+                return null;
+            }
+            
+            node.setAttributeType( schemaManager.lookupAttributeTypeRegistry( node.getAttribute() ) );
+        }
+        
+        Value<?> normalized = normalizeValue( node.getAttributeType(), node.getValue() );
+
+        if ( normalized == null )
+        {
+            return null;
+        }
+
+        node.setValue( normalized );
+
+        return node;
+    }
+
+
+    /**
+     * Visit a SubstringNode. If the attribute exists, the node is returned, otherwise
+     * null is returned. 
+     * 
+     * Normalizing substring value is pretty complex. It's not currently implemented...
+     * 
+     * @param node the node to visit
+     * @return the visited node
+     */
+    private ExprNode visitSubstringNode( SubstringNode node ) throws LdapException
+    {
+        // still need this check here in case the top level is a leaf node
+        // with an undefined attributeType for its attribute
+        if ( !ncn.isDefined( node.getAttribute() ) )
+        {
+            return null;
+        }
+
+        node.setAttributeType( schemaManager.lookupAttributeTypeRegistry( node.getAttribute() ) );
+
+        Value<?> normInitial = null;
+
+        if ( node.getInitial() != null )
+        {
+            normInitial = normalizeValue( node.getAttributeType(), new StringValue( node.getInitial() ) );
+
+            if ( normInitial == null )
+            {
+                return null;
+            }
+        }
+
+        List<String> normAnys = null;
+
+        if ( ( node.getAny() != null ) && ( node.getAny().size() != 0 ) )
+        {
+            normAnys = new ArrayList<String>( node.getAny().size() );
+
+            for ( String any : node.getAny() )
+            {
+                Value<?> normAny = normalizeValue( node.getAttributeType(), new StringValue( any ) );
+
+                if ( normAny != null )
+                {
+                    normAnys.add( normAny.getString() );
+                }
+            }
+
+            if ( normAnys.size() == 0 )
+            {
+                return null;
+            }
+        }
+
+        Value<?> normFinal = null;
+
+        if ( node.getFinal() != null )
+        {
+            normFinal = normalizeValue( node.getAttributeType(), new StringValue( node.getFinal() ) );
+
+            if ( normFinal == null )
+            {
+                return null;
+            }
+        }
+
+        if ( normInitial != null )
+        {
+            node.setInitial( normInitial.getString() );
+        }
+        else
+        {
+            node.setInitial( null );
+        }
+
+        node.setAny( normAnys );
+
+        if ( normFinal != null )
+        {
+            node.setFinal( normFinal.getString() );
+        }
+        else
+        {
+            node.setFinal( null );
+        }
+
+        return node;
+    }
+
+
+    /**
+     * Visit a ExtensibleNode. If the attribute exists, the node is returned, otherwise
+     * null is returned. 
+     * 
+     * TODO implement the logic for ExtensibleNode
+     * 
+     * @param node the node to visit
+     * @return the visited node
+     */
+    private ExprNode visitExtensibleNode( ExtensibleNode node ) throws LdapException
+    {
+        // still need this check here in case the top level is a leaf node
+        // with an undefined attributeType for its attribute
+        if ( !ncn.isDefined( node.getAttribute() ) )
+        {
+            return null;
+        }
+
+        node.setAttributeType( schemaManager.lookupAttributeTypeRegistry( node.getAttribute() ) );
+
+        return node;
+    }
+
+
+    /**
+     * Visit a BranchNode. BranchNodes are :
+     *  - AndNode
+     *  - NotNode
+     *  - OrNode
+     *  
+     * @param node the node to visit
+     * @return the visited node
+     */
+    private ExprNode visitBranchNode( BranchNode node )
+    {
+        // Two differente cases :
+        // - AND or OR
+        // - NOT
+
+        if ( node instanceof NotNode )
+        {
+            // Manage the NOT
+            ExprNode child = node.getFirstChild();
+
+            ExprNode result = ( ExprNode ) visit( child );
+
+            if ( result == null )
+            {
+                return null;
+            }
+            else if ( result instanceof BranchNode )
+            {
+                List<ExprNode> newChildren = new ArrayList<ExprNode>( 1 );
+                newChildren.add( result );
+                node.setChildren( newChildren );
+                return node;
+            }
+            else if ( result instanceof LeafNode)
+            {
+                List<ExprNode> newChildren = new ArrayList<ExprNode>( 1 );
+                newChildren.add( result );
+                node.setChildren( newChildren );
+                return node;
+            }
+        }
+        else
+        {
+            // Manage AND and OR nodes.
+            BranchNode branchNode = node;
+            List<ExprNode> children = node.getChildren();
+
+            // For AND and OR, we may have more than one children.
+            // We may have to remove some of them, so let's create
+            // a new handler to store the correct nodes.
+            List<ExprNode> newChildren = new ArrayList<ExprNode>( children.size() );
+
+            // Now, iterate through all the children
+            for ( int i = 0; i < children.size(); i++ )
+            {
+                ExprNode child = children.get( i );
+
+                ExprNode result = ( ExprNode ) visit( child );
+
+                if ( result != null )
+                {
+                    // As the node is correct, add it to the children 
+                    // list.
+                    newChildren.add( result );
+                }
+            }
+
+            if ( ( branchNode instanceof AndNode ) && ( newChildren.size() != children.size() ) )
+            {
+                return null;
+            }
+
+            if ( newChildren.size() == 0 )
+            {
+                // No more children, return null
+                return null;
+            }
+            else if ( newChildren.size() == 1 )
+            {
+                // As we only have one child, return it
+                // to the caller.
+                return newChildren.get( 0 );
+            }
+            else
+            {
+                branchNode.setChildren( newChildren );
+            }
+        }
+
+        return node;
+    }
+
+
+    /**
+     * Visit the tree, normalizing the leaves and recusrsively visit the branches.
+     * 
+     * Here are the leaves we are visiting :
+     * - PresenceNode ( attr =* )
+     * - ExtensibleNode ( ? )
+     * - SubStringNode ( attr = *X*Y* )
+     * - ApproximateNode ( attr ~= value )
+     * - EqualityNode ( attr = value )
+     * - GreaterEqNode ( attr >= value )
+     * - LessEqNode ( attr <= value )
+     * 
+     * The PresencNode is managed differently from other nodes, as it just check
+     * for the attribute, not the value.
+     * 
+     * @param node the node to visit
+     * @return the visited node
+     */
+    public Object visit( ExprNode node )
+    {
+        try
+        {
+            // -------------------------------------------------------------------
+            // Handle PresenceNodes
+            // -------------------------------------------------------------------
+
+            if ( node instanceof PresenceNode )
+            {
+                return visitPresenceNode( ( PresenceNode ) node );
+            }
+
+            // -------------------------------------------------------------------
+            // Handle BranchNodes (AndNode, NotNode and OrNode)
+            // -------------------------------------------------------------------
+
+            else if ( node instanceof BranchNode )
+            {
+                return visitBranchNode( ( BranchNode ) node );
+            }
+
+            // -------------------------------------------------------------------
+            // Handle SimpleNodes (ApproximateNode, EqualityNode, GreaterEqNode,
+            // and LesserEqNode) 
+            // -------------------------------------------------------------------
+
+            else if ( node instanceof SimpleNode )
+            {
+                return visitSimpleNode( ( SimpleNode ) node );
+            }
+            else if ( node instanceof ExtensibleNode )
+            {
+                return visitExtensibleNode( ( ExtensibleNode ) node );
+            }
+            else if ( node instanceof SubstringNode )
+            {
+                return visitSubstringNode( ( SubstringNode ) node );
+            }
+            else
+            {
+                return null;
+            }
+        }
+        catch ( LdapException e )
+        {
+            throw new RuntimeException( e );
+        }
+    }
+
+
+    public boolean canVisit( ExprNode node )
+    {
+        return true;
+    }
+
+
+    public boolean isPrefix()
+    {
+        return false;
+    }
+
+
+    public List<ExprNode> getOrder( BranchNode node, List<ExprNode> children )
+    {
+        return children;
+    }
+}

Added: directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/LdapClassLoader.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/LdapClassLoader.java?rev=1185571&view=auto
==============================================================================
--- directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/LdapClassLoader.java (added)
+++ directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/LdapClassLoader.java Tue Oct 18 11:14:40 2011
@@ -0,0 +1,211 @@
+/*
+ *  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.api.sp;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.directory.server.constants.ApacheSchemaConstants;
+import org.apache.directory.server.core.api.DirectoryService;
+import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
+import org.apache.directory.server.i18n.I18n;
+import org.apache.directory.shared.ldap.model.constants.SchemaConstants;
+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.StringValue;
+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.filter.AndNode;
+import org.apache.directory.shared.ldap.model.filter.BranchNode;
+import org.apache.directory.shared.ldap.model.filter.EqualityNode;
+import org.apache.directory.shared.ldap.model.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.model.message.SearchScope;
+import org.apache.directory.shared.ldap.model.name.Dn;
+import org.apache.directory.shared.ldap.model.schema.AttributeType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A class loader that loads classes from an LDAP DIT.
+ * 
+ * <p>
+ * This loader looks for an configuration entry whose Dn is
+ * determined by defaultSearchContextsConfig variable. If there is such
+ * an entry it gets the search contexts from the entry and searches the 
+ * class to be loaded in those contexts.
+ * If there is no default search context configuration entry it searches
+ * the class in the whole DIT. 
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class LdapClassLoader extends ClassLoader
+{
+    private static final Logger LOG = LoggerFactory.getLogger( LdapClassLoader.class );
+    public static final String DEFAULT_SEARCH_CONTEXTS_CONFIG = "cn=classLoaderDefaultSearchContext,ou=configuration,ou=system";
+    
+    private Dn defaultSearchDn;
+    private DirectoryService directoryService;
+
+    /** A storage for the ObjectClass attributeType */
+    private AttributeType OBJECT_CLASS_AT;
+
+    
+    public LdapClassLoader( DirectoryService directoryService ) throws LdapException
+    {
+        super( LdapClassLoader.class.getClassLoader() );
+        this.directoryService = directoryService;
+        defaultSearchDn = directoryService.getDnFactory().create( DEFAULT_SEARCH_CONTEXTS_CONFIG );
+        
+        OBJECT_CLASS_AT = directoryService.getSchemaManager().getAttributeType( SchemaConstants.OBJECT_CLASS_AT );
+    }
+
+    
+    private byte[] findClassInDIT( List<Dn> searchContexts, String name ) throws ClassNotFoundException
+    {
+        // Set up the search filter
+        BranchNode filter = new AndNode( );
+        AttributeType fqjcnAt = directoryService.getSchemaManager().getAttributeType( "fullyQualifiedJavaClassName" );
+        filter.addNode( new EqualityNode<String>( fqjcnAt, new StringValue( name ) ) );
+        filter.addNode( new EqualityNode<String>( OBJECT_CLASS_AT,
+            new StringValue( ApacheSchemaConstants.JAVA_CLASS_OC ) ) );
+        
+        try
+        {
+            for ( Dn base : searchContexts )
+            {
+                EntryFilteringCursor cursor = null;
+                try
+                {
+                    cursor = directoryService.getAdminSession()
+                        .search( base, SearchScope.SUBTREE, filter, AliasDerefMode.DEREF_ALWAYS, null );
+                    
+                    cursor.beforeFirst();
+                    if ( cursor.next() ) // there should be only one!
+                    {
+                        LOG.debug( "Class {} found under {} search context.", name, base );
+                        Entry classEntry = cursor.get();
+
+                        if ( cursor.next() )
+                        {
+                            Entry other = cursor.get();
+                            LOG.warn( "More than one class found on classpath at locations: {} \n\tand {}", 
+                                classEntry, other );
+                        }
+
+                        return classEntry.get( "javaClassByteCode" ).getBytes();
+                    }
+                }
+                finally
+                {
+                    if ( cursor != null )
+                    {
+                        cursor.close();
+                    }
+                }
+            }
+        }
+        catch ( Exception e )
+        {
+            LOG.error( I18n.err( I18n.ERR_69, name ), e );
+        }
+
+        throw new ClassNotFoundException();
+    }
+    
+    
+    public Class<?> findClass( String name ) throws ClassNotFoundException
+    {
+        byte[] classBytes = null;
+
+        try 
+        {   
+            // TODO we should cache this information and register with the event
+            // service to get notified if this changes so we can update the cached
+            // copy - there's absolutely no reason why we should be performing this
+            // lookup every time!!!
+            
+            Entry configEntry = null;
+            
+            try
+            {
+                configEntry = directoryService.getAdminSession().lookup( defaultSearchDn );
+            }
+            catch ( LdapException e )
+            {
+                LOG.debug( "No configuration data found for class loader default search contexts." );
+            }
+            
+            if ( configEntry != null )
+            {
+                List<Dn> searchContexts = new ArrayList<Dn>();
+                Attribute attr = configEntry.get( "classLoaderDefaultSearchContext" );
+                
+                for ( Value<?> val : attr )
+                {
+                    Dn dn = directoryService.getDnFactory().create( val.getString() );
+                    searchContexts.add( dn );
+                }
+                
+                try
+                {
+                    classBytes = findClassInDIT( searchContexts, name );
+                    
+                    LOG.debug( "Class " + name + " found under default search contexts." );
+                }
+                catch ( ClassNotFoundException e )
+                {
+                    LOG.debug( "Class " + name + " could not be found under default search contexts." );
+                }
+            }
+            
+            if ( classBytes == null )
+            {
+                List<Dn> namingContexts = new ArrayList<Dn>();
+                
+                Set<String> suffixes = directoryService.getPartitionNexus().listSuffixes();
+
+                for ( String suffix:suffixes )
+                {
+                    Dn dn = directoryService.getDnFactory().create( suffix );
+                    namingContexts.add( dn );
+                }
+                
+                classBytes = findClassInDIT( namingContexts, name );
+            }
+        } 
+        catch ( ClassNotFoundException e )
+        {
+            String msg = I18n.err( I18n.ERR_293, name );
+            LOG.debug( msg );
+            throw new ClassNotFoundException( msg );
+        }
+        catch ( Exception e ) 
+        {
+            String msg = I18n.err( I18n.ERR_70, name );
+            LOG.error( msg, e );
+            throw new ClassNotFoundException( msg );
+        }
+        
+        return defineClass( name, classBytes, 0, classBytes.length );
+    }
+}

Added: directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/StoredProcEngine.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/StoredProcEngine.java?rev=1185571&view=auto
==============================================================================
--- directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/StoredProcEngine.java (added)
+++ directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/StoredProcEngine.java Tue Oct 18 11:14:40 2011
@@ -0,0 +1,62 @@
+/*
+ *  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.api.sp;
+
+
+import org.apache.directory.server.core.api.CoreSession;
+import org.apache.directory.shared.ldap.model.entry.Entry;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+
+
+/**
+ * An abstraction over stored procedure execution depending on the type of the language supported.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface StoredProcEngine
+{
+    
+    /**
+     * Returns the unique identifier of the supported stored procedure language.
+     * 
+     */
+    public String getSPLangId();
+    
+    
+    /**
+     * Registers an entry found to be contaning a stored procedure unit which this engine can operate on.
+     *
+     * <p>
+     * This method should be called before an attempt to invoke a stored procedure via this Engine.
+     */
+    public void setSPUnitEntry( final Entry spUnit );
+    
+    
+    /**
+     * Invokes the stored procedure handled by the engine.
+     * 
+     * @param fullSPName A fully qualified name of the stored procedure including its unit name.
+     * @param spArgs A list or arguments to be passed to the stored procedure. It should be an empty array if there aren't any parameters defined.
+     * @return The value obtained from invoked procedure. The client should know what will return exactly so that it can downcast to the appropriate type.
+     * @throws org.apache.directory.shared.ldap.model.exception.LdapException If an error occurs during invocation.
+     */
+    public Object invokeProcedure( CoreSession session, String fullSPName, Object[] spArgs ) throws LdapException;
+
+}

Added: directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/StoredProcEngineConfig.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/StoredProcEngineConfig.java?rev=1185571&view=auto
==============================================================================
--- directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/StoredProcEngineConfig.java (added)
+++ directory/apacheds/trunk/core-api/src/main/java/org/apache/directory/server/core/api/sp/StoredProcEngineConfig.java Tue Oct 18 11:14:40 2011
@@ -0,0 +1,45 @@
+/*
+ *  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.api.sp;
+
+
+/**
+ * A configuration wrapper for {@link StoredProcEngine}s.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface StoredProcEngineConfig
+{
+    /**
+     * Returns the type of the associated {@link StoredProcEngine}.
+     * 
+     */
+    public Class<? extends StoredProcEngine> getStoredProcEngineType();
+    
+    
+    /**
+     * Returns the unique language identifier of the {@link StoredProcEngine}.
+     * 
+     */
+    public String getStoredProcLangId();
+
+}



Mime
View raw message