View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.server.core.admin;
21  
22  
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.concurrent.locks.ReentrantReadWriteLock;
30  
31  import javax.naming.directory.SearchControls;
32  
33  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
34  import org.apache.directory.api.ldap.model.entry.Attribute;
35  import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
36  import org.apache.directory.api.ldap.model.entry.Entry;
37  import org.apache.directory.api.ldap.model.entry.Modification;
38  import org.apache.directory.api.ldap.model.entry.Value;
39  import org.apache.directory.api.ldap.model.exception.LdapException;
40  import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
41  import org.apache.directory.api.ldap.model.exception.LdapNoSuchAttributeException;
42  import org.apache.directory.api.ldap.model.exception.LdapOperationException;
43  import org.apache.directory.api.ldap.model.exception.LdapOtherException;
44  import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException;
45  import org.apache.directory.api.ldap.model.filter.ExprNode;
46  import org.apache.directory.api.ldap.model.filter.PresenceNode;
47  import org.apache.directory.api.ldap.model.message.AliasDerefMode;
48  import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
49  import org.apache.directory.api.ldap.model.name.Dn;
50  import org.apache.directory.api.ldap.model.subtree.AdministrativeRole;
51  import org.apache.directory.api.ldap.util.tree.DnNode;
52  import org.apache.directory.api.util.Strings;
53  import org.apache.directory.server.core.api.CoreSession;
54  import org.apache.directory.server.core.api.DirectoryService;
55  import org.apache.directory.server.core.api.InterceptorEnum;
56  import org.apache.directory.server.core.api.administrative.AccessControlAAP;
57  import org.apache.directory.server.core.api.administrative.AccessControlAdministrativePoint;
58  import org.apache.directory.server.core.api.administrative.AccessControlIAP;
59  import org.apache.directory.server.core.api.administrative.AccessControlSAP;
60  import org.apache.directory.server.core.api.administrative.AdministrativePoint;
61  import org.apache.directory.server.core.api.administrative.CollectiveAttributeAAP;
62  import org.apache.directory.server.core.api.administrative.CollectiveAttributeAdministrativePoint;
63  import org.apache.directory.server.core.api.administrative.CollectiveAttributeIAP;
64  import org.apache.directory.server.core.api.administrative.CollectiveAttributeSAP;
65  import org.apache.directory.server.core.api.administrative.SubschemaAAP;
66  import org.apache.directory.server.core.api.administrative.SubschemaAdministrativePoint;
67  import org.apache.directory.server.core.api.administrative.SubschemaSAP;
68  import org.apache.directory.server.core.api.administrative.TriggerExecutionAAP;
69  import org.apache.directory.server.core.api.administrative.TriggerExecutionAdministrativePoint;
70  import org.apache.directory.server.core.api.administrative.TriggerExecutionIAP;
71  import org.apache.directory.server.core.api.administrative.TriggerExecutionSAP;
72  import org.apache.directory.server.core.api.entry.ClonedServerEntry;
73  import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
74  import org.apache.directory.server.core.api.interceptor.BaseInterceptor;
75  import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
76  import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
77  import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
78  import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
79  import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
80  import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
81  import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
82  import org.apache.directory.server.core.api.partition.Partition;
83  import org.apache.directory.server.core.api.partition.PartitionNexus;
84  import org.apache.directory.server.core.api.partition.PartitionTxn;
85  import org.slf4j.Logger;
86  import org.slf4j.LoggerFactory;
87  
88  
89  /**
90   * An interceptor to manage the Administrative model
91   *
92   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
93   */
94  public class AdministrativePointInterceptor extends BaseInterceptor
95  {
96      /** A {@link Logger} for this class */
97      private static final Logger LOG = LoggerFactory.getLogger( AdministrativePointInterceptor.class );
98  
99      /** 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/../org/apache/directory/server/core/api/administrative/AdministrativePoint.html#AdministrativePoint">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../org/apache/directory/server/core/api/administrative/AdministrativePoint.html#AdministrativePoint">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         SearchOperationContextxt/SearchOperationContext.html#SearchOperationContext">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 }