You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2011/10/20 21:42:13 UTC

svn commit: r1187013 [6/12] - in /directory/apacheds/branches/apacheds-txns: ./ all/ apache-felix/ apache-felix/bundle/ apache-felix/src/ apache-felix/src/main/ apache-felix/src/main/resources/ core-annotations/ core-annotations/src/main/java/org/apach...

Added: directory/apacheds/branches/apacheds-txns/interceptors/admin/src/main/java/org/apache/directory/server/core/admin/AdministrativePointInterceptor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/interceptors/admin/src/main/java/org/apache/directory/server/core/admin/AdministrativePointInterceptor.java?rev=1187013&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/interceptors/admin/src/main/java/org/apache/directory/server/core/admin/AdministrativePointInterceptor.java (added)
+++ directory/apacheds/branches/apacheds-txns/interceptors/admin/src/main/java/org/apache/directory/server/core/admin/AdministrativePointInterceptor.java Thu Oct 20 19:41:49 2011
@@ -0,0 +1,1515 @@
+/*
+ *  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.admin;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import javax.naming.directory.SearchControls;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.shared.DefaultCoreSession;
+import org.apache.directory.server.core.api.CoreSession;
+import org.apache.directory.server.core.api.DirectoryService;
+import org.apache.directory.server.core.api.LdapPrincipal;
+import org.apache.directory.server.core.api.administrative.AccessControlAAP;
+import org.apache.directory.server.core.api.administrative.AccessControlAdministrativePoint;
+import org.apache.directory.server.core.api.administrative.AccessControlIAP;
+import org.apache.directory.server.core.api.administrative.AccessControlSAP;
+import org.apache.directory.server.core.api.administrative.AdministrativePoint;
+import org.apache.directory.server.core.api.administrative.CollectiveAttributeAAP;
+import org.apache.directory.server.core.api.administrative.CollectiveAttributeAdministrativePoint;
+import org.apache.directory.server.core.api.administrative.CollectiveAttributeIAP;
+import org.apache.directory.server.core.api.administrative.CollectiveAttributeSAP;
+import org.apache.directory.server.core.api.administrative.SubschemaAAP;
+import org.apache.directory.server.core.api.administrative.SubschemaAdministrativePoint;
+import org.apache.directory.server.core.api.administrative.SubschemaSAP;
+import org.apache.directory.server.core.api.administrative.TriggerExecutionAAP;
+import org.apache.directory.server.core.api.administrative.TriggerExecutionAdministrativePoint;
+import org.apache.directory.server.core.api.administrative.TriggerExecutionIAP;
+import org.apache.directory.server.core.api.administrative.TriggerExecutionSAP;
+import org.apache.directory.server.core.api.entry.ClonedServerEntry;
+import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
+import org.apache.directory.server.core.api.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.api.interceptor.Interceptor;
+import org.apache.directory.server.core.api.interceptor.NextInterceptor;
+import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.api.partition.PartitionNexus;
+import org.apache.directory.shared.ldap.model.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.model.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.model.entry.Attribute;
+import org.apache.directory.shared.ldap.model.entry.DefaultAttribute;
+import org.apache.directory.shared.ldap.model.entry.Entry;
+import org.apache.directory.shared.ldap.model.entry.Modification;
+import org.apache.directory.shared.ldap.model.entry.Value;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+import org.apache.directory.shared.ldap.model.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.shared.ldap.model.exception.LdapNoSuchAttributeException;
+import org.apache.directory.shared.ldap.model.exception.LdapOperationException;
+import org.apache.directory.shared.ldap.model.exception.LdapUnwillingToPerformException;
+import org.apache.directory.shared.ldap.model.filter.ExprNode;
+import org.apache.directory.shared.ldap.model.filter.PresenceNode;
+import org.apache.directory.shared.ldap.model.message.AliasDerefMode;
+import org.apache.directory.shared.ldap.model.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.model.name.Dn;
+import org.apache.directory.shared.ldap.model.subtree.AdministrativeRole;
+import org.apache.directory.shared.ldap.util.tree.DnNode;
+import org.apache.directory.shared.util.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An interceptor to manage the Administrative model
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class AdministrativePointInterceptor extends BaseInterceptor
+{
+    /** A {@link Logger} for this class */
+    private static final Logger LOG = LoggerFactory.getLogger( AdministrativePointInterceptor.class );
+
+    /**
+     * Speedup for logs
+     */
+    private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /** A reference to the nexus for direct backend operations */
+    private PartitionNexus nexus;
+
+    /** The possible roles */
+    private static final Set<String> ROLES = new HashSet<String>();
+
+    // Initialize the ROLES field
+    static
+    {
+        ROLES.add( Strings.toLowerCase( SchemaConstants.AUTONOMOUS_AREA ) );
+        ROLES.add( SchemaConstants.AUTONOMOUS_AREA_OID );
+        ROLES.add( Strings.toLowerCase( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA ) );
+        ROLES.add( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
+        ROLES.add( Strings.toLowerCase( SchemaConstants.ACCESS_CONTROL_INNER_AREA ) );
+        ROLES.add( SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID );
+        ROLES.add( Strings.toLowerCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA ) );
+        ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
+        ROLES.add( Strings.toLowerCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA ) );
+        ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID );
+        ROLES.add( Strings.toLowerCase( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA ) );
+        ROLES.add( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID );
+        ROLES.add( Strings.toLowerCase( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA ) );
+        ROLES.add( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
+        ROLES.add( Strings.toLowerCase( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA ) );
+        ROLES.add( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID );
+    }
+
+    /** A Map to associate a role with it's OID */
+    private static final Map<String, String> ROLES_OID = new HashMap<String, String>();
+
+    // Initialize the roles/oid map
+    static
+    {
+        ROLES_OID.put( Strings.toLowerCase( SchemaConstants.AUTONOMOUS_AREA ), SchemaConstants.AUTONOMOUS_AREA_OID );
+        ROLES_OID.put( Strings.toLowerCase( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA ),
+            SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
+        ROLES_OID.put( Strings.toLowerCase( SchemaConstants.ACCESS_CONTROL_INNER_AREA ),
+            SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID );
+        ROLES_OID.put( Strings.toLowerCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA ),
+            SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
+        ROLES_OID.put( Strings.toLowerCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA ),
+            SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID );
+        ROLES_OID.put( Strings.toLowerCase( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA ),
+            SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID );
+        ROLES_OID.put( Strings.toLowerCase( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA ),
+            SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
+        ROLES_OID.put( Strings.toLowerCase( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA ),
+            SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID );
+    }
+
+    /** The possible inner area roles */
+    private static final Set<String> INNER_AREA_ROLES = new HashSet<String>();
+
+    static
+    {
+        INNER_AREA_ROLES.add( Strings.toLowerCase( SchemaConstants.ACCESS_CONTROL_INNER_AREA ) );
+        INNER_AREA_ROLES.add( SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID );
+        INNER_AREA_ROLES.add( Strings.toLowerCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA ) );
+        INNER_AREA_ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID );
+        INNER_AREA_ROLES.add( Strings.toLowerCase( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA ) );
+        INNER_AREA_ROLES.add( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID );
+    }
+
+    /** The possible specific area roles */
+    private static final Set<String> SPECIFIC_AREA_ROLES = new HashSet<String>();
+
+    static
+    {
+        SPECIFIC_AREA_ROLES.add( Strings.toLowerCase( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA ) );
+        SPECIFIC_AREA_ROLES.add( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
+        SPECIFIC_AREA_ROLES.add( Strings.toLowerCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA ) );
+        SPECIFIC_AREA_ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
+        SPECIFIC_AREA_ROLES.add( Strings.toLowerCase( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA ) );
+        SPECIFIC_AREA_ROLES.add( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID );
+        SPECIFIC_AREA_ROLES.add( Strings.toLowerCase( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA ) );
+        SPECIFIC_AREA_ROLES.add( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
+    }
+
+    /** A lock to guarantee the AP cache consistency */
+    private ReentrantReadWriteLock mutex = new ReentrantReadWriteLock();
+
+    /**
+     * Get a read-lock on the AP cache.
+     * No read operation can be done on the AP cache if this
+     * method is not called before.
+     */
+    public void lockRead()
+    {
+        mutex.readLock().lock();
+    }
+
+
+    /**
+     * Get a write-lock on the AP cache.
+     * No write operation can be done on the apCache if this
+     * method is not called before.
+     */
+    public void lockWrite()
+    {
+        mutex.writeLock().lock();
+    }
+
+
+    /**
+     * Release the read-write lock on the AP cache.
+     * This method must be called after having read or modified the
+     * AP cache
+     */
+    public void unlock()
+    {
+        if ( mutex.isWriteLockedByCurrentThread() )
+        {
+            mutex.writeLock().unlock();
+        }
+        else
+        {
+            mutex.readLock().unlock();
+        }
+    }
+
+
+    /**
+     * Create the list of AP for a given entry
+     */
+    private void createAdministrativePoints( Attribute adminPoint, Dn dn, String uuid ) throws LdapException
+    {
+        if ( isAAP( adminPoint ) )
+        {
+            // The AC AAP
+            AccessControlAdministrativePoint acAap = new AccessControlAAP( dn, uuid );
+            directoryService.getAccessControlAPCache().add( dn, acAap );
+
+            // The CA AAP
+            CollectiveAttributeAdministrativePoint caAap = new CollectiveAttributeAAP( dn, uuid );
+            directoryService.getCollectiveAttributeAPCache().add( dn, caAap );
+
+            // The TE AAP
+            TriggerExecutionAdministrativePoint teAap = new TriggerExecutionAAP( dn, uuid );
+            directoryService.getTriggerExecutionAPCache().add( dn, teAap );
+
+            // The SS AAP
+            SubschemaAdministrativePoint ssAap = new SubschemaAAP( dn, uuid );
+            directoryService.getSubschemaAPCache().add( dn, ssAap );
+
+            // TODO : Here, we have to update the children, removing any 
+            // reference to any other underlying AP
+
+            // If it's an AAP, we can get out immediately
+            return;
+        }
+
+        for ( Value<?> value : adminPoint )
+        {
+            String role = value.getString();
+
+            // Deal with AccessControl AP
+            if ( isAccessControlSpecificRole( role ) )
+            {
+                AccessControlAdministrativePoint sap = new AccessControlSAP( dn, uuid );
+                directoryService.getAccessControlAPCache().add( dn, sap );
+
+                // TODO : Here, we have to update the children, removing any 
+                // reference to any other underlying AccessControl IAP or SAP 
+
+                continue;
+            }
+
+            if ( isAccessControlInnerRole( role ) )
+            {
+                AccessControlAdministrativePoint iap = new AccessControlIAP( dn, uuid );
+                directoryService.getAccessControlAPCache().add( dn, iap );
+
+                continue;
+            }
+
+            // Deal with CollectiveAttribute AP
+            if ( isCollectiveAttributeSpecificRole( role ) )
+            {
+                CollectiveAttributeAdministrativePoint sap = new CollectiveAttributeSAP( dn, uuid );
+                directoryService.getCollectiveAttributeAPCache().add( dn, sap );
+
+                // TODO : Here, we have to update the children, removing any 
+                // reference to any other underlying CollectiveAttribute IAP or SAP 
+                
+                continue;
+            }
+
+            if ( isCollectiveAttributeInnerRole( role ) )
+            {
+                CollectiveAttributeAdministrativePoint iap = new CollectiveAttributeIAP( dn, uuid );
+                directoryService.getCollectiveAttributeAPCache().add( dn, iap );
+
+                continue;
+            }
+
+            // Deal with SubSchema AP
+            if ( isSubschemaSpecficRole( role ) )
+            {
+                SubschemaAdministrativePoint sap = new SubschemaSAP( dn, uuid );
+                directoryService.getSubschemaAPCache().add( dn, sap );
+
+                // TODO : Here, we have to update the children, removing any 
+                // reference to any other underlying Subschema IAP or SAP 
+
+                continue;
+            }
+
+            // Deal with TriggerExecution AP
+            if ( isTriggerExecutionSpecificRole( role ) )
+            {
+                TriggerExecutionAdministrativePoint sap = new TriggerExecutionSAP( dn, uuid );
+                directoryService.getTriggerExecutionAPCache().add( dn, sap );
+
+                // TODO : Here, we have to update the children, removing any 
+                // reference to any other underlying TriggerExecution IAP or SAP
+                
+                continue;
+            }
+
+            if ( isTriggerExecutionInnerRole( role ) )
+            {
+                TriggerExecutionAdministrativePoint iap = new TriggerExecutionIAP( dn, uuid );
+                directoryService.getTriggerExecutionAPCache().add( dn, iap );
+
+                continue;
+            }
+        }
+
+        return;
+    }
+
+
+    /**
+     * Update the cache clones with the added roles
+     */
+    private void addRole( String role, Dn dn, String uuid, DnNode<AccessControlAdministrativePoint> acapCache,
+        DnNode<CollectiveAttributeAdministrativePoint> caapCache, DnNode<TriggerExecutionAdministrativePoint> teapCache,
+        DnNode<SubschemaAdministrativePoint> ssapCache ) throws LdapException
+    {
+        // Deal with Autonomous AP : create the 4 associated SAP/AAP
+        if ( isAutonomousAreaRole( role ) )
+        {
+            // The AC AAP
+            AccessControlAdministrativePoint acAap = new AccessControlAAP( dn, uuid );
+            acapCache.add( dn, acAap );
+
+            // The CA AAP
+            CollectiveAttributeAdministrativePoint caAap = new CollectiveAttributeAAP( dn, uuid );
+            caapCache.add( dn, caAap );
+
+            // The TE AAP
+            TriggerExecutionAdministrativePoint teAap = new TriggerExecutionAAP( dn, uuid );
+            teapCache.add( dn, teAap );
+
+            // The SS AAP
+            SubschemaAdministrativePoint ssAap = new SubschemaAAP( dn, uuid );
+            ssapCache.add( dn, ssAap );
+            
+            // If it's an AAP, we can get out immediately
+            return;
+        }
+
+        // Deal with AccessControl AP
+        if ( isAccessControlSpecificRole( role ) )
+        {
+            AccessControlAdministrativePoint sap = new AccessControlSAP( dn, uuid );
+            acapCache.add( dn, sap );
+
+            return;
+        }
+
+        if ( isAccessControlInnerRole( role ) )
+        {
+            AccessControlAdministrativePoint iap = new AccessControlIAP( dn, uuid );
+            acapCache.add( dn, iap );
+
+            return;
+        }
+
+        // Deal with CollectiveAttribute AP
+        if ( isCollectiveAttributeSpecificRole( role ) )
+        {
+            CollectiveAttributeAdministrativePoint sap = new CollectiveAttributeSAP( dn, uuid );
+            caapCache.add( dn, sap );
+
+            return;
+        }
+
+        if ( isCollectiveAttributeInnerRole( role ) )
+        {
+            CollectiveAttributeAdministrativePoint iap = new CollectiveAttributeIAP( dn, uuid );
+            caapCache.add( dn, iap );
+
+            return;
+        }
+
+        // Deal with SubSchema AP
+        if ( isSubschemaSpecficRole( role ) )
+        {
+            SubschemaAdministrativePoint sap = new SubschemaSAP( dn, uuid );
+            ssapCache.add( dn, sap );
+
+            return;
+        }
+
+        // Deal with TriggerExecution AP
+        if ( isTriggerExecutionSpecificRole( role ) )
+        {
+            TriggerExecutionAdministrativePoint sap = new TriggerExecutionSAP( dn, uuid );
+            teapCache.add( dn, sap );
+
+            return;
+        }
+
+        if ( isTriggerExecutionInnerRole( role ) )
+        {
+            TriggerExecutionAdministrativePoint iap = new TriggerExecutionIAP( dn, uuid );
+            teapCache.add( dn, iap );
+
+            return;
+        }
+    }
+
+
+
+
+    /**
+     * Update the cache clones with the added roles
+     */
+    private void delRole( String role, Dn dn, String uuid, DnNode<AccessControlAdministrativePoint> acapCache,
+        DnNode<CollectiveAttributeAdministrativePoint> caapCache, DnNode<TriggerExecutionAdministrativePoint> teapCache,
+        DnNode<SubschemaAdministrativePoint> ssapCache ) throws LdapException
+    {
+        // Deal with Autonomous AP : remove the 4 associated SAP/AAP
+        if ( isAutonomousAreaRole( role ) )
+        {
+            // The AC AAP
+            acapCache.remove( dn );
+
+            // The CA AAP
+            caapCache.remove( dn );
+
+            // The TE AAP
+            teapCache.remove( dn );
+
+            // The SS AAP
+            ssapCache.remove( dn );
+            
+            return;
+        }
+
+        // Deal with AccessControl AP
+        if ( isAccessControlSpecificRole( role ) || isAccessControlInnerRole( role ) )
+        {
+            acapCache.remove( dn );
+
+            return;
+        }
+
+        // Deal with CollectiveAttribute AP
+        if ( isCollectiveAttributeSpecificRole( role ) || isCollectiveAttributeInnerRole( role ) )
+        {
+            caapCache.remove( dn );
+
+            return;
+        }
+
+        // Deal with SubSchema AP
+        if ( isSubschemaSpecficRole( role ) )
+        {
+            ssapCache.remove( dn );
+
+            return;
+        }
+
+        // Deal with TriggerExecution AP
+        if ( isTriggerExecutionSpecificRole( role ) || isTriggerExecutionInnerRole( role ) )
+        {
+            teapCache.remove( dn );
+
+            return;
+        }
+    }
+
+    
+    private AdministrativePoint getParent( AdministrativePoint ap, List<AdministrativePoint> aps,
+        AdministrativeRole role, DnNode<List<AdministrativePoint>> currentNode )
+    {
+        AdministrativePoint parent = null;
+
+        for ( AdministrativePoint adminPoint : aps )
+        {
+            if ( adminPoint.isAutonomous() || ( adminPoint.getRole() == ap.getRole() ) )
+            {
+                // Same role or AP : this is the parent
+                return adminPoint;
+            }
+            else if ( adminPoint.getRole() == role )
+            {
+                parent = adminPoint;
+            }
+        }
+
+        if ( parent != null )
+        {
+            return parent;
+        }
+
+        // We have to go down one level
+        if ( currentNode.hasParent() )
+        {
+            return findParent( ap, currentNode );
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+
+    /**
+     * Find the parent for the given administrative point. If the AP is an AAP, the parent will be the closest
+     * AAP or the closest SAP. If we have a SAP between the added AAP and a AAP, then 
+     */
+    private AdministrativePoint findParent( AdministrativePoint ap, DnNode<List<AdministrativePoint>> currentNode )
+    {
+        List<AdministrativePoint> aps = currentNode.getElement();
+
+        if ( aps != null )
+        {
+            // Check if the current element is a valid parent
+            switch ( ap.getRole() )
+            {
+                case AutonomousArea:
+                    AdministrativePoint currentAp = aps.get( 0 );
+
+                    if ( currentAp.isAutonomous() )
+                    {
+                        return currentAp;
+                    }
+                    else
+                    {
+                        // We have to go down one level, as an AAP
+                        // must have another AAP as a parent
+                        if ( currentNode.hasParent() )
+                        {
+                            return findParent( ap, currentNode );
+                        }
+                        else
+                        {
+                            return null;
+                        }
+                    }
+
+                case AccessControlInnerArea:
+                    return getParent( ap, aps, AdministrativeRole.AccessControlSpecificArea, currentNode );
+
+                case CollectiveAttributeInnerArea:
+                    return getParent( ap, aps, AdministrativeRole.CollectiveAttributeSpecificArea, currentNode );
+
+                case TriggerExecutionInnerArea:
+                    return getParent( ap, aps, AdministrativeRole.TriggerExecutionSpecificArea, currentNode );
+
+                case AccessControlSpecificArea:
+                    return getParent( ap, aps, AdministrativeRole.AccessControlSpecificArea, currentNode );
+
+                case CollectiveAttributeSpecificArea:
+                    return getParent( ap, aps, AdministrativeRole.CollectiveAttributeSpecificArea, currentNode );
+
+                case SubSchemaSpecificArea:
+                    return getParent( ap, aps, AdministrativeRole.SubSchemaSpecificArea, currentNode );
+
+                case TriggerExecutionSpecificArea:
+                    return getParent( ap, aps, AdministrativeRole.TriggerExecutionSpecificArea, currentNode );
+
+                default:
+                    return null;
+            }
+        }
+        else
+        {
+            if ( currentNode.hasParent() )
+            {
+                return findParent( ap, currentNode.getParent() );
+            }
+            else
+            {
+                return null;
+            }
+        }
+    }
+
+
+    /**
+     * Check if we can safely add a role. If it's an AAP, we have to be sure that
+     * it's the only role present in the AT.
+     */
+    private void checkAddRole( Value<?> role, Attribute adminPoint, Dn dn ) throws LdapException
+    {
+        String roleStr = Strings.toLowerCase( Strings.trim( role.getString() ) );
+
+        // Check that the added AdministrativeRole is valid
+        if ( !ROLES.contains( roleStr ) )
+        {
+            String message = "Cannot add the given role, it's not a valid one :" + role;
+            LOG.error( message );
+            throw new LdapUnwillingToPerformException( message );
+        }
+
+        // If we are trying to add an AAP, we have to check that 
+        // it's the only role in the AdminPoint AT
+        if ( isAutonomousAreaRole( roleStr ) )
+        {
+            if ( adminPoint.size() > 1 )
+            {
+                String message = "Cannot add an Autonomous Administratve Point when some other roles are added : "
+                    + adminPoint;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+            else
+            {
+                // Fine : we only have one AAP
+                return;
+            }
+        }
+        
+        // Check that we don't have already an AAP in the AdminPoint AT when we try to
+        // add a role
+        if ( adminPoint.contains( SchemaConstants.AUTONOMOUS_AREA ) )
+        {
+            String message = "Cannot add a role when an Autonomous Administratve Point is already present : "
+                + adminPoint;
+            LOG.error( message );
+            throw new LdapUnwillingToPerformException( message );
+        }
+
+        // check that we can't mix Inner and Specific areas
+        checkInnerSpecificMix( roleStr, adminPoint );
+
+        // Check that we don't add an IAP with no parent. The IAP must be added under
+        // either a AAP, or a SAP/IAP within the same family
+        if ( isIAP( roleStr ) )
+        {
+            checkIAPHasParent( roleStr, adminPoint, dn );
+        }
+    }
+
+
+    /**
+     * Check if we can safely delete a role
+     */
+    private void checkDelRole( Value<?> role, Attribute adminPoint, Dn dn ) throws LdapException
+    {
+        String roleStr = Strings.toLowerCase( Strings.trim( role.getString() ) );
+
+        // Check that the removed AdministrativeRole is valid
+        if ( !ROLES.contains( roleStr ) )
+        {
+            String message = "Cannot delete the given role, it's not a valid one :" + role;
+            LOG.error( message );
+            throw new LdapUnwillingToPerformException( message );
+        }
+
+        // Now we are trying to delete an Administrative point. We have to check that 
+        // we only have one role if the deleted role is an AAP
+        if ( isAutonomousAreaRole( roleStr ) )
+        {
+            // We know have to check that removing the AAP, we will not 
+            // left any pending IAP. We should check for the 3 potential IAPs :
+            // AccessControl, CollectiveAttribute and TriggerExecution.
+            // If the removed AP has a parent, no need to go any further :
+            // the children IAPs will depend on this parent.
+            
+            // Process the ACs
+            DnNode<AccessControlAdministrativePoint> acAps = directoryService.getAccessControlAPCache();
+            
+            if ( !acAps.hasParent( dn ) )
+            {
+                // No parent, check for any IAP
+                List<AccessControlAdministrativePoint> children = acAps.getDescendantElements( dn );
+                
+                for ( AccessControlAdministrativePoint child : children )
+                {
+                    if ( child.isInner() )
+                    {
+                        // Ok, we are dead : the IAP will remain with no parent.
+                        String message = "Cannot delete the given role, the " + child.getDn() + " AccessControl IAP will remain orphan";
+                        LOG.error( message );
+                        throw new LdapUnwillingToPerformException( message );
+                    }
+                }
+            }
+            
+            // Process the CAs
+            DnNode<CollectiveAttributeAdministrativePoint> caAps = directoryService.getCollectiveAttributeAPCache();
+            
+            if ( !acAps.hasParent( dn ) )
+            {
+                // No parent, check for any IAP
+                List<CollectiveAttributeAdministrativePoint> children = caAps.getDescendantElements( dn );
+                
+                for ( CollectiveAttributeAdministrativePoint child : children )
+                {
+                    if ( child.isInner() )
+                    {
+                        // Ok, we are dead : the IAP will remain with no parent.
+                        String message = "Cannot delete the given role, the " + child.getDn() + " CollectiveAttribute IAP will remain orphan";
+                        LOG.error( message );
+                        throw new LdapUnwillingToPerformException( message );
+                    }
+                }
+            }
+            
+            // Process the TEs
+            DnNode<TriggerExecutionAdministrativePoint> teAps = directoryService.getTriggerExecutionAPCache();
+            
+            if ( !acAps.hasParent( dn ) )
+            {
+                // No parent, check for any IAP
+                List<TriggerExecutionAdministrativePoint> children = teAps.getDescendantElements( dn );
+                
+                for ( TriggerExecutionAdministrativePoint child : children )
+                {
+                    if ( child.isInner() )
+                    {
+                        // Ok, we are dead : the IAP will remain with no parent.
+                        String message = "Cannot delete the given role, the " + child.getDn() + " TriggerExecution IAP will remain orphan";
+                        LOG.error( message );
+                        throw new LdapUnwillingToPerformException( message );
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Creates an Administrative service interceptor.
+     */
+    public AdministrativePointInterceptor()
+    {
+    }
+
+
+    //-------------------------------------------------------------------------------------------
+    // Helper methods
+    //-------------------------------------------------------------------------------------------
+    private List<Entry> getAdministrativePoints() throws LdapException
+    {
+        List<Entry> entries = new ArrayList<Entry>();
+
+        Dn adminDn = new Dn( schemaManager, ServerDNConstants.ADMIN_SYSTEM_DN );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setReturningAttributes( new String[]
+            { SchemaConstants.ADMINISTRATIVE_ROLE_AT, SchemaConstants.ENTRY_UUID_AT } );
+
+        // Search for all the adminstrativePoints in the base
+        ExprNode filter = new PresenceNode( ADMINISTRATIVE_ROLE_AT );
+
+        CoreSession adminSession = new DefaultCoreSession( new LdapPrincipal( schemaManager, adminDn, AuthenticationLevel.STRONG ),
+            directoryService );
+
+        SearchOperationContext searchOperationContext = new SearchOperationContext( adminSession, Dn.ROOT_DSE, filter,
+            controls );
+
+        searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
+
+        EntryFilteringCursor results = nexus.search( searchOperationContext );
+
+        try
+        {
+            while ( results.next() )
+            {
+                Entry entry = results.get();
+
+                entries.add( entry );
+            }
+            
+            results.close();
+        }
+        catch ( Exception e )
+        {
+            throw new LdapOperationException( e.getMessage(), e );
+        }
+
+        return entries;
+    }
+
+
+    /**
+     * Tells if a given role is a valid administrative role. We check the lower cased
+     * and trimmed value, and also the OID value.
+     */
+    private boolean isValidRole( String role )
+    {
+        return ROLES.contains( Strings.toLowerCase( Strings.trim( role ) ) );
+    }
+
+
+    /**
+     * Update The Administrative Points cache, adding the given AdminPoints
+     */
+    private void addAdminPointCache( List<Entry> adminPointEntries ) throws LdapException
+    {
+        for ( Entry adminPointEntry : adminPointEntries )
+        {
+            // update the cache
+            Dn dn = adminPointEntry.getDn();
+
+            String uuid = adminPointEntry.get( ENTRY_UUID_AT ).getString();
+            Attribute adminPoint = adminPointEntry.get( ADMINISTRATIVE_ROLE_AT );
+
+            createAdministrativePoints( adminPoint, dn, uuid );
+        }
+    }
+
+
+    /**
+     * Update The Administrative Points cache, removing the given AdminPoint
+     */
+    private void deleteAdminPointCache( Attribute adminPoint, DeleteOperationContext deleteContext )
+        throws LdapException
+    {
+        Dn dn = deleteContext.getDn();
+        
+        // Remove the APs in the AP cache
+        for ( Value<?> value : adminPoint )
+        {
+            String role = value.getString();
+
+            // Deal with Autonomous AP : delete the 4 associated SAP/AAP
+            if ( isAutonomousAreaRole( role ) )
+            {
+                // The AC AAP
+                directoryService.getAccessControlAPCache().remove( dn );
+
+                // The CA AAP
+                directoryService.getCollectiveAttributeAPCache().remove( dn );
+
+                // The TE AAP
+                directoryService.getTriggerExecutionAPCache().remove( dn );
+
+                // The SS AAP
+                directoryService.getSubschemaAPCache().remove( dn );
+
+                // If it's an AAP, we can get out immediately
+                return;
+            }
+
+            // Deal with AccessControl AP
+            if ( isAccessControlSpecificRole( role ) || isAccessControlInnerRole( role ) )
+            {
+                directoryService.getAccessControlAPCache().remove( dn );
+
+                continue;
+            }
+
+            // Deal with CollectveAttribute AP
+            if ( isCollectiveAttributeSpecificRole( role ) || isCollectiveAttributeInnerRole( role ) )
+            {
+                directoryService.getCollectiveAttributeAPCache().remove( dn );
+
+                continue;
+            }
+
+
+            // Deal with SubSchema AP
+            if ( isSubschemaSpecficRole( role ) )
+            {
+                directoryService.getSubschemaAPCache().remove( dn );
+
+                continue;
+            }
+
+            // Deal with TriggerExecution AP
+            if ( isTriggerExecutionSpecificRole( role ) || isTriggerExecutionInnerRole( role ))
+            {
+                directoryService.getTriggerExecutionAPCache().remove( dn );
+
+                continue;
+            }
+        }
+    }
+
+
+    /**
+     * Tells if the role is an AC IAP
+     */
+    private boolean isAccessControlInnerRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.ACCESS_CONTROL_INNER_AREA ) ||
+               role.equals( SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is an AC SAP
+     */
+    private boolean isAccessControlSpecificRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA ) ||
+               role.equals( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is a CA IAP
+     */
+    private boolean isCollectiveAttributeInnerRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA ) ||
+               role.equals( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is a CA SAP
+     */
+    private boolean isCollectiveAttributeSpecificRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA ) ||
+               role.equals( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is a TE IAP
+     */
+    private boolean isTriggerExecutionInnerRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA ) ||
+               role.equals( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is a TE SAP
+     */
+    private boolean isTriggerExecutionSpecificRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA ) ||
+               role.equals( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is a SS SAP
+     */
+    private boolean isSubschemaSpecficRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA ) ||
+               role.equals( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is an AAP
+     */
+    private boolean isAutonomousAreaRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.AUTONOMOUS_AREA ) ||
+               role.equals( SchemaConstants.AUTONOMOUS_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the Administrative Point role is an AAP
+     */
+    private boolean isAAP( Attribute adminPoint )
+    {
+        return ( adminPoint.contains( SchemaConstants.AUTONOMOUS_AREA ) || adminPoint
+            .contains( SchemaConstants.AUTONOMOUS_AREA_OID ) );
+    }
+
+
+    private boolean hasAccessControlSpecificRole( Attribute adminPoint )
+    {
+        return adminPoint.contains( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA ) ||
+               adminPoint.contains( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
+    }
+
+
+    private boolean isIAP( String role )
+    {
+        return INNER_AREA_ROLES.contains( role );
+    }
+
+
+    private boolean hasCollectiveAttributeSpecificRole( Attribute adminPoint )
+    {
+        return adminPoint.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA ) ||
+               adminPoint.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
+    }
+
+
+    private boolean hasTriggerExecutionSpecificRole( Attribute adminPoint )
+    {
+        return adminPoint.contains( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA ) ||
+               adminPoint.contains( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
+    }
+
+
+    /**
+     * Check that we don't have an IAP and a SAP with the same family
+     */
+    private void checkInnerSpecificMix( String role, Attribute adminPoint ) throws LdapUnwillingToPerformException
+    {
+        if ( isAccessControlInnerRole( role ) )
+        {
+            if ( hasAccessControlSpecificRole( adminPoint ) )
+            {
+                // This is inconsistent
+                String message = "Cannot add a specific Administrative Point and the same"
+                    + " inner Administrative point at the same time : " + adminPoint;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+            else
+            {
+                return;
+            }
+        }
+
+        if ( isCollectiveAttributeInnerRole( role ) )
+        {
+            if ( hasCollectiveAttributeSpecificRole( adminPoint ) )
+            {
+                // This is inconsistent
+                String message = "Cannot add a specific Administrative Point and the same"
+                    + " inner Administrative point at the same time : " + adminPoint;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+            else
+            {
+                return;
+            }
+        }
+
+        if ( isTriggerExecutionInnerRole( role ) )
+        {
+            if ( hasTriggerExecutionSpecificRole( adminPoint ) )
+            {
+                // This is inconsistent
+                String message = "Cannot add a specific Administrative Point and the same"
+                    + " inner Administrative point at the same time : " + adminPoint;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+            else
+            {
+                return;
+            }
+        }
+    }
+
+
+    /**
+     * Check that the IAPs (if any) have a parent. We will check for each kind or role :
+     * AC, CA and TE.
+     */
+    private void checkIAPHasParent( String role, Attribute adminPoint, Dn dn )
+        throws LdapUnwillingToPerformException
+    {
+        // Check for the AC role
+        if ( isAccessControlInnerRole( role ) )
+        {
+            DnNode<AccessControlAdministrativePoint> acCache = directoryService.getAccessControlAPCache();
+            
+            DnNode<AccessControlAdministrativePoint> parent =  acCache.getNode( dn );
+            
+            if ( parent == null )
+            {
+                // We don't have any AC administrativePoint in the tree, this is an error
+                String message = "Cannot add an IAP with no parent : " + adminPoint;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+        }
+        else if ( isCollectiveAttributeInnerRole( role ) )
+        {
+            DnNode<CollectiveAttributeAdministrativePoint> caCache = directoryService.getCollectiveAttributeAPCache();
+            
+            boolean hasAP = caCache.hasParentElement( dn );
+            
+            if ( !hasAP )
+            {
+                // We don't have any AC administrativePoint in the tree, this is an error
+                String message = "Cannot add an IAP with no parent : " + adminPoint;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+        }
+        else if ( isTriggerExecutionInnerRole( role ) )
+        {
+            DnNode<TriggerExecutionAdministrativePoint> caCache = directoryService.getTriggerExecutionAPCache();
+            
+            DnNode<TriggerExecutionAdministrativePoint> parent =  caCache.getNode( dn );
+            
+            if ( parent == null )
+            {
+                // We don't have any AC administrativePoint in the tree, this is an error
+                String message = "Cannot add an IAP with no parent : " + adminPoint;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+        }
+        else
+        {
+            // Wtf ? We *must* have an IAP here...
+            String message = "This is not an IAP : " + role;
+            LOG.error( message );
+            throw new LdapUnwillingToPerformException( message );
+        }
+    }
+
+
+    //-------------------------------------------------------------------------------------------
+    // Interceptor initialization
+    //-------------------------------------------------------------------------------------------
+    /**
+     * Registers and initializes all {@link Authenticator}s to this service.
+     */
+    public void init( DirectoryService directoryService ) throws LdapException
+    {
+        LOG.debug( "Initializing the AdministrativeInterceptor" );
+
+        super.init( directoryService );
+        nexus = directoryService.getPartitionNexus();
+
+        // Load all the AdministratvePoint :
+        // Autonomous Administrative Point first, then Specific
+        // administrative point, finally the Inner administrative Point
+        Dn adminDn = new Dn( schemaManager, ServerDNConstants.ADMIN_SYSTEM_DN );
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        controls.setReturningAttributes( new String[]
+            { SchemaConstants.ADMINISTRATIVE_ROLE_AT } );
+
+        // get the list of all the AAPs
+        List<Entry> administrativePoints = getAdministrativePoints();
+        
+        lockWrite();
+        addAdminPointCache( administrativePoints );
+        unlock();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void destroy()
+    {
+    }
+
+
+    private void updateAPEntries( Attribute adminPoint, String apUuid )
+    {
+        if ( isAAP( adminPoint ) )
+        {
+
+        }
+
+        return;
+    }
+
+
+    /**
+     * Add an administrative point into the DIT.
+     * 
+     * We have to deal with some specific cases :
+     * <ul>
+     * <li>If it's an AA, then the added role should be the only one</li>
+     * <li>It's not possible to add IA and SA at the same time</li>
+     * 
+     * @param next The next {@link Interceptor} in the chain
+     * @param addContext The {@link AddOperationContext} instance
+     * @throws LdapException If we had some error while processing the Add operation
+     */
+    public void add( NextInterceptor next, AddOperationContext addContext ) throws LdapException
+    {
+        LOG.debug( ">>> Entering into the Administrative Interceptor, addRequest" );
+        Entry entry = addContext.getEntry();
+        Dn dn = entry.getDn();
+
+        // Check if we are adding an Administrative Point
+        Attribute adminPoint = entry.get( ADMINISTRATIVE_ROLE_AT );
+
+        if ( adminPoint == null )
+        {
+            // Nope, go on.
+            next.add( addContext );
+
+            LOG.debug( "Exit from Administrative Interceptor, no AP in the added entry" );
+
+            return;
+        }
+
+        LOG.debug( "Addition of an administrative point at {} for the role {}", dn, adminPoint );
+
+        // Protect the AP caches against concurrent access
+        lockWrite();
+        
+        // Loop on all the added roles to check if they are valid
+        for ( Value<?> role : adminPoint )
+        {
+            checkAddRole( role, adminPoint, dn );
+        }
+
+        // Ok, we are golden.
+        next.add( addContext );
+
+        String apUuid = entry.get( ENTRY_UUID_AT ).getString();
+
+        // Now, update the AdminPoint cache
+        createAdministrativePoints( adminPoint, dn, apUuid );
+
+        // Release the APCaches lock
+        unlock();
+        
+        LOG.debug( "Added an Administrative Point at {}", dn );
+
+        return;
+    }
+
+
+    /**
+     * We have to check that we can remove the associated AdministrativePoint : <br/> 
+     * <ul>
+     * <li> if we remove an AAP, no descendant IAP should remain orphan</li>
+     * <li> If we remove a SAP, no descendant IAP should remain orphan</li>
+     * </ul> 
+     * {@inheritDoc}
+     */
+    public void delete( NextInterceptor next, DeleteOperationContext deleteContext ) throws LdapException
+    {
+        LOG.debug( ">>> Entering into the Administrative Interceptor, delRequest" );
+        Entry entry = deleteContext.getEntry();
+        Dn dn = entry.getDn();
+
+        // Check if we are deleting an Administrative Point
+        Attribute adminPoint = entry.get( ADMINISTRATIVE_ROLE_AT );
+
+        if ( adminPoint == null )
+        {
+            // Nope, go on.
+            next.delete( deleteContext );
+
+            LOG.debug( "Exit from Administrative Interceptor" );
+
+            return;
+        }
+
+        LOG.debug( "Deletion of an administrative point at {} for the role {}", dn, adminPoint );
+        
+        // Protect the AP caches against concurrent access
+        lockWrite();
+        
+        // Check that the removed AdministrativeRoles are valid. We don't have to do
+        // any other check, as the deleted entry has no children.
+        for ( Value<?> role : adminPoint )
+        {
+            if ( !isValidRole( role.getString() ) )
+            {
+                String message = "Cannot remove the given role, it's not a valid one :" + role;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+        }
+
+        // Ok, we can remove the AP
+        next.delete( deleteContext );
+
+        // Now, update the AdminPoint cache
+        deleteAdminPointCache( adminPoint, deleteContext );
+
+        // Release the APCaches lock
+        unlock();
+        
+        LOG.debug( "Deleted an Administrative Point at {}", dn );
+
+        return;
+    }
+
+
+    /**
+     * Only the add and remove modifications are fully supported. We have to check that the
+     * underlying APs are still consistent.
+     * We first have to compute the final AdministrativeRole, then do a diff with the
+     * initial attribute, to determinate which roles have been added and which ones have
+     * been deleted.
+     * Once this is done, we have to check that when deleting or adding each of those roles
+     * the admin model remains consistent.
+     * 
+     * {@inheritDoc}
+     */
+    public void modify( NextInterceptor next, ModifyOperationContext modifyContext ) throws LdapException
+    {
+        LOG.debug( ">>> Entering into the Administrative Interceptor, modifyRequest" );
+        // We have to check that the modification is acceptable
+        List<Modification> modifications = modifyContext.getModItems();
+        Dn dn = modifyContext.getDn();
+        String uuid = modifyContext.getEntry().get( ENTRY_UUID_AT ).getString();
+
+        // Create a clone of the current AdminRole AT
+        Attribute modifiedAdminRole = ((ClonedServerEntry)modifyContext.getEntry() ).getOriginalEntry().get( ADMINISTRATIVE_ROLE_AT );
+
+        if ( modifiedAdminRole == null )
+        {
+            // Create the attribute
+            modifiedAdminRole = new DefaultAttribute( ADMINISTRATIVE_ROLE_AT );
+        }
+        else
+        {
+            modifiedAdminRole = modifiedAdminRole.clone();
+        }
+
+        // Clone the AP caches before applying modifications to them modify it
+        DnNode<AccessControlAdministrativePoint> acapCacheCopy = directoryService.getAccessControlAPCache().clone();
+        DnNode<CollectiveAttributeAdministrativePoint> caapCacheCopy = directoryService.getCollectiveAttributeAPCache().clone();
+        DnNode<TriggerExecutionAdministrativePoint> teapCacheCopy = directoryService.getTriggerExecutionAPCache().clone();
+        DnNode<SubschemaAdministrativePoint> ssapCacheCopy = directoryService.getSubschemaAPCache().clone();
+        
+        // Loop on the modification to select the AdministrativeRole and process it :
+        // we will create a new AT containing all the roles after having applied the modifications
+        // on it
+        for ( Modification modification : modifications )
+        {
+            Attribute attribute = modification.getAttribute();
+
+            // Skip all the attributes but AdministrativeRole
+            if ( attribute.getAttributeType() == ADMINISTRATIVE_ROLE_AT )
+            {
+                // Ok, we have a modification impacting the administrative role
+                // Apply it to a virtual AdministrativeRole attribute
+                switch ( modification.getOperation() )
+                {
+                    case ADD_ATTRIBUTE:
+                        if ( modifiedAdminRole == null )
+                        {
+                            // Create the attribute
+                            modifiedAdminRole = new DefaultAttribute( ADMINISTRATIVE_ROLE_AT, attribute.get() );
+                        }
+
+                        for ( Value<?> role : attribute )
+                        {
+                            addRole( role.getString(), dn, uuid, acapCacheCopy, caapCacheCopy, teapCacheCopy, ssapCacheCopy );
+
+                            // Add the role to the modified attribute
+                            modifiedAdminRole.add( role );
+                        }
+
+                        break;
+
+                    case REMOVE_ATTRIBUTE:
+                        if ( modifiedAdminRole == null )
+                        {
+                            // We can't remove a value when the attribute does not exist.
+                            String msg = "Cannot remove the administrative role, it does not exist";
+                            LOG.error( msg );
+                            throw new LdapNoSuchAttributeException( msg );
+                        }
+
+                        // It may be a complete removal
+                        if ( attribute.size() == 0 )
+                        {
+                            // Complete removal. Loop on all the existing roles and remove them
+                            for ( Value<?> role : modifiedAdminRole )
+                            {
+                                //checkDelRole( role, modifiedAdminRole, dn, directoryService.getAdministrativePoints() );
+                                delRole( role.getString(), dn, uuid, acapCacheCopy, caapCacheCopy, teapCacheCopy, ssapCacheCopy );
+                            }
+
+                            modifiedAdminRole.clear();
+                            break;
+                        }
+
+                        // Now deal with the values to remove
+                        for ( Value<?> value : attribute )
+                        {
+                            if ( !isValidRole( value.getString() ) )
+                            {
+                                // Not a valid role : we will throw an exception
+                                String msg = "Invalid role : " + value.getString();
+                                LOG.error( msg );
+                                throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX,
+                                    msg );
+                            }
+
+                            if ( !modifiedAdminRole.contains( value ) )
+                            {
+                                // We can't remove a value if it does not exist !
+                                String msg = "Cannot remove the administrative role value" + value
+                                    + ", it does not exist";
+                                LOG.error( msg );
+                                throw new LdapNoSuchAttributeException( msg );
+                            }
+
+                            modifiedAdminRole.remove( value );
+                            delRole( value.getString(), dn, uuid, acapCacheCopy, caapCacheCopy, teapCacheCopy,
+                                ssapCacheCopy );
+
+                        }
+
+                        break;
+
+                    case REPLACE_ATTRIBUTE:
+                        // Not supported
+                        String msg = "Cannot replace an administrative role, the opertion is not supported";
+                        LOG.error( msg );
+                        throw new LdapUnwillingToPerformException( msg );
+                }
+            }
+        }
+
+        // At this point, we have a new AdministrativeRole AT, and we need to get the lists of
+        // added roles and removed roles, in order to process them
+
+        next.modify( modifyContext );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void move( NextInterceptor next, MoveOperationContext moveContext ) throws LdapException
+    {
+        LOG.debug( ">>> Entering into the Administrative Interceptor, moveRequest" );
+        Entry entry = moveContext.getOriginalEntry();
+
+        // Check if we are moving an Administrative Point
+        Attribute adminPoint = entry.get( ADMINISTRATIVE_ROLE_AT );
+
+        if ( adminPoint == null )
+        {
+            // Nope, go on.
+            next.move( moveContext );
+
+            LOG.debug( "Exit from Administrative Interceptor" );
+
+            return;
+        }
+
+        // Else throw an UnwillingToPerform exception ATM
+        String message = "Cannot move an Administrative Point in the current version";
+        LOG.error( message );
+        throw new LdapUnwillingToPerformException( message );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext moveAndRenameContext )
+        throws LdapException
+    {
+        LOG.debug( ">>> Entering into the Administrative Interceptor, moveAndRenameRequest" );
+        Entry entry = moveAndRenameContext.getOriginalEntry();
+
+        // Check if we are moving and renaming an Administrative Point
+        Attribute adminPoint = entry.get( ADMINISTRATIVE_ROLE_AT );
+
+        if ( adminPoint == null )
+        {
+            // Nope, go on.
+            next.moveAndRename( moveAndRenameContext );
+
+            LOG.debug( "Exit from Administrative Interceptor" );
+
+            return;
+        }
+
+        // Else throw an UnwillingToPerform exception ATM
+        String message = "Cannot move and rename an Administrative Point in the current version";
+        LOG.error( message );
+        throw new LdapUnwillingToPerformException( message );
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void rename( NextInterceptor next, RenameOperationContext renameContext ) throws LdapException
+    {
+        LOG.debug( ">>> Entering into the Administrative Interceptor, renameRequest" );
+        Entry entry = renameContext.getEntry();
+
+        // Check if we are renaming an Administrative Point
+        Attribute adminPoint = entry.get( ADMINISTRATIVE_ROLE_AT );
+
+        if ( adminPoint == null )
+        {
+            // Nope, go on.
+            next.rename( renameContext );
+
+            LOG.debug( "Exit from Administrative Interceptor" );
+
+            return;
+        }
+
+        // Else throw an UnwillingToPerform exception ATM
+        String message = "Cannot rename an Administrative Point in the current version";
+        LOG.error( message );
+        throw new LdapUnwillingToPerformException( message );
+    }
+}

Propchange: directory/apacheds/branches/apacheds-txns/interceptors/authn/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu Oct 20 19:41:49 2011
@@ -0,0 +1,5 @@
+bin
+target
+.classpath
+.project
+.settings

Added: directory/apacheds/branches/apacheds-txns/interceptors/authn/pom.xml
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/interceptors/authn/pom.xml?rev=1187013&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/interceptors/authn/pom.xml (added)
+++ directory/apacheds/branches/apacheds-txns/interceptors/authn/pom.xml Thu Oct 20 19:41:49 2011
@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.directory.server</groupId>
+    <artifactId>apacheds-interceptors</artifactId>
+    <version>2.0.0-M4-SNAPSHOT</version>
+  </parent>
+  
+  <artifactId>apacheds-interceptors-authn</artifactId>
+  <name>ApacheDS Authentication Interceptor</name>
+  <packaging>bundle</packaging>
+
+  <description>
+    Authentication interceptor
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.junit</groupId>
+      <artifactId>junit-addons</artifactId>
+      <scope>test</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>apacheds-i18n</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>apacheds-core-api</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>apacheds-core-api</artifactId>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>apacheds-core-shared</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>commons-collections</groupId>
+      <artifactId>commons-collections</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-ldap-client-api</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-i18n</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-ldap-codec-standalone</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-ldap-codec-core</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-ldap-extras-aci</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-ldap-extras-trigger</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-ldap-extras-util</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-ldap-model</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-ldap-schema-data</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-util</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>bouncycastle</groupId>
+      <artifactId>bcprov-jdk15</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>net.sf.ehcache</groupId>
+      <artifactId>ehcache-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.directory.shared</groupId>
+      <artifactId>shared-ldap-extras-codec</artifactId>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration> 
+          <systemPropertyVariables>
+            <workingDirectory>${basedir}/target/server-work</workingDirectory>
+          </systemPropertyVariables>
+        </configuration>
+      </plugin>
+      
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>META-INF/MANIFEST.MF</manifestFile>
+            <addMavenDescriptor>false</addMavenDescriptor>
+          </archive>
+        </configuration>
+      </plugin>
+      
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <inherited>true</inherited>
+        <extensions>true</extensions>
+        <configuration>
+          <manifestLocation>META-INF</manifestLocation>
+          <instructions>
+            <Bundle-SymbolicName>${project.groupId}.interceptors.authn</Bundle-SymbolicName>
+            <Export-Package>
+                {local-packages};version=${project.version};-noimport:=true
+            </Export-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+        <excludes>
+          <exclude>**/*.gif</exclude>
+        </excludes>
+      </resource>
+    </resources>
+  </build>
+</project>
+

Added: directory/apacheds/branches/apacheds-txns/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/AbstractAuthenticator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/AbstractAuthenticator.java?rev=1187013&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/AbstractAuthenticator.java (added)
+++ directory/apacheds/branches/apacheds-txns/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/AbstractAuthenticator.java Thu Oct 20 19:41:49 2011
@@ -0,0 +1,260 @@
+/*
+ *  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.authn;
+
+
+import static org.apache.directory.shared.ldap.extras.controls.ppolicy.PasswordPolicyErrorEnum.ACCOUNT_LOCKED;
+import static org.apache.directory.shared.ldap.extras.controls.ppolicy.PasswordPolicyErrorEnum.PASSWORD_EXPIRED;
+import static org.apache.directory.shared.ldap.model.constants.PasswordPolicySchemaConstants.PWD_ACCOUNT_LOCKED_TIME_AT;
+import static org.apache.directory.shared.ldap.model.constants.PasswordPolicySchemaConstants.PWD_CHANGED_TIME_AT;
+import static org.apache.directory.shared.ldap.model.constants.PasswordPolicySchemaConstants.PWD_END_TIME_AT;
+import static org.apache.directory.shared.ldap.model.constants.PasswordPolicySchemaConstants.PWD_GRACE_USE_TIME_AT;
+import static org.apache.directory.shared.ldap.model.constants.PasswordPolicySchemaConstants.PWD_LAST_SUCCESS_AT;
+import static org.apache.directory.shared.ldap.model.constants.PasswordPolicySchemaConstants.PWD_START_TIME_AT;
+
+import java.util.Date;
+
+import org.apache.directory.server.core.api.DirectoryService;
+import org.apache.directory.server.core.api.authn.ppolicy.PasswordPolicyConfiguration;
+import org.apache.directory.server.core.api.authn.ppolicy.PasswordPolicyException;
+import org.apache.directory.shared.ldap.model.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.model.entry.Attribute;
+import org.apache.directory.shared.ldap.model.entry.DefaultModification;
+import org.apache.directory.shared.ldap.model.entry.Entry;
+import org.apache.directory.shared.ldap.model.entry.Modification;
+import org.apache.directory.shared.ldap.model.entry.ModificationOperation;
+import org.apache.directory.shared.ldap.model.exception.LdapException;
+import org.apache.directory.shared.ldap.model.name.Dn;
+import org.apache.directory.shared.util.DateUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Base class for all Authenticators.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public abstract class AbstractAuthenticator implements Authenticator
+{
+    /** A logger for the extending classes */
+    protected static final Logger LOG = LoggerFactory.getLogger( AbstractAuthenticator.class );
+
+    /** The associated DirectoryService */
+    private DirectoryService directoryService;
+    
+    /** authenticator type */
+    private final AuthenticationLevel authenticatorType;
+    
+    /**
+     * Creates a new instance.
+     *
+     * @param type the type of this authenticator (e.g. <tt>'simple'</tt>, <tt>'none'</tt>...)
+     */
+    protected AbstractAuthenticator( AuthenticationLevel type )
+    {
+        this.authenticatorType = type;
+    }
+
+
+    /**
+     * Returns {@link DirectoryService} for this authenticator.
+     * @return the directory service core
+     */
+    public DirectoryService getDirectoryService()
+    {
+        return directoryService;
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    public AuthenticationLevel getAuthenticatorType()
+    {
+        return authenticatorType;
+    }
+
+
+    /**
+     * Initializes (<tt>directoryService</tt> and and calls {@link #doInit()} method.
+     * Please put your initialization code into {@link #doInit()}.
+     * @param directoryService the directory core for this authenticator
+     * @throws LdapException if there is a problem starting up the authenticator
+     */
+    public final void init( DirectoryService directoryService ) throws LdapException
+    {
+        this.directoryService = directoryService;
+        doInit();
+    }
+
+
+    /**
+     * Implement your initialization code here.
+     */
+    protected void doInit()
+    {
+    }
+
+
+    /**
+     * Calls {@link #doDestroy()} method, and clears default properties
+     * (<tt>factoryConfiguration</tt> and <tt>configuration</tt>).
+     * Please put your deinitialization code into {@link #doDestroy()}. 
+     */
+    public final void destroy()
+    {
+        try
+        {
+            doDestroy();
+        }
+        finally
+        {
+            this.directoryService = null;
+        }
+    }
+
+
+    /**
+     * Implement your deinitialization code here.
+     */
+    protected void doDestroy()
+    {
+    }
+
+
+    /**
+     * Does nothing leaving it so subclasses can override.
+     */
+    public void invalidateCache( Dn bindDn )
+    {
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void checkPwdPolicy( Entry userEntry ) throws LdapException
+    {
+        if( !directoryService.isPwdPolicyEnabled() )
+        {
+            return;
+        }
+
+        AuthenticationInterceptor authenticationInterceptor = (AuthenticationInterceptor)directoryService.getInterceptor( AuthenticationInterceptor.class.getSimpleName() );
+        PasswordPolicyConfiguration pPolicyConfig = authenticationInterceptor.getPwdPolicy( userEntry );
+        
+        // check for locked out account
+        if( pPolicyConfig.isPwdLockout() )
+        {
+            LOG.debug( "checking if account with the Dn {} is locked", userEntry.getDn() );
+            
+            Attribute accountLockAttr = userEntry.get( PWD_ACCOUNT_LOCKED_TIME_AT );
+            if( accountLockAttr != null )
+            {
+                String lockedTime = accountLockAttr.getString();
+                if( lockedTime.equals( "000001010000Z" ) )
+                {
+                    throw new PasswordPolicyException( "account was permanently locked", ACCOUNT_LOCKED.getValue() );
+                }
+                else
+                {
+                    Date lockedDate = DateUtils.getDate( lockedTime );
+                    long time = pPolicyConfig.getPwdLockoutDuration() * 1000;
+                    time += lockedDate.getTime();
+                    
+                    Date unlockedDate = new Date( time );
+                    if( lockedDate.before( unlockedDate ) )
+                    {
+                        throw new PasswordPolicyException( "account will remain locked till " + unlockedDate, ACCOUNT_LOCKED.getValue() );
+                    }
+                    else
+                    {
+                        // remove pwdAccountLockedTime attribute
+                        Modification pwdAccountLockMod = new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE,  accountLockAttr );
+                        
+                        // DO NOT bypass the interceptor chain, otherwise the changes can't be replicated
+                        directoryService.getAdminSession().modify( userEntry.getDn(), pwdAccountLockMod );
+                    }
+                }
+            }
+        }
+        
+        Attribute pwdStartTimeAttr = userEntry.get( PWD_START_TIME_AT );
+        if( pwdStartTimeAttr != null )
+        {
+            Date pwdStartTime = DateUtils.getDate( pwdStartTimeAttr.getString() );
+            
+            if( System.currentTimeMillis() < pwdStartTime.getTime() )
+            {
+                throw new PasswordPolicyException( "account is locked, will be activated after " + pwdStartTime, ACCOUNT_LOCKED.getValue() ); 
+            }
+        }
+        
+        Attribute pwdEndTimeAttr = userEntry.get( PWD_END_TIME_AT );
+        if( pwdEndTimeAttr != null )
+        {
+            Date pwdEndTime = DateUtils.getDate( pwdEndTimeAttr.getString() );
+            
+            if( System.currentTimeMillis() >= pwdEndTime.getTime() )
+            {
+                throw new PasswordPolicyException( "password end time reached, will be locked till administrator activates it", ACCOUNT_LOCKED.getValue() );
+            }
+        }
+        
+        if( pPolicyConfig.getPwdMaxIdle() > 0 )
+        {
+            Attribute pwdLastSuccessTimeAttr = userEntry.get( PWD_LAST_SUCCESS_AT );
+            long time = pPolicyConfig.getPwdMaxIdle() * 1000;
+            time += DateUtils.getDate( pwdLastSuccessTimeAttr.getString() ).getTime();
+            
+            if( System.currentTimeMillis() >= time )
+            {
+                throw new PasswordPolicyException( "account locked due to the max idle time of the password was exceeded", ACCOUNT_LOCKED.getValue() );
+            }
+        }
+        
+        if ( pPolicyConfig.getPwdMaxAge() > 0 )
+        {
+            if( pPolicyConfig.getPwdGraceAuthNLimit() > 0 )
+            {
+                Attribute pwdGraceUseAttr = userEntry.get( PWD_GRACE_USE_TIME_AT );
+
+                // check for grace authentication count
+                if( pwdGraceUseAttr != null )
+                {
+                    if( pwdGraceUseAttr.size() >= pPolicyConfig.getPwdGraceAuthNLimit() )
+                    {
+                        throw new PasswordPolicyException( "paasword expired and max grace logins were used", PASSWORD_EXPIRED.getValue() );
+                    }
+                }
+            }
+            else
+            {
+                Attribute pwdChangeTimeAttr = userEntry.get( PWD_CHANGED_TIME_AT );
+                boolean expired = PasswordUtil.isPwdExpired( pwdChangeTimeAttr.getString(), pPolicyConfig.getPwdMaxAge() );
+                
+                if( expired )
+                {
+                    throw new PasswordPolicyException( "paasword expired", PASSWORD_EXPIRED.getValue() );
+                }
+            }
+        }
+    }
+}

Added: directory/apacheds/branches/apacheds-txns/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/AnonymousAuthenticator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/AnonymousAuthenticator.java?rev=1187013&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/AnonymousAuthenticator.java (added)
+++ directory/apacheds/branches/apacheds-txns/interceptors/authn/src/main/java/org/apache/directory/server/core/authn/AnonymousAuthenticator.java Thu Oct 20 19:41:49 2011
@@ -0,0 +1,79 @@
+/*
+ *  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.authn;
+
+
+import java.net.SocketAddress;
+
+import org.apache.directory.server.core.api.LdapPrincipal;
+import org.apache.directory.server.core.api.interceptor.context.BindOperationContext;
+import org.apache.directory.server.i18n.I18n;
+import org.apache.directory.shared.ldap.model.constants.AuthenticationLevel;
+import org.apache.directory.shared.ldap.model.exception.LdapNoPermissionException;
+import org.apache.mina.core.session.IoSession;
+
+
+/**
+ * An {@link Authenticator} that handles anonymous connections
+ * (type <tt>'none'</tt>).
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class AnonymousAuthenticator extends AbstractAuthenticator
+{
+    /**
+     * Creates a new instance.
+     */
+    public AnonymousAuthenticator()
+    {
+        super( AuthenticationLevel.NONE );
+    }
+
+
+    /**
+     * If the context is not configured to allow anonymous connections,
+     * this method throws a {@link javax.naming.NoPermissionException}.
+     */
+    public LdapPrincipal authenticate( BindOperationContext bindContext ) throws LdapNoPermissionException
+    {
+        // We only allow Anonymous binds if the service allows them
+        if ( getDirectoryService().isAllowAnonymousAccess() )
+        {
+            LdapPrincipal principal = getDirectoryService().getAdminSession().getAnonymousPrincipal();
+            
+            IoSession session = bindContext.getIoSession();
+            
+            if ( session != null )
+            {
+                SocketAddress clientAddress = session.getRemoteAddress();
+                principal.setClientAddress( clientAddress );
+                SocketAddress serverAddress = session.getServiceAddress();
+                principal.setServerAddress( serverAddress );
+            }
+            
+            return principal;
+        }
+        else
+        {
+            LOG.info( "Cannot authenticate as anonymous, the server does not allow it" );
+            throw new LdapNoPermissionException( I18n.err( I18n.ERR_228 ) );
+        }
+    }
+}