Modified: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/subtree/SubentryInterceptor.java URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/subtree/SubentryInterceptor.java?rev=964837&r1=964836&r2=964837&view=diff ============================================================================== --- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/subtree/SubentryInterceptor.java (original) +++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/subtree/SubentryInterceptor.java Fri Jul 16 15:27:19 2010 @@ -22,7 +22,6 @@ package org.apache.directory.server.core import java.util.ArrayList; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Set; @@ -133,7 +132,64 @@ public class SubentryInterceptor extends /** A reference to the SubtreeSpecification AT */ private static AttributeType SUBTREE_SPECIFICATION_AT; + /** A reference to the AccessControlSubentries AT */ + private static AttributeType ACCESS_CONTROL_SUBENTRIES_AT; + + /** A reference to the AccessControlSubentries AT */ + private static AttributeType SUBSCHEMA_SUBENTRY_AT; + + /** A reference to the CollectiveAttributeSubentries AT */ + private static AttributeType COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT; + + /** A reference to the TriggerExecutionSubentries AT */ + private static AttributeType TRIGGER_EXECUTION_SUBENTRIES_AT; + + + //------------------------------------------------------------------------------------------- + // Search filter methods + //------------------------------------------------------------------------------------------- + /** + * SearchResultFilter used to filter out subentries based on objectClass values. + */ + public class HideSubentriesFilter implements EntryFilter + { + public boolean accept( SearchingOperationContext searchContext, ClonedServerEntry entry ) throws Exception + { + // See if the requested entry is a subentry + if ( subentryCache.hasSubentry( entry.getDn() ) ) + { + return false; + } + + // see if we can use objectclass if present + return !entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ); + } + } + + + /** + * SearchResultFilter used to filter out normal entries but shows subentries based on + * objectClass values. + */ + public class HideEntriesFilter implements EntryFilter + { + public boolean accept( SearchingOperationContext searchContext, ClonedServerEntry entry ) throws Exception + { + // See if the requested entry is a subentry + if ( subentryCache.hasSubentry( entry.getDn() ) ) + { + return true; + } + + // see if we can use objectclass if present + return entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ); + } + } + + //------------------------------------------------------------------------------------------- + // Interceptor initialization + //------------------------------------------------------------------------------------------- /** * Initialize the Subentry Interceptor * @@ -150,6 +206,10 @@ public class SubentryInterceptor extends OBJECT_CLASS_AT = schemaManager.getAttributeType( SchemaConstants.OBJECT_CLASS_AT ); ADMINISTRATIVE_ROLE_AT = schemaManager.getAttributeType( SchemaConstants.ADMINISTRATIVE_ROLE_AT ); SUBTREE_SPECIFICATION_AT = schemaManager.getAttributeType( SchemaConstants.SUBTREE_SPECIFICATION_AT ); + ACCESS_CONTROL_SUBENTRIES_AT = schemaManager.getAttributeType( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ); + SUBSCHEMA_SUBENTRY_AT = schemaManager.getAttributeType( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ); + COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT = schemaManager.getAttributeType( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ); + TRIGGER_EXECUTION_SUBENTRIES_AT = schemaManager.getAttributeType( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ); ssParser = new SubtreeSpecificationParser( schemaManager ); evaluator = new SubtreeEvaluator( schemaManager ); @@ -221,6 +281,12 @@ public class SubentryInterceptor extends } + //------------------------------------------------------------------------------------------- + // Helper methods + //------------------------------------------------------------------------------------------- + /** + * Return the list of AdministrativeRole for a subentry + */ private Set getSubentryAdminRoles( Entry subentry ) throws LdapException { Set adminRoles = new HashSet(); @@ -256,49 +322,6 @@ public class SubentryInterceptor extends } - // ----------------------------------------------------------------------- - // Methods/Code dealing with Subentry Visibility - // ----------------------------------------------------------------------- - - public EntryFilteringCursor list( NextInterceptor nextInterceptor, ListOperationContext listContext ) - throws LdapException - { - EntryFilteringCursor cursor = nextInterceptor.list( listContext ); - - if ( !isSubentryVisible( listContext ) ) - { - cursor.addEntryFilter( new HideSubentriesFilter() ); - } - - return cursor; - } - - - public EntryFilteringCursor search( NextInterceptor nextInterceptor, SearchOperationContext searchContext ) - throws LdapException - { - EntryFilteringCursor cursor = nextInterceptor.search( searchContext ); - - // object scope searches by default return subentries - if ( searchContext.getScope() == SearchScope.OBJECT ) - { - return cursor; - } - - // for subtree and one level scope we filter - if ( !isSubentryVisible( searchContext ) ) - { - cursor.addEntryFilter( new HideSubentriesFilter() ); - } - else - { - cursor.addEntryFilter( new HideEntriesFilter() ); - } - - return cursor; - } - - /** * Checks to see if subentries for the search and list operations should be * made visible based on the availability of the search request control @@ -318,463 +341,484 @@ public class SubentryInterceptor extends if ( opContext.hasRequestControl( SUBENTRY_CONTROL ) ) { SubentriesControl subentriesControl = ( SubentriesControl ) opContext.getRequestControl( SUBENTRY_CONTROL ); + return subentriesControl.isVisible(); } return false; } - - // ----------------------------------------------------------------------- - // Methods dealing with entry and subentry addition - // ----------------------------------------------------------------------- - /** - * Evaluates the set of subentry subtrees upon an entry and returns the - * operational subentry attributes that will be added to the entry if - * added at the dn specified. - * - * @param dn the normalized distinguished name of the entry - * @param entryAttrs the entry attributes are generated for - * @return the set of subentry op attrs for an entry - * @throws Exception if there are problems accessing entry information + * Update all the entries under an AP adding the */ - public Entry getSubentryAttributes( DN dn, Entry entryAttrs ) throws LdapException + private void updateEntries( CoreSession session, DN apDn, SubtreeSpecification ss, DN baseDn, List modifications ) throws LdapException { - Entry subentryAttrs = new DefaultEntry( schemaManager, dn ); - Iterator list = subentryCache.nameIterator(); - - while ( list.hasNext() ) - { - String subentryDnStr = list.next(); - DN subentryDn = new DN( subentryDnStr ); - subentryDn.normalize( schemaManager.getNormalizerMapping() ); - DN apDn = subentryDn.getParent(); - Subentry subentry = subentryCache.getSubentry( subentryDn ); - SubtreeSpecification ss = subentry.getSubtreeSpecification(); - - if ( evaluator.evaluate( ss, apDn, dn, entryAttrs ) ) - { - EntryAttribute operational; + ExprNode filter = new PresenceNode( OBJECT_CLASS_AT ); // (objectClass=*) + SearchControls controls = new SearchControls(); + controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); + controls.setReturningAttributes( new String[] + { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); - if ( subentry.isAccessControlAdminRole() ) - { - operational = subentryAttrs.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ); + SearchOperationContext searchOperationContext = new SearchOperationContext( session, + baseDn, filter, controls ); + searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); - if ( operational == null ) - { - operational = new DefaultEntryAttribute( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, - schemaManager.lookupAttributeTypeRegistry( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) ); - subentryAttrs.put( operational ); - } + EntryFilteringCursor subentries = nexus.search( searchOperationContext ); - operational.add( subentryDn.getNormName() ); - } + try + { + while ( subentries.next() ) + { + Entry candidate = subentries.get(); + DN candidateDn = candidate.getDn(); - if ( subentry.isSchemaAdminRole() ) + if ( evaluator.evaluate( ss, apDn, candidateDn, candidate ) ) { - operational = subentryAttrs.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ); + nexus.modify( new ModifyOperationContext( session, candidateDn, + getOperationalModsForAdd( + candidate, modifications ) ) ); + } + } + } + catch ( Exception e ) + { + throw new LdapOtherException( e.getMessage() ); + } + } - if ( operational == null ) - { - operational = new DefaultEntryAttribute( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, schemaManager - .lookupAttributeTypeRegistry( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) ); - subentryAttrs.put( operational ); - } + + /** + * Checks if the given DN is a namingContext + */ + private boolean isNamingContext( DN dn ) throws LdapException + { + DN namingContext = nexus.findSuffix( dn ); + + return dn.equals( namingContext ); + } + + + /** + * Get the administrativePoint role + */ + private void checkAdministrativeRole( OperationContext opContext, DN apDn ) throws LdapException + { + Entry administrationPoint = opContext.lookup( apDn, ByPassConstants.LOOKUP_BYPASS ); + + // The administrativeRole AT must exist and not be null + EntryAttribute administrativeRole = administrationPoint.get( ADMINISTRATIVE_ROLE_AT ); - operational.add( subentryDn.getNormName() ); - } - - if ( subentry.isCollectiveAdminRole() ) - { - operational = subentryAttrs.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ); + // check that administrativeRole has something valid in it for us + if ( ( administrativeRole == null ) || ( administrativeRole.size() <= 0 ) ) + { + throw new LdapNoSuchAttributeException( I18n.err( I18n.ERR_306, apDn ) ); + } + } + + + /** + * Get the SubtreeSpecification, parse it and stores it into the subentry + */ + private void setSubtreeSpecification( Subentry subentry, Entry entry ) throws LdapException + { + String subtree = entry.get( SUBTREE_SPECIFICATION_AT ).getString(); + SubtreeSpecification ss; - if ( operational == null ) - { - operational = new DefaultEntryAttribute( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, - schemaManager - .lookupAttributeTypeRegistry( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) ); - subentryAttrs.put( operational ); - } + try + { + ss = ssParser.parse( subtree ); + } + catch ( Exception e ) + { + String msg = I18n.err( I18n.ERR_307, entry.getDn() ); + LOG.warn( msg ); + throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg ); + } + + subentry.setSubtreeSpecification( ss ); + } + - operational.add( subentryDn.getNormName() ); - } - - if ( subentry.isTriggersAdminRole() ) - { - operational = subentryAttrs.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ); + /** + * Checks to see if an entry being renamed has a descendant that is an + * administrative point. + * + * @param name the name of the entry which is used as the search base + * @return true if name is an administrative point or one of its descendants + * are, false otherwise + * @throws Exception if there are errors while searching the directory + */ + private boolean hasAdministrativeDescendant( OperationContext opContext, DN name ) throws LdapException + { + ExprNode filter = new PresenceNode( ADMINISTRATIVE_ROLE_AT ); + SearchControls controls = new SearchControls(); + controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); - if ( operational == null ) - { - operational = new DefaultEntryAttribute( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT, - schemaManager.lookupAttributeTypeRegistry( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) ); - subentryAttrs.put( operational ); - } + SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), name, + filter, controls ); + searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); - operational.add( subentryDn.getNormName() ); - } + EntryFilteringCursor aps = nexus.search( searchOperationContext ); + + try + { + if ( aps.next() ) + { + aps.close(); + return true; } } + catch ( Exception e ) + { + throw new LdapOperationException( e.getMessage() ); + } + - return subentryAttrs; + return false; } - public void add( NextInterceptor next, AddOperationContext addContext ) throws LdapException + private List getModsOnEntryRdnChange( DN oldName, DN newName, Entry entry ) throws LdapException { - DN name = addContext.getDn(); - ClonedServerEntry entry = addContext.getEntry(); + List modList = new ArrayList(); - if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) + /* + * There are two different situations warranting action. Firt if + * an ss evalutating to true with the old name no longer evalutates + * to true with the new name. This would be caused by specific chop + * exclusions that effect the new name but did not effect the old + * name. In this case we must remove subentry operational attribute + * values associated with the dn of that subentry. + * + * In the second case an ss selects the entry with the new name when + * it did not previously with the old name. Again this situation + * would be caused by chop exclusions. In this case we must add subentry + * operational attribute values with the dn of this subentry. + */ + for ( DN subentryDn : subentryCache ) { - // get the name of the administrative point and its administrativeRole attributes - DN apName = name.getParent(); - Entry administrationPoint = addContext.lookup( apName, ByPassConstants.LOOKUP_BYPASS ); - - // The administrativeRole AT must exist and not be null - EntryAttribute administrativeRole = administrationPoint.get( ADMINISTRATIVE_ROLE_AT ); + DN apDn = subentryDn.getParent(); + SubtreeSpecification ss = subentryCache.getSubentry( subentryDn ).getSubtreeSpecification(); + boolean isOldNameSelected = evaluator.evaluate( ss, apDn, oldName, entry ); + boolean isNewNameSelected = evaluator.evaluate( ss, apDn, newName, entry ); - // check that administrativeRole has something valid in it for us - if ( ( administrativeRole == null ) || ( administrativeRole.size() <= 0 ) ) + if ( isOldNameSelected == isNewNameSelected ) { - throw new LdapNoSuchAttributeException( I18n.err( I18n.ERR_306, apName ) ); + continue; } - /* ---------------------------------------------------------------- - * Build the set of operational attributes to be injected into - * entries that are contained within the subtree represented by this - * new subentry. In the process we make sure the proper roles are - * supported by the administrative point to allow the addition of - * this new subentry. - * ---------------------------------------------------------------- - */ - Subentry subentry = new Subentry(); - subentry.setAdministrativeRoles( getSubentryAdminRoles( entry ) ); - Entry operational = getSubentryOperationalAttributes( name, subentry ); - - /* ---------------------------------------------------------------- - * Parse the subtreeSpecification of the subentry and add it to the - * SubtreeSpecification cache. If the parse succeeds we continue - * to add the entry to the DIT. Thereafter we search out entries - * to modify the subentry operational attributes of. - * ---------------------------------------------------------------- - */ - String subtree = entry.get( SUBTREE_SPECIFICATION_AT ).getString(); - SubtreeSpecification ss; - - try - { - ss = ssParser.parse( subtree ); - } - catch ( Exception e ) + // need to remove references to the subentry + if ( isOldNameSelected && !isNewNameSelected ) { - String msg = I18n.err( I18n.ERR_307, name.getName() ); - LOG.warn( msg ); - throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg ); - } - - subentry.setSubtreeSpecification( ss ); - - subentryCache.addSubentry( name, subentry ); - - next.add( addContext ); - - /* ---------------------------------------------------------------- - * Find the baseDn for the subentry and use that to search the tree - * while testing each entry returned for inclusion within the - * subtree of the subentry's subtreeSpecification. All included - * entries will have their operational attributes merged with the - * operational attributes calculated above. - * ---------------------------------------------------------------- - */ - DN baseDn = ( DN ) apName.clone(); - baseDn.addAll( ss.getBase() ); - - /// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - /// - /// We loop on *all* the entries under this subtree - /// and we will add some operational attribute in each - /// one of them. This is *utter crap* !!! - /// - /// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - /// What we should do : - /// have a filter to process each entry when they are manipulated - /// instead of doing such a killing modification... This simply does - /// not scale when we have more than a few hundred entries ! - ExprNode filter = new PresenceNode( OBJECT_CLASS_AT ); // (objectClass=*) - SearchControls controls = new SearchControls(); - controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); - controls.setReturningAttributes( new String[] - { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); + for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS ) + { + ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE; + EntryAttribute opAttr = entry.get( aSUBENTRY_OPATTRS ); - SearchOperationContext searchOperationContext = new SearchOperationContext( addContext.getSession(), - baseDn, filter, controls ); - searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); + if ( opAttr != null ) + { + opAttr = opAttr.clone(); + opAttr.remove( subentryDn.getNormName() ); - EntryFilteringCursor subentries = nexus.search( searchOperationContext ); + if ( opAttr.size() < 1 ) + { + op = ModificationOperation.REMOVE_ATTRIBUTE; + } - try - { - while ( subentries.next() ) - { - Entry candidate = subentries.get(); - DN candidateDn = candidate.getDn(); - - if ( evaluator.evaluate( ss, apName, candidateDn, candidate ) ) - { - nexus.modify( new ModifyOperationContext( addContext.getSession(), candidateDn, getOperationalModsForAdd( - candidate, operational ) ) ); + modList.add( new DefaultModification( op, opAttr ) ); } } } - catch ( Exception e ) + // need to add references to the subentry + else if ( isNewNameSelected && !isOldNameSelected ) { - throw new LdapOtherException( e.getMessage() ); + for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS ) + { + ModificationOperation op = ModificationOperation.ADD_ATTRIBUTE; + EntryAttribute opAttr = new DefaultEntryAttribute( aSUBENTRY_OPATTRS, schemaManager + .lookupAttributeTypeRegistry( aSUBENTRY_OPATTRS ) ); + opAttr.add( subentryDn.getNormName() ); + modList.add( new DefaultModification( op, opAttr ) ); + } } - - // TODO why are we doing this here if we got the entry from the - // opContext in the first place - got to look into this - addContext.setEntry( entry ); } - else - { - Iterator list = subentryCache.nameIterator(); - while ( list.hasNext() ) - { - String subentryDnStr = list.next(); - DN subentryDn = new DN( subentryDnStr ).normalize( schemaManager.getNormalizerMapping() ); - DN apDn = subentryDn.getParent(); - Subentry subentry = subentryCache.getSubentry( subentryDn ); - SubtreeSpecification ss = subentry.getSubtreeSpecification(); - - if ( evaluator.evaluate( ss, apDn, name, entry ) ) - { - EntryAttribute operational; + return modList; + } - if ( subentry.isAccessControlAdminRole() ) - { - operational = entry.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ); - if ( operational == null ) - { - operational = new DefaultEntryAttribute( schemaManager - .lookupAttributeTypeRegistry( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) ); - entry.put( operational ); - } - - operational.add( subentryDn.getNormName() ); - } + // ----------------------------------------------------------------------- + // Methods dealing with subentry modification + // ----------------------------------------------------------------------- - if ( subentry.isSchemaAdminRole() ) - { - operational = entry.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ); + private Set getSubentryTypes( Entry entry, List mods ) throws LdapException + { + EntryAttribute ocFinalState = entry.get( OBJECT_CLASS_AT ).clone(); - if ( operational == null ) + for ( Modification mod : mods ) + { + if ( mod.getAttribute().getId().equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT ) || + mod.getAttribute().getId().equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT_OID ) ) + { + switch ( mod.getOperation() ) + { + case ADD_ATTRIBUTE: + for ( Value value : mod.getAttribute() ) { - operational = new DefaultEntryAttribute( schemaManager - .lookupAttributeTypeRegistry( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) ); - entry.put( operational ); + ocFinalState.add( value.getString() ); } - operational.add( subentryDn.getNormName() ); - } - - if ( subentry.isCollectiveAdminRole() ) - { - operational = entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ); + break; - if ( operational == null ) + case REMOVE_ATTRIBUTE: + for ( Value value : mod.getAttribute() ) { - operational = new DefaultEntryAttribute( schemaManager - .lookupAttributeTypeRegistry( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) ); - entry.put( operational ); + ocFinalState.remove( value.getString() ); } - operational.add( subentryDn.getNormName() ); - } - - if ( subentry.isTriggersAdminRole() ) - { - operational = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ); - - if ( operational == null ) - { - operational = new DefaultEntryAttribute( schemaManager - .lookupAttributeTypeRegistry( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) ); - entry.put( operational ); - } + break; - operational.add( subentryDn.getNormName() ); - } + case REPLACE_ATTRIBUTE: + ocFinalState = mod.getAttribute(); + break; } } - - // TODO why are we doing this here if we got the entry from the - // opContext in the first place - got to look into this - addContext.setEntry( entry ); - - next.add( addContext ); } + + Entry attrs = new DefaultEntry( schemaManager, DN.EMPTY_DN ); + attrs.put( ocFinalState ); + return getSubentryAdminRoles( attrs ); } // ----------------------------------------------------------------------- - // Methods dealing with subentry deletion + // Utility Methods // ----------------------------------------------------------------------- - public void delete( NextInterceptor next, DeleteOperationContext deleteContext ) throws LdapException + + private List getOperationalModsForReplace( DN oldName, DN newName, Subentry subentry, Entry entry ) + throws Exception { - DN name = deleteContext.getDn(); - Entry entry = deleteContext.getEntry(); + List modList = new ArrayList(); - // If the entry has a "subentry" Objectclass, we can process the entry. - if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) + EntryAttribute operational; + + if ( subentry.isAccessControlAdminRole() ) { - next.delete( deleteContext ); + operational = entry.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ).clone(); - Subentry removedSubentry = subentryCache.removeSubentry( name ); - SubtreeSpecification ss = removedSubentry.getSubtreeSpecification(); + if ( operational == null ) + { + operational = new DefaultEntryAttribute( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, schemaManager + .lookupAttributeTypeRegistry( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) ); + operational.add( newName.toString() ); + } + else + { + operational.remove( oldName.toString() ); + operational.add( newName.toString() ); + } - /* ---------------------------------------------------------------- - * Find the baseDn for the subentry and use that to search the tree - * for all entries included by the subtreeSpecification. Then we - * check the entry for subentry operational attribute that contain - * the DN of the subentry. These are the subentry operational - * attributes we remove from the entry in a modify operation. - * ---------------------------------------------------------------- - */ - DN apName = name.getParent(); - DN baseDn = ( DN ) apName.clone(); - baseDn.addAll( ss.getBase() ); + modList.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) ); + } - ExprNode filter = new PresenceNode( OBJECT_CLASS_AT ); - SearchControls controls = new SearchControls(); - controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); - controls.setReturningAttributes( new String[] - { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); + if ( subentry.isSchemaAdminRole() ) + { + operational = entry.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).clone(); - SearchOperationContext searchOperationContext = new SearchOperationContext( deleteContext.getSession(), baseDn, - filter, controls ); - searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); + if ( operational == null ) + { + operational = new DefaultEntryAttribute( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, schemaManager + .lookupAttributeTypeRegistry( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) ); + operational.add( newName.toString() ); + } + else + { + operational.remove( oldName.toString() ); + operational.add( newName.toString() ); + } - EntryFilteringCursor subentries = nexus.search( searchOperationContext ); + modList.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) ); + } - try + if ( subentry.isCollectiveAdminRole() ) + { + operational = entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ).clone(); + + if ( operational == null ) { - while ( subentries.next() ) - { - Entry candidate = subentries.get(); - DN candidateDn = candidate.getDn(); - - if ( evaluator.evaluate( ss, apName, candidateDn, candidate ) ) - { - nexus.modify( new ModifyOperationContext( deleteContext.getSession(), candidateDn, getOperationalModsForRemove( - name, candidate ) ) ); - } - } - - subentries.close(); + operational = new DefaultEntryAttribute( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, + schemaManager.lookupAttributeTypeRegistry( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) ); + operational.add( newName.toString() ); } - catch ( Exception e ) + else { - throw new LdapOperationException( e.getMessage() ); + operational.remove( oldName.toString() ); + operational.add( newName.toString() ); } + + modList.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) ); } - else + + if ( subentry.isTriggersAdminRole() ) { - next.delete( deleteContext ); + operational = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ).clone(); + + if ( operational == null ) + { + operational = new DefaultEntryAttribute( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT, schemaManager + .lookupAttributeTypeRegistry( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) ); + operational.add( newName.toString() ); + } + else + { + operational.remove( oldName.toString() ); + operational.add( newName.toString() ); + } + + modList.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) ); } + + return modList; } - // ----------------------------------------------------------------------- - // Methods dealing subentry name changes - // ----------------------------------------------------------------------- + /** + * Gets the subschema operational attributes to be added to or removed from + * an entry selected by a subentry's subtreeSpecification. + */ + private List getSubentryOperationalAttributes( DN dn, Subentry subentry ) throws LdapException + { + List attributes = new ArrayList(); + + if ( subentry.isAccessControlAdminRole() ) + { + EntryAttribute accessControlSubentries = new DefaultEntryAttribute( ACCESS_CONTROL_SUBENTRIES_AT, dn.getNormName() ); + attributes.add( accessControlSubentries ); + } + + if ( subentry.isSchemaAdminRole() ) + { + EntryAttribute subschemaSubentry = new DefaultEntryAttribute( SUBSCHEMA_SUBENTRY_AT, dn.getNormName() ); + attributes.add( subschemaSubentry ); + } + + if ( subentry.isCollectiveAdminRole() ) + { + EntryAttribute collectiveAttributeSubentries = new DefaultEntryAttribute( COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, dn.getNormName() ); + attributes.add( collectiveAttributeSubentries ); + } + + if ( subentry.isTriggersAdminRole() ) + { + EntryAttribute tiggerExecutionSubentries = new DefaultEntryAttribute( TRIGGER_EXECUTION_SUBENTRIES_AT, dn.getNormName() ); + attributes.add( tiggerExecutionSubentries ); + } + + return attributes; + } + /** - * Checks to see if an entry being renamed has a descendant that is an - * administrative point. + * Calculates the subentry operational attributes to remove from a candidate + * entry selected by a subtreeSpecification. When we remove a subentry we + * must remove the operational attributes in the entries that were once selected + * by the subtree specification of that subentry. To do so we must perform + * a modify operation with the set of modifications to perform. This method + * calculates those modifications. * - * @param name the name of the entry which is used as the search base - * @return true if name is an administrative point or one of its descendants - * are, false otherwise - * @throws Exception if there are errors while searching the directory + * @param subentryDn the distinguished name of the subentry + * @param candidate the candidate entry to removed from the + * @return the set of modifications required to remove an entry's reference to + * a subentry */ - private boolean hasAdministrativeDescendant( OperationContext opContext, DN name ) throws LdapException + private List getOperationalModsForRemove( DN subentryDn, Entry candidate ) throws LdapException { - ExprNode filter = new PresenceNode( ADMINISTRATIVE_ROLE_AT ); - SearchControls controls = new SearchControls(); - controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); - - SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), name, - filter, controls ); - searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); + List modList = new ArrayList(); + String dn = subentryDn.getNormName(); - EntryFilteringCursor aps = nexus.search( searchOperationContext ); + for ( String opAttrId : SUBENTRY_OPATTRS ) + { + EntryAttribute opAttr = candidate.get( opAttrId ); - try - { - if ( aps.next() ) + if ( ( opAttr != null ) && opAttr.contains( dn ) ) { - aps.close(); - return true; + AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( opAttrId ); + EntryAttribute attr = new DefaultEntryAttribute( opAttrId, attributeType, dn ); + modList.add( new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, attr ) ); } } - catch ( Exception e ) + + return modList; + } + + + /** + * Calculates the subentry operational attributes to add or replace from + * a candidate entry selected by a subtree specification. When a subentry + * is added or it's specification is modified some entries must have new + * operational attributes added to it to point back to the associated + * subentry. To do so a modify operation must be performed on entries + * selected by the subtree specification. This method calculates the + * modify operation to be performed on the entry. + */ + private List getOperationalModsForAdd( Entry entry, List operationalAttributes ) throws LdapException + { + List modifications = new ArrayList(); + + for ( EntryAttribute operationalAttribute : operationalAttributes ) { - throw new LdapOperationException( e.getMessage() ); + EntryAttribute opAttrInEntry = entry.get( operationalAttribute.getAttributeType() ); + + if ( ( opAttrInEntry != null ) && ( opAttrInEntry.size() > 0 ) ) + { + for ( Value value : opAttrInEntry ) + { + operationalAttribute.add( value ); + } + + modifications.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, operationalAttribute ) ); + } + else + { + modifications.add( new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, operationalAttribute ) ); + } } - - return false; + return modifications; } + - - private List getModsOnEntryRdnChange( DN oldName, DN newName, Entry entry ) throws LdapException + /** + * Get the list of modification to apply to all the entries + */ + private List getModsOnEntryModification( DN name, Entry oldEntry, Entry newEntry ) throws LdapException { List modList = new ArrayList(); - /* - * There are two different situations warranting action. Firt if - * an ss evalutating to true with the old name no longer evalutates - * to true with the new name. This would be caused by specific chop - * exclusions that effect the new name but did not effect the old - * name. In this case we must remove subentry operational attribute - * values associated with the dn of that subentry. - * - * In the second case an ss selects the entry with the new name when - * it did not previously with the old name. Again this situation - * would be caused by chop exclusions. In this case we must add subentry - * operational attribute values with the dn of this subentry. - */ - Iterator subentries = subentryCache.nameIterator(); - - while ( subentries.hasNext() ) + for ( DN subentryDn : subentryCache ) { - String subentryDnStr = subentries.next(); - DN subentryDn = new DN( subentryDnStr ).normalize( schemaManager.getNormalizerMapping() ); DN apDn = subentryDn.getParent(); SubtreeSpecification ss = subentryCache.getSubentry( subentryDn ).getSubtreeSpecification(); - boolean isOldNameSelected = evaluator.evaluate( ss, apDn, oldName, entry ); - boolean isNewNameSelected = evaluator.evaluate( ss, apDn, newName, entry ); + boolean isOldEntrySelected = evaluator.evaluate( ss, apDn, name, oldEntry ); + boolean isNewEntrySelected = evaluator.evaluate( ss, apDn, name, newEntry ); - if ( isOldNameSelected == isNewNameSelected ) + if ( isOldEntrySelected == isNewEntrySelected ) { continue; } // need to remove references to the subentry - if ( isOldNameSelected && !isNewNameSelected ) + if ( isOldEntrySelected && !isNewEntrySelected ) { for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS ) { ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE; - EntryAttribute opAttr = entry.get( aSUBENTRY_OPATTRS ); + EntryAttribute opAttr = oldEntry.get( aSUBENTRY_OPATTRS ); if ( opAttr != null ) { opAttr = opAttr.clone(); - opAttr.remove( subentryDnStr ); + opAttr.remove( subentryDn.getNormName() ); if ( opAttr.size() < 1 ) { @@ -786,214 +830,201 @@ public class SubentryInterceptor extends } } // need to add references to the subentry - else if ( isNewNameSelected && !isOldNameSelected ) + else if ( isNewEntrySelected && !isOldEntrySelected ) { - for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS ) + for ( String attribute : SUBENTRY_OPATTRS ) { ModificationOperation op = ModificationOperation.ADD_ATTRIBUTE; - EntryAttribute opAttr = new DefaultEntryAttribute( aSUBENTRY_OPATTRS, schemaManager - .lookupAttributeTypeRegistry( aSUBENTRY_OPATTRS ) ); - opAttr.add( subentryDnStr ); + AttributeType type = schemaManager.lookupAttributeTypeRegistry( attribute ); + EntryAttribute opAttr = new DefaultEntryAttribute( attribute, type ); + opAttr.add( subentryDn.getNormName() ); modList.add( new DefaultModification( op, opAttr ) ); } } } - return modList; - } - - - public void rename( NextInterceptor next, RenameOperationContext renameContext ) throws LdapException - { - DN oldDn = renameContext.getDn(); - - Entry entry = renameContext.getEntry().getClonedEntry(); - - if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) - { - // @Todo To be reviewed !!! - Subentry subentry = subentryCache.removeSubentry( oldDn ); - SubtreeSpecification ss = subentry.getSubtreeSpecification(); - DN apName = oldDn.getParent(); - DN baseDn = ( DN ) apName.clone(); - baseDn.addAll( ss.getBase() ); - DN newName = oldDn.getParent(); - - newName.add( renameContext.getNewRdn() ); - newName.normalize( schemaManager.getNormalizerMapping() ); - - subentryCache.addSubentry( newName, subentry ); - next.rename( renameContext ); - - subentry = subentryCache.getSubentry( newName ); - ExprNode filter = new PresenceNode( OBJECT_CLASS_AT ); - SearchControls controls = new SearchControls(); - controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); - controls.setReturningAttributes( new String[] - { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); - - SearchOperationContext searchOperationContext = new SearchOperationContext( renameContext.getSession(), baseDn, - filter, controls ); - searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); - - EntryFilteringCursor subentries = nexus.search( searchOperationContext ); - - try - { - while ( subentries.next() ) - { - Entry candidate = subentries.get(); - DN dn = candidate.getDn(); - dn.normalize( schemaManager.getNormalizerMapping() ); - - if ( evaluator.evaluate( ss, apName, dn, candidate ) ) - { - nexus.modify( new ModifyOperationContext( renameContext.getSession(), dn, getOperationalModsForReplace( - oldDn, newName, subentry, candidate ) ) ); - } - } - - subentries.close(); - } - catch ( Exception e ) - { - throw new LdapOperationException( e.getMessage() ); - } - } - else + return modList; + } + + + //------------------------------------------------------------------------------------------- + // Interceptor API methods + //------------------------------------------------------------------------------------------- + /** + * {@inheritDoc} + */ + public void add( NextInterceptor next, AddOperationContext addContext ) throws LdapException + { + DN dn = addContext.getDn(); + ClonedServerEntry entry = addContext.getEntry(); + + // Check that the added entry is a subentry + if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) { - if ( hasAdministrativeDescendant( renameContext, oldDn ) ) + // get the name of the administrative point and its administrativeRole attributes + // The AP must be the parent DN, but we also have to check that the given DN + // is not the rootDSE or a NamingContext + if ( dn.isRootDSE() || isNamingContext( dn ) ) { - String msg = I18n.err( I18n.ERR_308 ); - LOG.warn( msg ); - throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg ); + // Not allowed : we can't get a parent in those cases + throw new LdapOtherException( "Cannot find an AdministrativePoint for " + dn ); } + + // Get the administrativePoint role + DN apDn = dn.getParent(); + checkAdministrativeRole( addContext, apDn ); - next.rename( renameContext ); + /* ---------------------------------------------------------------- + * Build the set of operational attributes to be injected into + * entries that are contained within the subtree represented by this + * new subentry. In the process we make sure the proper roles are + * supported by the administrative point to allow the addition of + * this new subentry. + * ---------------------------------------------------------------- + */ + Subentry subentry = new Subentry(); + subentry.setAdministrativeRoles( getSubentryAdminRoles( entry ) ); + List operationalAttributes = getSubentryOperationalAttributes( dn, subentry ); - // calculate the new DN now for use below to modify subentry operational - // attributes contained within this regular entry with name changes - DN newName = renameContext.getNewDn(); + /* ---------------------------------------------------------------- + * Parse the subtreeSpecification of the subentry and add it to the + * SubtreeSpecification cache. If the parse succeeds we continue + * to add the entry to the DIT. Thereafter we search out entries + * to modify the subentry operational attributes of. + * ---------------------------------------------------------------- + */ + setSubtreeSpecification( subentry, entry ); + subentryCache.addSubentry( dn, subentry ); - List mods = getModsOnEntryRdnChange( oldDn, newName, entry ); + // Now inject the subentry into the backend + next.add( addContext ); - if ( mods.size() > 0 ) - { - nexus.modify( new ModifyOperationContext( renameContext.getSession(), newName, mods ) ); - } + /* ---------------------------------------------------------------- + * Find the baseDn for the subentry and use that to search the tree + * while testing each entry returned for inclusion within the + * subtree of the subentry's subtreeSpecification. All included + * entries will have their operational attributes merged with the + * operational attributes calculated above. + * ---------------------------------------------------------------- + */ + DN baseDn = ( DN ) apDn.clone(); + baseDn.addAll( subentry.getSubtreeSpecification().getBase() ); + + updateEntries( addContext.getSession(), apDn, subentry.getSubtreeSpecification(), baseDn, operationalAttributes ); + + // TODO why are we doing this here if we got the entry from the + // opContext in the first place - got to look into this + addContext.setEntry( entry ); } - } + else + { + // The added entry is not a Subentry + // Nevertheless, we have to check if the entry is added into an AdministrativePoint + // and is associated with a SubtreeSpecification + for ( DN subentryDn : subentryCache ) + { + DN apDn = subentryDn.getParent(); + Subentry subentry = subentryCache.getSubentry( subentryDn ); + SubtreeSpecification ss = subentry.getSubtreeSpecification(); + if ( evaluator.evaluate( ss, apDn, dn, entry ) ) + { + EntryAttribute operational; - public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext moveAndRenameContext ) throws LdapException - { - DN oldDn = moveAndRenameContext.getDn(); - DN newSuperiorDn = moveAndRenameContext.getNewSuperiorDn(); + if ( subentry.isAccessControlAdminRole() ) + { + operational = entry.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ); - Entry entry = moveAndRenameContext.getOriginalEntry(); + if ( operational == null ) + { + operational = new DefaultEntryAttribute( schemaManager + .lookupAttributeTypeRegistry( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) ); + entry.put( operational ); + } - if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) - { - Subentry subentry = subentryCache.removeSubentry( oldDn ); - SubtreeSpecification ss = subentry.getSubtreeSpecification(); - DN apName = oldDn.getParent(); - DN baseDn = ( DN ) apName.clone(); - baseDn.addAll( ss.getBase() ); - DN newName = newSuperiorDn.getParent(); + operational.add( subentryDn.getNormName() ); + } - newName.add( moveAndRenameContext.getNewRdn() ); - newName.normalize( schemaManager.getNormalizerMapping() ); + if ( subentry.isSchemaAdminRole() ) + { + operational = entry.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ); - subentryCache.addSubentry( newName, subentry ); - - next.moveAndRename( moveAndRenameContext ); + if ( operational == null ) + { + operational = new DefaultEntryAttribute( schemaManager + .lookupAttributeTypeRegistry( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) ); + entry.put( operational ); + } - subentry = subentryCache.getSubentry( newName ); + operational.add( subentryDn.getNormName() ); + } - ExprNode filter = new PresenceNode( OBJECT_CLASS_AT ); - SearchControls controls = new SearchControls(); - controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); - controls.setReturningAttributes( new String[] - { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); + if ( subentry.isCollectiveAdminRole() ) + { + operational = entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ); - SearchOperationContext searchOperationContext = new SearchOperationContext( moveAndRenameContext.getSession(), baseDn, - filter, controls ); - searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); + if ( operational == null ) + { + operational = new DefaultEntryAttribute( schemaManager + .lookupAttributeTypeRegistry( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) ); + entry.put( operational ); + } - EntryFilteringCursor subentries = nexus.search( searchOperationContext ); + operational.add( subentryDn.getNormName() ); + } - try - { - while ( subentries.next() ) - { - Entry candidate = subentries.get(); - DN dn = candidate.getDn(); - dn.normalize( schemaManager.getNormalizerMapping() ); - - if ( evaluator.evaluate( ss, apName, dn, candidate ) ) + if ( subentry.isTriggersAdminRole() ) { - nexus.modify( new ModifyOperationContext( moveAndRenameContext.getSession(), dn, getOperationalModsForReplace( - oldDn, newName, subentry, candidate ) ) ); + operational = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ); + + if ( operational == null ) + { + operational = new DefaultEntryAttribute( schemaManager + .lookupAttributeTypeRegistry( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) ); + entry.put( operational ); + } + + operational.add( subentryDn.getNormName() ); } } - - subentries.close(); - } - catch ( Exception e ) - { - throw new LdapOperationException( e.getMessage() ); - } - } - else - { - if ( hasAdministrativeDescendant( moveAndRenameContext, oldDn ) ) - { - String msg = I18n.err( I18n.ERR_308 ); - LOG.warn( msg ); - throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg ); } - next.moveAndRename( moveAndRenameContext ); - - // calculate the new DN now for use below to modify subentry operational - // attributes contained within this regular entry with name changes - DN newDn = moveAndRenameContext.getNewDn(); - List mods = getModsOnEntryRdnChange( oldDn, newDn, entry ); + // TODO why are we doing this here if we got the entry from the + // opContext in the first place - got to look into this + addContext.setEntry( entry ); - if ( mods.size() > 0 ) - { - nexus.modify( new ModifyOperationContext( moveAndRenameContext.getSession(), newDn, mods ) ); - } + next.add( addContext ); } } - + /** * {@inheritDoc} */ - public void move( NextInterceptor next, MoveOperationContext moveContext ) throws LdapException + public void delete( NextInterceptor next, DeleteOperationContext deleteContext ) throws LdapException { - DN oldDn = moveContext.getDn(); - DN newSuperiorDn = moveContext.getNewSuperior(); - - Entry entry = moveContext.getOriginalEntry(); + DN name = deleteContext.getDn(); + Entry entry = deleteContext.getEntry(); + // If the entry has a "subentry" Objectclass, we can process the entry. if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) { - Subentry subentry = subentryCache.removeSubentry( oldDn ); - SubtreeSpecification ss = subentry.getSubtreeSpecification(); - DN apName = oldDn.getParent(); - DN baseDn = ( DN ) apName.clone(); - baseDn.addAll( ss.getBase() ); - DN newName = (DN)newSuperiorDn.clone(); - newName.add( oldDn.getRdn() ); - newName.normalize( schemaManager.getNormalizerMapping() ); + next.delete( deleteContext ); - subentryCache.addSubentry( newName, subentry ); - - next.move( moveContext ); + Subentry removedSubentry = subentryCache.removeSubentry( name ); + SubtreeSpecification ss = removedSubentry.getSubtreeSpecification(); - subentry = subentryCache.getSubentry( newName ); + /* ---------------------------------------------------------------- + * Find the baseDn for the subentry and use that to search the tree + * for all entries included by the subtreeSpecification. Then we + * check the entry for subentry operational attribute that contain + * the DN of the subentry. These are the subentry operational + * attributes we remove from the entry in a modify operation. + * ---------------------------------------------------------------- + */ + DN apName = name.getParent(); + DN baseDn = ( DN ) apName.clone(); + baseDn.addAll( ss.getBase() ); ExprNode filter = new PresenceNode( OBJECT_CLASS_AT ); SearchControls controls = new SearchControls(); @@ -1001,7 +1032,7 @@ public class SubentryInterceptor extends controls.setReturningAttributes( new String[] { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); - SearchOperationContext searchOperationContext = new SearchOperationContext( moveContext.getSession(), baseDn, + SearchOperationContext searchOperationContext = new SearchOperationContext( deleteContext.getSession(), baseDn, filter, controls ); searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); @@ -1009,92 +1040,46 @@ public class SubentryInterceptor extends try { - // Modify all the entries under this subentry while ( subentries.next() ) { Entry candidate = subentries.get(); - DN dn = candidate.getDn(); - dn.normalize( schemaManager.getNormalizerMapping() ); + DN candidateDn = candidate.getDn(); - if ( evaluator.evaluate( ss, apName, dn, candidate ) ) + if ( evaluator.evaluate( ss, apName, candidateDn, candidate ) ) { - nexus.modify( new ModifyOperationContext( moveContext.getSession(), dn, getOperationalModsForReplace( - oldDn, newName, subentry, candidate ) ) ); - } - } - - subentries.close(); - } - catch ( Exception e ) - { - throw new LdapOperationException( e.getMessage() ); - } - } - else - { - if ( hasAdministrativeDescendant( moveContext, oldDn ) ) - { - String msg = I18n.err( I18n.ERR_308 ); - LOG.warn( msg ); - throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg ); - } - - next.move( moveContext ); - - // calculate the new DN now for use below to modify subentry operational - // attributes contained within this regular entry with name changes - DN newName = moveContext.getNewDn(); - List mods = getModsOnEntryRdnChange( oldDn, newName, entry ); - - if ( mods.size() > 0 ) + nexus.modify( new ModifyOperationContext( deleteContext.getSession(), candidateDn, getOperationalModsForRemove( + name, candidate ) ) ); + } + } + + subentries.close(); + } + catch ( Exception e ) { - nexus.modify( new ModifyOperationContext( moveContext.getSession(), newName, mods ) ); + throw new LdapOperationException( e.getMessage() ); } } + else + { + next.delete( deleteContext ); + } } - - // ----------------------------------------------------------------------- - // Methods dealing with subentry modification - // ----------------------------------------------------------------------- - - private Set getSubentryTypes( Entry entry, List mods ) throws LdapException + + /** + * {@inheritDoc} + */ + public EntryFilteringCursor list( NextInterceptor nextInterceptor, ListOperationContext listContext ) + throws LdapException { - EntryAttribute ocFinalState = entry.get( OBJECT_CLASS_AT ).clone(); + EntryFilteringCursor cursor = nextInterceptor.list( listContext ); - for ( Modification mod : mods ) + if ( !isSubentryVisible( listContext ) ) { - if ( mod.getAttribute().getId().equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT ) || - mod.getAttribute().getId().equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT_OID ) ) - { - switch ( mod.getOperation() ) - { - case ADD_ATTRIBUTE: - for ( Value value : mod.getAttribute() ) - { - ocFinalState.add( value.getString() ); - } - - break; - - case REMOVE_ATTRIBUTE: - for ( Value value : mod.getAttribute() ) - { - ocFinalState.remove( value.getString() ); - } - - break; - - case REPLACE_ATTRIBUTE: - ocFinalState = mod.getAttribute(); - break; - } - } + cursor.addEntryFilter( new HideSubentriesFilter() ); } - Entry attrs = new DefaultEntry( schemaManager, DN.EMPTY_DN ); - attrs.put( ocFinalState ); - return getSubentryAdminRoles( attrs ); + return cursor; } @@ -1185,7 +1170,7 @@ public class SubentryInterceptor extends // search for all selected entries by the new SS and add references to subentry subentry = subentryCache.getSubentry( dn ); - Entry operational = getSubentryOperationalAttributes( dn, subentry ); + List operationalAttributes = getSubentryOperationalAttributes( dn, subentry ); DN newBaseDn = ( DN ) apName.clone(); newBaseDn.addAll( ssNew.getBase() ); @@ -1204,7 +1189,7 @@ public class SubentryInterceptor extends if ( evaluator.evaluate( ssNew, apName, candidateDn, candidate ) ) { nexus.modify( new ModifyOperationContext( modifyContext.getSession(), candidateDn, - getOperationalModsForAdd( candidate, operational ) ) ); + getOperationalModsForAdd( candidate, operationalAttributes ) ) ); } } } @@ -1232,338 +1217,366 @@ public class SubentryInterceptor extends } - // ----------------------------------------------------------------------- - // Utility Methods - // ----------------------------------------------------------------------- - - private List getOperationalModsForReplace( DN oldName, DN newName, Subentry subentry, Entry entry ) - throws Exception + /** + * {@inheritDoc} + */ + public void move( NextInterceptor next, MoveOperationContext moveContext ) throws LdapException { - List modList = new ArrayList(); + DN oldDn = moveContext.getDn(); + DN newSuperiorDn = moveContext.getNewSuperior(); - EntryAttribute operational; + Entry entry = moveContext.getOriginalEntry(); - if ( subentry.isAccessControlAdminRole() ) + if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) { - operational = entry.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ).clone(); + Subentry subentry = subentryCache.removeSubentry( oldDn ); + SubtreeSpecification ss = subentry.getSubtreeSpecification(); + DN apName = oldDn.getParent(); + DN baseDn = ( DN ) apName.clone(); + baseDn.addAll( ss.getBase() ); + DN newName = (DN)newSuperiorDn.clone(); + newName.add( oldDn.getRdn() ); + newName.normalize( schemaManager.getNormalizerMapping() ); - if ( operational == null ) - { - operational = new DefaultEntryAttribute( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, schemaManager - .lookupAttributeTypeRegistry( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) ); - operational.add( newName.toString() ); - } - else - { - operational.remove( oldName.toString() ); - operational.add( newName.toString() ); - } + subentryCache.addSubentry( newName, subentry ); + + next.move( moveContext ); - modList.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) ); - } + subentry = subentryCache.getSubentry( newName ); - if ( subentry.isSchemaAdminRole() ) - { - operational = entry.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).clone(); + ExprNode filter = new PresenceNode( OBJECT_CLASS_AT ); + SearchControls controls = new SearchControls(); + controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); + controls.setReturningAttributes( new String[] + { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); - if ( operational == null ) + SearchOperationContext searchOperationContext = new SearchOperationContext( moveContext.getSession(), baseDn, + filter, controls ); + searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); + + EntryFilteringCursor subentries = nexus.search( searchOperationContext ); + + try { - operational = new DefaultEntryAttribute( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, schemaManager - .lookupAttributeTypeRegistry( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) ); - operational.add( newName.toString() ); + // Modify all the entries under this subentry + while ( subentries.next() ) + { + Entry candidate = subentries.get(); + DN dn = candidate.getDn(); + dn.normalize( schemaManager.getNormalizerMapping() ); + + if ( evaluator.evaluate( ss, apName, dn, candidate ) ) + { + nexus.modify( new ModifyOperationContext( moveContext.getSession(), dn, getOperationalModsForReplace( + oldDn, newName, subentry, candidate ) ) ); + } + } + + subentries.close(); } - else + catch ( Exception e ) { - operational.remove( oldName.toString() ); - operational.add( newName.toString() ); + throw new LdapOperationException( e.getMessage() ); } - - modList.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) ); } - - if ( subentry.isCollectiveAdminRole() ) + else { - operational = entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ).clone(); - - if ( operational == null ) - { - operational = new DefaultEntryAttribute( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, - schemaManager.lookupAttributeTypeRegistry( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) ); - operational.add( newName.toString() ); - } - else + if ( hasAdministrativeDescendant( moveContext, oldDn ) ) { - operational.remove( oldName.toString() ); - operational.add( newName.toString() ); + String msg = I18n.err( I18n.ERR_308 ); + LOG.warn( msg ); + throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg ); } - modList.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) ); - } + next.move( moveContext ); - if ( subentry.isTriggersAdminRole() ) - { - operational = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ).clone(); + // calculate the new DN now for use below to modify subentry operational + // attributes contained within this regular entry with name changes + DN newName = moveContext.getNewDn(); + List mods = getModsOnEntryRdnChange( oldDn, newName, entry ); - if ( operational == null ) - { - operational = new DefaultEntryAttribute( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT, schemaManager - .lookupAttributeTypeRegistry( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) ); - operational.add( newName.toString() ); - } - else + if ( mods.size() > 0 ) { - operational.remove( oldName.toString() ); - operational.add( newName.toString() ); + nexus.modify( new ModifyOperationContext( moveContext.getSession(), newName, mods ) ); } - - modList.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) ); } - - return modList; } - /** - * Gets the subschema operational attributes to be added to or removed from - * an entry selected by a subentry's subtreeSpecification. - * - * @param name the normalized distinguished name of the subentry (the value of op attrs) - * @param subentry the subentry to get attributes from - * @return the set of attributes to be added or removed from entries - */ - private Entry getSubentryOperationalAttributes( DN name, Subentry subentry ) throws LdapException + public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext moveAndRenameContext ) throws LdapException { - Entry operational = new DefaultEntry( schemaManager, name ); + DN oldDn = moveAndRenameContext.getDn(); + DN newSuperiorDn = moveAndRenameContext.getNewSuperiorDn(); - if ( subentry.isAccessControlAdminRole() ) - { - if ( operational.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) == null ) - { - operational.put( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, name.getNormName() ); - } - else - { - operational.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ).add( name.getNormName() ); - } - } - - if ( subentry.isSchemaAdminRole() ) - { - if ( operational.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) == null ) - { - operational.put( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, name.getNormName() ); - } - else - { - operational.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).add( name.getNormName() ); - } - } - - if ( subentry.isCollectiveAdminRole() ) + Entry entry = moveAndRenameContext.getOriginalEntry(); + + if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) { - if ( operational.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) == null ) + Subentry subentry = subentryCache.removeSubentry( oldDn ); + SubtreeSpecification ss = subentry.getSubtreeSpecification(); + DN apName = oldDn.getParent(); + DN baseDn = ( DN ) apName.clone(); + baseDn.addAll( ss.getBase() ); + DN newName = newSuperiorDn.getParent(); + + newName.add( moveAndRenameContext.getNewRdn() ); + newName.normalize( schemaManager.getNormalizerMapping() ); + + subentryCache.addSubentry( newName, subentry ); + + next.moveAndRename( moveAndRenameContext ); + + subentry = subentryCache.getSubentry( newName ); + + ExprNode filter = new PresenceNode( OBJECT_CLASS_AT ); + SearchControls controls = new SearchControls(); + controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); + controls.setReturningAttributes( new String[] + { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); + + SearchOperationContext searchOperationContext = new SearchOperationContext( moveAndRenameContext.getSession(), baseDn, + filter, controls ); + searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); + + EntryFilteringCursor subentries = nexus.search( searchOperationContext ); + + try { - operational.put( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, name.getNormName() ); + while ( subentries.next() ) + { + Entry candidate = subentries.get(); + DN dn = candidate.getDn(); + dn.normalize( schemaManager.getNormalizerMapping() ); + + if ( evaluator.evaluate( ss, apName, dn, candidate ) ) + { + nexus.modify( new ModifyOperationContext( moveAndRenameContext.getSession(), dn, getOperationalModsForReplace( + oldDn, newName, subentry, candidate ) ) ); + } + } + + subentries.close(); } - else + catch ( Exception e ) { - operational.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ).add( name.getNormName() ); + throw new LdapOperationException( e.getMessage() ); } } - - if ( subentry.isTriggersAdminRole() ) + else { - if ( operational.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) == null ) + if ( hasAdministrativeDescendant( moveAndRenameContext, oldDn ) ) { - operational.put( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT, name.getNormName() ); + String msg = I18n.err( I18n.ERR_308 ); + LOG.warn( msg ); + throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg ); } - else + + next.moveAndRename( moveAndRenameContext ); + + // calculate the new DN now for use below to modify subentry operational + // attributes contained within this regular entry with name changes + DN newDn = moveAndRenameContext.getNewDn(); + List mods = getModsOnEntryRdnChange( oldDn, newDn, entry ); + + if ( mods.size() > 0 ) { - operational.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ).add( name.getNormName() ); + nexus.modify( new ModifyOperationContext( moveAndRenameContext.getSession(), newDn, mods ) ); } } - - return operational; } - /** - * Calculates the subentry operational attributes to remove from a candidate - * entry selected by a subtreeSpecification. When we remove a subentry we - * must remove the operational attributes in the entries that were once selected - * by the subtree specification of that subentry. To do so we must perform - * a modify operation with the set of modifications to perform. This method - * calculates those modifications. - * - * @param subentryDn the distinguished name of the subentry - * @param candidate the candidate entry to removed from the - * @return the set of modifications required to remove an entry's reference to - * a subentry - */ - private List getOperationalModsForRemove( DN subentryDn, Entry candidate ) throws LdapException + public void rename( NextInterceptor next, RenameOperationContext renameContext ) throws LdapException { - List modList = new ArrayList(); - String dn = subentryDn.getNormName(); + DN oldDn = renameContext.getDn(); - for ( String opAttrId : SUBENTRY_OPATTRS ) - { - EntryAttribute opAttr = candidate.get( opAttrId ); + Entry entry = renameContext.getEntry().getClonedEntry(); - if ( ( opAttr != null ) && opAttr.contains( dn ) ) - { - AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( opAttrId ); - EntryAttribute attr = new DefaultEntryAttribute( opAttrId, attributeType, dn ); - modList.add( new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, attr ) ); - } - } + if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) + { + // @Todo To be reviewed !!! + Subentry subentry = subentryCache.removeSubentry( oldDn ); + SubtreeSpecification ss = subentry.getSubtreeSpecification(); + DN apName = oldDn.getParent(); + DN baseDn = ( DN ) apName.clone(); + baseDn.addAll( ss.getBase() ); + DN newName = oldDn.getParent(); - return modList; - } + newName.add( renameContext.getNewRdn() ); + newName.normalize( schemaManager.getNormalizerMapping() ); + subentryCache.addSubentry( newName, subentry ); + next.rename( renameContext ); - /** - * Calculates the subentry operational attributes to add or replace from - * a candidate entry selected by a subtree specification. When a subentry - * is added or it's specification is modified some entries must have new - * operational attributes added to it to point back to the associated - * subentry. To do so a modify operation must be performed on entries - * selected by the subtree specification. This method calculates the - * modify operation to be performed on the entry. - * - * @param entry the entry being modified - * @param operational the set of operational attributes supported by the AP - * of the subentry - * @return the set of modifications needed to update the entry - * @throws Exception if there are probelms accessing modification items - */ - public List getOperationalModsForAdd( Entry entry, Entry operational ) throws LdapException - { - List modList = new ArrayList(); + subentry = subentryCache.getSubentry( newName ); + ExprNode filter = new PresenceNode( OBJECT_CLASS_AT ); + SearchControls controls = new SearchControls(); + controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); + controls.setReturningAttributes( new String[] + { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } ); - for ( AttributeType attributeType : operational.getAttributeTypes() ) - { - ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE; - EntryAttribute result = new DefaultEntryAttribute( attributeType ); - EntryAttribute opAttrAdditions = operational.get( attributeType ); - EntryAttribute opAttrInEntry = entry.get( attributeType ); + SearchOperationContext searchOperationContext = new SearchOperationContext( renameContext.getSession(), baseDn, + filter, controls ); + searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); - for ( Value value : opAttrAdditions ) - { - result.add( value ); - } + EntryFilteringCursor subentries = nexus.search( searchOperationContext ); - if ( opAttrInEntry != null && opAttrInEntry.size() > 0 ) + try { - for ( Value value : opAttrInEntry ) + while ( subentries.next() ) { - result.add( value ); + Entry candidate = subentries.get(); + DN dn = candidate.getDn(); + dn.normalize( schemaManager.getNormalizerMapping() ); + + if ( evaluator.evaluate( ss, apName, dn, candidate ) ) + { + nexus.modify( new ModifyOperationContext( renameContext.getSession(), dn, getOperationalModsForReplace( + oldDn, newName, subentry, candidate ) ) ); + } } + + subentries.close(); } - else + catch ( Exception e ) { - op = ModificationOperation.ADD_ATTRIBUTE; + throw new LdapOperationException( e.getMessage() ); } - - modList.add( new DefaultModification( op, result ) ); } - - return modList; - } - - /** - * SearchResultFilter used to filter out subentries based on objectClass values. - */ - public class HideSubentriesFilter implements EntryFilter - { - public boolean accept( SearchingOperationContext searchContext, ClonedServerEntry entry ) throws Exception + else { - // see if we can get a match without normalization - if ( subentryCache.hasSubentry( entry.getDn() ) ) + if ( hasAdministrativeDescendant( renameContext, oldDn ) ) { - return false; + String msg = I18n.err( I18n.ERR_308 ); + LOG.warn( msg ); + throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg ); } - // see if we can use objectclass if present - return !entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ); + next.rename( renameContext ); + + // calculate the new DN now for use below to modify subentry operational + // attributes contained within this regular entry with name changes + DN newName = renameContext.getNewDn(); + + List mods = getModsOnEntryRdnChange( oldDn, newName, entry ); + + if ( mods.size() > 0 ) + { + nexus.modify( new ModifyOperationContext( renameContext.getSession(), newName, mods ) ); + } } } + /** - * SearchResultFilter used to filter out normal entries but shows subentries based on - * objectClass values. + * {@inheritDoc} */ - public class HideEntriesFilter implements EntryFilter + public EntryFilteringCursor search( NextInterceptor nextInterceptor, SearchOperationContext searchContext ) + throws LdapException { - public boolean accept( SearchingOperationContext searchContext, ClonedServerEntry entry ) throws Exception + EntryFilteringCursor cursor = nextInterceptor.search( searchContext ); + + // object scope searches by default return subentries + if ( searchContext.getScope() == SearchScope.OBJECT ) { - // see if we can get a match without normalization - if ( subentryCache.hasSubentry( entry.getDn() ) ) - { - return true; - } + return cursor; + } - // see if we can use objectclass if present - return entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ); [... 137 lines stripped ...]