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}