directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From erodrig...@apache.org
Subject svn commit: r541123 [5/25] - in /directory/apacheds/branches/apacheds-sasl-branch: ./ benchmarks/ bootstrap-extract/ bootstrap-extract/src/ bootstrap-extract/src/main/ bootstrap-extract/src/main/java/ bootstrap-extract/src/main/java/org/ bootstrap-extr...
Date Thu, 24 May 2007 00:27:07 GMT
Modified: directory/apacheds/branches/apacheds-sasl-branch/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-sasl-branch/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java?view=diff&rev=541123&r1=541122&r2=541123
==============================================================================
--- directory/apacheds/branches/apacheds-sasl-branch/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java (original)
+++ directory/apacheds/branches/apacheds-sasl-branch/core/src/main/java/org/apache/directory/server/core/authn/SimpleAuthenticator.java Wed May 23 17:26:40 2007
@@ -20,31 +20,47 @@
 package org.apache.directory.server.core.authn;
 
 
+import java.io.UnsupportedEncodingException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
-import java.util.WeakHashMap;
 
 import javax.naming.Context;
 import javax.naming.NamingException;
 import javax.naming.directory.Attribute;
 import javax.naming.directory.Attributes;
 
+import org.apache.commons.collections.map.LRUMap;
+import org.apache.directory.server.core.authz.AuthorizationService;
+import org.apache.directory.server.core.authz.DefaultAuthorizationService;
+import org.apache.directory.server.core.collective.CollectiveAttributeService;
+import org.apache.directory.server.core.event.EventService;
+import org.apache.directory.server.core.exception.ExceptionService;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
 import org.apache.directory.server.core.invocation.Invocation;
 import org.apache.directory.server.core.invocation.InvocationStack;
 import org.apache.directory.server.core.jndi.ServerContext;
+import org.apache.directory.server.core.normalization.NormalizationService;
+import org.apache.directory.server.core.operational.OperationalAttributeService;
 import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.referral.ReferralService;
+import org.apache.directory.server.core.schema.SchemaService;
+import org.apache.directory.server.core.subtree.SubentryService;
 import org.apache.directory.server.core.trigger.TriggerService;
 import org.apache.directory.shared.ldap.aci.AuthenticationLevel;
+import org.apache.directory.shared.ldap.constants.LdapSecurityConstants;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
 import org.apache.directory.shared.ldap.exception.LdapAuthenticationException;
 import org.apache.directory.shared.ldap.name.LdapDN;
 import org.apache.directory.shared.ldap.util.ArrayUtils;
 import org.apache.directory.shared.ldap.util.Base64;
 import org.apache.directory.shared.ldap.util.StringTools;
+import org.apache.directory.shared.ldap.util.UnixCrypt;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -56,125 +72,532 @@
  * password is stored with a one-way encryption applied (e.g. SHA), the password
  * is hashed the same way before comparison.
  * 
+ * We use a cache to speedup authentication, where the DN/password are stored.
+ * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
 public class SimpleAuthenticator extends AbstractAuthenticator
 {
     private static final Logger log = LoggerFactory.getLogger( SimpleAuthenticator.class );
-    private static final Collection USERLOOKUP_BYPASS;
+    
+    /** A speedup for logger in debug mode */
+    private static final boolean IS_DEBUG = log.isDebugEnabled();
 
-    private WeakHashMap<String, byte[]> credentialCache = new WeakHashMap<String, byte[]>( 1000 );
+    /**
+     * A cache to store passwords. It's a speedup, we will be able to avoid backend lookups.
+     * 
+     * Note that the backend also use a cache mechanism, but for performance gain, it's good 
+     * to manage a cache here. The main problem is that when a user modify his password, we will
+     * have to update it at three different places :
+     * - in the backend,
+     * - in the partition cache,
+     * - in this cache.
+     * 
+     * The update of the backend and partition cache is already correctly handled, so we will
+     * just have to offer an access to refresh the local cache.
+     * 
+     * We need to be sure that frequently used passwords be always in cache, and not discarded.
+     * We will use a LRU cache for this purpose. 
+     */ 
+    private LRUMap credentialCache;
     
+    /** Declare a default for this cache. 100 entries seems to be enough */
+    private static final int DEFAULT_CACHE_SIZE = 100;
+
+    /**
+     * Define the interceptors we should *not* go through when we will have to request the backend
+     * about a userPassword.
+     */
+    private static final Collection USERLOOKUP_BYPASS;
     static
     {
         Set<String> c = new HashSet<String>();
-        c.add( "normalizationService" );
-        c.add( "collectiveAttributeService" );
-        c.add( "authenticationService" );
-        c.add( "authorizationService" );
-        c.add( "defaultAuthorizationService" );
-        c.add( "schemaService" );
-        c.add( "subentryService" );
-        c.add( "operationalAttributeService" );
-        c.add( "eventService" );
-        c.add( TriggerService.SERVICE_NAME );
+        c.add( NormalizationService.NAME );
+        c.add( AuthenticationService.NAME );
+        c.add( ReferralService.NAME );
+        c.add( AuthorizationService.NAME );
+        c.add( DefaultAuthorizationService.NAME );
+        c.add( ExceptionService.NAME );
+        c.add( OperationalAttributeService.NAME );
+        c.add( SchemaService.NAME );
+        c.add( SubentryService.NAME );
+        c.add( CollectiveAttributeService.NAME );
+        c.add( EventService.NAME );
+        c.add( TriggerService.NAME );
         USERLOOKUP_BYPASS = Collections.unmodifiableCollection( c );
     }
 
 
     /**
      * Creates a new instance.
+     * @
      */
+    @SuppressWarnings( "unchecked" )
     public SimpleAuthenticator()
     {
         super( "simple" );
+        
+        credentialCache = new LRUMap( DEFAULT_CACHE_SIZE );
     }
 
+    /**
+     * Creates a new instance, with an initial cache size
+     */
+    @SuppressWarnings( "unchecked" )
+    public SimpleAuthenticator( int cacheSize)
+    {
+        super( "simple" );
+
+        credentialCache = new LRUMap( cacheSize > 0 ? cacheSize : DEFAULT_CACHE_SIZE );
+    }
+
+    /**
+     * A private 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([<salt (8 bytes)>]<password>)
+     * - crypt : <salt (2 btytes)><password> 
+     * 
+     * Algorithm are currently MD5, SMD5, SHA, SSHA, CRYPT and empty
+     */
+    private class EncryptionMethod
+    {
+        private byte[] salt;
+        private String algorithm;
+        
+        private EncryptionMethod( String algorithm, byte[] salt )
+        {
+        	this.algorithm = algorithm;
+        	this.salt = salt;
+        }
+    }
     
     /**
-     * Looks up <tt>userPassword</tt> attribute of the entry whose name is the
-     * value of {@link Context#SECURITY_PRINCIPAL} environment variable, and
-     * authenticates a user with the plain-text password.
+     * Get the password either from cache or from backend.
+     * @param principalDN The DN from which we want the password
+     * @return A byte array which can be empty if the password was not found
+     * @throws NamingException If we have a problem during the lookup operation
      */
-    public LdapPrincipal authenticate( LdapDN principalDn, ServerContext ctx ) throws NamingException
+    private LdapPrincipal getStoredPassword( LdapDN principalDN ) throws NamingException
     {
-        // ---- extract password from JNDI environment
+        LdapPrincipal principal = null;
+        String principalNorm = principalDN.getNormName();
+        
+        synchronized( credentialCache )
+        {
+            principal = (LdapPrincipal)credentialCache.get( principalNorm );
+        }
+        
+        byte[] storedPassword = null;
+        
+        if ( principal == null )
+        {
+            // Not found in the cache
+            // Get the user password from the backend
+            storedPassword = lookupUserPassword( principalDN );
+            
+            
+            // Deal with the special case where the user didn't enter a password
+            // We will compare the empty array with the credentials. Sometime,
+            // a user does not set a password. This is bad, but there is nothing
+            // we can do against that, except education ...
+            if ( storedPassword == null )
+            {
+                storedPassword = ArrayUtils.EMPTY_BYTE_ARRAY;
+            }
 
+            // Create the new principal before storing it in the cache
+            principal = new LdapPrincipal( principalDN, AuthenticationLevel.SIMPLE, storedPassword );
+            
+            // Now, update the local cache.
+            synchronized( credentialCache )
+            {
+                credentialCache.put( principalDN.getNormName(), principal );
+            }
+        }
+        else
+        {
+            // Found ! 
+            storedPassword = principal.getUserPassword();
+        }
+        
+        return principal;
+    }
+
+    /**
+     * Get the user credentials from the environment. It is stored into the
+     * ServcerContext.
+     * @param ctx
+     * @param principalDn
+     * @return
+     * @throws LdapAuthenticationException
+     */
+    private byte[] getCredentials( ServerContext ctx, LdapDN principalDn ) throws LdapAuthenticationException
+    {
         Object creds = ctx.getEnvironment().get( Context.SECURITY_CREDENTIALS );
+        byte[] credentials = null;
 
         if ( creds == null )
         {
-            creds = ArrayUtils.EMPTY_BYTE_ARRAY;
+            credentials = ArrayUtils.EMPTY_BYTE_ARRAY;
         }
         else if ( creds instanceof String )
         {
-            creds = StringTools.getBytesUtf8( ( String ) creds );
+            credentials = StringTools.getBytesUtf8( ( String ) creds );
         }
-
-        byte[] userPassword = null;
-        if ( credentialCache.containsKey( principalDn.getNormName() ) )
+        else if ( creds instanceof byte[] )
         {
-            userPassword = credentialCache.get( principalDn.getNormName() );
+            // This is the general case. When dealing with a BindRequest operation,
+            // received by the server, the credentials are always stored into a byte array
+            credentials = (byte[])creds;
         }
         else
         {
-            userPassword = lookupUserPassword( principalDn );
+            log.info( "Incorrect credentials stored in {}", Context.SECURITY_CREDENTIALS );
+            throw new LdapAuthenticationException();
         }
+        
+        return credentials;
+    }
 
-        boolean credentialsMatch = false;
 
-        // Check if password is stored as a message digest, i.e. one-way
-        // encrypted
-        if ( this.isPasswordOneWayEncrypted( userPassword ) )
+    /**
+     * Looks up <tt>userPassword</tt> attribute of the entry whose name is the
+     * value of {@link Context#SECURITY_PRINCIPAL} environment variable, and
+     * authenticates a user with the plain-text password.
+     * 
+     * We have at least 6 algorithms to encrypt the password :
+     * - SHA
+     * - SSHA (salted SHA)
+     * - MD5
+     * - SMD5 (slated MD5)
+     * - crypt (unix crypt)
+     * - plain text, ie no encryption.
+     * 
+     *  If we get an encrypted password, it is prefixed by the used algorithm, between
+     *  brackets : {SSHA}password ...
+     *  
+     *  If the password is using SSHA, SMD5 or crypt, some 'salt' is added to the password :
+     *  - length(password) - 20, starting at 21th position for SSHA
+     *  - length(password) - 16, starting at 16th position for SMD5
+     *  - length(password) - 2, starting at 3rd position for crypt
+     *  
+     *  For (S)SHA and (S)MD5, we have to transform the password from Base64 encoded text
+     *  to a byte[] before comparing the password with the stored one.
+     *  For crypt, we only have to remove the salt.
+     *  
+     *  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.
+     *  
+     *  The stored password is always using the unsalted form, and is stored as a bytes array.
+     */
+    public LdapPrincipal authenticate( LdapDN principalDn, ServerContext ctx ) throws NamingException
+    {
+        if ( IS_DEBUG )
         {
-            try
+            log.debug( "Authenticating {}", principalDn );
+        }
+        
+        // ---- extract password from JNDI environment
+        byte[] credentials = getCredentials( ctx, principalDn );
+        
+        LdapPrincipal principal = getStoredPassword( principalDn );
+        
+        // Get the stored password, either from cache or from backend
+        byte[] storedPassword = principal.getUserPassword();
+        
+        // Short circuit for PLAIN TEXT passwords : we compare the byte array directly
+        // Are the passwords equal ?
+        if ( Arrays.equals( credentials, storedPassword ) )
+        {
+            if ( IS_DEBUG )
             {
-                // create a corresponding digested password from creds
-                String algorithm = this.getAlgorithmForHashedPassword( userPassword );
-                String digestedCredits = this.createDigestedPassword( algorithm, creds );
-
-                credentialsMatch = Arrays.equals( StringTools.getBytesUtf8( digestedCredits ), userPassword );
+                log.debug( "{} Authenticated", principalDn );
             }
-            catch ( NoSuchAlgorithmException nsae )
+            
+        	return principal;
+        }
+        
+        // Let's see if the stored password was encrypted
+        String algorithm = findAlgorithm( storedPassword );
+        
+        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 = splitCredentials( storedPassword, encryptionMethod );
+            
+            // Reuse the slatedPassword informations to construct the encrypted
+            // password given by the user.
+            byte[] userPassword = encryptPassword( credentials, encryptionMethod );
+            
+            // Now, compare the two passwords.
+            if ( Arrays.equals( userPassword, encryptedStored ) )
             {
-                log.warn( "Password stored with unknown algorithm.", nsae );
+                if ( IS_DEBUG )
+                {
+                    log.debug( "{} Authenticated", principalDn );
+                }
+
+                return principal;
             }
-            catch ( IllegalArgumentException e )
+            else
             {
-                log.warn( "Exception during authentication", e );
+                // Bad password ...
+                String message = "Password not correct for user '" + principalDn.getUpName() + "'";
+                log.info( message );
+                throw new LdapAuthenticationException(message);
             }
         }
         else
         {
-            // password is not stored one-way encrypted
-            credentialsMatch = Arrays.equals( (byte[])creds, userPassword );
+            // Bad password ...
+            String message = "Password not correct for user '" + principalDn.getUpName() + "'";
+            log.info( message );
+            throw new LdapAuthenticationException(message);
         }
+    }
+    
+    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 );
+    }
 
-        if ( credentialsMatch )
+    /**
+     * Decopose 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
+     */
+    private byte[] splitCredentials( byte[] credentials, EncryptionMethod encryptionMethod )
+    {
+        String algorithm = encryptionMethod.algorithm;
+        
+        int pos = algorithm.length() + 2;
+        
+        if ( ( LdapSecurityConstants.HASH_METHOD_MD5.equals( algorithm ) ) ||
+            ( LdapSecurityConstants.HASH_METHOD_SHA.equals( algorithm ) ) )
+        {
+            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, pos, credentials.length - pos, "UTF-8" ).toCharArray() );
+            }
+            catch ( UnsupportedEncodingException uee )
+            {
+                // do nothing 
+                return credentials;
+            }
+        }
+        else if ( ( LdapSecurityConstants.HASH_METHOD_SMD5.equals( algorithm ) ) ||
+                 ( LdapSecurityConstants.HASH_METHOD_SSHA.equals( algorithm ) ) )
+        {
+            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, pos, credentials.length - pos, "UTF-8" ).toCharArray() );
+                
+                encryptionMethod.salt = new byte[8];
+                byte[] password = new byte[passwordAndSalt.length - encryptionMethod.salt.length];
+                split( passwordAndSalt, 0, password, encryptionMethod.salt );
+                
+                return password;
+            }
+            catch ( UnsupportedEncodingException uee )
+            {
+                // do nothing 
+                return credentials;
+            }
+        }
+        else if ( LdapSecurityConstants.HASH_METHOD_CRYPT.equals( algorithm ) )
         {
-            LdapPrincipal principal = new LdapPrincipal( principalDn, AuthenticationLevel.SIMPLE );
-            credentialCache.put( principalDn.getNormName(), userPassword );
-            return principal;
+        	// 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.salt = new byte[2];
+            byte[] password = new byte[credentials.length - encryptionMethod.salt.length - pos];
+            split( credentials, pos, encryptionMethod.salt, password );
+            
+            return password;
         }
         else
         {
-            throw new LdapAuthenticationException();
+            // unknown method
+            return credentials;
         }
     }
     
-    
-    protected byte[] lookupUserPassword( LdapDN principalDn ) throws NamingException
+    /**
+     * Get the algorithm from the stored password. 
+     * It can be found on the beginning of the stored password, between 
+     * curly brackets.
+     */
+    private String findAlgorithm( byte[] credentials )
     {
-        // ---- lookup the principal entry's userPassword attribute
+        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 = new String( credentials, 1, pos - 1 ).toLowerCase();
+                
+                if ( ( LdapSecurityConstants.HASH_METHOD_MD5.equals( algorithm ) ) ||
+                    ( LdapSecurityConstants.HASH_METHOD_SHA.equals( algorithm ) ) ||
+                    ( LdapSecurityConstants.HASH_METHOD_SMD5.equals( algorithm ) ) ||
+                    ( LdapSecurityConstants.HASH_METHOD_SSHA.equals( algorithm ) ) ||
+                    ( LdapSecurityConstants.HASH_METHOD_CRYPT.equals( algorithm ) ) )
+                {
+                    return algorithm;
+                }
+                else
+                {
+                    // unknown method
+                    return null;
+                }
+            }
+            else
+            {
+                // We don't have an algorithm
+                return null;
+            }
+        }
+        else
+        {
+            // No '{algo}' part
+            return null;
+        }
+    }
 
+    /**
+     * Compute the hashed password given an algorithm, the credentials and 
+     * an optional salt.
+     */
+    private static byte[] digest( String algorithm, byte[] password, byte[] salt )
+    {
+        MessageDigest digest;
+
+        try
+        {
+            digest = MessageDigest.getInstance( algorithm );
+        }
+        catch ( NoSuchAlgorithmException e1 )
+        {
+            return null;
+        }
+
+        if ( salt != null )
+        {
+            digest.update( password );
+            digest.update( salt );
+            return digest.digest();
+        }
+        else
+        {
+            return digest.digest( password );
+        }
+    }
+
+    private byte[] encryptPassword( byte[] credentials, EncryptionMethod encryptionMethod )
+    {
+        String algorithm = encryptionMethod.algorithm;
+        byte[] salt = encryptionMethod.salt;
+        
+        if ( LdapSecurityConstants.HASH_METHOD_SHA.equals( algorithm ) || 
+             LdapSecurityConstants.HASH_METHOD_SSHA.equals( algorithm ) )
+        {   
+            return digest( LdapSecurityConstants.HASH_METHOD_SHA, credentials, salt );
+        }
+        else if ( LdapSecurityConstants.HASH_METHOD_MD5.equals( algorithm ) ||
+                  LdapSecurityConstants.HASH_METHOD_SMD5.equals( algorithm ) )
+       {            
+            return digest( LdapSecurityConstants.HASH_METHOD_MD5, credentials, salt );
+        }
+        else if ( LdapSecurityConstants.HASH_METHOD_CRYPT.equals( algorithm ) )
+        {
+            if ( salt == null )
+            {
+                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 ) );
+            }
+
+            String saltWithCrypted = UnixCrypt.crypt( StringTools.utf8ToString( credentials ), StringTools.utf8ToString( salt ) );
+            String crypted = saltWithCrypted.substring( 2 );
+            
+            return StringTools.getBytesUtf8( crypted );
+        }
+        else
+        {
+            return credentials;
+        }
+    }
+
+    /**
+     * Local function which request the password from the backend
+     */
+    private byte[] lookupUserPassword( LdapDN principalDn ) throws NamingException
+    {
+        // ---- lookup the principal entry's userPassword attribute
         Invocation invocation = InvocationStack.getInstance().peek();
         PartitionNexusProxy proxy = invocation.getProxy();
         Attributes userEntry;
 
         try
         {
-            userEntry = proxy.lookup( principalDn, new String[]
-                { "userPassword" }, USERLOOKUP_BYPASS );
+            LookupOperationContext lookupContex  = new LookupOperationContext( new String[] { SchemaConstants.USER_PASSWORD_AT } );
+            lookupContex.setDn( principalDn );
+            
+            userEntry = proxy.lookup( lookupContex, USERLOOKUP_BYPASS );
 
             if ( userEntry == null )
             {
@@ -191,10 +614,9 @@
 
         Object userPassword;
 
-        Attribute userPasswordAttr = userEntry.get( "userPassword" );
+        Attribute userPasswordAttr = userEntry.get( SchemaConstants.USER_PASSWORD_AT );
 
         // ---- assert that credentials match
-
         if ( userPasswordAttr == null )
         {
             userPassword = ArrayUtils.EMPTY_BYTE_ARRAY;
@@ -212,66 +634,35 @@
         return ( byte[] ) userPassword;
     }
 
-
-    /**
-     * Checks if the argument is one-way encryped. If it is a string or a
-     * byte-array which looks like "{XYZ}...", and XYZ is a known lessage
-     * digest, the method returns true. The method does not throw an exception
-     * otherwise, e.g. if the algorithm XYZ is not known to the runtime.
-     * 
-     * @param password
-     *            agument, either a string or a byte-array
-     * @return true, if the value is a digested password with algorithm included
-     */
-    protected boolean isPasswordOneWayEncrypted( Object password )
-    {
-        boolean result = false;
-        try
-        {
-            String algorithm = getAlgorithmForHashedPassword( password );
-            result = ( algorithm != null );
-        }
-        catch ( IllegalArgumentException ignored )
-        {
-        }
-        return result;
-    }
-
-
     /**
      * Get the algorithm of a password, which is stored in the form "{XYZ}...".
      * The method returns null, if the argument is not in this form. It returns
      * XYZ, if XYZ is an algorithm known to the MessageDigest class of
      * java.security.
      * 
-     * @param password
-     *            either a String or a byte[]
+     * @param password a byte[]
      * @return included message digest alorithm, if any
      */
-    protected String getAlgorithmForHashedPassword( Object password ) throws IllegalArgumentException
+    protected String getAlgorithmForHashedPassword( byte[] password ) throws IllegalArgumentException
     {
         String result = null;
 
         // Check if password arg is string or byte[]
-        String sPassword = null;
-        if ( password instanceof byte[] )
-        {
-            sPassword = new String( ( byte[] ) password );
-        }
-        else if ( password instanceof String )
-        {
-            sPassword = ( String ) password;
-        }
-        else
-        {
-            throw new IllegalArgumentException( "password is neither a String nor a byte-Array." );
-        }
+        String sPassword = StringTools.utf8ToString( password );
+        int rightParen = sPassword.indexOf( '}' );
 
-        if ( sPassword != null && sPassword.length() > 2 && sPassword.charAt( 0 ) == '{'
-            && sPassword.indexOf( '}' ) > -1 )
+        if ( ( sPassword != null ) && 
+             ( sPassword.length() > 2 ) && 
+             ( sPassword.charAt( 0 ) == '{' ) &&
+             ( rightParen > -1 ) )
         {
-            int algPosEnd = sPassword.indexOf( '}' );
-            String algorithm = sPassword.substring( 1, algPosEnd );
+            String algorithm = sPassword.substring( 1, rightParen );
+
+            if ( "crypt".equals( algorithm ) )
+            {
+                return algorithm;
+            }
+            
             try
             {
                 MessageDigest.getInstance( algorithm );
@@ -298,7 +689,7 @@
      *            an algorithm which is supported by
      *            java.security.MessageDigest, e.g. SHA
      * @param password
-     *            password value, either a string or a byte[]
+     *            password value, a byte[]
      * 
      * @return a digested password, which looks like
      *         {SHA}LhkDrSoM6qr0fW6hzlfOJQW61tc=
@@ -307,52 +698,45 @@
      *             if password is neither a String nor a byte[], or algorithm is
      *             not known to java.security.MessageDigest class
      */
-    protected String createDigestedPassword( String algorithm, Object password ) throws NoSuchAlgorithmException,
-        IllegalArgumentException
+    protected String createDigestedPassword( String algorithm, byte[] password ) throws IllegalArgumentException
     {
-        // Check if password arg is string or byte[]
-        byte[] data = null;
-        if ( password instanceof byte[] )
-        {
-            data = ( byte[] ) password;
-        }
-        else if ( password instanceof String )
-        {
-            data = StringTools.getBytesUtf8( ( String ) password );
-        }
-        else
-        {
-            throw new IllegalArgumentException( "password is neither a String nor a byte-Array." );
-        }
-
         // create message digest object
-        MessageDigest digest = null;
         try
         {
-            digest = MessageDigest.getInstance( algorithm );
+            if ( "crypt".equalsIgnoreCase( algorithm ) )
+            {
+                String saltWithCrypted = UnixCrypt.crypt( StringTools.utf8ToString( password ), "" );
+                String crypted = saltWithCrypted.substring( 2 );
+                return '{' + algorithm + '}' + StringTools.getBytesUtf8( crypted );
+            }
+            else
+            {
+                MessageDigest digest = MessageDigest.getInstance( algorithm );
+                
+                // calculate hashed value of password
+                byte[] fingerPrint = digest.digest( password );
+                char[] encoded = Base64.encode( fingerPrint );
+
+                // create return result of form "{alg}bbbbbbb"
+                return '{' + algorithm + '}' + new String( encoded );
+            }
         }
         catch ( NoSuchAlgorithmException nsae )
         {
+            log.error( "Cannot create a digested password for algorithm '{}'", algorithm );
             throw new IllegalArgumentException( nsae.getMessage() );
         }
-
-        // calculate hashed value of password
-        byte[] fingerPrint = digest.digest( data );
-        char[] encoded = Base64.encode( fingerPrint );
-
-        // create return result of form "{alg}bbbbbbb"
-        StringBuffer result = new StringBuffer();
-        result.append( '{' );
-        result.append( algorithm );
-        result.append( '}' );
-        result.append( encoded );
-
-        return result.toString();
     }
 
-
+    /**
+     * Remove the principal form the cache. This is used when the user changes
+     * his password.
+     */
     public void invalidateCache( LdapDN bindDn )
     {
-        credentialCache.remove( bindDn.getNormName() );
+        synchronized( credentialCache )
+        {
+            credentialCache.remove( bindDn.getNormName() );
+        }
     }
 }

Modified: directory/apacheds/branches/apacheds-sasl-branch/core/src/main/java/org/apache/directory/server/core/authz/AuthorizationService.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-sasl-branch/core/src/main/java/org/apache/directory/server/core/authz/AuthorizationService.java?view=diff&rev=541123&r1=541122&r2=541123
==============================================================================
--- directory/apacheds/branches/apacheds-sasl-branch/core/src/main/java/org/apache/directory/server/core/authz/AuthorizationService.java (original)
+++ directory/apacheds/branches/apacheds-sasl-branch/core/src/main/java/org/apache/directory/server/core/authz/AuthorizationService.java Wed May 23 17:26:40 2007
@@ -20,6 +20,21 @@
 package org.apache.directory.server.core.authz;
 
 
+
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
 import org.apache.directory.server.core.DirectoryServiceConfiguration;
 import org.apache.directory.server.core.authn.LdapPrincipal;
 import org.apache.directory.server.core.authz.support.ACDFEngine;
@@ -29,6 +44,15 @@
 import org.apache.directory.server.core.interceptor.BaseInterceptor;
 import org.apache.directory.server.core.interceptor.InterceptorChain;
 import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.OperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
 import org.apache.directory.server.core.invocation.Invocation;
 import org.apache.directory.server.core.invocation.InvocationStack;
 import org.apache.directory.server.core.jndi.ServerContext;
@@ -42,32 +66,16 @@
 import org.apache.directory.shared.ldap.aci.ACIItemParser;
 import org.apache.directory.shared.ldap.aci.ACITuple;
 import org.apache.directory.shared.ldap.aci.MicroOperation;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
 import org.apache.directory.shared.ldap.exception.LdapNamingException;
-import org.apache.directory.shared.ldap.filter.ExprNode;
 import org.apache.directory.shared.ldap.message.ModificationItemImpl;
 import org.apache.directory.shared.ldap.message.ResultCodeEnum;
 import org.apache.directory.shared.ldap.name.LdapDN;
 import org.apache.directory.shared.ldap.schema.AttributeType;
 import org.apache.directory.shared.ldap.util.AttributeUtils;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.naming.NamingException;
-import javax.naming.NamingEnumeration;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.SearchControls;
-import javax.naming.directory.SearchResult;
-
-import java.text.ParseException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
 
 /**
  * An ACI based authorization service.
@@ -79,10 +87,10 @@
 {
     /** the logger for this class */
     private static final Logger log = LoggerFactory.getLogger( AuthorizationService.class );
-    /** the entry ACI attribute string: entryACI */
-    private static final String ENTRYACI_ATTR = "entryACI";
-    /** the subentry ACI attribute string: subentryACI */
-    private static final String SUBENTRYACI_ATTR = "subentryACI";
+    
+    /** The service name */
+    public static final String NAME = "authorizationService";
+
     /**
      * the multivalued op attr used to track the perscriptive access control
      * subentries that apply to an entry.
@@ -140,18 +148,25 @@
 
     /** a tupleCache that responds to add, delete, and modify attempts */
     private TupleCache tupleCache;
+    
     /** a groupCache that responds to add, delete, and modify attempts */
     private GroupCache groupCache;
+    
     /** a normalizing ACIItem parser */
     private ACIItemParser aciParser;
+    
     /** use and instance of the ACDF engine */
     private ACDFEngine engine;
+    
     /** interceptor chain */
     private InterceptorChain chain;
+    
     /** attribute type registry */
     private AttributeTypeRegistry attrRegistry;
+    
     /** whether or not this interceptor is activated */
     private boolean enabled = false;
+    
     /** the system wide subschemaSubentryDn */
     private String subschemaSubentryDn;
 
@@ -162,7 +177,14 @@
     private String subentryOid;
     private String acSubentryOid;
 
+    /** A storage for the entryACI attributeType */
+    private AttributeType entryAciType;
+
+    /** the subentry ACI attribute type */
+    private AttributeType subentryAciType;
     
+    public static final SearchControls DEFAULT_SEARCH_CONTROLS = new SearchControls();
+
     /**
      * Initializes this interceptor based service by getting a handle on the nexus, setting up
      * the tupe and group membership caches and the ACIItem parser and the ACDF engine.
@@ -180,11 +202,13 @@
         OidRegistry oidRegistry = factoryCfg.getRegistries().getOidRegistry();
         
         // look up some constant information
-        objectClassOid = oidRegistry.getOid( "objectClass" );
-        subentryOid = oidRegistry.getOid( "subentry" );
+        objectClassOid = oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT );
+        subentryOid = oidRegistry.getOid( SchemaConstants.SUBENTRY_OC );
         acSubentryOid = oidRegistry.getOid( AC_SUBENTRY_ATTR );
         objectClassType = attrRegistry.lookup( objectClassOid );
         acSubentryType = attrRegistry.lookup( acSubentryOid );
+        entryAciType = attrRegistry.lookup( SchemaConstants.ENTRY_ACI_AT_OID ); 
+        subentryAciType = attrRegistry.lookup( SchemaConstants.SUBENTRY_ACI_AT_OID );
         
         aciParser = new ACIItemParser( new ConcreteNameComponentNormalizer( attrRegistry, oidRegistry ), attrRegistry.getNormalizerMapping() );
         engine = new ACDFEngine( factoryCfg.getRegistries().getOidRegistry(), attrRegistry );
@@ -192,8 +216,8 @@
         enabled = factoryCfg.getStartupConfiguration().isAccessControlEnabled();
 
         // stuff for dealing with subentries (garbage for now)
-        String subschemaSubentry = ( String ) factoryCfg.getPartitionNexus().getRootDSE().get( "subschemaSubentry" )
-            .get();
+        String subschemaSubentry = ( String ) factoryCfg.getPartitionNexus().getRootDSE( null ).
+        get( "subschemaSubentry" ).get();
         LdapDN subschemaSubentryDnName = new LdapDN( subschemaSubentry );
         subschemaSubentryDnName.normalize( attrRegistry.getNormalizerMapping() );
         subschemaSubentryDn = subschemaSubentryDnName.toNormName();
@@ -235,18 +259,21 @@
          * to be in the same naming context as their access point so the subentries
          * effecting their parent entry applies to them as well.
          */
-        if ( AttributeUtils.containsValue( oc, "subentry", objectClassType ) || oc.contains( subentryOid ) )
+        if ( AttributeUtils.containsValue( oc, SchemaConstants.SUBENTRY_OC, objectClassType ) || 
+             AttributeUtils.containsValue( oc, subentryOid, objectClassType ) )
         {
             LdapDN parentDn = ( LdapDN ) dn.clone();
             parentDn.remove( dn.size() - 1 );
-            entry = proxy.lookup( parentDn, PartitionNexusProxy.LOOKUP_BYPASS );
+            entry = proxy.lookup( new LookupOperationContext( parentDn), PartitionNexusProxy.LOOKUP_BYPASS );
         }
 
         Attribute subentries = AttributeUtils.getAttribute( entry, acSubentryType );
+        
         if ( subentries == null )
         {
             return;
         }
+        
         for ( int ii = 0; ii < subentries.size(); ii++ )
         {
             String subentryDn = ( String ) subentries.get( ii );
@@ -265,7 +292,8 @@
      */
     private void addEntryAciTuples( Collection<ACITuple> tuples, Attributes entry ) throws NamingException
     {
-        Attribute entryAci = entry.get( ENTRYACI_ATTR );
+        Attribute entryAci = AttributeUtils.getAttribute( entry, entryAciType );
+        
         if ( entryAci == null )
         {
             return;
@@ -305,7 +333,7 @@
         throws NamingException
     {
         // only perform this for subentries
-        if ( !entry.get( "objectClass" ).contains( "subentry" ) )
+        if ( !AttributeUtils.containsValueCaseIgnore( entry.get( SchemaConstants.OBJECT_CLASS_AT ), SchemaConstants.SUBENTRY_OC ) )
         {
             return;
         }
@@ -314,9 +342,10 @@
         // will contain the subentryACI attributes that effect subentries
         LdapDN parentDn = ( LdapDN ) dn.clone();
         parentDn.remove( dn.size() - 1 );
-        Attributes administrativeEntry = proxy.lookup( parentDn, new String[]
-            { SUBENTRYACI_ATTR }, PartitionNexusProxy.LOOKUP_BYPASS );
-        Attribute subentryAci = administrativeEntry.get( SUBENTRYACI_ATTR );
+        Attributes administrativeEntry = proxy.lookup( 
+        		new LookupOperationContext( parentDn, new String[]
+            { SchemaConstants.SUBENTRY_ACI_AT }) , PartitionNexusProxy.LOOKUP_BYPASS );
+        Attribute subentryAci = AttributeUtils.getAttribute( administrativeEntry, subentryAciType );
 
         if ( subentryAci == null )
         {
@@ -365,33 +394,37 @@
      * -------------------------------------------------------------------------------
      */
 
-    public void add( NextInterceptor next, LdapDN normName, Attributes entry ) throws NamingException
+    public void add( NextInterceptor next, OperationContext addContext ) throws NamingException
     {
         // Access the principal requesting the operation, and bypass checks if it is the admin
         Invocation invocation = InvocationStack.getInstance().peek();
         LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
         LdapDN principalDn = principal.getJndiName();
+        
+        Attributes entry = ((AddOperationContext)addContext).getEntry();
+        LdapDN name = addContext.getDn();
 
         // bypass authz code if we are disabled
         if ( !enabled )
         {
-            next.add( normName, entry );
+            next.add( addContext );
             return;
         }
 
         // bypass authz code but manage caches if operation is performed by the admin
         if ( isPrincipalAnAdministrator( principalDn ) )
         {
-            next.add( normName, entry );
-            tupleCache.subentryAdded( normName.toNormName(), normName, entry );
-            groupCache.groupAdded( normName.toNormName(), normName, entry );
+            next.add( addContext );
+            tupleCache.subentryAdded( name.getUpName(), name, entry );
+            groupCache.groupAdded( name, entry );
             return;
         }
 
         // perform checks below here for all non-admin users
-        SubentryService subentryService = ( SubentryService ) chain.get( "subentryService" );
-        Attributes subentryAttrs = subentryService.getSubentryAttributes( normName, entry );
+        SubentryService subentryService = ( SubentryService ) chain.get( SubentryService.NAME );
+        Attributes subentryAttrs = subentryService.getSubentryAttributes( name, entry );
         NamingEnumeration attrList = entry.getAll();
+        
         while ( attrList.hasMore() )
         {
             subentryAttrs.put( ( Attribute ) attrList.next() );
@@ -403,56 +436,60 @@
 
         // Build the total collection of tuples to be considered for add rights
         // NOTE: entryACI are NOT considered in adds (it would be a security breech)
-        addPerscriptiveAciTuples( invocation.getProxy(), tuples, normName, subentryAttrs );
-        addSubentryAciTuples( invocation.getProxy(), tuples, normName, subentryAttrs );
+        addPerscriptiveAciTuples( invocation.getProxy(), tuples, name, subentryAttrs );
+        addSubentryAciTuples( invocation.getProxy(), tuples, name, subentryAttrs );
 
         // check if entry scope permission is granted
         PartitionNexusProxy proxy = invocation.getProxy();
-        engine.checkPermission( proxy, userGroups, principalDn, principal.getAuthenticationLevel(), normName, null, null,
+        engine.checkPermission( proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, null, null,
             ADD_PERMS, tuples, subentryAttrs );
 
         // now we must check if attribute type and value scope permission is granted
         NamingEnumeration attributeList = entry.getAll();
+        
         while ( attributeList.hasMore() )
         {
             Attribute attr = ( Attribute ) attributeList.next();
+        
             for ( int ii = 0; ii < attr.size(); ii++ )
             {
-                engine.checkPermission( proxy, userGroups, principalDn, principal.getAuthenticationLevel(), normName, attr
+                engine.checkPermission( proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, attr
                     .getID(), attr.get( ii ), ADD_PERMS, tuples, entry );
             }
         }
 
         // if we've gotten this far then access has been granted
-        next.add( normName, entry );
+        next.add( addContext );
 
         // if the entry added is a subentry or a groupOf[Unique]Names we must
         // update the ACITuple cache and the groups cache to keep them in sync
-        tupleCache.subentryAdded( normName.toNormName(), normName, entry );
-        groupCache.groupAdded( normName.toNormName(), normName, entry );
+        tupleCache.subentryAdded( name.getUpName(), name, entry );
+        groupCache.groupAdded( name, entry );
     }
 
 
-    public void delete( NextInterceptor next, LdapDN name ) throws NamingException
+    public void delete( NextInterceptor next, OperationContext deleteContext ) throws NamingException
     {
+    	LdapDN name = deleteContext.getDn();
+    	
         // Access the principal requesting the operation, and bypass checks if it is the admin
         Invocation invocation = InvocationStack.getInstance().peek();
         PartitionNexusProxy proxy = invocation.getProxy();
-        Attributes entry = proxy.lookup( name, PartitionNexusProxy.LOOKUP_BYPASS );
+        Attributes entry = proxy.lookup( new LookupOperationContext( name ) , PartitionNexusProxy.LOOKUP_BYPASS );
         LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
         LdapDN principalDn = principal.getJndiName();
 
         // bypass authz code if we are disabled
         if ( !enabled )
         {
-            next.delete( name );
+            next.delete( deleteContext );
             return;
         }
 
         // bypass authz code but manage caches if operation is performed by the admin
         if ( isPrincipalAnAdministrator( principalDn ) )
         {
-            next.delete( name );
+            next.delete( deleteContext );
             tupleCache.subentryDeleted( name, entry );
             groupCache.groupDeleted( name, entry );
             return;
@@ -467,97 +504,38 @@
         engine.checkPermission( proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, null, null,
             REMOVE_PERMS, tuples, entry );
 
-        next.delete( name );
+        next.delete( deleteContext );
         tupleCache.subentryDeleted( name, entry );
         groupCache.groupDeleted( name, entry );
     }
 
 
-    public void modify( NextInterceptor next, LdapDN name, int modOp, Attributes mods ) throws NamingException
+    public void modify( NextInterceptor next, OperationContext opContext ) throws NamingException
     {
         // Access the principal requesting the operation, and bypass checks if it is the admin
         Invocation invocation = InvocationStack.getInstance().peek();
         PartitionNexusProxy proxy = invocation.getProxy();
-        Attributes entry = proxy.lookup( name, PartitionNexusProxy.LOOKUP_BYPASS );
-        LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
-        LdapDN principalDn = principal.getJndiName();
-
-        // bypass authz code if we are disabled
-        if ( !enabled )
-        {
-            next.modify( name, modOp, mods );
-            return;
-        }
+        LdapDN name = opContext.getDn();
 
-        // bypass authz code but manage caches if operation is performed by the admin
-        if ( isPrincipalAnAdministrator( principalDn ) )
-        {
-            next.modify( name, modOp, mods );
-            tupleCache.subentryModified( name, modOp, mods, entry );
-            groupCache.groupModified( name, modOp, mods, entry );
-            return;
-        }
-
-        Set userGroups = groupCache.getGroups( principalDn.toString() );
-        Collection<ACITuple> tuples = new HashSet<ACITuple>();
-        addPerscriptiveAciTuples( proxy, tuples, name, entry );
-        addEntryAciTuples( tuples, entry );
-        addSubentryAciTuples( proxy, tuples, name, entry );
-
-        engine.checkPermission( proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, null, null,
-            Collections.singleton( MicroOperation.MODIFY ), tuples, entry );
-
-        NamingEnumeration attrList = mods.getAll();
-        Collection<MicroOperation> perms = null;
-        switch ( modOp )
-        {
-            case ( DirContext.ADD_ATTRIBUTE  ):
-                perms = ADD_PERMS;
-                break;
-            case ( DirContext.REMOVE_ATTRIBUTE  ):
-                perms = REMOVE_PERMS;
-                break;
-            case ( DirContext.REPLACE_ATTRIBUTE  ):
-                perms = REPLACE_PERMS;
-                break;
-        }
-
-        while ( attrList.hasMore() )
-        {
-            Attribute attr = ( Attribute ) attrList.next();
-            for ( int ii = 0; ii < attr.size(); ii++ )
-            {
-                engine.checkPermission( proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, attr
-                    .getID(), attr.get( ii ), perms, tuples, entry );
-            }
-        }
-
-        next.modify( name, modOp, mods );
-        tupleCache.subentryModified( name, modOp, mods, entry );
-        groupCache.groupModified( name, modOp, mods, entry );
-    }
-
-
-    public void modify( NextInterceptor next, LdapDN name, ModificationItemImpl[] mods ) throws NamingException
-    {
         // Access the principal requesting the operation, and bypass checks if it is the admin
-        Invocation invocation = InvocationStack.getInstance().peek();
-        PartitionNexusProxy proxy = invocation.getProxy();
-        Attributes entry = proxy.lookup( name, PartitionNexusProxy.LOOKUP_BYPASS );
+        Attributes entry = proxy.lookup( new LookupOperationContext( name ), PartitionNexusProxy.LOOKUP_BYPASS );
         LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
         LdapDN principalDn = principal.getJndiName();
 
         // bypass authz code if we are disabled
         if ( !enabled )
         {
-            next.modify( name, mods );
+            next.modify( opContext );
             return;
         }
 
+        ModificationItemImpl[] mods =((ModifyOperationContext)opContext).getModItems();
+
         // bypass authz code but manage caches if operation is performed by the admin
         if ( isPrincipalAnAdministrator( principalDn ) )
         {
-            next.modify( name, mods );
+            
+            next.modify( opContext );
             tupleCache.subentryModified( name, mods, entry );
             groupCache.groupModified( name, mods, entry );
             return;
@@ -573,6 +551,7 @@
             Collections.singleton( MicroOperation.MODIFY ), tuples, entry );
 
         Collection<MicroOperation> perms = null;
+
         for ( int ii = 0; ii < mods.length; ii++ )
         {
             switch ( mods[ii].getModificationOp() )
@@ -580,15 +559,18 @@
                 case ( DirContext.ADD_ATTRIBUTE  ):
                     perms = ADD_PERMS;
                     break;
+                    
                 case ( DirContext.REMOVE_ATTRIBUTE  ):
                     perms = REMOVE_PERMS;
                     break;
+                    
                 case ( DirContext.REPLACE_ATTRIBUTE  ):
                     perms = REPLACE_PERMS;
                     break;
             }
 
             Attribute attr = mods[ii].getAttribute();
+            
             for ( int jj = 0; jj < attr.size(); jj++ )
             {
                 engine.checkPermission( proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, attr
@@ -596,23 +578,33 @@
             }
         }
 
-        next.modify( name, mods );
+        
+
+        next.modify( opContext );
         tupleCache.subentryModified( name, mods, entry );
         groupCache.groupModified( name, mods, entry );
     }
 
-
-    public boolean hasEntry( NextInterceptor next, LdapDN name ) throws NamingException
+    public boolean hasEntry( NextInterceptor next, OperationContext entryContext ) throws NamingException
     {
+        LdapDN name = entryContext.getDn();
         Invocation invocation = InvocationStack.getInstance().peek();
         PartitionNexusProxy proxy = invocation.getProxy();
-        Attributes entry = proxy.lookup( name, PartitionNexusProxy.LOOKUP_BYPASS );
+        Attributes entry = proxy.lookup( new LookupOperationContext( name ), PartitionNexusProxy.LOOKUP_BYPASS );
         LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
         LdapDN principalDn = principal.getJndiName();
 
-        if ( isPrincipalAnAdministrator( principalDn ) || !enabled || name.toString().trim().equals( "" ) ) // no checks on the rootdse
+        if ( isPrincipalAnAdministrator( principalDn ) || !enabled || ( name.size() == 0 ) ) // no checks on the rootdse
         {
-            return next.hasEntry( name );
+            // No need to go down to the stack, if the dn is empty : it's the rootDSE, and it exists !
+            if ( name.size() == 0 )
+            {
+                return true;
+            }
+            else
+            {
+                return next.hasEntry( entryContext );
+            }
         }
 
         Set userGroups = groupCache.getGroups( principalDn.toNormName() );
@@ -625,7 +617,7 @@
         engine.checkPermission( proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, null, null,
             BROWSE_PERMS, tuples, entry );
 
-        return next.hasEntry( name );
+        return next.hasEntry( entryContext );
     }
 
 
@@ -678,71 +670,59 @@
     }
 
 
-    public Attributes lookup( NextInterceptor next, LdapDN dn, String[] attrIds ) throws NamingException
+    public Attributes lookup( NextInterceptor next, OperationContext lookupContext ) throws NamingException
     {
         Invocation invocation = InvocationStack.getInstance().peek();
         LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
-        LdapDN principalDn = new LdapDN( principal.getName() );
-        principalDn.normalize( attrRegistry.getNormalizerMapping() );
+        LdapDN principalDn = principal.getJndiName();
         
-        if ( isPrincipalAnAdministrator( principalDn ) || !enabled )
+        if ( !principalDn.isNormalized() )
         {
-            return next.lookup( dn, attrIds );
+        	principalDn.normalize( attrRegistry.getNormalizerMapping() );
         }
-
-        PartitionNexusProxy proxy = invocation.getProxy();
-        Attributes entry = proxy.lookup( dn, PartitionNexusProxy.LOOKUP_BYPASS );
-        checkLookupAccess( principal, dn, entry );
-        return next.lookup( dn, attrIds );
-    }
-
-
-    public Attributes lookup( NextInterceptor next, LdapDN name ) throws NamingException
-    {
-        Invocation invocation = InvocationStack.getInstance().peek();
-        PartitionNexusProxy proxy = invocation.getProxy();
-        Attributes entry = proxy.lookup( name, PartitionNexusProxy.LOOKUP_BYPASS );
-        LdapPrincipal user = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
-        LdapDN principalDn = (LdapDN)user.getJndiName();
-        principalDn.normalize( attrRegistry.getNormalizerMapping() );
         
         if ( isPrincipalAnAdministrator( principalDn ) || !enabled )
         {
-            return next.lookup( name );
+            return next.lookup( lookupContext );
         }
 
-        checkLookupAccess( user, name, entry );
-        return next.lookup( name );
+        PartitionNexusProxy proxy = invocation.getProxy();
+        Attributes entry = proxy.lookup( lookupContext, PartitionNexusProxy.LOOKUP_BYPASS );
+        checkLookupAccess( principal, ((LookupOperationContext)lookupContext).getDn(), entry );
+        return next.lookup( lookupContext );
     }
 
-
-    public void modifyRn( NextInterceptor next, LdapDN name, String newRn, boolean deleteOldRn ) throws NamingException
+    public void rename( NextInterceptor next, OperationContext renameContext ) throws NamingException
     {
+        LdapDN name = renameContext.getDn();
+        String newRdn = ((RenameOperationContext)renameContext).getNewRdn();
+        
         // Access the principal requesting the operation, and bypass checks if it is the admin
         Invocation invocation = InvocationStack.getInstance().peek();
         PartitionNexusProxy proxy = invocation.getProxy();
-        Attributes entry = proxy.lookup( name, PartitionNexusProxy.LOOKUP_BYPASS );
+        Attributes entry = proxy.lookup( new LookupOperationContext( name ), PartitionNexusProxy.LOOKUP_BYPASS );
         LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
         LdapDN principalDn = principal.getJndiName();
         LdapDN newName = ( LdapDN ) name.clone();
         newName.remove( name.size() - 1 );
-        newName.add( parseNormalized( newRn ).get( 0 ) );
+        newName.add( parseNormalized( newRdn ).get( 0 ) );
 
         // bypass authz code if we are disabled
         if ( !enabled )
         {
-            next.modifyRn( name, newRn, deleteOldRn );
+            next.rename( renameContext );
             return;
         }
 
         // bypass authz code but manage caches if operation is performed by the admin
         if ( isPrincipalAnAdministrator( principalDn ) )
         {
-            next.modifyRn( name, newRn, deleteOldRn );
+            next.rename( renameContext );
             tupleCache.subentryRenamed( name, newName );
-            if ( groupCache.groupRenamed( name, newName ) )
-            {
-            }
+            
+            // TODO : this method returns a boolean : what should we do with the result ?
+            groupCache.groupRenamed( name, newName );
+
             return;
         }
 
@@ -755,46 +735,23 @@
         engine.checkPermission( proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, null, null,
             RENAME_PERMS, tuples, entry );
 
-        //        if ( deleteOldRn )
-        //        {
-        //            String oldRn = name.get( name.size() - 1 );
-        //            if ( NamespaceTools.hasCompositeComponents( oldRn ) )
-        //            {
-        //                String[] comps = NamespaceTools.getCompositeComponents( oldRn );
-        //                for ( int ii = 0; ii < comps.length; ii++ )
-        //                {
-        //                    String id = NamespaceTools.getRdnAttribute( comps[ii] );
-        //                    String value = NamespaceTools.getRdnValue( comps[ii] );
-        //                    engine.checkPermission( next, userGroups, user.getJndiName(),
-        //                            user.getAuthenticationLevel(), name, id,
-        //                            value, Collections.singleton( MicroOperation.REMOVE ),
-        //                            tuples, entry );
-        //                }
-        //            }
-        //            else
-        //            {
-        //                String id = NamespaceTools.getRdnAttribute( oldRn );
-        //                String value = NamespaceTools.getRdnValue( oldRn );
-        //                engine.checkPermission( next, userGroups, user.getJndiName(),
-        //                        user.getAuthenticationLevel(), name, id,
-        //                        value, Collections.singleton( MicroOperation.REMOVE ),
-        //                        tuples, entry );
-        //            }
-        //        }
-
-        next.modifyRn( name, newRn, deleteOldRn );
+        next.rename( renameContext );
         tupleCache.subentryRenamed( name, newName );
         groupCache.groupRenamed( name, newName );
     }
 
 
-    public void move( NextInterceptor next, LdapDN oriChildName, LdapDN newParentName, String newRn, boolean deleteOldRn )
+    public void moveAndRename( NextInterceptor next, OperationContext moveAndRenameContext )
         throws NamingException
     {
+        LdapDN oriChildName = moveAndRenameContext.getDn();
+        LdapDN newParentName = ((MoveAndRenameOperationContext)moveAndRenameContext).getParent();
+        String newRn = ((MoveAndRenameOperationContext)moveAndRenameContext).getNewRdn();
+        
         // Access the principal requesting the operation, and bypass checks if it is the admin
         Invocation invocation = InvocationStack.getInstance().peek();
         PartitionNexusProxy proxy = invocation.getProxy();
-        Attributes entry = proxy.lookup( oriChildName, PartitionNexusProxy.LOOKUP_BYPASS );
+        Attributes entry = proxy.lookup( new LookupOperationContext( oriChildName ), PartitionNexusProxy.LOOKUP_BYPASS );
         LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
         LdapDN principalDn = principal.getJndiName();
         LdapDN newName = ( LdapDN ) newParentName.clone();
@@ -803,14 +760,14 @@
         // bypass authz code if we are disabled
         if ( !enabled )
         {
-            next.move( oriChildName, newParentName, newRn, deleteOldRn );
+            next.moveAndRename( moveAndRenameContext );
             return;
         }
 
         // bypass authz code but manage caches if operation is performed by the admin
         if ( isPrincipalAnAdministrator( principalDn ) )
         {
-            next.move( oriChildName, newParentName, newRn, deleteOldRn );
+            next.moveAndRename( moveAndRenameContext );
             tupleCache.subentryRenamed( oriChildName, newName );
             groupCache.groupRenamed( oriChildName, newName );
             return;
@@ -830,15 +787,18 @@
         // will not be valid at the new location.
         // This will certainly be fixed by the SubentryService,
         // but after this service.
-        Attributes importedEntry = proxy.lookup( oriChildName, PartitionNexusProxy.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
+        Attributes importedEntry = proxy.lookup( new LookupOperationContext( oriChildName ), 
+            PartitionNexusProxy.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
+        
         // As the target entry does not exist yet and so
         // its subentry operational attributes are not there,
         // we need to construct an entry to represent it
         // at least with minimal requirements which are object class
         // and access control subentry operational attributes.
-        SubentryService subentryService = ( SubentryService ) chain.get( "subentryService" );
+        SubentryService subentryService = ( SubentryService ) chain.get( SubentryService.NAME );
         Attributes subentryAttrs = subentryService.getSubentryAttributes( newName, importedEntry );
         NamingEnumeration attrList = importedEntry.getAll();
+        
         while ( attrList.hasMore() )
         {
             subentryAttrs.put( ( Attribute ) attrList.next() );
@@ -852,45 +812,22 @@
         engine.checkPermission( proxy, userGroups, principalDn, principal.getAuthenticationLevel(), newName, null,
             null, IMPORT_PERMS, destTuples, subentryAttrs );
 
-        //        if ( deleteOldRn )
-        //        {
-        //            String oldRn = oriChildName.get( oriChildName.size() - 1 );
-        //            if ( NamespaceTools.hasCompositeComponents( oldRn ) )
-        //            {
-        //                String[] comps = NamespaceTools.getCompositeComponents( oldRn );
-        //                for ( int ii = 0; ii < comps.length; ii++ )
-        //                {
-        //                    String id = NamespaceTools.getRdnAttribute( comps[ii] );
-        //                    String value = NamespaceTools.getRdnValue( comps[ii] );
-        //                    engine.checkPermission( next, userGroups, user.getJndiName(),
-        //                            user.getAuthenticationLevel(), oriChildName, id,
-        //                            value, Collections.singleton( MicroOperation.REMOVE ),
-        //                            tuples, entry );
-        //                }
-        //            }
-        //            else
-        //            {
-        //                String id = NamespaceTools.getRdnAttribute( oldRn );
-        //                String value = NamespaceTools.getRdnValue( oldRn );
-        //                engine.checkPermission( next, userGroups, user.getJndiName(),
-        //                        user.getAuthenticationLevel(), oriChildName, id,
-        //                        value, Collections.singleton( MicroOperation.REMOVE ),
-        //                        tuples, entry );
-        //            }
-        //        }
 
-        next.move( oriChildName, newParentName, newRn, deleteOldRn );
+        next.moveAndRename( moveAndRenameContext );
         tupleCache.subentryRenamed( oriChildName, newName );
         groupCache.groupRenamed( oriChildName, newName );
     }
 
 
-    public void move( NextInterceptor next, LdapDN oriChildName, LdapDN newParentName ) throws NamingException
+    public void move( NextInterceptor next, OperationContext moveContext ) throws NamingException
     {
+        LdapDN oriChildName = moveContext.getDn();
+        LdapDN newParentName = ((MoveOperationContext)moveContext).getParent();
+        
         // Access the principal requesting the operation, and bypass checks if it is the admin
         Invocation invocation = InvocationStack.getInstance().peek();
         PartitionNexusProxy proxy = invocation.getProxy();
-        Attributes entry = proxy.lookup( oriChildName, PartitionNexusProxy.LOOKUP_BYPASS );
+        Attributes entry = proxy.lookup( new LookupOperationContext( oriChildName ), PartitionNexusProxy.LOOKUP_BYPASS );
         LdapDN newName = ( LdapDN ) newParentName.clone();
         newName.add( oriChildName.get( oriChildName.size() - 1 ) );
         LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
@@ -899,14 +836,14 @@
         // bypass authz code if we are disabled
         if ( !enabled )
         {
-            next.move( oriChildName, newParentName );
+            next.move( moveContext );
             return;
         }
 
         // bypass authz code but manage caches if operation is performed by the admin
         if ( isPrincipalAnAdministrator( principalDn ) )
         {
-            next.move( oriChildName, newParentName );
+            next.move( moveContext );
             tupleCache.subentryRenamed( oriChildName, newName );
             groupCache.groupRenamed( oriChildName, newName );
             return;
@@ -926,15 +863,17 @@
         // will not be valid at the new location.
         // This will certainly be fixed by the SubentryService,
         // but after this service.
-        Attributes importedEntry = proxy.lookup( oriChildName, PartitionNexusProxy.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
+        Attributes importedEntry = proxy.lookup( new LookupOperationContext( oriChildName ), 
+            PartitionNexusProxy.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
         // As the target entry does not exist yet and so
         // its subentry operational attributes are not there,
         // we need to construct an entry to represent it
         // at least with minimal requirements which are object class
         // and access control subentry operational attributes.
-        SubentryService subentryService = ( SubentryService ) chain.get( "subentryService" );
+        SubentryService subentryService = ( SubentryService ) chain.get( SubentryService.NAME );
         Attributes subentryAttrs = subentryService.getSubentryAttributes( newName, importedEntry );
         NamingEnumeration attrList = importedEntry.getAll();
+        
         while ( attrList.hasMore() )
         {
             subentryAttrs.put( ( Attribute ) attrList.next() );
@@ -948,46 +887,47 @@
         engine.checkPermission( proxy, userGroups, principalDn, principal.getAuthenticationLevel(), newName, null,
             null, IMPORT_PERMS, destTuples, subentryAttrs );
 
-        next.move( oriChildName, newParentName );
+        next.move( moveContext );
         tupleCache.subentryRenamed( oriChildName, newName );
         groupCache.groupRenamed( oriChildName, newName );
     }
 
-    public static final SearchControls DEFAULT_SEARCH_CONTROLS = new SearchControls();
-
-
-    public NamingEnumeration list( NextInterceptor next, LdapDN base ) throws NamingException
+    public NamingEnumeration list( NextInterceptor next, OperationContext opContext ) throws NamingException
     {
         Invocation invocation = InvocationStack.getInstance().peek();
         ServerLdapContext ctx = ( ServerLdapContext ) invocation.getCaller();
         LdapPrincipal user = ctx.getPrincipal();
-        NamingEnumeration e = next.list( base );
+        NamingEnumeration e = next.list( opContext );
+        
         if ( isPrincipalAnAdministrator( user.getJndiName() ) || !enabled )
         {
             return e;
         }
+        
         AuthorizationFilter authzFilter = new AuthorizationFilter();
-        return new SearchResultFilteringEnumeration( e, DEFAULT_SEARCH_CONTROLS, invocation, authzFilter );
+        return new SearchResultFilteringEnumeration( e, DEFAULT_SEARCH_CONTROLS, invocation, authzFilter, "List authorization Filter" );
     }
 
 
-    public NamingEnumeration search( NextInterceptor next, LdapDN base, Map env, ExprNode filter,
-        SearchControls searchCtls ) throws NamingException
+    public NamingEnumeration<SearchResult> search( NextInterceptor next, OperationContext opContext ) throws NamingException
     {
         Invocation invocation = InvocationStack.getInstance().peek();
         ServerLdapContext ctx = ( ServerLdapContext ) invocation.getCaller();
         LdapPrincipal user = ctx.getPrincipal();
         LdapDN principalDn = user.getJndiName();
-        NamingEnumeration e = next.search( base, env, filter, searchCtls );
+        NamingEnumeration<SearchResult> e = next.search( opContext );
+
+        boolean isSubschemaSubentryLookup = subschemaSubentryDn.equals( opContext.getDn().getNormName() );
+        SearchControls searchCtls = ((SearchOperationContext)opContext).getSearchControls();
+        boolean isRootDSELookup = opContext.getDn().size() == 0 && searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE;
 
-        boolean isSubschemaSubentryLookup = subschemaSubentryDn.equals( base.toNormName() );
-        boolean isRootDSELookup = base.size() == 0 && searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE;
         if ( isPrincipalAnAdministrator( principalDn ) || !enabled || isRootDSELookup || isSubschemaSubentryLookup )
         {
             return e;
         }
+        
         AuthorizationFilter authzFilter = new AuthorizationFilter();
-        return new SearchResultFilteringEnumeration( e, searchCtls, invocation, authzFilter );
+        return new SearchResultFilteringEnumeration( e, searchCtls, invocation, authzFilter, "Search authorization Filter" );
     }
 
     
@@ -997,18 +937,26 @@
     }
     
 
-    public boolean compare( NextInterceptor next, LdapDN name, String oid, Object value ) throws NamingException
+    public boolean compare( NextInterceptor next, OperationContext opContext ) throws NamingException
     {
+    	CompareOperationContext ctx = (CompareOperationContext)opContext;
+    	LdapDN name = ctx.getDn();
+    	String oid = ctx.getOid();
+    	Object value = ctx.getValue();
+    	
         // Access the principal requesting the operation, and bypass checks if it is the admin
         Invocation invocation = InvocationStack.getInstance().peek();
         PartitionNexusProxy proxy = invocation.getProxy();
-        Attributes entry = proxy.lookup( name, PartitionNexusProxy.LOOKUP_BYPASS );
+        Attributes entry = proxy.lookup( 
+        		new LookupOperationContext( name ), 
+        		PartitionNexusProxy.LOOKUP_BYPASS );
+
         LdapPrincipal principal = ( ( ServerContext ) invocation.getCaller() ).getPrincipal();
         LdapDN principalDn = principal.getJndiName();
 
         if ( isPrincipalAnAdministrator( principalDn ) || !enabled )
         {
-            return next.compare( name, oid, value );
+            return next.compare( opContext );
         }
 
         Set userGroups = groupCache.getGroups( principalDn.toNormName() );
@@ -1022,11 +970,11 @@
         engine.checkPermission( proxy, userGroups, principalDn, principal.getAuthenticationLevel(), name, oid, value,
             COMPARE_PERMS, tuples, entry );
 
-        return next.compare( name, oid, value );
+        return next.compare( opContext );
     }
 
 
-    public LdapDN getMatchedName ( NextInterceptor next, LdapDN dn ) throws NamingException
+    public LdapDN getMatchedName ( NextInterceptor next, OperationContext opContext ) throws NamingException
     {
         // Access the principal requesting the operation, and bypass checks if it is the admin
         Invocation invocation = InvocationStack.getInstance().peek();
@@ -1036,19 +984,19 @@
         
         if ( isPrincipalAnAdministrator( principalDn ) || !enabled )
         {
-            return next.getMatchedName( dn );
+            return next.getMatchedName( opContext );
         }
 
         // get the present matched name
         Attributes entry;
-        LdapDN matched = next.getMatchedName( dn );
+        LdapDN matched = next.getMatchedName( opContext );
 
         // check if we have disclose on error permission for the entry at the matched dn
         // if not remove rdn and check that until nothing is left in the name and return
         // that but if permission is granted then short the process and return the dn
         while ( matched.size() > 0 )
         {
-            entry = proxy.lookup( matched, PartitionNexusProxy.GETMATCHEDDN_BYPASS );
+            entry = proxy.lookup( new LookupOperationContext( matched ), PartitionNexusProxy.GETMATCHEDDN_BYPASS );
             Set userGroups = groupCache.getGroups( principalDn.toString() );
             Collection<ACITuple> tuples = new HashSet<ACITuple>();
             addPerscriptiveAciTuples( proxy, tuples, matched, entry );
@@ -1068,9 +1016,9 @@
     }
 
 
-    public void cacheNewGroup( String upName, LdapDN normName, Attributes entry ) throws NamingException
+    public void cacheNewGroup( LdapDN name, Attributes entry ) throws NamingException
     {
-        this.groupCache.groupAdded( upName, normName, entry );
+        groupCache.groupAdded( name, entry );
     }
 
 
@@ -1081,7 +1029,7 @@
          * tests.  If we hasPermission() returns false we immediately short the
          * process and return false.
          */
-        Attributes entry = invocation.getProxy().lookup( normName, PartitionNexusProxy.LOOKUP_BYPASS );
+        Attributes entry = invocation.getProxy().lookup( new LookupOperationContext( normName ), PartitionNexusProxy.LOOKUP_BYPASS );
         ServerLdapContext ctx = ( ServerLdapContext ) invocation.getCaller();
         LdapDN userDn = ctx.getPrincipal().getJndiName();
         Set userGroups = groupCache.getGroups( userDn.toNormName() );
@@ -1104,11 +1052,13 @@
          * values remaining then the entire attribute is removed.
          */
         NamingEnumeration idList = result.getAttributes().getIDs();
+
         while ( idList.hasMore() )
         {
             // if attribute type scope access is not allowed then remove the attribute and continue
             String id = ( String ) idList.next();
             Attribute attr = result.getAttributes().get( id );
+        
             if ( !engine.hasPermission( invocation.getProxy(), userGroups, userDn, ctx.getPrincipal()
                 .getAuthenticationLevel(), normName, attr.getID(), null, SEARCH_ATTRVAL_PERMS, tuples, entry ) )
             {



Mime
View raw message