001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.directory.server.core.admin;
021
022
023import java.util.ArrayList;
024import java.util.HashMap;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Map;
028import java.util.Set;
029import java.util.concurrent.locks.ReentrantReadWriteLock;
030
031import javax.naming.directory.SearchControls;
032
033import org.apache.directory.api.ldap.model.constants.SchemaConstants;
034import org.apache.directory.api.ldap.model.entry.Attribute;
035import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
036import org.apache.directory.api.ldap.model.entry.Entry;
037import org.apache.directory.api.ldap.model.entry.Modification;
038import org.apache.directory.api.ldap.model.entry.Value;
039import org.apache.directory.api.ldap.model.exception.LdapException;
040import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
041import org.apache.directory.api.ldap.model.exception.LdapNoSuchAttributeException;
042import org.apache.directory.api.ldap.model.exception.LdapOperationException;
043import org.apache.directory.api.ldap.model.exception.LdapOtherException;
044import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException;
045import org.apache.directory.api.ldap.model.filter.ExprNode;
046import org.apache.directory.api.ldap.model.filter.PresenceNode;
047import org.apache.directory.api.ldap.model.message.AliasDerefMode;
048import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
049import org.apache.directory.api.ldap.model.name.Dn;
050import org.apache.directory.api.ldap.model.subtree.AdministrativeRole;
051import org.apache.directory.api.ldap.util.tree.DnNode;
052import org.apache.directory.api.util.Strings;
053import org.apache.directory.server.core.api.CoreSession;
054import org.apache.directory.server.core.api.DirectoryService;
055import org.apache.directory.server.core.api.InterceptorEnum;
056import org.apache.directory.server.core.api.administrative.AccessControlAAP;
057import org.apache.directory.server.core.api.administrative.AccessControlAdministrativePoint;
058import org.apache.directory.server.core.api.administrative.AccessControlIAP;
059import org.apache.directory.server.core.api.administrative.AccessControlSAP;
060import org.apache.directory.server.core.api.administrative.AdministrativePoint;
061import org.apache.directory.server.core.api.administrative.CollectiveAttributeAAP;
062import org.apache.directory.server.core.api.administrative.CollectiveAttributeAdministrativePoint;
063import org.apache.directory.server.core.api.administrative.CollectiveAttributeIAP;
064import org.apache.directory.server.core.api.administrative.CollectiveAttributeSAP;
065import org.apache.directory.server.core.api.administrative.SubschemaAAP;
066import org.apache.directory.server.core.api.administrative.SubschemaAdministrativePoint;
067import org.apache.directory.server.core.api.administrative.SubschemaSAP;
068import org.apache.directory.server.core.api.administrative.TriggerExecutionAAP;
069import org.apache.directory.server.core.api.administrative.TriggerExecutionAdministrativePoint;
070import org.apache.directory.server.core.api.administrative.TriggerExecutionIAP;
071import org.apache.directory.server.core.api.administrative.TriggerExecutionSAP;
072import org.apache.directory.server.core.api.entry.ClonedServerEntry;
073import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
074import org.apache.directory.server.core.api.interceptor.BaseInterceptor;
075import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
076import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
077import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
078import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
079import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
080import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
081import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
082import org.apache.directory.server.core.api.partition.Partition;
083import org.apache.directory.server.core.api.partition.PartitionNexus;
084import org.apache.directory.server.core.api.partition.PartitionTxn;
085import org.slf4j.Logger;
086import org.slf4j.LoggerFactory;
087
088
089/**
090 * An interceptor to manage the Administrative model
091 *
092 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
093 */
094public class AdministrativePointInterceptor extends BaseInterceptor
095{
096    /** A {@link Logger} for this class */
097    private static final Logger LOG = LoggerFactory.getLogger( AdministrativePointInterceptor.class );
098
099    /** A reference to the nexus for direct backend operations */
100    private PartitionNexus nexus;
101
102    /** The possible roles */
103    private static final Set<String> ROLES = new HashSet<>();
104
105    // Initialize the ROLES field
106    static
107    {
108        ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.AUTONOMOUS_AREA ) );
109        ROLES.add( SchemaConstants.AUTONOMOUS_AREA_OID );
110        ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA ) );
111        ROLES.add( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
112        ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.ACCESS_CONTROL_INNER_AREA ) );
113        ROLES.add( SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID );
114        ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA ) );
115        ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
116        ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA ) );
117        ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID );
118        ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA ) );
119        ROLES.add( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID );
120        ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA ) );
121        ROLES.add( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
122        ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA ) );
123        ROLES.add( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID );
124    }
125
126    /** A Map to associate a role with it's OID */
127    private static final Map<String, String> ROLES_OID = new HashMap<>();
128
129    // Initialize the roles/oid map
130    static
131    {
132        ROLES_OID.put( Strings.toLowerCaseAscii( SchemaConstants.AUTONOMOUS_AREA ), SchemaConstants.AUTONOMOUS_AREA_OID );
133        ROLES_OID.put( Strings.toLowerCaseAscii( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA ),
134            SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
135        ROLES_OID.put( Strings.toLowerCaseAscii( SchemaConstants.ACCESS_CONTROL_INNER_AREA ),
136            SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID );
137        ROLES_OID.put( Strings.toLowerCaseAscii( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA ),
138            SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
139        ROLES_OID.put( Strings.toLowerCaseAscii( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA ),
140            SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID );
141        ROLES_OID.put( Strings.toLowerCaseAscii( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA ),
142            SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID );
143        ROLES_OID.put( Strings.toLowerCaseAscii( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA ),
144            SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
145        ROLES_OID.put( Strings.toLowerCaseAscii( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA ),
146            SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID );
147    }
148
149    /** The possible inner area roles */
150    private static final Set<String> INNER_AREA_ROLES = new HashSet<>();
151
152    static
153    {
154        INNER_AREA_ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.ACCESS_CONTROL_INNER_AREA ) );
155        INNER_AREA_ROLES.add( SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID );
156        INNER_AREA_ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA ) );
157        INNER_AREA_ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID );
158        INNER_AREA_ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA ) );
159        INNER_AREA_ROLES.add( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID );
160    }
161
162    /** The possible specific area roles */
163    private static final Set<String> SPECIFIC_AREA_ROLES = new HashSet<>();
164
165    static
166    {
167        SPECIFIC_AREA_ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA ) );
168        SPECIFIC_AREA_ROLES.add( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
169        SPECIFIC_AREA_ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA ) );
170        SPECIFIC_AREA_ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
171        SPECIFIC_AREA_ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA ) );
172        SPECIFIC_AREA_ROLES.add( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID );
173        SPECIFIC_AREA_ROLES.add( Strings.toLowerCaseAscii( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA ) );
174        SPECIFIC_AREA_ROLES.add( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
175    }
176
177    /** A lock to guarantee the AP cache consistency */
178    private ReentrantReadWriteLock mutex = new ReentrantReadWriteLock();
179
180
181    /**
182     * Creates a new instance of a AdministrativePointInterceptor.
183     */
184    public AdministrativePointInterceptor()
185    {
186        super( InterceptorEnum.ADMINISTRATIVE_POINT_INTERCEPTOR );
187    }
188
189
190    /**
191     * Get a read-lock on the AP cache.
192     * No read operation can be done on the AP cache if this
193     * method is not called before.
194     */
195    public void lockRead()
196    {
197        mutex.readLock().lock();
198    }
199
200
201    /**
202     * Get a write-lock on the AP cache.
203     * No write operation can be done on the apCache if this
204     * method is not called before.
205     */
206    public void lockWrite()
207    {
208        mutex.writeLock().lock();
209    }
210
211
212    /**
213     * Release the read-write lock on the AP cache.
214     * This method must be called after having read or modified the
215     * AP cache
216     */
217    public void unlock()
218    {
219        if ( mutex.isWriteLockedByCurrentThread() )
220        {
221            mutex.writeLock().unlock();
222        }
223        else
224        {
225            mutex.readLock().unlock();
226        }
227    }
228
229
230    /**
231     * Create the list of AP for a given entry
232     */
233    private void createAdministrativePoints( Attribute adminPoint, Dn dn, String uuid ) throws LdapException
234    {
235        if ( isAAP( adminPoint ) )
236        {
237            // The AC AAP
238            AccessControlAdministrativePoint acAap = new AccessControlAAP( dn, uuid );
239            directoryService.getAccessControlAPCache().add( dn, acAap );
240
241            // The CA AAP
242            CollectiveAttributeAdministrativePoint caAap = new CollectiveAttributeAAP( dn, uuid );
243            directoryService.getCollectiveAttributeAPCache().add( dn, caAap );
244
245            // The TE AAP
246            TriggerExecutionAdministrativePoint teAap = new TriggerExecutionAAP( dn, uuid );
247            directoryService.getTriggerExecutionAPCache().add( dn, teAap );
248
249            // The SS AAP
250            SubschemaAdministrativePoint ssAap = new SubschemaAAP( dn, uuid );
251            directoryService.getSubschemaAPCache().add( dn, ssAap );
252
253            // TODO : Here, we have to update the children, removing any
254            // reference to any other underlying AP
255
256            // If it's an AAP, we can get out immediately
257            return;
258        }
259
260        for ( Value value : adminPoint )
261        {
262            String role = value.getString();
263
264            // Deal with AccessControl AP
265            if ( isAccessControlSpecificRole( role ) )
266            {
267                AccessControlAdministrativePoint sap = new AccessControlSAP( dn, uuid );
268                directoryService.getAccessControlAPCache().add( dn, sap );
269
270                // TODO : Here, we have to update the children, removing any
271                // reference to any other underlying AccessControl IAP or SAP
272
273                continue;
274            }
275
276            if ( isAccessControlInnerRole( role ) )
277            {
278                AccessControlAdministrativePoint iap = new AccessControlIAP( dn, uuid );
279                directoryService.getAccessControlAPCache().add( dn, iap );
280
281                continue;
282            }
283
284            // Deal with CollectiveAttribute AP
285            if ( isCollectiveAttributeSpecificRole( role ) )
286            {
287                CollectiveAttributeAdministrativePoint sap = new CollectiveAttributeSAP( dn, uuid );
288                directoryService.getCollectiveAttributeAPCache().add( dn, sap );
289
290                // TODO : Here, we have to update the children, removing any
291                // reference to any other underlying CollectiveAttribute IAP or SAP
292
293                continue;
294            }
295
296            if ( isCollectiveAttributeInnerRole( role ) )
297            {
298                CollectiveAttributeAdministrativePoint iap = new CollectiveAttributeIAP( dn, uuid );
299                directoryService.getCollectiveAttributeAPCache().add( dn, iap );
300
301                continue;
302            }
303
304            // Deal with SubSchema AP
305            if ( isSubschemaSpecficRole( role ) )
306            {
307                SubschemaAdministrativePoint sap = new SubschemaSAP( dn, uuid );
308                directoryService.getSubschemaAPCache().add( dn, sap );
309
310                // TODO : Here, we have to update the children, removing any
311                // reference to any other underlying Subschema IAP or SAP
312
313                continue;
314            }
315
316            // Deal with TriggerExecution AP
317            if ( isTriggerExecutionSpecificRole( role ) )
318            {
319                TriggerExecutionAdministrativePoint sap = new TriggerExecutionSAP( dn, uuid );
320                directoryService.getTriggerExecutionAPCache().add( dn, sap );
321
322                // TODO : Here, we have to update the children, removing any
323                // reference to any other underlying TriggerExecution IAP or SAP
324
325                continue;
326            }
327
328            if ( isTriggerExecutionInnerRole( role ) )
329            {
330                TriggerExecutionAdministrativePoint iap = new TriggerExecutionIAP( dn, uuid );
331                directoryService.getTriggerExecutionAPCache().add( dn, iap );
332            }
333        }
334    }
335
336
337    /**
338     * Update the cache clones with the added roles
339     */
340    private void addRole( String role, Dn dn, String uuid, DnNode<AccessControlAdministrativePoint> acapCache,
341        DnNode<CollectiveAttributeAdministrativePoint> caapCache,
342        DnNode<TriggerExecutionAdministrativePoint> teapCache,
343        DnNode<SubschemaAdministrativePoint> ssapCache ) throws LdapException
344    {
345        // Deal with Autonomous AP : create the 4 associated SAP/AAP
346        if ( isAutonomousAreaRole( role ) )
347        {
348            // The AC AAP
349            AccessControlAdministrativePoint acAap = new AccessControlAAP( dn, uuid );
350            acapCache.add( dn, acAap );
351
352            // The CA AAP
353            CollectiveAttributeAdministrativePoint caAap = new CollectiveAttributeAAP( dn, uuid );
354            caapCache.add( dn, caAap );
355
356            // The TE AAP
357            TriggerExecutionAdministrativePoint teAap = new TriggerExecutionAAP( dn, uuid );
358            teapCache.add( dn, teAap );
359
360            // The SS AAP
361            SubschemaAdministrativePoint ssAap = new SubschemaAAP( dn, uuid );
362            ssapCache.add( dn, ssAap );
363
364            // If it's an AAP, we can get out immediately
365            return;
366        }
367
368        // Deal with AccessControl AP
369        if ( isAccessControlSpecificRole( role ) )
370        {
371            AccessControlAdministrativePoint sap = new AccessControlSAP( dn, uuid );
372            acapCache.add( dn, sap );
373
374            return;
375        }
376
377        if ( isAccessControlInnerRole( role ) )
378        {
379            AccessControlAdministrativePoint iap = new AccessControlIAP( dn, uuid );
380            acapCache.add( dn, iap );
381
382            return;
383        }
384
385        // Deal with CollectiveAttribute AP
386        if ( isCollectiveAttributeSpecificRole( role ) )
387        {
388            CollectiveAttributeAdministrativePoint sap = new CollectiveAttributeSAP( dn, uuid );
389            caapCache.add( dn, sap );
390
391            return;
392        }
393
394        if ( isCollectiveAttributeInnerRole( role ) )
395        {
396            CollectiveAttributeAdministrativePoint iap = new CollectiveAttributeIAP( dn, uuid );
397            caapCache.add( dn, iap );
398
399            return;
400        }
401
402        // Deal with SubSchema AP
403        if ( isSubschemaSpecficRole( role ) )
404        {
405            SubschemaAdministrativePoint sap = new SubschemaSAP( dn, uuid );
406            ssapCache.add( dn, sap );
407
408            return;
409        }
410
411        // Deal with TriggerExecution AP
412        if ( isTriggerExecutionSpecificRole( role ) )
413        {
414            TriggerExecutionAdministrativePoint sap = new TriggerExecutionSAP( dn, uuid );
415            teapCache.add( dn, sap );
416
417            return;
418        }
419
420        if ( isTriggerExecutionInnerRole( role ) )
421        {
422            TriggerExecutionAdministrativePoint iap = new TriggerExecutionIAP( dn, uuid );
423            teapCache.add( dn, iap );
424        }
425    }
426
427
428    /**
429     * Update the cache clones with the added roles
430     */
431    private void delRole( String role, Dn dn, String uuid, DnNode<AccessControlAdministrativePoint> acapCache,
432        DnNode<CollectiveAttributeAdministrativePoint> caapCache,
433        DnNode<TriggerExecutionAdministrativePoint> teapCache,
434        DnNode<SubschemaAdministrativePoint> ssapCache ) throws LdapException
435    {
436        // Deal with Autonomous AP : remove the 4 associated SAP/AAP
437        if ( isAutonomousAreaRole( role ) )
438        {
439            // The AC AAP
440            acapCache.remove( dn );
441
442            // The CA AAP
443            caapCache.remove( dn );
444
445            // The TE AAP
446            teapCache.remove( dn );
447
448            // The SS AAP
449            ssapCache.remove( dn );
450
451            return;
452        }
453
454        // Deal with AccessControl AP
455        if ( isAccessControlSpecificRole( role ) || isAccessControlInnerRole( role ) )
456        {
457            acapCache.remove( dn );
458
459            return;
460        }
461
462        // Deal with CollectiveAttribute AP
463        if ( isCollectiveAttributeSpecificRole( role ) || isCollectiveAttributeInnerRole( role ) )
464        {
465            caapCache.remove( dn );
466
467            return;
468        }
469
470        // Deal with SubSchema AP
471        if ( isSubschemaSpecficRole( role ) )
472        {
473            ssapCache.remove( dn );
474
475            return;
476        }
477
478        // Deal with TriggerExecution AP
479        if ( isTriggerExecutionSpecificRole( role ) || isTriggerExecutionInnerRole( role ) )
480        {
481            teapCache.remove( dn );
482        }
483    }
484
485
486    private AdministrativePoint getParent( AdministrativePoint ap, List<AdministrativePoint> aps,
487        AdministrativeRole role, DnNode<List<AdministrativePoint>> currentNode )
488    {
489        AdministrativePoint parent = null;
490
491        for ( AdministrativePoint adminPoint : aps )
492        {
493            if ( adminPoint.isAutonomous() || ( adminPoint.getRole() == ap.getRole() ) )
494            {
495                // Same role or AP : this is the parent
496                return adminPoint;
497            }
498            else if ( adminPoint.getRole() == role )
499            {
500                parent = adminPoint;
501            }
502        }
503
504        if ( parent != null )
505        {
506            return parent;
507        }
508
509        // We have to go down one level
510        if ( currentNode.hasParent() )
511        {
512            return findParent( ap, currentNode );
513        }
514        else
515        {
516            return null;
517        }
518    }
519
520
521    /**
522     * Find the parent for the given administrative point. If the AP is an AAP, the parent will be the closest
523     * AAP or the closest SAP. If we have a SAP between the added AAP and a AAP, then
524     */
525    private AdministrativePoint findParent( AdministrativePoint ap, DnNode<List<AdministrativePoint>> currentNode )
526    {
527        List<AdministrativePoint> aps = currentNode.getElement();
528
529        if ( aps != null )
530        {
531            // Check if the current element is a valid parent
532            switch ( ap.getRole() )
533            {
534                case AutonomousArea:
535                    AdministrativePoint currentAp = aps.get( 0 );
536
537                    if ( currentAp.isAutonomous() )
538                    {
539                        return currentAp;
540                    }
541                    else
542                    {
543                        // We have to go down one level, as an AAP
544                        // must have another AAP as a parent
545                        if ( currentNode.hasParent() )
546                        {
547                            return findParent( ap, currentNode );
548                        }
549                        else
550                        {
551                            return null;
552                        }
553                    }
554
555                case AccessControlInnerArea:
556                    return getParent( ap, aps, AdministrativeRole.AccessControlSpecificArea, currentNode );
557
558                case CollectiveAttributeInnerArea:
559                    return getParent( ap, aps, AdministrativeRole.CollectiveAttributeSpecificArea, currentNode );
560
561                case TriggerExecutionInnerArea:
562                    return getParent( ap, aps, AdministrativeRole.TriggerExecutionSpecificArea, currentNode );
563
564                case AccessControlSpecificArea:
565                    return getParent( ap, aps, AdministrativeRole.AccessControlSpecificArea, currentNode );
566
567                case CollectiveAttributeSpecificArea:
568                    return getParent( ap, aps, AdministrativeRole.CollectiveAttributeSpecificArea, currentNode );
569
570                case SubSchemaSpecificArea:
571                    return getParent( ap, aps, AdministrativeRole.SubSchemaSpecificArea, currentNode );
572
573                case TriggerExecutionSpecificArea:
574                    return getParent( ap, aps, AdministrativeRole.TriggerExecutionSpecificArea, currentNode );
575
576                default:
577                    return null;
578            }
579        }
580        else
581        {
582            if ( currentNode.hasParent() )
583            {
584                return findParent( ap, currentNode.getParent() );
585            }
586            else
587            {
588                return null;
589            }
590        }
591    }
592
593
594    /**
595     * Check if we can safely add a role. If it's an AAP, we have to be sure that
596     * it's the only role present in the AT.
597     */
598    private void checkAddRole( Value role, Attribute adminPoint, Dn dn ) throws LdapException
599    {
600        String roleStr = Strings.toLowerCaseAscii( Strings.trim( role.getString() ) );
601
602        // Check that the added AdministrativeRole is valid
603        if ( !ROLES.contains( roleStr ) )
604        {
605            String message = "Cannot add the given role, it's not a valid one :" + role;
606            LOG.error( message );
607            throw new LdapUnwillingToPerformException( message );
608        }
609
610        // If we are trying to add an AAP, we have to check that
611        // it's the only role in the AdminPoint AT
612        if ( isAutonomousAreaRole( roleStr ) )
613        {
614            if ( adminPoint.size() > 1 )
615            {
616                String message = "Cannot add an Autonomous Administratve Point when some other roles are added : "
617                    + adminPoint;
618                LOG.error( message );
619                throw new LdapUnwillingToPerformException( message );
620            }
621            else
622            {
623                // Fine : we only have one AAP
624                return;
625            }
626        }
627
628        // Check that we don't have already an AAP in the AdminPoint AT when we try to
629        // add a role
630        if ( adminPoint.contains( SchemaConstants.AUTONOMOUS_AREA ) )
631        {
632            String message = "Cannot add a role when an Autonomous Administratve Point is already present : "
633                + adminPoint;
634            LOG.error( message );
635            throw new LdapUnwillingToPerformException( message );
636        }
637
638        // check that we can't mix Inner and Specific areas
639        checkInnerSpecificMix( roleStr, adminPoint );
640
641        // Check that we don't add an IAP with no parent. The IAP must be added under
642        // either a AAP, or a SAP/IAP within the same family
643        if ( isIAP( roleStr ) )
644        {
645            checkIAPHasParent( roleStr, adminPoint, dn );
646        }
647    }
648
649
650    /**
651     * Check if we can safely delete a role
652     */
653    private void checkDelRole( Value role, Attribute adminPoint, Dn dn ) throws LdapException
654    {
655        String roleStr = Strings.toLowerCaseAscii( Strings.trim( role.getString() ) );
656
657        // Check that the removed AdministrativeRole is valid
658        if ( !ROLES.contains( roleStr ) )
659        {
660            String message = "Cannot delete the given role, it's not a valid one :" + role;
661            LOG.error( message );
662            throw new LdapUnwillingToPerformException( message );
663        }
664
665        // Now we are trying to delete an Administrative point. We have to check that
666        // we only have one role if the deleted role is an AAP
667        if ( isAutonomousAreaRole( roleStr ) )
668        {
669            // We know have to check that removing the AAP, we will not
670            // left any pending IAP. We should check for the 3 potential IAPs :
671            // AccessControl, CollectiveAttribute and TriggerExecution.
672            // If the removed AP has a parent, no need to go any further :
673            // the children IAPs will depend on this parent.
674
675            // Process the ACs
676            DnNode<AccessControlAdministrativePoint> acAps = directoryService.getAccessControlAPCache();
677
678            if ( !acAps.hasParent( dn ) )
679            {
680                // No parent, check for any IAP
681                List<AccessControlAdministrativePoint> children = acAps.getDescendantElements( dn );
682
683                for ( AccessControlAdministrativePoint child : children )
684                {
685                    if ( child.isInner() )
686                    {
687                        // Ok, we are dead : the IAP will remain with no parent.
688                        String message = "Cannot delete the given role, the " + child.getDn()
689                            + " AccessControl IAP will remain orphan";
690                        LOG.error( message );
691                        throw new LdapUnwillingToPerformException( message );
692                    }
693                }
694            }
695
696            // Process the CAs
697            DnNode<CollectiveAttributeAdministrativePoint> caAps = directoryService.getCollectiveAttributeAPCache();
698
699            if ( !acAps.hasParent( dn ) )
700            {
701                // No parent, check for any IAP
702                List<CollectiveAttributeAdministrativePoint> children = caAps.getDescendantElements( dn );
703
704                for ( CollectiveAttributeAdministrativePoint child : children )
705                {
706                    if ( child.isInner() )
707                    {
708                        // Ok, we are dead : the IAP will remain with no parent.
709                        String message = "Cannot delete the given role, the " + child.getDn()
710                            + " CollectiveAttribute IAP will remain orphan";
711                        LOG.error( message );
712                        throw new LdapUnwillingToPerformException( message );
713                    }
714                }
715            }
716
717            // Process the TEs
718            DnNode<TriggerExecutionAdministrativePoint> teAps = directoryService.getTriggerExecutionAPCache();
719
720            if ( !acAps.hasParent( dn ) )
721            {
722                // No parent, check for any IAP
723                List<TriggerExecutionAdministrativePoint> children = teAps.getDescendantElements( dn );
724
725                for ( TriggerExecutionAdministrativePoint child : children )
726                {
727                    if ( child.isInner() )
728                    {
729                        // Ok, we are dead : the IAP will remain with no parent.
730                        String message = "Cannot delete the given role, the " + child.getDn()
731                            + " TriggerExecution IAP will remain orphan";
732                        LOG.error( message );
733                        throw new LdapUnwillingToPerformException( message );
734                    }
735                }
736            }
737        }
738    }
739
740
741    //-------------------------------------------------------------------------------------------
742    // Helper methods
743    //-------------------------------------------------------------------------------------------
744    private List<Entry> getAdministrativePoints() throws LdapException
745    {
746        List<Entry> entries = new ArrayList<>();
747
748        SearchControls controls = new SearchControls();
749        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
750        controls.setReturningAttributes( new String[]
751            { SchemaConstants.ADMINISTRATIVE_ROLE_AT, SchemaConstants.ENTRY_UUID_AT } );
752
753        // Search for all the adminstrativePoints in the base
754        ExprNode filter = new PresenceNode( directoryService.getAtProvider().getAdministrativeRole() );
755
756        CoreSession adminSession = directoryService.getAdminSession();
757
758        SearchOperationContext searchOperationContext = new SearchOperationContext( adminSession, Dn.ROOT_DSE, filter,
759            controls );
760        Partition partition = nexus.getPartition( Dn.ROOT_DSE );
761        searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
762        searchOperationContext.setPartition( partition );
763        
764        try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
765        {
766            searchOperationContext.setTransaction( partitionTxn );
767            EntryFilteringCursor results = nexus.search( searchOperationContext );
768    
769            try
770            {
771                while ( results.next() )
772                {
773                    Entry entry = results.get();
774    
775                    entries.add( entry );
776                }
777    
778                results.close();
779            }
780            catch ( Exception e )
781            {
782                throw new LdapOperationException( e.getMessage(), e );
783            }
784        }
785        catch ( Exception e )
786        {
787            throw new LdapOtherException( e.getMessage(), e );
788        }
789
790        return entries;
791    }
792
793
794    /**
795     * Tells if a given role is a valid administrative role. We check the lower cased
796     * and trimmed value, and also the OID value.
797     */
798    private boolean isValidRole( String role )
799    {
800        return ROLES.contains( Strings.toLowerCaseAscii( Strings.trim( role ) ) );
801    }
802
803
804    /**
805     * Update The Administrative Points cache, adding the given AdminPoints
806     */
807    private void addAdminPointCache( List<Entry> adminPointEntries ) throws LdapException
808    {
809        for ( Entry adminPointEntry : adminPointEntries )
810        {
811            // update the cache
812            Dn dn = adminPointEntry.getDn();
813
814            String uuid = adminPointEntry.get( directoryService.getAtProvider().getEntryUUID() ).getString();
815            Attribute adminPoint = adminPointEntry.get( directoryService.getAtProvider().getAdministrativeRole() );
816
817            createAdministrativePoints( adminPoint, dn, uuid );
818        }
819    }
820
821
822    /**
823     * Update The Administrative Points cache, removing the given AdminPoint
824     */
825    private void deleteAdminPointCache( Attribute adminPoint, DeleteOperationContext deleteContext )
826        throws LdapException
827    {
828        Dn dn = deleteContext.getDn();
829
830        // Remove the APs in the AP cache
831        for ( Value value : adminPoint )
832        {
833            String role = value.getString();
834
835            // Deal with Autonomous AP : delete the 4 associated SAP/AAP
836            if ( isAutonomousAreaRole( role ) )
837            {
838                // The AC AAP
839                directoryService.getAccessControlAPCache().remove( dn );
840
841                // The CA AAP
842                directoryService.getCollectiveAttributeAPCache().remove( dn );
843
844                // The TE AAP
845                directoryService.getTriggerExecutionAPCache().remove( dn );
846
847                // The SS AAP
848                directoryService.getSubschemaAPCache().remove( dn );
849
850                // If it's an AAP, we can get out immediately
851                return;
852            }
853
854            // Deal with AccessControl AP
855            if ( isAccessControlSpecificRole( role ) || isAccessControlInnerRole( role ) )
856            {
857                directoryService.getAccessControlAPCache().remove( dn );
858
859                continue;
860            }
861
862            // Deal with CollectveAttribute AP
863            if ( isCollectiveAttributeSpecificRole( role ) || isCollectiveAttributeInnerRole( role ) )
864            {
865                directoryService.getCollectiveAttributeAPCache().remove( dn );
866
867                continue;
868            }
869
870            // Deal with SubSchema AP
871            if ( isSubschemaSpecficRole( role ) )
872            {
873                directoryService.getSubschemaAPCache().remove( dn );
874
875                continue;
876            }
877
878            // Deal with TriggerExecution AP
879            if ( isTriggerExecutionSpecificRole( role ) || isTriggerExecutionInnerRole( role ) )
880            {
881                directoryService.getTriggerExecutionAPCache().remove( dn );
882            }
883        }
884    }
885
886
887    /**
888     * Tells if the role is an AC IAP
889     */
890    private boolean isAccessControlInnerRole( String role )
891    {
892        return role.equalsIgnoreCase( SchemaConstants.ACCESS_CONTROL_INNER_AREA )
893            || role.equals( SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID );
894    }
895
896
897    /**
898     * Tells if the role is an AC SAP
899     */
900    private boolean isAccessControlSpecificRole( String role )
901    {
902        return role.equalsIgnoreCase( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA )
903            || role.equals( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
904    }
905
906
907    /**
908     * Tells if the role is a CA IAP
909     */
910    private boolean isCollectiveAttributeInnerRole( String role )
911    {
912        return role.equalsIgnoreCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA )
913            || role.equals( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID );
914    }
915
916
917    /**
918     * Tells if the role is a CA SAP
919     */
920    private boolean isCollectiveAttributeSpecificRole( String role )
921    {
922        return role.equalsIgnoreCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA )
923            || role.equals( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
924    }
925
926
927    /**
928     * Tells if the role is a TE IAP
929     */
930    private boolean isTriggerExecutionInnerRole( String role )
931    {
932        return role.equalsIgnoreCase( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA )
933            || role.equals( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID );
934    }
935
936
937    /**
938     * Tells if the role is a TE SAP
939     */
940    private boolean isTriggerExecutionSpecificRole( String role )
941    {
942        return role.equalsIgnoreCase( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA )
943            || role.equals( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
944    }
945
946
947    /**
948     * Tells if the role is a SS SAP
949     */
950    private boolean isSubschemaSpecficRole( String role )
951    {
952        return role.equalsIgnoreCase( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA )
953            || role.equals( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID );
954    }
955
956
957    /**
958     * Tells if the role is an AAP
959     */
960    private boolean isAutonomousAreaRole( String role )
961    {
962        return role.equalsIgnoreCase( SchemaConstants.AUTONOMOUS_AREA )
963            || role.equals( SchemaConstants.AUTONOMOUS_AREA_OID );
964    }
965
966
967    /**
968     * Tells if the Administrative Point role is an AAP
969     */
970    private boolean isAAP( Attribute adminPoint )
971    {
972        return adminPoint.contains( SchemaConstants.AUTONOMOUS_AREA ) 
973            || adminPoint.contains( SchemaConstants.AUTONOMOUS_AREA_OID );
974    }
975
976
977    private boolean hasAccessControlSpecificRole( Attribute adminPoint )
978    {
979        return adminPoint.contains( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA )
980            || adminPoint.contains( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
981    }
982
983
984    private boolean isIAP( String role )
985    {
986        return INNER_AREA_ROLES.contains( role );
987    }
988
989
990    private boolean hasCollectiveAttributeSpecificRole( Attribute adminPoint )
991    {
992        return adminPoint.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA )
993            || adminPoint.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
994    }
995
996
997    private boolean hasTriggerExecutionSpecificRole( Attribute adminPoint )
998    {
999        return adminPoint.contains( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA )
1000            || adminPoint.contains( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
1001    }
1002
1003
1004    /**
1005     * Check that we don't have an IAP and a SAP with the same family
1006     */
1007    private void checkInnerSpecificMix( String role, Attribute adminPoint ) throws LdapUnwillingToPerformException
1008    {
1009        if ( isAccessControlInnerRole( role ) )
1010        {
1011            if ( hasAccessControlSpecificRole( adminPoint ) )
1012            {
1013                // This is inconsistent
1014                String message = "Cannot add a specific Administrative Point and the same"
1015                    + " inner Administrative point at the same time : " + adminPoint;
1016                LOG.error( message );
1017                throw new LdapUnwillingToPerformException( message );
1018            }
1019            else
1020            {
1021                return;
1022            }
1023        }
1024
1025        if ( isCollectiveAttributeInnerRole( role ) )
1026        {
1027            if ( hasCollectiveAttributeSpecificRole( adminPoint ) )
1028            {
1029                // This is inconsistent
1030                String message = "Cannot add a specific Administrative Point and the same"
1031                    + " inner Administrative point at the same time : " + adminPoint;
1032                LOG.error( message );
1033                throw new LdapUnwillingToPerformException( message );
1034            }
1035            else
1036            {
1037                return;
1038            }
1039        }
1040
1041        if ( isTriggerExecutionInnerRole( role ) )
1042        {
1043            if ( hasTriggerExecutionSpecificRole( adminPoint ) )
1044            {
1045                // This is inconsistent
1046                String message = "Cannot add a specific Administrative Point and the same"
1047                    + " inner Administrative point at the same time : " + adminPoint;
1048                LOG.error( message );
1049                throw new LdapUnwillingToPerformException( message );
1050            }
1051        }
1052    }
1053
1054
1055    /**
1056     * Check that the IAPs (if any) have a parent. We will check for each kind or role :
1057     * AC, CA and TE.
1058     */
1059    private void checkIAPHasParent( String role, Attribute adminPoint, Dn dn ) throws LdapUnwillingToPerformException
1060    {
1061        // Check for the AC role
1062        if ( isAccessControlInnerRole( role ) )
1063        {
1064            DnNode<AccessControlAdministrativePoint> acCache = directoryService.getAccessControlAPCache();
1065
1066            DnNode<AccessControlAdministrativePoint> parent = acCache.getNode( dn );
1067
1068            if ( parent == null )
1069            {
1070                // We don't have any AC administrativePoint in the tree, this is an error
1071                String message = "Cannot add an IAP with no parent : " + adminPoint;
1072                LOG.error( message );
1073                throw new LdapUnwillingToPerformException( message );
1074            }
1075        }
1076        else if ( isCollectiveAttributeInnerRole( role ) )
1077        {
1078            DnNode<CollectiveAttributeAdministrativePoint> caCache = directoryService.getCollectiveAttributeAPCache();
1079
1080            boolean hasAP = caCache.hasParentElement( dn );
1081
1082            if ( !hasAP )
1083            {
1084                // We don't have any AC administrativePoint in the tree, this is an error
1085                String message = "Cannot add an IAP with no parent : " + adminPoint;
1086                LOG.error( message );
1087                throw new LdapUnwillingToPerformException( message );
1088            }
1089        }
1090        else if ( isTriggerExecutionInnerRole( role ) )
1091        {
1092            DnNode<TriggerExecutionAdministrativePoint> caCache = directoryService.getTriggerExecutionAPCache();
1093
1094            DnNode<TriggerExecutionAdministrativePoint> parent = caCache.getNode( dn );
1095
1096            if ( parent == null )
1097            {
1098                // We don't have any AC administrativePoint in the tree, this is an error
1099                String message = "Cannot add an IAP with no parent : " + adminPoint;
1100                LOG.error( message );
1101                throw new LdapUnwillingToPerformException( message );
1102            }
1103        }
1104        else
1105        {
1106            // Wtf ? We *must* have an IAP here...
1107            String message = "This is not an IAP : " + role;
1108            LOG.error( message );
1109            throw new LdapUnwillingToPerformException( message );
1110        }
1111    }
1112
1113
1114    //-------------------------------------------------------------------------------------------
1115    // Interceptor initialization
1116    //-------------------------------------------------------------------------------------------
1117    /**
1118     * Registers and initializes all AdministrativePoints to this service.
1119     */
1120    @Override
1121    public void init( DirectoryService directoryService ) throws LdapException
1122    {
1123        LOG.debug( "Initializing the AdministrativeInterceptor" );
1124
1125        super.init( directoryService );
1126        nexus = directoryService.getPartitionNexus();
1127
1128        // Load all the AdministratvePoint :
1129        // Autonomous Administrative Point first, then Specific
1130        // administrative point, finally the Inner administrative Point
1131        // get the list of all the AAPs
1132        List<Entry> administrativePoints = getAdministrativePoints();
1133
1134        lockWrite();
1135
1136        try
1137        {
1138            addAdminPointCache( administrativePoints );
1139        }
1140        finally
1141        {
1142            unlock();
1143        }
1144    }
1145
1146
1147    /**
1148     * {@inheritDoc}
1149     */
1150    @Override
1151    public void destroy()
1152    {
1153    }
1154
1155
1156    /**
1157     * Add an administrative point into the DIT.
1158     * 
1159     * We have to deal with some specific cases :
1160     * <ul>
1161     * <li>If it's an AA, then the added role should be the only one</li>
1162     * <li>It's not possible to add IA and SA at the same time</li>
1163     * </ul>
1164     * 
1165     * @param addContext The {@link AddOperationContext} instance
1166     * @throws LdapException If we had some error while processing the Add operation
1167     */
1168    @Override
1169    public void add( AddOperationContext addContext ) throws LdapException
1170    {
1171        LOG.debug( ">>> Entering into the Administrative Interceptor, addRequest" );
1172        Entry entry = addContext.getEntry();
1173        Dn dn = entry.getDn();
1174
1175        // Check if we are adding an Administrative Point
1176        Attribute adminPoint = entry.get( directoryService.getAtProvider().getAdministrativeRole() );
1177
1178        if ( adminPoint == null )
1179        {
1180            // Nope, go on.
1181            next( addContext );
1182
1183            LOG.debug( "Exit from Administrative Interceptor, no AP in the added entry" );
1184
1185            return;
1186        }
1187
1188        LOG.debug( "Addition of an administrative point at {} for the role {}", dn, adminPoint );
1189
1190        // Protect the AP caches against concurrent access
1191        lockWrite();
1192
1193        try
1194        {
1195            // Loop on all the added roles to check if they are valid
1196            for ( Value role : adminPoint )
1197            {
1198                checkAddRole( role, adminPoint, dn );
1199            }
1200
1201            // Ok, we are golden.
1202            next( addContext );
1203
1204            String apUuid = entry.get( directoryService.getAtProvider().getEntryUUID() ).getString();
1205
1206            // Now, update the AdminPoint cache
1207            createAdministrativePoints( adminPoint, dn, apUuid );
1208        }
1209        finally
1210        {
1211            // Release the APCaches lock
1212            unlock();
1213        }
1214
1215        if ( LOG.isDebugEnabled() )
1216        {
1217            LOG.debug( "Added an Administrative Point at {}", dn );
1218        }
1219    }
1220
1221
1222    /**
1223     * We have to check that we can remove the associated AdministrativePoint : <br>
1224     * <ul>
1225     * <li> if we remove an AAP, no descendant IAP should remain orphan</li>
1226     * <li> If we remove a SAP, no descendant IAP should remain orphan</li>
1227     * </ul>
1228     * {@inheritDoc}
1229     */
1230    @Override
1231    public void delete( DeleteOperationContext deleteContext ) throws LdapException
1232    {
1233        LOG.debug( ">>> Entering into the Administrative Interceptor, delRequest" );
1234        Entry entry = deleteContext.getEntry();
1235        Dn dn = entry.getDn();
1236
1237        // Check if we are deleting an Administrative Point
1238        Attribute adminPoint = entry.get( directoryService.getAtProvider().getAdministrativeRole() );
1239
1240        if ( adminPoint == null )
1241        {
1242            // Nope, go on.
1243            next( deleteContext );
1244
1245            LOG.debug( "Exit from Administrative Interceptor" );
1246
1247            return;
1248        }
1249
1250        LOG.debug( "Deletion of an administrative point at {} for the role {}", dn, adminPoint );
1251
1252        // Protect the AP caches against concurrent access
1253        lockWrite();
1254
1255        try
1256        {
1257            // Check that the removed AdministrativeRoles are valid. We don't have to do
1258            // any other check, as the deleted entry has no children.
1259            for ( Value role : adminPoint )
1260            {
1261                if ( !isValidRole( role.getString() ) )
1262                {
1263                    String message = "Cannot remove the given role, it's not a valid one :" + role;
1264                    LOG.error( message );
1265                    throw new LdapUnwillingToPerformException( message );
1266                }
1267            }
1268
1269            // Ok, we can remove the AP
1270            next( deleteContext );
1271
1272            // Now, update the AdminPoint cache
1273            deleteAdminPointCache( adminPoint, deleteContext );
1274        }
1275        finally
1276        {
1277            // Release the APCaches lock
1278            unlock();
1279        }
1280
1281        if ( LOG.isDebugEnabled() )
1282        {
1283            LOG.debug( "Deleted an Administrative Point at {}", dn );
1284        }
1285    }
1286
1287
1288    /**
1289     * Only the add and remove modifications are fully supported. We have to check that the
1290     * underlying APs are still consistent.
1291     * We first have to compute the final AdministrativeRole, then do a diff with the
1292     * initial attribute, to determinate which roles have been added and which ones have
1293     * been deleted.
1294     * Once this is done, we have to check that when deleting or adding each of those roles
1295     * the admin model remains consistent.
1296     * 
1297     * {@inheritDoc}
1298     */
1299    @Override
1300    public void modify( ModifyOperationContext modifyContext ) throws LdapException
1301    {
1302        LOG.debug( ">>> Entering into the Administrative Interceptor, modifyRequest" );
1303        // We have to check that the modification is acceptable
1304        List<Modification> modifications = modifyContext.getModItems();
1305        Dn dn = modifyContext.getDn();
1306        String uuid = modifyContext.getEntry().get( directoryService.getAtProvider().getEntryUUID() ).getString();
1307
1308        // Check if we are modifying any AdminRole
1309        boolean adminRolePresent = false;
1310
1311        for ( Modification modification : modifications )
1312        {
1313            if ( modification.getAttribute().getAttributeType() == directoryService.getAtProvider().getAdministrativeRole() )
1314            {
1315                adminRolePresent = true;
1316                break;
1317            }
1318        }
1319
1320        if ( adminRolePresent )
1321        {
1322            // We have modified any AdministrativeRole attribute, we can continue
1323
1324            // Create a clone of the current AdminRole AT
1325            Attribute modifiedAdminRole = ( ( ClonedServerEntry ) modifyContext.getEntry() ).getOriginalEntry().get(
1326                directoryService.getAtProvider().getAdministrativeRole() );
1327
1328            if ( modifiedAdminRole == null )
1329            {
1330                // Create the attribute, as it does not already exist in the entry
1331                modifiedAdminRole = new DefaultAttribute( directoryService.getAtProvider().getAdministrativeRole() );
1332            }
1333            else
1334            {
1335                // We have already an AdminRole AT clone it
1336                modifiedAdminRole = modifiedAdminRole.clone();
1337            }
1338
1339            try
1340            {
1341                // Acquire the lock
1342                lockWrite();
1343
1344                // Get the AP caches as we will apply modifications to them
1345                DnNode<AccessControlAdministrativePoint> acapCache = directoryService.getAccessControlAPCache();
1346                DnNode<CollectiveAttributeAdministrativePoint> caapCache = directoryService
1347                    .getCollectiveAttributeAPCache();
1348                DnNode<TriggerExecutionAdministrativePoint> teapCache = directoryService.getTriggerExecutionAPCache();
1349                DnNode<SubschemaAdministrativePoint> ssapCache = directoryService.getSubschemaAPCache();
1350
1351                // Loop on the modification to select the AdministrativeRole and process it :
1352                // we will create a new AT containing all the roles after having applied the modifications
1353                // on it
1354                for ( Modification modification : modifications )
1355                {
1356                    Attribute attribute = modification.getAttribute();
1357
1358                    // Skip all the attributes but AdministrativeRole
1359                    if ( attribute.getAttributeType() == directoryService.getAtProvider().getAdministrativeRole() )
1360                    {
1361                        // Ok, we have a modification impacting the administrative role
1362                        // Apply it to a virtual AdministrativeRole attribute
1363                        switch ( modification.getOperation() )
1364                        {
1365                            case ADD_ATTRIBUTE:
1366                                for ( Value role : attribute )
1367                                {
1368                                    addRole( role.getString(), dn, uuid, acapCache, caapCache, teapCache,
1369                                        ssapCache );
1370
1371                                    // Add the role to the modified attribute
1372                                    modifiedAdminRole.add( role );
1373                                }
1374
1375                                break;
1376
1377                            case REMOVE_ATTRIBUTE:
1378                                // It may be a complete removal
1379                                if ( attribute.size() == 0 )
1380                                {
1381                                    // Complete removal. Loop on all the existing roles and remove them
1382                                    for ( Value role : modifiedAdminRole )
1383                                    {
1384                                        delRole( role.getString(), dn, uuid, acapCache, caapCache, teapCache, ssapCache );
1385                                    }
1386
1387                                    modifiedAdminRole.clear();
1388                                    break;
1389                                }
1390
1391                                // Now deal with the values to remove
1392                                for ( Value value : attribute )
1393                                {
1394                                    if ( !isValidRole( value.getString() ) )
1395                                    {
1396                                        // Not a valid role : we will throw an exception
1397                                        String msg = "Invalid role : " + value.getString();
1398                                        LOG.error( msg );
1399                                        throw new LdapInvalidAttributeValueException(
1400                                            ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX,
1401                                            msg );
1402                                    }
1403
1404                                    if ( !modifiedAdminRole.contains( value ) )
1405                                    {
1406                                        // We can't remove a value if it does not exist !
1407                                        String msg = "Cannot remove the administrative role value" + value
1408                                            + ", it does not exist";
1409                                        LOG.error( msg );
1410                                        throw new LdapNoSuchAttributeException( msg );
1411                                    }
1412
1413                                    modifiedAdminRole.remove( value );
1414                                    delRole( value.getString(), dn, uuid, acapCache, caapCache, teapCache, ssapCache );
1415
1416                                }
1417
1418                                break;
1419
1420                            case REPLACE_ATTRIBUTE:
1421                                if ( !( modifyContext.isReplEvent() && modifyContext.getSession().isAdministrator() ) )
1422                                {
1423                                    // Not supported in non-replication related operations
1424                                    String msg = "Cannot replace an administrative role, the opertion is not supported";
1425                                    LOG.error( msg );
1426                                    throw new LdapUnwillingToPerformException( msg );
1427                                }
1428
1429                                break;
1430
1431                            default:
1432                                throw new IllegalArgumentException( "Unexpected modify operation "
1433                                    + modification.getOperation() );
1434                        }
1435                    }
1436                }
1437
1438                // At this point, we have a new AdministrativeRole AT, we need to check that the 
1439                // roles hierarchy is still correct
1440                // TODO !!!
1441            }
1442            finally
1443            {
1444                unlock();
1445            }
1446        }
1447
1448        next( modifyContext );
1449    }
1450
1451
1452    /**
1453     * {@inheritDoc}
1454     */
1455    @Override
1456    public void move( MoveOperationContext moveContext ) throws LdapException
1457    {
1458        LOG.debug( ">>> Entering into the Administrative Interceptor, moveRequest" );
1459        Entry entry = moveContext.getOriginalEntry();
1460
1461        // Check if we are moving an Administrative Point
1462        Attribute adminPoint = entry.get( directoryService.getAtProvider().getAdministrativeRole() );
1463
1464        if ( adminPoint == null )
1465        {
1466            // Nope, go on.
1467            next( moveContext );
1468
1469            LOG.debug( "Exit from Administrative Interceptor" );
1470
1471            return;
1472        }
1473
1474        // Else throw an UnwillingToPerform exception ATM
1475        String message = "Cannot move an Administrative Point in the current version";
1476        LOG.error( message );
1477        throw new LdapUnwillingToPerformException( message );
1478    }
1479
1480
1481    /**
1482     * {@inheritDoc}
1483     */
1484    @Override
1485    public void moveAndRename( MoveAndRenameOperationContext moveAndRenameContext ) throws LdapException
1486    {
1487        LOG.debug( ">>> Entering into the Administrative Interceptor, moveAndRenameRequest" );
1488        Entry entry = moveAndRenameContext.getOriginalEntry();
1489
1490        // Check if we are moving and renaming an Administrative Point
1491        Attribute adminPoint = entry.get( directoryService.getAtProvider().getAdministrativeRole() );
1492
1493        if ( adminPoint == null )
1494        {
1495            // Nope, go on.
1496            next( moveAndRenameContext );
1497
1498            LOG.debug( "Exit from Administrative Interceptor" );
1499
1500            return;
1501        }
1502
1503        // Else throw an UnwillingToPerform exception ATM
1504        String message = "Cannot move and rename an Administrative Point in the current version";
1505        LOG.error( message );
1506        throw new LdapUnwillingToPerformException( message );
1507    }
1508
1509
1510    /**
1511     * {@inheritDoc}
1512     */
1513    @Override
1514    public void rename( RenameOperationContext renameContext ) throws LdapException
1515    {
1516        LOG.debug( ">>> Entering into the Administrative Interceptor, renameRequest" );
1517        Entry entry = renameContext.getEntry();
1518
1519        // Check if we are renaming an Administrative Point
1520        Attribute adminPoint = entry.get( directoryService.getAtProvider().getAdministrativeRole() );
1521
1522        if ( adminPoint == null )
1523        {
1524            // Nope, go on.
1525            next( renameContext );
1526
1527            LOG.debug( "Exit from Administrative Interceptor" );
1528
1529            return;
1530        }
1531
1532        // Else throw an UnwillingToPerform exception ATM
1533        String message = "Cannot rename an Administrative Point in the current version";
1534        LOG.error( message );
1535        throw new LdapUnwillingToPerformException( message );
1536    }
1537}