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 */ 020 package org.apache.directory.server.core.subtree; 021 022 023 import java.util.ArrayList; 024 import java.util.Iterator; 025 import java.util.List; 026 import java.util.Map; 027 import java.util.Set; 028 029 import javax.naming.directory.SearchControls; 030 031 import org.apache.directory.server.constants.ApacheSchemaConstants; 032 import org.apache.directory.server.constants.ServerDNConstants; 033 import org.apache.directory.server.core.CoreSession; 034 import org.apache.directory.server.core.DefaultCoreSession; 035 import org.apache.directory.server.core.DirectoryService; 036 import org.apache.directory.server.core.LdapPrincipal; 037 import org.apache.directory.server.core.entry.ClonedServerEntry; 038 import org.apache.directory.server.core.filtering.EntryFilter; 039 import org.apache.directory.server.core.filtering.EntryFilteringCursor; 040 import org.apache.directory.server.core.interceptor.BaseInterceptor; 041 import org.apache.directory.server.core.interceptor.NextInterceptor; 042 import org.apache.directory.server.core.interceptor.context.AddOperationContext; 043 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext; 044 import org.apache.directory.server.core.interceptor.context.ListOperationContext; 045 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext; 046 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext; 047 import org.apache.directory.server.core.interceptor.context.MoveOperationContext; 048 import org.apache.directory.server.core.interceptor.context.OperationContext; 049 import org.apache.directory.server.core.interceptor.context.RenameOperationContext; 050 import org.apache.directory.server.core.interceptor.context.SearchOperationContext; 051 import org.apache.directory.server.core.interceptor.context.SearchingOperationContext; 052 import org.apache.directory.server.core.partition.ByPassConstants; 053 import org.apache.directory.server.core.partition.PartitionNexus; 054 import org.apache.directory.server.i18n.I18n; 055 import org.apache.directory.shared.ldap.codec.search.controls.subentries.SubentriesControl; 056 import org.apache.directory.shared.ldap.constants.AuthenticationLevel; 057 import org.apache.directory.shared.ldap.constants.SchemaConstants; 058 import org.apache.directory.shared.ldap.entry.StringValue; 059 import org.apache.directory.shared.ldap.entry.DefaultServerAttribute; 060 import org.apache.directory.shared.ldap.entry.DefaultServerEntry; 061 import org.apache.directory.shared.ldap.entry.EntryAttribute; 062 import org.apache.directory.shared.ldap.entry.Modification; 063 import org.apache.directory.shared.ldap.entry.ModificationOperation; 064 import org.apache.directory.shared.ldap.entry.ServerEntry; 065 import org.apache.directory.shared.ldap.entry.ServerModification; 066 import org.apache.directory.shared.ldap.entry.Value; 067 import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException; 068 import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException; 069 import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException; 070 import org.apache.directory.shared.ldap.filter.EqualityNode; 071 import org.apache.directory.shared.ldap.filter.ExprNode; 072 import org.apache.directory.shared.ldap.filter.PresenceNode; 073 import org.apache.directory.shared.ldap.filter.SearchScope; 074 import org.apache.directory.shared.ldap.message.AliasDerefMode; 075 import org.apache.directory.shared.ldap.message.ResultCodeEnum; 076 import org.apache.directory.shared.ldap.name.DN; 077 import org.apache.directory.shared.ldap.schema.AttributeType; 078 import org.apache.directory.shared.ldap.schema.NormalizerMappingResolver; 079 import org.apache.directory.shared.ldap.schema.SchemaManager; 080 import org.apache.directory.shared.ldap.schema.normalizers.OidNormalizer; 081 import org.apache.directory.shared.ldap.schema.registries.OidRegistry; 082 import org.apache.directory.shared.ldap.subtree.SubtreeSpecification; 083 import org.apache.directory.shared.ldap.subtree.SubtreeSpecificationParser; 084 import org.slf4j.Logger; 085 import org.slf4j.LoggerFactory; 086 087 088 /** 089 * The Subentry interceptor service which is responsible for filtering 090 * out subentries on search operations and injecting operational attributes 091 * 092 * @org.apache.xbean.XBean 093 * 094 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 095 * @version $Rev: 928945 $ 096 */ 097 public class SubentryInterceptor extends BaseInterceptor 098 { 099 /** the subentry control OID */ 100 private static final String SUBENTRY_CONTROL = SubentriesControl.CONTROL_OID; 101 102 public static final String AC_AREA = "accessControlSpecificArea"; 103 public static final String AC_INNERAREA = "accessControlInnerArea"; 104 105 public static final String SCHEMA_AREA = "subschemaAdminSpecificArea"; 106 107 public static final String COLLECTIVE_AREA = "collectiveAttributeSpecificArea"; 108 public static final String COLLECTIVE_INNERAREA = "collectiveAttributeInnerArea"; 109 110 public static final String TRIGGER_AREA = "triggerExecutionSpecificArea"; 111 public static final String TRIGGER_INNERAREA = "triggerExecutionInnerArea"; 112 113 public static final String[] SUBENTRY_OPATTRS = 114 { SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, SchemaConstants.SUBSCHEMA_SUBENTRY_AT, 115 SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT }; 116 117 private static final Logger LOG = LoggerFactory.getLogger( SubentryInterceptor.class ); 118 119 /** the hash mapping the DN of a subentry to its SubtreeSpecification/types */ 120 private final SubentryCache subentryCache = new SubentryCache(); 121 122 private SubtreeSpecificationParser ssParser; 123 private SubtreeEvaluator evaluator; 124 private PartitionNexus nexus; 125 126 /** The global registries */ 127 private SchemaManager schemaManager; 128 129 /** The OID registry */ 130 private OidRegistry oidRegistry; 131 132 private AttributeType objectClassType; 133 134 135 public void init( DirectoryService directoryService ) throws Exception 136 { 137 super.init( directoryService ); 138 nexus = directoryService.getPartitionNexus(); 139 schemaManager = directoryService.getSchemaManager(); 140 oidRegistry = schemaManager.getGlobalOidRegistry(); 141 142 // setup various attribute type values 143 objectClassType = schemaManager.lookupAttributeTypeRegistry( schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.OBJECT_CLASS_AT ) ); 144 145 ssParser = new SubtreeSpecificationParser( new NormalizerMappingResolver() 146 { 147 public Map<String, OidNormalizer> getNormalizerMapping() throws Exception 148 { 149 return schemaManager.getNormalizerMapping(); 150 } 151 }, schemaManager.getNormalizerMapping() ); 152 evaluator = new SubtreeEvaluator( oidRegistry, schemaManager ); 153 154 // prepare to find all subentries in all namingContexts 155 Set<String> suffixes = this.nexus.listSuffixes( null ); 156 ExprNode filter = new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT, new StringValue( 157 SchemaConstants.SUBENTRY_OC ) ); 158 SearchControls controls = new SearchControls(); 159 controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 160 controls.setReturningAttributes( new String[] 161 { SchemaConstants.SUBTREE_SPECIFICATION_AT, SchemaConstants.OBJECT_CLASS_AT } ); 162 163 // search each namingContext for subentries 164 for ( String suffix:suffixes ) 165 { 166 DN suffixDn = new DN( suffix ); 167 suffixDn.normalize( schemaManager.getNormalizerMapping() ); 168 169 DN adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 170 adminDn.normalize( schemaManager.getNormalizerMapping() ); 171 CoreSession adminSession = new DefaultCoreSession( 172 new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), directoryService ); 173 174 SearchOperationContext searchOperationContext = new SearchOperationContext( adminSession, suffixDn, 175 filter, controls ); 176 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); 177 178 EntryFilteringCursor subentries = nexus.search( searchOperationContext ); 179 180 while ( subentries.next() ) 181 { 182 ServerEntry subentry = subentries.get(); 183 DN dnName = subentry.getDn(); 184 185 String subtree = subentry.get( SchemaConstants.SUBTREE_SPECIFICATION_AT ).getString(); 186 SubtreeSpecification ss; 187 188 try 189 { 190 ss = ssParser.parse( subtree ); 191 } 192 catch ( Exception e ) 193 { 194 LOG.warn( "Failed while parsing subtreeSpecification for " + dnName ); 195 continue; 196 } 197 198 dnName.normalize( schemaManager.getNormalizerMapping() ); 199 subentryCache.setSubentry( dnName.getNormName(), ss, getSubentryTypes( subentry ) ); 200 } 201 } 202 } 203 204 205 private int getSubentryTypes( ServerEntry subentry ) throws Exception 206 { 207 int types = 0; 208 209 EntryAttribute oc = subentry.get( SchemaConstants.OBJECT_CLASS_AT ); 210 211 if ( oc == null ) 212 { 213 throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION, 214 I18n.err( I18n.ERR_305 ) ); 215 } 216 217 if ( oc.contains( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) ) 218 { 219 types |= Subentry.ACCESS_CONTROL_SUBENTRY; 220 } 221 222 if ( oc.contains( "subschema" ) ) 223 { 224 types |= Subentry.SCHEMA_SUBENTRY; 225 } 226 227 if ( oc.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC ) ) 228 { 229 types |= Subentry.COLLECTIVE_SUBENTRY; 230 } 231 232 if ( oc.contains( ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) ) 233 { 234 types |= Subentry.TRIGGER_SUBENTRY; 235 } 236 237 return types; 238 } 239 240 241 // ----------------------------------------------------------------------- 242 // Methods/Code dealing with Subentry Visibility 243 // ----------------------------------------------------------------------- 244 245 246 public EntryFilteringCursor list( NextInterceptor nextInterceptor, ListOperationContext opContext ) 247 throws Exception 248 { 249 EntryFilteringCursor cursor = nextInterceptor.list( opContext ); 250 251 if ( !isSubentryVisible( opContext ) ) 252 { 253 cursor.addEntryFilter( new HideSubentriesFilter() ); 254 } 255 256 return cursor; 257 } 258 259 260 public EntryFilteringCursor search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) 261 throws Exception 262 { 263 EntryFilteringCursor cursor = nextInterceptor.search( opContext ); 264 265 // object scope searches by default return subentries 266 if ( opContext.getScope() == SearchScope.OBJECT ) 267 { 268 return cursor; 269 } 270 271 // for subtree and one level scope we filter 272 if ( !isSubentryVisible( opContext ) ) 273 { 274 cursor.addEntryFilter( new HideSubentriesFilter() ); 275 } 276 else 277 { 278 cursor.addEntryFilter( new HideEntriesFilter() ); 279 } 280 281 return cursor; 282 } 283 284 285 /** 286 * Checks to see if subentries for the search and list operations should be 287 * made visible based on the availability of the search request control 288 * 289 * @param invocation the invocation object to use for determining subentry visibility 290 * @return true if subentries should be visible, false otherwise 291 * @throws Exception if there are problems accessing request controls 292 */ 293 private boolean isSubentryVisible( OperationContext opContext ) throws Exception 294 { 295 if ( !opContext.hasRequestControls() ) 296 { 297 return false; 298 } 299 300 // found the subentry request control so we return its value 301 if ( opContext.hasRequestControl( SUBENTRY_CONTROL ) ) 302 { 303 SubentriesControl subentriesControl = ( SubentriesControl ) opContext.getRequestControl( SUBENTRY_CONTROL ); 304 return subentriesControl.isVisible(); 305 } 306 307 return false; 308 } 309 310 311 // ----------------------------------------------------------------------- 312 // Methods dealing with entry and subentry addition 313 // ----------------------------------------------------------------------- 314 315 /** 316 * Evaluates the set of subentry subtrees upon an entry and returns the 317 * operational subentry attributes that will be added to the entry if 318 * added at the dn specified. 319 * 320 * @param dn the normalized distinguished name of the entry 321 * @param entryAttrs the entry attributes are generated for 322 * @return the set of subentry op attrs for an entry 323 * @throws Exception if there are problems accessing entry information 324 */ 325 public ServerEntry getSubentryAttributes( DN dn, ServerEntry entryAttrs ) throws Exception 326 { 327 ServerEntry subentryAttrs = new DefaultServerEntry( schemaManager, dn ); 328 Iterator<String> list = subentryCache.nameIterator(); 329 330 while ( list.hasNext() ) 331 { 332 String subentryDnStr = list.next(); 333 DN subentryDn = new DN( subentryDnStr ); 334 DN apDn = ( DN ) subentryDn.clone(); 335 apDn.remove( apDn.size() - 1 ); 336 Subentry subentry = subentryCache.getSubentry( subentryDnStr ); 337 SubtreeSpecification ss = subentry.getSubtreeSpecification(); 338 339 if ( evaluator.evaluate( ss, apDn, dn, entryAttrs ) ) 340 { 341 EntryAttribute operational; 342 343 if ( subentry.isAccessControlSubentry() ) 344 { 345 operational = subentryAttrs.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ); 346 347 if ( operational == null ) 348 { 349 operational = new DefaultServerAttribute( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, 350 schemaManager.lookupAttributeTypeRegistry( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) ); 351 subentryAttrs.put( operational ); 352 } 353 354 operational.add( subentryDn.getNormName() ); 355 } 356 if ( subentry.isSchemaSubentry() ) 357 { 358 operational = subentryAttrs.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ); 359 360 if ( operational == null ) 361 { 362 operational = new DefaultServerAttribute( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, schemaManager 363 .lookupAttributeTypeRegistry( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) ); 364 subentryAttrs.put( operational ); 365 } 366 367 operational.add( subentryDn.getNormName() ); 368 } 369 if ( subentry.isCollectiveSubentry() ) 370 { 371 operational = subentryAttrs.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ); 372 373 if ( operational == null ) 374 { 375 operational = new DefaultServerAttribute( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, 376 schemaManager.lookupAttributeTypeRegistry( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) ); 377 subentryAttrs.put( operational ); 378 } 379 380 operational.add( subentryDn.getNormName() ); 381 } 382 if ( subentry.isTriggerSubentry() ) 383 { 384 operational = subentryAttrs.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ); 385 386 if ( operational == null ) 387 { 388 operational = new DefaultServerAttribute( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT, 389 schemaManager.lookupAttributeTypeRegistry( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) ); 390 subentryAttrs.put( operational ); 391 } 392 393 operational.add( subentryDn.getNormName() ); 394 } 395 } 396 } 397 398 return subentryAttrs; 399 } 400 401 402 public void add( NextInterceptor next, AddOperationContext addContext ) throws Exception 403 { 404 DN name = addContext.getDn(); 405 ClonedServerEntry entry = addContext.getEntry(); 406 407 EntryAttribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT ); 408 409 if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) ) 410 { 411 // get the name of the administrative point and its administrativeRole attributes 412 DN apName = ( DN ) name.clone(); 413 apName.remove( name.size() - 1 ); 414 ServerEntry ap = addContext.lookup( apName, ByPassConstants.LOOKUP_BYPASS ); 415 EntryAttribute administrativeRole = ap.get( "administrativeRole" ); 416 417 // check that administrativeRole has something valid in it for us 418 if ( administrativeRole == null || administrativeRole.size() <= 0 ) 419 { 420 throw new LdapNoSuchAttributeException( I18n.err( I18n.ERR_306, apName ) ); 421 } 422 423 /* ---------------------------------------------------------------- 424 * Build the set of operational attributes to be injected into 425 * entries that are contained within the subtree repesented by this 426 * new subentry. In the process we make sure the proper roles are 427 * supported by the administrative point to allow the addition of 428 * this new subentry. 429 * ---------------------------------------------------------------- 430 */ 431 Subentry subentry = new Subentry(); 432 subentry.setTypes( getSubentryTypes( entry ) ); 433 ServerEntry operational = getSubentryOperatationalAttributes( name, subentry ); 434 435 /* ---------------------------------------------------------------- 436 * Parse the subtreeSpecification of the subentry and add it to the 437 * SubtreeSpecification cache. If the parse succeeds we continue 438 * to add the entry to the DIT. Thereafter we search out entries 439 * to modify the subentry operational attributes of. 440 * ---------------------------------------------------------------- 441 */ 442 String subtree = entry.get( SchemaConstants.SUBTREE_SPECIFICATION_AT ).getString(); 443 SubtreeSpecification ss; 444 445 try 446 { 447 ss = ssParser.parse( subtree ); 448 } 449 catch ( Exception e ) 450 { 451 String msg = I18n.err( I18n.ERR_307, name.getName() ); 452 LOG.warn( msg ); 453 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg ); 454 } 455 456 subentryCache.setSubentry( name.getNormName(), ss, getSubentryTypes( entry ) ); 457 458 next.add( addContext ); 459 460 /* ---------------------------------------------------------------- 461 * Find the baseDn for the subentry and use that to search the tree 462 * while testing each entry returned for inclusion within the 463 * subtree of the subentry's subtreeSpecification. All included 464 * entries will have their operational attributes merged with the 465 * operational attributes calculated above. 466 * ---------------------------------------------------------------- 467 */ 468 DN baseDn = ( DN ) apName.clone(); 469 baseDn.addAll( ss.getBase() ); 470 471 ExprNode filter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT_OID ); // (objectClass=*) 472 SearchControls controls = new SearchControls(); 473 controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 474 controls.setReturningAttributes( new String[] 475 { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); 476 477 SearchOperationContext searchOperationContext = new SearchOperationContext( addContext.getSession(), baseDn, 478 filter, controls ); 479 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); 480 481 EntryFilteringCursor subentries = nexus.search( searchOperationContext ); 482 483 while ( subentries.next() ) 484 { 485 ServerEntry candidate = subentries.get(); 486 DN dn = candidate.getDn(); 487 dn.normalize( schemaManager.getNormalizerMapping() ); 488 489 if ( evaluator.evaluate( ss, apName, dn, candidate ) ) 490 { 491 nexus.modify( new ModifyOperationContext( addContext.getSession(), dn, 492 getOperationalModsForAdd( candidate, operational ) ) ); 493 } 494 } 495 496 // TODO why are we doing this here if we got the entry from the 497 // opContext in the first place - got to look into this 498 addContext.setEntry( entry ); 499 } 500 else 501 { 502 Iterator<String> list = subentryCache.nameIterator(); 503 504 while ( list.hasNext() ) 505 { 506 String subentryDnStr = list.next(); 507 DN subentryDn = new DN( subentryDnStr ); 508 DN apDn = ( DN ) subentryDn.clone(); 509 apDn.remove( apDn.size() - 1 ); 510 Subentry subentry = subentryCache.getSubentry( subentryDnStr ); 511 SubtreeSpecification ss = subentry.getSubtreeSpecification(); 512 513 if ( evaluator.evaluate( ss, apDn, name, entry ) ) 514 { 515 EntryAttribute operational; 516 517 if ( subentry.isAccessControlSubentry() ) 518 { 519 operational = entry.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ); 520 521 if ( operational == null ) 522 { 523 operational = new DefaultServerAttribute( schemaManager 524 .lookupAttributeTypeRegistry( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) ); 525 entry.put( operational ); 526 } 527 528 operational.add( subentryDn.getNormName() ); 529 } 530 531 if ( subentry.isSchemaSubentry() ) 532 { 533 operational = entry.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ); 534 535 if ( operational == null ) 536 { 537 operational = new DefaultServerAttribute( schemaManager 538 .lookupAttributeTypeRegistry( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) ); 539 entry.put( operational ); 540 } 541 542 operational.add( subentryDn.getNormName() ); 543 } 544 545 if ( subentry.isCollectiveSubentry() ) 546 { 547 operational = entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ); 548 549 if ( operational == null ) 550 { 551 operational = new DefaultServerAttribute( schemaManager 552 .lookupAttributeTypeRegistry( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) ); 553 entry.put( operational ); 554 } 555 556 operational.add( subentryDn.getNormName() ); 557 } 558 559 if ( subentry.isTriggerSubentry() ) 560 { 561 operational = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ); 562 563 if ( operational == null ) 564 { 565 operational = new DefaultServerAttribute( schemaManager 566 .lookupAttributeTypeRegistry( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) ); 567 entry.put( operational ); 568 } 569 570 operational.add( subentryDn.getNormName() ); 571 } 572 } 573 } 574 575 // TODO why are we doing this here if we got the entry from the 576 // opContext in the first place - got to look into this 577 addContext.setEntry( entry ); 578 579 next.add( addContext ); 580 } 581 } 582 583 584 // ----------------------------------------------------------------------- 585 // Methods dealing subentry deletion 586 // ----------------------------------------------------------------------- 587 588 public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws Exception 589 { 590 DN name = opContext.getDn(); 591 ServerEntry entry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS ); 592 EntryAttribute objectClasses = entry.get( objectClassType ); 593 594 if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) ) 595 { 596 SubtreeSpecification ss = subentryCache.removeSubentry( name.getNormName() ).getSubtreeSpecification(); 597 next.delete( opContext ); 598 599 /* ---------------------------------------------------------------- 600 * Find the baseDn for the subentry and use that to search the tree 601 * for all entries included by the subtreeSpecification. Then we 602 * check the entry for subentry operational attribute that contain 603 * the DN of the subentry. These are the subentry operational 604 * attributes we remove from the entry in a modify operation. 605 * ---------------------------------------------------------------- 606 */ 607 DN apName = ( DN ) name.clone(); 608 apName.remove( name.size() - 1 ); 609 DN baseDn = ( DN ) apName.clone(); 610 baseDn.addAll( ss.getBase() ); 611 612 ExprNode filter = new PresenceNode( schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.OBJECT_CLASS_AT ) ); 613 SearchControls controls = new SearchControls(); 614 controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 615 controls.setReturningAttributes( new String[] 616 { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); 617 618 SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), baseDn, 619 filter, controls ); 620 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); 621 622 EntryFilteringCursor subentries = nexus.search( searchOperationContext ); 623 624 while ( subentries.next() ) 625 { 626 ServerEntry candidate = subentries.get(); 627 DN dn = new DN( candidate.getDn() ); 628 dn.normalize( schemaManager.getNormalizerMapping() ); 629 630 if ( evaluator.evaluate( ss, apName, dn, candidate ) ) 631 { 632 nexus.modify( new ModifyOperationContext( opContext.getSession(), dn, 633 getOperationalModsForRemove( name, candidate ) ) ); 634 } 635 } 636 } 637 else 638 { 639 next.delete( opContext ); 640 } 641 } 642 643 644 // ----------------------------------------------------------------------- 645 // Methods dealing subentry name changes 646 // ----------------------------------------------------------------------- 647 648 /** 649 * Checks to see if an entry being renamed has a descendant that is an 650 * administrative point. 651 * 652 * @param name the name of the entry which is used as the search base 653 * @return true if name is an administrative point or one of its descendants 654 * are, false otherwise 655 * @throws Exception if there are errors while searching the directory 656 */ 657 private boolean hasAdministrativeDescendant( OperationContext opContext, DN name ) throws Exception 658 { 659 ExprNode filter = new PresenceNode( "administrativeRole" ); 660 SearchControls controls = new SearchControls(); 661 controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 662 663 SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), name, 664 filter, controls ); 665 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); 666 667 EntryFilteringCursor aps = nexus.search( searchOperationContext ); 668 669 if ( aps.next() ) 670 { 671 aps.close(); 672 return true; 673 } 674 675 return false; 676 } 677 678 679 private List<Modification> getModsOnEntryRdnChange( DN oldName, DN newName, ServerEntry entry ) 680 throws Exception 681 { 682 List<Modification> modList = new ArrayList<Modification>(); 683 684 /* 685 * There are two different situations warranting action. Firt if 686 * an ss evalutating to true with the old name no longer evalutates 687 * to true with the new name. This would be caused by specific chop 688 * exclusions that effect the new name but did not effect the old 689 * name. In this case we must remove subentry operational attribute 690 * values associated with the dn of that subentry. 691 * 692 * In the second case an ss selects the entry with the new name when 693 * it did not previously with the old name. Again this situation 694 * would be caused by chop exclusions. In this case we must add subentry 695 * operational attribute values with the dn of this subentry. 696 */ 697 Iterator<String> subentries = subentryCache.nameIterator(); 698 699 while ( subentries.hasNext() ) 700 { 701 String subentryDn = subentries.next(); 702 DN apDn = new DN( subentryDn ); 703 apDn.remove( apDn.size() - 1 ); 704 SubtreeSpecification ss = subentryCache.getSubentry( subentryDn ).getSubtreeSpecification(); 705 boolean isOldNameSelected = evaluator.evaluate( ss, apDn, oldName, entry ); 706 boolean isNewNameSelected = evaluator.evaluate( ss, apDn, newName, entry ); 707 708 if ( isOldNameSelected == isNewNameSelected ) 709 { 710 continue; 711 } 712 713 // need to remove references to the subentry 714 if ( isOldNameSelected && !isNewNameSelected ) 715 { 716 for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS ) 717 { 718 ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE; 719 EntryAttribute opAttr = entry.get( aSUBENTRY_OPATTRS ); 720 721 if ( opAttr != null ) 722 { 723 opAttr = opAttr.clone(); 724 opAttr.remove( subentryDn ); 725 726 if ( opAttr.size() < 1 ) 727 { 728 op = ModificationOperation.REMOVE_ATTRIBUTE; 729 } 730 731 modList.add( new ServerModification( op, opAttr ) ); 732 } 733 } 734 } 735 // need to add references to the subentry 736 else if ( isNewNameSelected && !isOldNameSelected ) 737 { 738 for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS ) 739 { 740 ModificationOperation op = ModificationOperation.ADD_ATTRIBUTE; 741 EntryAttribute opAttr = new DefaultServerAttribute( aSUBENTRY_OPATTRS, schemaManager 742 .lookupAttributeTypeRegistry( aSUBENTRY_OPATTRS ) ); 743 opAttr.add( subentryDn ); 744 modList.add( new ServerModification( op, opAttr ) ); 745 } 746 } 747 } 748 749 return modList; 750 } 751 752 753 public void rename( NextInterceptor next, RenameOperationContext opContext ) throws Exception 754 { 755 DN name = opContext.getDn(); 756 757 ServerEntry entry = (ServerEntry)opContext.getEntry().getClonedEntry(); 758 759 EntryAttribute objectClasses = entry.get( objectClassType ); 760 761 if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) ) 762 { 763 // @Todo To be reviewed !!! 764 Subentry subentry = subentryCache.getSubentry( name.getNormName() ); 765 SubtreeSpecification ss = subentry.getSubtreeSpecification(); 766 DN apName = ( DN ) name.clone(); 767 apName.remove( apName.size() - 1 ); 768 DN baseDn = ( DN ) apName.clone(); 769 baseDn.addAll( ss.getBase() ); 770 DN newName = ( DN ) name.clone(); 771 newName.remove( newName.size() - 1 ); 772 773 newName.add( opContext.getNewRdn() ); 774 775 String newNormName = newName.getNormName(); 776 subentryCache.setSubentry( newNormName, ss, subentry.getTypes() ); 777 next.rename( opContext ); 778 779 subentry = subentryCache.getSubentry( newNormName ); 780 ExprNode filter = new PresenceNode( schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.OBJECT_CLASS_AT ) ); 781 SearchControls controls = new SearchControls(); 782 controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 783 controls.setReturningAttributes( new String[] 784 { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); 785 786 SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), baseDn, 787 filter, controls ); 788 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); 789 790 EntryFilteringCursor subentries = nexus.search( searchOperationContext ); 791 792 while ( subentries.next() ) 793 { 794 ServerEntry candidate = subentries.get(); 795 DN dn = candidate.getDn(); 796 dn.normalize( schemaManager.getNormalizerMapping() ); 797 798 799 if ( evaluator.evaluate( ss, apName, dn, candidate ) ) 800 { 801 nexus.modify( new ModifyOperationContext( opContext.getSession(), dn, 802 getOperationalModsForReplace( name, newName, subentry, candidate ) ) ); 803 } 804 } 805 } 806 else 807 { 808 if ( hasAdministrativeDescendant( opContext, name ) ) 809 { 810 String msg = I18n.err( I18n.ERR_308 ); 811 LOG.warn( msg ); 812 throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg ); 813 } 814 815 next.rename( opContext ); 816 817 // calculate the new DN now for use below to modify subentry operational 818 // attributes contained within this regular entry with name changes 819 DN newName = opContext.getNewDn(); 820 821 List<Modification> mods = getModsOnEntryRdnChange( name, newName, entry ); 822 823 if ( mods.size() > 0 ) 824 { 825 nexus.modify( new ModifyOperationContext( opContext.getSession(), newName, mods ) ); 826 } 827 } 828 } 829 830 831 public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext ) throws Exception 832 { 833 DN oriChildName = opContext.getDn(); 834 DN parent = opContext.getParent(); 835 836 ServerEntry entry = opContext.lookup( oriChildName, ByPassConstants.LOOKUP_BYPASS ); 837 838 EntryAttribute objectClasses = entry.get( objectClassType ); 839 840 if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) ) 841 { 842 Subentry subentry = subentryCache.getSubentry( oriChildName.getNormName() ); 843 SubtreeSpecification ss = subentry.getSubtreeSpecification(); 844 DN apName = ( DN ) oriChildName.clone(); 845 apName.remove( apName.size() - 1 ); 846 DN baseDn = ( DN ) apName.clone(); 847 baseDn.addAll( ss.getBase() ); 848 DN newName = ( DN ) parent.clone(); 849 newName.remove( newName.size() - 1 ); 850 851 newName.add( opContext.getNewRdn() ); 852 853 String newNormName = newName.getNormName(); 854 subentryCache.setSubentry( newNormName, ss, subentry.getTypes() ); 855 next.moveAndRename( opContext ); 856 857 subentry = subentryCache.getSubentry( newNormName ); 858 859 ExprNode filter = new PresenceNode( schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.OBJECT_CLASS_AT ) ); 860 SearchControls controls = new SearchControls(); 861 controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 862 controls.setReturningAttributes( new String[] 863 { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); 864 865 SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), baseDn, 866 filter, controls ); 867 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); 868 869 EntryFilteringCursor subentries = nexus.search( searchOperationContext ); 870 871 while ( subentries.next() ) 872 { 873 ServerEntry candidate = subentries.get(); 874 DN dn = candidate.getDn(); 875 dn.normalize( schemaManager.getNormalizerMapping() ); 876 877 if ( evaluator.evaluate( ss, apName, dn, candidate ) ) 878 { 879 nexus.modify( new ModifyOperationContext( opContext.getSession(), dn, 880 getOperationalModsForReplace( oriChildName, newName, subentry, candidate ) ) ); 881 } 882 } 883 } 884 else 885 { 886 if ( hasAdministrativeDescendant( opContext, oriChildName ) ) 887 { 888 String msg = I18n.err( I18n.ERR_308 ); 889 LOG.warn( msg ); 890 throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg ); 891 } 892 893 next.moveAndRename( opContext ); 894 895 // calculate the new DN now for use below to modify subentry operational 896 // attributes contained within this regular entry with name changes 897 DN newName = ( DN ) parent.clone(); 898 newName.add( opContext.getNewRdn() ); 899 newName.normalize( schemaManager.getNormalizerMapping() ); 900 List<Modification> mods = getModsOnEntryRdnChange( oriChildName, newName, entry ); 901 902 if ( mods.size() > 0 ) 903 { 904 nexus.modify( new ModifyOperationContext( opContext.getSession(), newName, mods ) ); 905 } 906 } 907 } 908 909 910 public void move( NextInterceptor next, MoveOperationContext opContext ) throws Exception 911 { 912 DN oriChildName = opContext.getDn(); 913 DN newParentName = opContext.getParent(); 914 915 ServerEntry entry = opContext.lookup( oriChildName, ByPassConstants.LOOKUP_BYPASS ); 916 917 EntryAttribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT ); 918 919 if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) ) 920 { 921 Subentry subentry = subentryCache.getSubentry( oriChildName.getNormName() ); 922 SubtreeSpecification ss = subentry.getSubtreeSpecification(); 923 DN apName = ( DN ) oriChildName.clone(); 924 apName.remove( apName.size() - 1 ); 925 DN baseDn = ( DN ) apName.clone(); 926 baseDn.addAll( ss.getBase() ); 927 DN newName = ( DN ) newParentName.clone(); 928 newName.remove( newName.size() - 1 ); 929 newName.add( newParentName.get( newParentName.size() - 1 ) ); 930 931 String newNormName = newName.getNormName(); 932 subentryCache.setSubentry( newNormName, ss, subentry.getTypes() ); 933 next.move( opContext ); 934 935 subentry = subentryCache.getSubentry( newNormName ); 936 937 ExprNode filter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT ); 938 SearchControls controls = new SearchControls(); 939 controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 940 controls.setReturningAttributes( new String[] 941 { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); 942 943 SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), baseDn, 944 filter, controls ); 945 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); 946 947 EntryFilteringCursor subentries = nexus.search( searchOperationContext ); 948 949 while ( subentries.next() ) 950 { 951 ServerEntry candidate = subentries.get(); 952 DN dn = candidate.getDn(); 953 dn.normalize( schemaManager.getNormalizerMapping() ); 954 955 if ( evaluator.evaluate( ss, apName, dn, candidate ) ) 956 { 957 nexus.modify( new ModifyOperationContext( opContext.getSession(), dn, 958 getOperationalModsForReplace( oriChildName, newName, subentry, candidate ) ) ); 959 } 960 } 961 } 962 else 963 { 964 if ( hasAdministrativeDescendant( opContext, oriChildName ) ) 965 { 966 String msg = I18n.err( I18n.ERR_308 ); 967 LOG.warn( msg ); 968 throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg ); 969 } 970 971 next.move( opContext ); 972 973 // calculate the new DN now for use below to modify subentry operational 974 // attributes contained within this regular entry with name changes 975 DN newName = ( DN ) newParentName.clone(); 976 newName.add( oriChildName.get( oriChildName.size() - 1 ) ); 977 List<Modification> mods = getModsOnEntryRdnChange( oriChildName, newName, entry ); 978 979 if ( mods.size() > 0 ) 980 { 981 nexus.modify( new ModifyOperationContext( opContext.getSession(), newName, mods ) ); 982 } 983 } 984 } 985 986 987 // ----------------------------------------------------------------------- 988 // Methods dealing subentry modification 989 // ----------------------------------------------------------------------- 990 991 private int getSubentryTypes( ServerEntry entry, List<Modification> mods ) throws Exception 992 { 993 EntryAttribute ocFinalState = entry.get( SchemaConstants.OBJECT_CLASS_AT ).clone(); 994 995 for ( Modification mod : mods ) 996 { 997 if ( mod.getAttribute().getId().equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT ) ) 998 { 999 switch ( mod.getOperation() ) 1000 { 1001 case ADD_ATTRIBUTE: 1002 for ( Value<?> value : mod.getAttribute() ) 1003 { 1004 ocFinalState.add( value.getString() ); 1005 } 1006 1007 break; 1008 1009 case REMOVE_ATTRIBUTE: 1010 for ( Value<?> value : mod.getAttribute() ) 1011 { 1012 ocFinalState.remove( value.getString() ); 1013 } 1014 1015 break; 1016 1017 case REPLACE_ATTRIBUTE: 1018 ocFinalState = mod.getAttribute(); 1019 break; 1020 } 1021 } 1022 } 1023 1024 ServerEntry attrs = new DefaultServerEntry( schemaManager, DN.EMPTY_DN ); 1025 attrs.put( ocFinalState ); 1026 return getSubentryTypes( attrs ); 1027 } 1028 1029 1030 public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws Exception 1031 { 1032 DN name = opContext.getDn(); 1033 List<Modification> mods = opContext.getModItems(); 1034 1035 ServerEntry entry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS ); 1036 1037 ServerEntry oldEntry = ( ServerEntry ) entry.clone(); 1038 EntryAttribute objectClasses = entry.get( objectClassType ); 1039 boolean isSubtreeSpecificationModification = false; 1040 Modification subtreeMod = null; 1041 1042 for ( Modification mod : mods ) 1043 { 1044 if ( SchemaConstants.SUBTREE_SPECIFICATION_AT.equalsIgnoreCase( mod.getAttribute().getId() ) ) 1045 { 1046 isSubtreeSpecificationModification = true; 1047 subtreeMod = mod; 1048 } 1049 } 1050 1051 if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) && isSubtreeSpecificationModification ) 1052 { 1053 SubtreeSpecification ssOld = subentryCache.removeSubentry( name.getNormName() ).getSubtreeSpecification(); 1054 SubtreeSpecification ssNew; 1055 1056 try 1057 { 1058 ssNew = ssParser.parse( subtreeMod.getAttribute().getString() ); 1059 } 1060 catch ( Exception e ) 1061 { 1062 String msg = I18n.err( I18n.ERR_71 ); 1063 LOG.error( msg, e ); 1064 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg ); 1065 } 1066 1067 subentryCache.setSubentry( name.getNormName(), ssNew, getSubentryTypes( entry, mods ) ); 1068 next.modify( opContext ); 1069 1070 // search for all entries selected by the old SS and remove references to subentry 1071 DN apName = ( DN ) name.clone(); 1072 apName.remove( apName.size() - 1 ); 1073 DN oldBaseDn = ( DN ) apName.clone(); 1074 oldBaseDn.addAll( ssOld.getBase() ); 1075 ExprNode filter = new PresenceNode( schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.OBJECT_CLASS_AT ) ); 1076 SearchControls controls = new SearchControls(); 1077 controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 1078 controls.setReturningAttributes( new String[] 1079 { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); 1080 1081 SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), oldBaseDn, 1082 filter, controls ); 1083 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); 1084 1085 EntryFilteringCursor subentries = nexus.search( searchOperationContext ); 1086 1087 while ( subentries.next() ) 1088 { 1089 ServerEntry candidate = subentries.get(); 1090 DN dn = candidate.getDn(); 1091 dn.normalize( schemaManager.getNormalizerMapping() ); 1092 1093 if ( evaluator.evaluate( ssOld, apName, dn, candidate ) ) 1094 { 1095 nexus.modify( new ModifyOperationContext( opContext.getSession(), dn, 1096 getOperationalModsForRemove( name, candidate ) ) ); 1097 } 1098 } 1099 1100 // search for all selected entries by the new SS and add references to subentry 1101 Subentry subentry = subentryCache.getSubentry( name.getNormName() ); 1102 ServerEntry operational = getSubentryOperatationalAttributes( name, subentry ); 1103 DN newBaseDn = ( DN ) apName.clone(); 1104 newBaseDn.addAll( ssNew.getBase() ); 1105 1106 searchOperationContext = new SearchOperationContext( opContext.getSession(), newBaseDn, 1107 filter, controls ); 1108 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); 1109 1110 subentries = nexus.search( searchOperationContext ); 1111 1112 while ( subentries.next() ) 1113 { 1114 ServerEntry candidate = subentries.get(); 1115 DN dn = candidate.getDn(); 1116 dn.normalize( schemaManager.getNormalizerMapping() ); 1117 1118 if ( evaluator.evaluate( ssNew, apName, dn, candidate ) ) 1119 { 1120 nexus.modify( new ModifyOperationContext( opContext.getSession(), dn, 1121 getOperationalModsForAdd( candidate, operational ) ) ); 1122 } 1123 } 1124 } 1125 else 1126 { 1127 next.modify( opContext ); 1128 1129 if ( !objectClasses.contains( SchemaConstants.SUBENTRY_OC ) ) 1130 { 1131 ServerEntry newEntry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS ); 1132 1133 List<Modification> subentriesOpAttrMods = getModsOnEntryModification( name, oldEntry, newEntry ); 1134 1135 if ( subentriesOpAttrMods.size() > 0 ) 1136 { 1137 nexus.modify( new ModifyOperationContext( opContext.getSession(), name, subentriesOpAttrMods ) ); 1138 } 1139 } 1140 } 1141 } 1142 1143 1144 // ----------------------------------------------------------------------- 1145 // Utility Methods 1146 // ----------------------------------------------------------------------- 1147 1148 private List<Modification> getOperationalModsForReplace( DN oldName, DN newName, Subentry subentry, 1149 ServerEntry entry ) throws Exception 1150 { 1151 List<Modification> modList = new ArrayList<Modification>(); 1152 1153 EntryAttribute operational; 1154 1155 if ( subentry.isAccessControlSubentry() ) 1156 { 1157 operational = entry.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ).clone(); 1158 1159 if ( operational == null ) 1160 { 1161 operational = new DefaultServerAttribute( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, schemaManager 1162 .lookupAttributeTypeRegistry( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) ); 1163 operational.add( newName.toString() ); 1164 } 1165 else 1166 { 1167 operational.remove( oldName.toString() ); 1168 operational.add( newName.toString() ); 1169 } 1170 1171 modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) ); 1172 } 1173 1174 if ( subentry.isSchemaSubentry() ) 1175 { 1176 operational = entry.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).clone(); 1177 1178 if ( operational == null ) 1179 { 1180 operational = new DefaultServerAttribute( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, schemaManager 1181 .lookupAttributeTypeRegistry( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) ); 1182 operational.add( newName.toString() ); 1183 } 1184 else 1185 { 1186 operational.remove( oldName.toString() ); 1187 operational.add( newName.toString() ); 1188 } 1189 1190 modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) ); 1191 } 1192 1193 if ( subentry.isCollectiveSubentry() ) 1194 { 1195 operational = entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ).clone(); 1196 1197 if ( operational == null ) 1198 { 1199 operational = new DefaultServerAttribute( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, 1200 schemaManager.lookupAttributeTypeRegistry( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) ); 1201 operational.add( newName.toString() ); 1202 } 1203 else 1204 { 1205 operational.remove( oldName.toString() ); 1206 operational.add( newName.toString() ); 1207 } 1208 1209 modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) ); 1210 } 1211 1212 if ( subentry.isTriggerSubentry() ) 1213 { 1214 operational = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ).clone(); 1215 1216 if ( operational == null ) 1217 { 1218 operational = new DefaultServerAttribute( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT, schemaManager 1219 .lookupAttributeTypeRegistry( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) ); 1220 operational.add( newName.toString() ); 1221 } 1222 else 1223 { 1224 operational.remove( oldName.toString() ); 1225 operational.add( newName.toString() ); 1226 } 1227 1228 modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) ); 1229 } 1230 1231 return modList; 1232 } 1233 1234 1235 /** 1236 * Gets the subschema operational attributes to be added to or removed from 1237 * an entry selected by a subentry's subtreeSpecification. 1238 * 1239 * @param name the normalized distinguished name of the subentry (the value of op attrs) 1240 * @param subentry the subentry to get attributes from 1241 * @return the set of attributes to be added or removed from entries 1242 */ 1243 private ServerEntry getSubentryOperatationalAttributes( DN name, Subentry subentry ) throws Exception 1244 { 1245 ServerEntry operational = new DefaultServerEntry( schemaManager, name ); 1246 1247 if ( subentry.isAccessControlSubentry() ) 1248 { 1249 if ( operational.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) == null ) 1250 { 1251 operational.put( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, name.getNormName() ); 1252 } 1253 else 1254 { 1255 operational.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ).add( name.getNormName() ); 1256 } 1257 } 1258 if ( subentry.isSchemaSubentry() ) 1259 { 1260 if ( operational.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) == null ) 1261 { 1262 operational.put( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, name.getNormName() ); 1263 } 1264 else 1265 { 1266 operational.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).add( name.getNormName() ); 1267 } 1268 } 1269 if ( subentry.isCollectiveSubentry() ) 1270 { 1271 if ( operational.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) == null ) 1272 { 1273 operational.put( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, name.getNormName() ); 1274 } 1275 else 1276 { 1277 operational.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ).add( name.getNormName() ); 1278 } 1279 } 1280 if ( subentry.isTriggerSubentry() ) 1281 { 1282 if ( operational.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) == null ) 1283 { 1284 operational.put( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT, name.getNormName() ); 1285 } 1286 else 1287 { 1288 operational.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ).add( name.getNormName() ); 1289 } 1290 } 1291 1292 return operational; 1293 } 1294 1295 1296 /** 1297 * Calculates the subentry operational attributes to remove from a candidate 1298 * entry selected by a subtreeSpecification. When we remove a subentry we 1299 * must remove the operational attributes in the entries that were once selected 1300 * by the subtree specification of that subentry. To do so we must perform 1301 * a modify operation with the set of modifications to perform. This method 1302 * calculates those modifications. 1303 * 1304 * @param subentryDn the distinguished name of the subentry 1305 * @param candidate the candidate entry to removed from the 1306 * @return the set of modifications required to remove an entry's reference to 1307 * a subentry 1308 */ 1309 private List<Modification> getOperationalModsForRemove( DN subentryDn, ServerEntry candidate ) 1310 throws Exception 1311 { 1312 List<Modification> modList = new ArrayList<Modification>(); 1313 String dn = subentryDn.getNormName(); 1314 1315 for ( String opAttrId : SUBENTRY_OPATTRS ) 1316 { 1317 EntryAttribute opAttr = candidate.get( opAttrId ); 1318 1319 if ( ( opAttr != null ) && opAttr.contains( dn ) ) 1320 { 1321 AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( opAttrId ); 1322 EntryAttribute attr = new DefaultServerAttribute( opAttrId, attributeType, dn ); 1323 modList.add( new ServerModification( ModificationOperation.REMOVE_ATTRIBUTE, attr ) ); 1324 } 1325 } 1326 1327 return modList; 1328 } 1329 1330 1331 /** 1332 * Calculates the subentry operational attributes to add or replace from 1333 * a candidate entry selected by a subtree specification. When a subentry 1334 * is added or it's specification is modified some entries must have new 1335 * operational attributes added to it to point back to the associated 1336 * subentry. To do so a modify operation must be performed on entries 1337 * selected by the subtree specification. This method calculates the 1338 * modify operation to be performed on the entry. 1339 * 1340 * @param entry the entry being modified 1341 * @param operational the set of operational attributes supported by the AP 1342 * of the subentry 1343 * @return the set of modifications needed to update the entry 1344 * @throws Exception if there are probelms accessing modification items 1345 */ 1346 public List<Modification> getOperationalModsForAdd( ServerEntry entry, ServerEntry operational ) 1347 throws Exception 1348 { 1349 List<Modification> modList = new ArrayList<Modification>(); 1350 1351 for ( AttributeType attributeType : operational.getAttributeTypes() ) 1352 { 1353 ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE; 1354 EntryAttribute result = new DefaultServerAttribute( attributeType ); 1355 EntryAttribute opAttrAdditions = operational.get( attributeType ); 1356 EntryAttribute opAttrInEntry = entry.get( attributeType ); 1357 1358 for ( Value<?> value : opAttrAdditions ) 1359 { 1360 result.add( value ); 1361 } 1362 1363 if ( opAttrInEntry != null && opAttrInEntry.size() > 0 ) 1364 { 1365 for ( Value<?> value : opAttrInEntry ) 1366 { 1367 result.add( value ); 1368 } 1369 } 1370 else 1371 { 1372 op = ModificationOperation.ADD_ATTRIBUTE; 1373 } 1374 1375 modList.add( new ServerModification( op, result ) ); 1376 } 1377 1378 return modList; 1379 } 1380 1381 /** 1382 * SearchResultFilter used to filter out subentries based on objectClass values. 1383 */ 1384 public class HideSubentriesFilter implements EntryFilter 1385 { 1386 public boolean accept( SearchingOperationContext operation, ClonedServerEntry entry ) 1387 throws Exception 1388 { 1389 String dn = entry.getDn().getNormName(); 1390 1391 // see if we can get a match without normalization 1392 if ( subentryCache.hasSubentry( dn ) ) 1393 { 1394 return false; 1395 } 1396 1397 // see if we can use objectclass if present 1398 EntryAttribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT ); 1399 1400 if ( objectClasses != null ) 1401 { 1402 return !objectClasses.contains( SchemaConstants.SUBENTRY_OC ); 1403 } 1404 1405 DN ndn = new DN( dn ); 1406 ndn.normalize( schemaManager.getNormalizerMapping() ); 1407 String normalizedDn = ndn.getNormName(); 1408 return !subentryCache.hasSubentry( normalizedDn ); 1409 } 1410 } 1411 1412 /** 1413 * SearchResultFilter used to filter out normal entries but shows subentries based on 1414 * objectClass values. 1415 */ 1416 public class HideEntriesFilter implements EntryFilter 1417 { 1418 public boolean accept( SearchingOperationContext operation, ClonedServerEntry entry ) 1419 throws Exception 1420 { 1421 String dn = entry.getDn().getNormName(); 1422 1423 // see if we can get a match without normalization 1424 if ( subentryCache.hasSubentry( dn ) ) 1425 { 1426 return true; 1427 } 1428 1429 // see if we can use objectclass if present 1430 EntryAttribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT ); 1431 1432 if ( objectClasses != null ) 1433 { 1434 return objectClasses.contains( SchemaConstants.SUBENTRY_OC ); 1435 } 1436 1437 DN ndn = new DN( dn ); 1438 ndn.normalize( schemaManager.getNormalizerMapping() ); 1439 return subentryCache.hasSubentry( ndn.getNormName() ); 1440 } 1441 } 1442 1443 1444 private List<Modification> getModsOnEntryModification( DN name, ServerEntry oldEntry, ServerEntry newEntry ) 1445 throws Exception 1446 { 1447 List<Modification> modList = new ArrayList<Modification>(); 1448 1449 Iterator<String> subentries = subentryCache.nameIterator(); 1450 1451 while ( subentries.hasNext() ) 1452 { 1453 String subentryDn = subentries.next(); 1454 DN apDn = new DN( subentryDn ); 1455 apDn.remove( apDn.size() - 1 ); 1456 SubtreeSpecification ss = subentryCache.getSubentry( subentryDn ).getSubtreeSpecification(); 1457 boolean isOldEntrySelected = evaluator.evaluate( ss, apDn, name, oldEntry ); 1458 boolean isNewEntrySelected = evaluator.evaluate( ss, apDn, name, newEntry ); 1459 1460 if ( isOldEntrySelected == isNewEntrySelected ) 1461 { 1462 continue; 1463 } 1464 1465 // need to remove references to the subentry 1466 if ( isOldEntrySelected && !isNewEntrySelected ) 1467 { 1468 for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS ) 1469 { 1470 ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE; 1471 EntryAttribute opAttr = oldEntry.get( aSUBENTRY_OPATTRS ); 1472 1473 if ( opAttr != null ) 1474 { 1475 opAttr = opAttr.clone(); 1476 opAttr.remove( subentryDn ); 1477 1478 if ( opAttr.size() < 1 ) 1479 { 1480 op = ModificationOperation.REMOVE_ATTRIBUTE; 1481 } 1482 1483 modList.add( new ServerModification( op, opAttr ) ); 1484 } 1485 } 1486 } 1487 // need to add references to the subentry 1488 else if ( isNewEntrySelected && !isOldEntrySelected ) 1489 { 1490 for ( String attribute : SUBENTRY_OPATTRS ) 1491 { 1492 ModificationOperation op = ModificationOperation.ADD_ATTRIBUTE; 1493 AttributeType type = schemaManager.lookupAttributeTypeRegistry( attribute ); 1494 EntryAttribute opAttr = new DefaultServerAttribute( attribute, type ); 1495 opAttr.add( subentryDn ); 1496 modList.add( new ServerModification( op, opAttr ) ); 1497 } 1498 } 1499 } 1500 1501 return modList; 1502 } 1503 1504 }