| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| package org.apache.directory.server.core.subtree; |
| |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import javax.naming.directory.SearchControls; |
| |
| import org.apache.directory.api.ldap.codec.controls.search.subentries.SubentriesDecorator; |
| import org.apache.directory.api.ldap.model.constants.SchemaConstants; |
| import org.apache.directory.api.ldap.model.entry.Attribute; |
| import org.apache.directory.api.ldap.model.entry.DefaultAttribute; |
| import org.apache.directory.api.ldap.model.entry.DefaultEntry; |
| import org.apache.directory.api.ldap.model.entry.DefaultModification; |
| import org.apache.directory.api.ldap.model.entry.Entry; |
| import org.apache.directory.api.ldap.model.entry.Modification; |
| import org.apache.directory.api.ldap.model.entry.ModificationOperation; |
| import org.apache.directory.api.ldap.model.entry.StringValue; |
| import org.apache.directory.api.ldap.model.entry.Value; |
| import org.apache.directory.api.ldap.model.exception.LdapException; |
| import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; |
| import org.apache.directory.api.ldap.model.exception.LdapNoSuchAttributeException; |
| import org.apache.directory.api.ldap.model.exception.LdapOperationErrorException; |
| import org.apache.directory.api.ldap.model.exception.LdapOperationException; |
| import org.apache.directory.api.ldap.model.exception.LdapOtherException; |
| import org.apache.directory.api.ldap.model.exception.LdapSchemaViolationException; |
| import org.apache.directory.api.ldap.model.filter.EqualityNode; |
| import org.apache.directory.api.ldap.model.filter.ExprNode; |
| import org.apache.directory.api.ldap.model.filter.ObjectClassNode; |
| import org.apache.directory.api.ldap.model.filter.PresenceNode; |
| import org.apache.directory.api.ldap.model.message.AliasDerefMode; |
| import org.apache.directory.api.ldap.model.message.ResultCodeEnum; |
| import org.apache.directory.api.ldap.model.message.SearchScope; |
| import org.apache.directory.api.ldap.model.message.controls.Subentries; |
| import org.apache.directory.api.ldap.model.name.Dn; |
| import org.apache.directory.api.ldap.model.schema.AttributeType; |
| import org.apache.directory.api.ldap.model.subtree.AdministrativeRole; |
| import org.apache.directory.api.ldap.model.subtree.Subentry; |
| import org.apache.directory.api.ldap.model.subtree.SubtreeSpecification; |
| import org.apache.directory.api.ldap.model.subtree.SubtreeSpecificationParser; |
| import org.apache.directory.server.constants.ApacheSchemaConstants; |
| import org.apache.directory.server.constants.ServerDNConstants; |
| import org.apache.directory.server.core.api.CoreSession; |
| import org.apache.directory.server.core.api.DirectoryService; |
| import org.apache.directory.server.core.api.InterceptorEnum; |
| import org.apache.directory.server.core.api.entry.ClonedServerEntry; |
| import org.apache.directory.server.core.api.filtering.EntryFilter; |
| import org.apache.directory.server.core.api.filtering.EntryFilteringCursor; |
| import org.apache.directory.server.core.api.interceptor.BaseInterceptor; |
| import org.apache.directory.server.core.api.interceptor.context.AddOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.OperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext; |
| import org.apache.directory.server.core.api.partition.PartitionNexus; |
| import org.apache.directory.server.core.api.subtree.SubentryCache; |
| import org.apache.directory.server.core.api.subtree.SubtreeEvaluator; |
| import org.apache.directory.server.i18n.I18n; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| |
| /** |
| * The Subentry interceptor service which is responsible for filtering |
| * out subentries on search operations and injecting operational attributes |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| */ |
| public class SubentryInterceptor extends BaseInterceptor |
| { |
| /** The logger for this class */ |
| private static final Logger LOG = LoggerFactory.getLogger( SubentryInterceptor.class ); |
| |
| /** the subentry control OID */ |
| private static final String SUBENTRY_CONTROL = Subentries.OID; |
| |
| private static Value<String> SUBENTRY_OC; |
| |
| /** The set of Subentry operational attributes */ |
| public static AttributeType[] SUBENTRY_OPATTRS; |
| |
| /** The SubTree specification parser instance */ |
| private SubtreeSpecificationParser ssParser; |
| |
| /** A reference to the nexus for direct backend operations */ |
| private PartitionNexus nexus; |
| |
| /** An enum used for the entries update */ |
| private enum OperationEnum |
| { |
| ADD, |
| REMOVE, |
| REPLACE |
| } |
| |
| |
| /** |
| * Creates a new instance of SubentryInterceptor |
| */ |
| public SubentryInterceptor() |
| { |
| super( InterceptorEnum.SUBENTRY_INTERCEPTOR ); |
| } |
| |
| //------------------------------------------------------------------------------------------- |
| // Search filter methods |
| //------------------------------------------------------------------------------------------- |
| /** |
| * SearchResultFilter used to filter out subentries based on objectClass values. |
| */ |
| private class HideSubentriesFilter implements EntryFilter |
| { |
| /** |
| * {@inheritDoc} |
| */ |
| public boolean accept( SearchOperationContext searchContext, Entry entry ) throws LdapException |
| { |
| // See if the requested entry is a subentry |
| if ( directoryService.getSubentryCache().hasSubentry( entry.getDn() ) ) |
| { |
| return false; |
| } |
| |
| // see if we can use objectclass if present |
| return !entry.contains( OBJECT_CLASS_AT, SUBENTRY_OC ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String toString( String tabs ) |
| { |
| return tabs + "HideSubentriesFilter"; |
| } |
| } |
| |
| /** |
| * SearchResultFilter used to filter out normal entries but shows subentries based on |
| * objectClass values. |
| */ |
| private class HideEntriesFilter implements EntryFilter |
| { |
| /** |
| * {@inheritDoc} |
| */ |
| public boolean accept( SearchOperationContext searchContext, Entry entry ) throws LdapException |
| { |
| // See if the requested entry is a subentry |
| if ( directoryService.getSubentryCache().hasSubentry( entry.getDn() ) ) |
| { |
| return true; |
| } |
| |
| // see if we can use objectclass if present |
| return entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String toString( String tabs ) |
| { |
| return tabs + "HideEntriesFilter"; |
| } |
| } |
| |
| |
| //------------------------------------------------------------------------------------------- |
| // Interceptor initialization |
| //------------------------------------------------------------------------------------------- |
| /** |
| * Initialize the Subentry Interceptor |
| * |
| * @param directoryService The DirectoryService instance |
| */ |
| public void init( DirectoryService directoryService ) throws LdapException |
| { |
| super.init( directoryService ); |
| |
| nexus = directoryService.getPartitionNexus(); |
| |
| SUBENTRY_OPATTRS = new AttributeType[] |
| { |
| ACCESS_CONTROL_SUBENTRIES_AT, |
| SUBSCHEMA_SUBENTRY_AT, |
| COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, |
| TRIGGER_EXECUTION_SUBENTRIES_AT |
| }; |
| |
| ssParser = new SubtreeSpecificationParser( schemaManager ); |
| |
| // prepare to find all subentries in all namingContexts |
| Set<String> suffixes = nexus.listSuffixes(); |
| ExprNode filter = new EqualityNode<String>( OBJECT_CLASS_AT, new StringValue( |
| SchemaConstants.SUBENTRY_OC ) ); |
| SearchControls controls = new SearchControls(); |
| controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); |
| controls.setReturningAttributes( new String[] |
| { SchemaConstants.SUBTREE_SPECIFICATION_AT, SchemaConstants.OBJECT_CLASS_AT } ); |
| |
| Dn adminDn = dnFactory.create( ServerDNConstants.ADMIN_SYSTEM_DN ); |
| SUBENTRY_OC = new StringValue( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ); |
| |
| // search each namingContext for subentries |
| for ( String suffix : suffixes ) |
| { |
| Dn suffixDn = dnFactory.create( suffix ); |
| |
| CoreSession adminSession = directoryService.getAdminSession(); |
| |
| SearchOperationContext searchOperationContext = new SearchOperationContext( adminSession, suffixDn, filter, |
| controls ); |
| searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); |
| |
| EntryFilteringCursor subentries = nexus.search( searchOperationContext ); |
| |
| // Loop on all the found Subentries, parse the SubtreeSpecification |
| // and store the subentry in the subrentry cache |
| try |
| { |
| while ( subentries.next() ) |
| { |
| Entry subentry = subentries.get(); |
| Dn subentryDn = subentry.getDn(); |
| |
| String subtree = subentry.get( SUBTREE_SPECIFICATION_AT ).getString(); |
| SubtreeSpecification ss; |
| |
| try |
| { |
| ss = ssParser.parse( subtree ); |
| } |
| catch ( Exception e ) |
| { |
| LOG.warn( "Failed while parsing subtreeSpecification for " + subentryDn ); |
| continue; |
| } |
| |
| Subentry newSubentry = new Subentry(); |
| |
| newSubentry.setAdministrativeRoles( getSubentryAdminRoles( subentry ) ); |
| newSubentry.setSubtreeSpecification( ss ); |
| |
| directoryService.getSubentryCache().addSubentry( subentryDn, newSubentry ); |
| } |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapOperationException( e.getMessage(), e ); |
| } |
| finally |
| { |
| try |
| { |
| subentries.close(); |
| } |
| catch ( Exception e ) |
| { |
| LOG.error( I18n.err( I18n.ERR_168 ), e ); |
| } |
| } |
| } |
| } |
| |
| |
| //------------------------------------------------------------------------------------------- |
| // Helper methods |
| //------------------------------------------------------------------------------------------- |
| /** |
| * Return the list of AdministrativeRole for a subentry |
| */ |
| private Set<AdministrativeRole> getSubentryAdminRoles( Entry subentry ) throws LdapException |
| { |
| Set<AdministrativeRole> adminRoles = new HashSet<AdministrativeRole>(); |
| |
| Attribute oc = subentry.get( OBJECT_CLASS_AT ); |
| |
| if ( oc == null ) |
| { |
| throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION, I18n.err( I18n.ERR_305 ) ); |
| } |
| |
| if ( oc.contains( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) ) |
| { |
| adminRoles.add( AdministrativeRole.AccessControlInnerArea ); |
| } |
| |
| if ( oc.contains( SchemaConstants.SUBSCHEMA_OC ) ) |
| { |
| adminRoles.add( AdministrativeRole.SubSchemaSpecificArea ); |
| } |
| |
| if ( oc.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC ) ) |
| { |
| adminRoles.add( AdministrativeRole.CollectiveAttributeSpecificArea ); |
| } |
| |
| if ( oc.contains( ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) ) |
| { |
| adminRoles.add( AdministrativeRole.TriggerExecutionInnerArea ); |
| } |
| |
| return adminRoles; |
| } |
| |
| |
| /** |
| * Checks to see if subentries for the search and list operations should be |
| * made visible based on the availability of the search request control |
| * |
| * @param opContext the invocation object to use for determining subentry visibility |
| * @return true if subentries should be visible, false otherwise |
| * @throws Exception if there are problems accessing request controls |
| */ |
| private boolean isSubentryVisible( OperationContext opContext ) throws LdapException |
| { |
| if ( !opContext.hasRequestControls() ) |
| { |
| return false; |
| } |
| |
| // found the subentry request control so we return its value |
| if ( opContext.hasRequestControl( SUBENTRY_CONTROL ) ) |
| { |
| SubentriesDecorator subentriesDecorator = ( SubentriesDecorator ) opContext |
| .getRequestControl( SUBENTRY_CONTROL ); |
| return subentriesDecorator.getDecorated().isVisible(); |
| } |
| |
| return false; |
| } |
| |
| |
| /** |
| * Update all the entries under an AP adding the |
| */ |
| private void updateEntries( OperationEnum operation, CoreSession session, Dn subentryDn, Dn apDn, |
| SubtreeSpecification ss, Dn baseDn, List<Attribute> operationalAttributes ) throws LdapException |
| { |
| ExprNode filter = ObjectClassNode.OBJECT_CLASS_NODE; // (objectClass=*) |
| 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( session, |
| baseDn, filter, controls ); |
| searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); |
| |
| EntryFilteringCursor subentries = nexus.search( searchOperationContext ); |
| |
| try |
| { |
| while ( subentries.next() ) |
| { |
| Entry candidate = subentries.get(); |
| Dn candidateDn = candidate.getDn(); |
| |
| if ( directoryService.getEvaluator().evaluate( ss, apDn, candidateDn, candidate ) ) |
| { |
| List<Modification> modifications = null; |
| |
| switch ( operation ) |
| { |
| case ADD: |
| modifications = getOperationalModsForAdd( candidate, operationalAttributes ); |
| break; |
| |
| case REMOVE: |
| modifications = getOperationalModsForRemove( subentryDn, candidate ); |
| break; |
| |
| /* |
| case REPLACE : |
| modifications = getOperationalModsForReplace( subentryDn, candidate ); |
| break; |
| */ |
| } |
| |
| LOG.debug( "The entry {} has been evaluated to true for subentry {}", candidate.getDn(), subentryDn ); |
| nexus.modify( new ModifyOperationContext( session, candidateDn, modifications ) ); |
| } |
| } |
| |
| subentries.close(); |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapOtherException( e.getMessage(), e ); |
| } |
| finally |
| { |
| try |
| { |
| subentries.close(); |
| } |
| catch ( Exception e ) |
| { |
| LOG.error( I18n.err( I18n.ERR_168 ), e ); |
| } |
| } |
| } |
| |
| |
| /** |
| * Checks if the given Dn is a namingContext |
| */ |
| private boolean isNamingContext( Dn dn ) throws LdapException |
| { |
| Dn namingContext = nexus.getSuffixDn( dn ); |
| |
| return dn.equals( namingContext ); |
| } |
| |
| |
| /** |
| * Get the administrativePoint role |
| */ |
| private void checkAdministrativeRole( OperationContext opContext, Dn apDn ) throws LdapException |
| { |
| CoreSession session = opContext.getSession(); |
| LookupOperationContext lookupContext = new LookupOperationContext( session, apDn, |
| SchemaConstants.ALL_ATTRIBUTES_ARRAY ); |
| |
| Entry administrationPoint = directoryService.getPartitionNexus().lookup( lookupContext ); |
| |
| // The administrativeRole AT must exist and not be null |
| Attribute administrativeRole = administrationPoint.get( ADMINISTRATIVE_ROLE_AT ); |
| |
| // check that administrativeRole has something valid in it for us |
| if ( ( administrativeRole == null ) || ( administrativeRole.size() <= 0 ) ) |
| { |
| LOG.error( "The entry on {} is not an AdministrativePoint", apDn ); |
| 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; |
| |
| 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 ); |
| } |
| |
| |
| /** |
| * 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 ); |
| |
| SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), name, |
| filter, controls ); |
| searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); |
| |
| EntryFilteringCursor aps = nexus.search( searchOperationContext ); |
| |
| try |
| { |
| if ( aps.next() ) |
| { |
| return true; |
| } |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapOperationException( e.getMessage(), e ); |
| } |
| finally |
| { |
| try |
| { |
| aps.close(); |
| } |
| catch ( Exception e ) |
| { |
| LOG.error( I18n.err( I18n.ERR_168 ), e ); |
| } |
| } |
| |
| return false; |
| } |
| |
| |
| private List<Modification> getModsOnEntryRdnChange( Dn oldName, Dn newName, Entry entry ) throws LdapException |
| { |
| List<Modification> modifications = new ArrayList<Modification>(); |
| |
| /* |
| * There are two different situations warranting action. First 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. |
| */ |
| SubentryCache subentryCache = directoryService.getSubentryCache(); |
| SubtreeEvaluator evaluator = directoryService.getEvaluator(); |
| |
| for ( Dn subentryDn : subentryCache ) |
| { |
| 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 ); |
| |
| if ( isOldNameSelected == isNewNameSelected ) |
| { |
| continue; |
| } |
| |
| // need to remove references to the subentry |
| if ( isOldNameSelected && !isNewNameSelected ) |
| { |
| for ( AttributeType operationalAttribute : SUBENTRY_OPATTRS ) |
| { |
| ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE; |
| Attribute opAttr = entry.get( operationalAttribute ); |
| |
| if ( opAttr != null ) |
| { |
| opAttr = opAttr.clone(); |
| opAttr.remove( subentryDn.getNormName() ); |
| |
| if ( opAttr.size() < 1 ) |
| { |
| op = ModificationOperation.REMOVE_ATTRIBUTE; |
| } |
| |
| modifications.add( new DefaultModification( op, opAttr ) ); |
| } |
| } |
| } |
| // need to add references to the subentry |
| else if ( isNewNameSelected && !isOldNameSelected ) |
| { |
| for ( AttributeType operationalAttribute : SUBENTRY_OPATTRS ) |
| { |
| ModificationOperation op = ModificationOperation.ADD_ATTRIBUTE; |
| Attribute opAttr = new DefaultAttribute( operationalAttribute ); |
| opAttr.add( subentryDn.getNormName() ); |
| modifications.add( new DefaultModification( op, opAttr ) ); |
| } |
| } |
| } |
| |
| return modifications; |
| } |
| |
| |
| // ----------------------------------------------------------------------- |
| // Methods dealing with subentry modification |
| // ----------------------------------------------------------------------- |
| |
| private Set<AdministrativeRole> getSubentryTypes( Entry entry, List<Modification> mods ) throws LdapException |
| { |
| Attribute ocFinalState = entry.get( OBJECT_CLASS_AT ).clone(); |
| |
| 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() ) |
| { |
| 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; |
| } |
| } |
| } |
| |
| Entry attrs = new DefaultEntry( schemaManager, Dn.ROOT_DSE ); |
| attrs.put( ocFinalState ); |
| return getSubentryAdminRoles( attrs ); |
| } |
| |
| |
| /** |
| * Update the list of modifications with a modification associated with a specific |
| * role, if it's requested. |
| */ |
| private void getOperationalModForReplace( boolean hasRole, AttributeType attributeType, Entry entry, Dn oldDn, |
| Dn newDn, List<Modification> modifications ) throws LdapInvalidAttributeValueException |
| { |
| String oldDnStr = oldDn.getNormName(); |
| String newDnStr = newDn.getNormName(); |
| |
| if ( hasRole ) |
| { |
| Attribute operational = entry.get( attributeType ).clone(); |
| |
| if ( operational == null ) |
| { |
| operational = new DefaultAttribute( attributeType, newDnStr ); |
| } |
| else |
| { |
| operational.remove( oldDnStr ); |
| operational.add( newDnStr ); |
| } |
| |
| modifications.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) ); |
| } |
| } |
| |
| |
| /** |
| * Get the list of modifications to be applied on an entry to inject the operational attributes |
| * associated with the administrative roles. |
| */ |
| private List<Modification> getOperationalModsForReplace( Dn oldDn, Dn newDn, Subentry subentry, Entry entry ) |
| throws Exception |
| { |
| List<Modification> modifications = new ArrayList<Modification>(); |
| |
| getOperationalModForReplace( subentry.isAccessControlAdminRole(), ACCESS_CONTROL_SUBENTRIES_AT, entry, oldDn, |
| newDn, modifications ); |
| getOperationalModForReplace( subentry.isSchemaAdminRole(), SUBSCHEMA_SUBENTRY_AT, entry, oldDn, newDn, |
| modifications ); |
| getOperationalModForReplace( subentry.isCollectiveAdminRole(), COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, entry, |
| oldDn, newDn, modifications ); |
| getOperationalModForReplace( subentry.isTriggersAdminRole(), TRIGGER_EXECUTION_SUBENTRIES_AT, entry, oldDn, |
| newDn, modifications ); |
| |
| return modifications; |
| } |
| |
| |
| /** |
| * Gets the subschema operational attributes to be added to or removed from |
| * an entry selected by a subentry's subtreeSpecification. |
| */ |
| private List<Attribute> getSubentryOperationalAttributes( Dn dn, Subentry subentry ) throws LdapException |
| { |
| List<Attribute> attributes = new ArrayList<Attribute>(); |
| |
| if ( subentry.isAccessControlAdminRole() ) |
| { |
| Attribute accessControlSubentries = new DefaultAttribute( ACCESS_CONTROL_SUBENTRIES_AT, dn.getNormName() ); |
| attributes.add( accessControlSubentries ); |
| } |
| |
| if ( subentry.isSchemaAdminRole() ) |
| { |
| Attribute subschemaSubentry = new DefaultAttribute( SUBSCHEMA_SUBENTRY_AT, dn.getNormName() ); |
| attributes.add( subschemaSubentry ); |
| } |
| |
| if ( subentry.isCollectiveAdminRole() ) |
| { |
| Attribute collectiveAttributeSubentries = new DefaultAttribute( COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, |
| dn.getNormName() ); |
| attributes.add( collectiveAttributeSubentries ); |
| } |
| |
| if ( subentry.isTriggersAdminRole() ) |
| { |
| Attribute tiggerExecutionSubentries = new DefaultAttribute( TRIGGER_EXECUTION_SUBENTRIES_AT, |
| dn.getNormName() ); |
| attributes.add( tiggerExecutionSubentries ); |
| } |
| |
| return attributes; |
| } |
| |
| |
| /** |
| * 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<Modification> getOperationalModsForRemove( Dn subentryDn, Entry candidate ) throws LdapException |
| { |
| List<Modification> modifications = new ArrayList<Modification>(); |
| String dn = subentryDn.getNormName(); |
| |
| for ( AttributeType operationalAttribute : SUBENTRY_OPATTRS ) |
| { |
| Attribute opAttr = candidate.get( operationalAttribute ); |
| |
| if ( ( opAttr != null ) && opAttr.contains( dn ) ) |
| { |
| Attribute attr = new DefaultAttribute( operationalAttribute, dn ); |
| modifications.add( new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, attr ) ); |
| } |
| } |
| |
| return modifications; |
| } |
| |
| |
| /** |
| * 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<Modification> getOperationalModsForAdd( Entry entry, List<Attribute> operationalAttributes ) |
| throws LdapException |
| { |
| List<Modification> modifications = new ArrayList<Modification>(); |
| |
| for ( Attribute operationalAttribute : operationalAttributes ) |
| { |
| Attribute opAttrInEntry = entry.get( operationalAttribute.getAttributeType() ); |
| |
| if ( ( opAttrInEntry != null ) && ( opAttrInEntry.size() > 0 ) ) |
| { |
| Attribute newOperationalAttribute = operationalAttribute.clone(); |
| |
| for ( Value<?> value : opAttrInEntry ) |
| { |
| newOperationalAttribute.add( value ); |
| } |
| |
| modifications.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, |
| newOperationalAttribute ) ); |
| } |
| else |
| { |
| modifications |
| .add( new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, operationalAttribute ) ); |
| } |
| } |
| |
| return modifications; |
| } |
| |
| |
| /** |
| * Get the list of modification to apply to all the entries |
| */ |
| private List<Modification> getModsOnEntryModification( Dn name, Entry oldEntry, Entry newEntry ) |
| throws LdapException |
| { |
| List<Modification> modList = new ArrayList<Modification>(); |
| |
| for ( Dn subentryDn : directoryService.getSubentryCache() ) |
| { |
| Dn apDn = subentryDn.getParent(); |
| SubtreeSpecification ss = directoryService.getSubentryCache().getSubentry( subentryDn ) |
| .getSubtreeSpecification(); |
| boolean isOldEntrySelected = directoryService.getEvaluator().evaluate( ss, apDn, name, oldEntry ); |
| boolean isNewEntrySelected = directoryService.getEvaluator().evaluate( ss, apDn, name, newEntry ); |
| |
| if ( isOldEntrySelected == isNewEntrySelected ) |
| { |
| continue; |
| } |
| |
| // need to remove references to the subentry |
| if ( isOldEntrySelected && !isNewEntrySelected ) |
| { |
| for ( AttributeType operationalAttribute : SUBENTRY_OPATTRS ) |
| { |
| ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE; |
| Attribute opAttr = oldEntry.get( operationalAttribute ); |
| |
| if ( opAttr != null ) |
| { |
| opAttr = opAttr.clone(); |
| opAttr.remove( subentryDn.getNormName() ); |
| |
| if ( opAttr.size() < 1 ) |
| { |
| op = ModificationOperation.REMOVE_ATTRIBUTE; |
| } |
| |
| modList.add( new DefaultModification( op, opAttr ) ); |
| } |
| } |
| } |
| // need to add references to the subentry |
| else if ( isNewEntrySelected && !isOldEntrySelected ) |
| { |
| for ( AttributeType operationalAttribute : SUBENTRY_OPATTRS ) |
| { |
| ModificationOperation op = ModificationOperation.ADD_ATTRIBUTE; |
| Attribute opAttr = new DefaultAttribute( operationalAttribute ); |
| opAttr.add( subentryDn.getNormName() ); |
| modList.add( new DefaultModification( op, opAttr ) ); |
| } |
| } |
| } |
| |
| return modList; |
| } |
| |
| |
| /** |
| * Update the Operational Attribute with the reference to the subentry |
| */ |
| private void setOperationalAttribute( Entry entry, Dn subentryDn, AttributeType opAttr ) throws LdapException |
| { |
| Attribute operational = entry.get( opAttr ); |
| |
| if ( operational == null ) |
| { |
| operational = new DefaultAttribute( opAttr ); |
| entry.put( operational ); |
| } |
| |
| operational.add( subentryDn.getNormName() ); |
| } |
| |
| |
| //------------------------------------------------------------------------------------------- |
| // Interceptor API methods |
| //------------------------------------------------------------------------------------------- |
| /** |
| * {@inheritDoc} |
| */ |
| public void add( AddOperationContext addContext ) throws LdapException |
| { |
| Dn dn = addContext.getDn(); |
| Entry entry = addContext.getEntry(); |
| |
| // Check if the added entry is a subentry |
| if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) |
| { |
| // 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 ) ) |
| { |
| // Not allowed : we can't get a parent in those cases |
| throw new LdapOtherException( "Cannot find an AdministrativePoint for " + dn ); |
| } |
| |
| // Get the administrativePoint role : we must have one immediately |
| // upper |
| Dn apDn = dn.getParent(); |
| checkAdministrativeRole( addContext, apDn ); |
| |
| /* ---------------------------------------------------------------- |
| * 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<Attribute> operationalAttributes = getSubentryOperationalAttributes( dn, 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. |
| * ---------------------------------------------------------------- |
| */ |
| setSubtreeSpecification( subentry, entry ); |
| directoryService.getSubentryCache().addSubentry( dn, subentry ); |
| |
| // Now inject the subentry into the backend |
| next( 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 = apDn; |
| baseDn = baseDn.add( subentry.getSubtreeSpecification().getBase() ); |
| |
| updateEntries( OperationEnum.ADD, addContext.getSession(), dn, apDn, subentry.getSubtreeSpecification(), |
| baseDn, operationalAttributes ); |
| |
| // Store the newly modified entry into the context for later use in interceptor |
| // just in case |
| 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 some SubtreeSpecification |
| // We brutally check *all* the subentries, as we don't hold a hierarchy |
| // of AP |
| // TODO : add a hierarchy of subentries |
| for ( Dn subentryDn : directoryService.getSubentryCache() ) |
| { |
| Dn apDn = subentryDn.getParent(); |
| |
| // No need to evaluate the entry if it's not below an AP. |
| if ( dn.isDescendantOf( apDn ) ) |
| { |
| Subentry subentry = directoryService.getSubentryCache().getSubentry( subentryDn ); |
| SubtreeSpecification ss = subentry.getSubtreeSpecification(); |
| |
| // Now, evaluate the entry wrt the subentry ss |
| // and inject a ref to the subentry if it evaluates to true |
| if ( directoryService.getEvaluator().evaluate( ss, apDn, dn, entry ) ) |
| { |
| |
| if ( subentry.isAccessControlAdminRole() ) |
| { |
| setOperationalAttribute( entry, subentryDn, ACCESS_CONTROL_SUBENTRIES_AT ); |
| } |
| |
| if ( subentry.isSchemaAdminRole() ) |
| { |
| setOperationalAttribute( entry, subentryDn, SUBSCHEMA_SUBENTRY_AT ); |
| } |
| |
| if ( subentry.isCollectiveAdminRole() ) |
| { |
| setOperationalAttribute( entry, subentryDn, COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ); |
| } |
| |
| if ( subentry.isTriggersAdminRole() ) |
| { |
| setOperationalAttribute( entry, subentryDn, TRIGGER_EXECUTION_SUBENTRIES_AT ); |
| } |
| } |
| } |
| } |
| |
| // Now that the entry has been updated with the operational attributes, |
| // we can update it into the add context |
| addContext.setEntry( entry ); |
| |
| // Propagate the addition down to the backend. |
| next( addContext ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void delete( DeleteOperationContext deleteContext ) throws LdapException |
| { |
| Dn dn = deleteContext.getDn(); |
| Entry entry = deleteContext.getEntry(); |
| |
| // If the entry has a "subentry" Objectclass, we can process the entry. |
| // We first remove the re |
| if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) |
| { |
| Subentry removedSubentry = directoryService.getSubentryCache().getSubentry( dn ); |
| |
| /* ---------------------------------------------------------------- |
| * 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 apDn = dn.getParent(); |
| Dn baseDn = apDn; |
| baseDn = baseDn.add( removedSubentry.getSubtreeSpecification().getBase() ); |
| |
| // Remove all the references to this removed subentry from all the selected entries |
| updateEntries( OperationEnum.REMOVE, deleteContext.getSession(), dn, apDn, |
| removedSubentry.getSubtreeSpecification(), baseDn, null ); |
| |
| // Update the cache |
| directoryService.getSubentryCache().removeSubentry( dn ); |
| |
| // Now delete the subentry itself |
| next( deleteContext ); |
| } |
| else |
| { |
| // TODO : deal with AP removal. |
| next( deleteContext ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void modify( ModifyOperationContext modifyContext ) throws LdapException |
| { |
| Dn dn = modifyContext.getDn(); |
| List<Modification> modifications = modifyContext.getModItems(); |
| |
| Entry entry = modifyContext.getEntry(); |
| |
| // We have three types of modifications : |
| // 1) A modification applied on a normal entry |
| // 2) A modification done on a subentry (the entry will have a 'subentry' ObjectClass) |
| // 3) A modification on a normal entry on whch we add a 'subentry' ObjectClass |
| // The third case is a transformation of a normal entry to a subentry. Not sure if it's |
| // legal ... |
| |
| boolean isSubtreeSpecificationModification = false; |
| Modification subtreeMod = null; |
| |
| // Find the subtreeSpecification |
| for ( Modification mod : modifications ) |
| { |
| if ( mod.getAttribute().getAttributeType().equals( SUBTREE_SPECIFICATION_AT ) ) |
| { |
| isSubtreeSpecificationModification = true; |
| subtreeMod = mod; |
| break; |
| } |
| } |
| |
| boolean containsSubentryOC = entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ); |
| |
| // Check if we have a modified subentry attribute in a Subentry entry |
| if ( containsSubentryOC && isSubtreeSpecificationModification ) |
| { |
| Subentry subentry = directoryService.getSubentryCache().removeSubentry( dn ); |
| SubtreeSpecification ssOld = subentry.getSubtreeSpecification(); |
| SubtreeSpecification ssNew; |
| |
| try |
| { |
| ssNew = ssParser.parse( subtreeMod.getAttribute().getString() ); |
| } |
| catch ( Exception e ) |
| { |
| String msg = I18n.err( I18n.ERR_71 ); |
| LOG.error( msg, e ); |
| throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg ); |
| } |
| |
| subentry.setSubtreeSpecification( ssNew ); |
| subentry.setAdministrativeRoles( getSubentryTypes( entry, modifications ) ); |
| directoryService.getSubentryCache().addSubentry( dn, subentry ); |
| |
| next( modifyContext ); |
| |
| // search for all entries selected by the old SS and remove references to subentry |
| Dn apName = dn.getParent(); |
| Dn oldBaseDn = apName; |
| oldBaseDn = oldBaseDn.add( ssOld.getBase() ); |
| |
| 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( modifyContext.getSession(), |
| oldBaseDn, filter, controls ); |
| searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); |
| |
| EntryFilteringCursor subentries = nexus.search( searchOperationContext ); |
| |
| try |
| { |
| while ( subentries.next() ) |
| { |
| Entry candidate = subentries.get(); |
| Dn candidateDn = candidate.getDn(); |
| |
| if ( directoryService.getEvaluator().evaluate( ssOld, apName, candidateDn, candidate ) ) |
| { |
| nexus.modify( new ModifyOperationContext( modifyContext.getSession(), candidateDn, |
| getOperationalModsForRemove( dn, candidate ) ) ); |
| } |
| } |
| |
| subentries.close(); |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapOperationErrorException( e.getMessage(), e ); |
| } |
| finally |
| { |
| try |
| { |
| subentries.close(); |
| } |
| catch ( Exception e ) |
| { |
| LOG.error( I18n.err( I18n.ERR_168 ), e ); |
| } |
| } |
| |
| // search for all selected entries by the new SS and add references to subentry |
| subentry = directoryService.getSubentryCache().getSubentry( dn ); |
| List<Attribute> operationalAttributes = getSubentryOperationalAttributes( dn, subentry ); |
| Dn newBaseDn = apName; |
| newBaseDn = newBaseDn.add( ssNew.getBase() ); |
| |
| searchOperationContext = new SearchOperationContext( modifyContext.getSession(), newBaseDn, filter, |
| controls ); |
| searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); |
| |
| subentries = nexus.search( searchOperationContext ); |
| |
| try |
| { |
| while ( subentries.next() ) |
| { |
| Entry candidate = subentries.get(); |
| Dn candidateDn = candidate.getDn(); |
| |
| if ( directoryService.getEvaluator().evaluate( ssNew, apName, candidateDn, candidate ) ) |
| { |
| nexus.modify( new ModifyOperationContext( modifyContext.getSession(), candidateDn, |
| getOperationalModsForAdd( candidate, operationalAttributes ) ) ); |
| } |
| } |
| subentries.close(); |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapOperationErrorException( e.getMessage(), e ); |
| } |
| finally |
| { |
| try |
| { |
| subentries.close(); |
| } |
| catch ( Exception e ) |
| { |
| LOG.error( I18n.err( I18n.ERR_168 ), e ); |
| } |
| } |
| } |
| else |
| { |
| next( modifyContext ); |
| |
| if ( !containsSubentryOC ) |
| { |
| Entry newEntry = modifyContext.getAlteredEntry(); |
| |
| List<Modification> subentriesOpAttrMods = getModsOnEntryModification( dn, entry, newEntry ); |
| |
| if ( subentriesOpAttrMods.size() > 0 ) |
| { |
| nexus.modify( new ModifyOperationContext( modifyContext.getSession(), dn, subentriesOpAttrMods ) ); |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * The Move operation for a Subentry will deal with different cases : |
| * 1) we move a normal entry |
| * 2) we move a subentry |
| * 3) we move an administrationPoint |
| * <p> |
| * <u>Case 1 :</u><br> |
| * A normal entry (ie, not a subentry or an AP) may be part of some administrative areas. |
| * We have to remove the references to the associated areas if the entry gets out of them.<br> |
| * This entry can also be moved to some other administrative area, and it should then be |
| * updated to point to the associated subentries. |
| * <br><br> |
| * There is one preliminary condition : If the entry has a descendant which is an |
| * Administrative Point, then the move cannot be done. |
| * <br><br> |
| * <u>Case 2 :</u><br> |
| * The subentry has to be moved under a new AP, otherwise this is an error. Once moved, |
| * we have to update all the entries selected by the old subtreeSpecification, removing |
| * the references to the subentry from all the selected entry, and update the entries |
| * selected by the new subtreeSpecification by adding a reference to the subentry into them. |
| * <br><br> |
| * <u>Case 3 :</u><br> |
| * |
| * |
| * @param next The next interceptor in the chain |
| * @param moveContext The context containing all the needed informations to proceed |
| * @throws LdapException If the move failed |
| */ |
| public void move( MoveOperationContext moveContext ) throws LdapException |
| { |
| Dn oldDn = moveContext.getDn(); |
| Dn newSuperiorDn = moveContext.getNewSuperior(); |
| |
| Entry entry = moveContext.getOriginalEntry(); |
| |
| if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) |
| { |
| // This is a subentry. Moving a subentry means we have to: |
| // o Check that there is a new AP where we move the subentry |
| // o Remove the op Attr from all the entry selected by the subentry |
| // o Add the op Attr in all the selected entry by the subentry |
| |
| // If we move it, we have to check that |
| // the new parent is an AP |
| checkAdministrativeRole( moveContext, newSuperiorDn ); |
| |
| Subentry subentry = directoryService.getSubentryCache().removeSubentry( oldDn ); |
| SubtreeSpecification ss = subentry.getSubtreeSpecification(); |
| Dn apName = oldDn.getParent(); |
| Dn baseDn = apName; |
| baseDn = baseDn.add( ss.getBase() ); |
| Dn newName = newSuperiorDn; |
| newName = newName.add( oldDn.getRdn() ); |
| newName.apply( schemaManager ); |
| |
| directoryService.getSubentryCache().addSubentry( newName, subentry ); |
| |
| next( moveContext ); |
| |
| subentry = directoryService.getSubentryCache().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( moveContext.getSession(), |
| baseDn, |
| filter, controls ); |
| searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); |
| |
| EntryFilteringCursor subentries = nexus.search( searchOperationContext ); |
| |
| try |
| { |
| // Modify all the entries under this subentry |
| while ( subentries.next() ) |
| { |
| Entry candidate = subentries.get(); |
| Dn dn = candidate.getDn(); |
| dn.apply( schemaManager ); |
| |
| if ( directoryService.getEvaluator().evaluate( ss, apName, dn, candidate ) ) |
| { |
| nexus.modify( new ModifyOperationContext( moveContext.getSession(), dn, |
| getOperationalModsForReplace( |
| oldDn, newName, subentry, candidate ) ) ); |
| } |
| } |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapOperationException( e.getMessage(), e ); |
| } |
| finally |
| { |
| try |
| { |
| subentries.close(); |
| } |
| catch ( Exception e ) |
| { |
| LOG.error( I18n.err( I18n.ERR_168 ), e ); |
| } |
| } |
| } |
| else |
| { |
| // A normal entry. It may be part of a SubtreeSpecifciation. In this |
| // case, we have to update the opAttrs (removing old ones and adding the |
| // new ones) |
| |
| // First, an moved entry which has an AP in one of its descendant |
| // can't be moved. |
| if ( hasAdministrativeDescendant( moveContext, oldDn ) ) |
| { |
| String msg = I18n.err( I18n.ERR_308 ); |
| LOG.warn( msg ); |
| throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg ); |
| } |
| |
| // Move the entry |
| next( moveContext ); |
| |
| // calculate the new Dn now for use below to modify subentry operational |
| // attributes contained within this regular entry with name changes |
| Dn newDn = moveContext.getNewDn(); |
| List<Modification> mods = getModsOnEntryRdnChange( oldDn, newDn, entry ); |
| |
| // Update the entry operational attributes |
| if ( mods.size() > 0 ) |
| { |
| nexus.modify( new ModifyOperationContext( moveContext.getSession(), newDn, mods ) ); |
| } |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void moveAndRename( MoveAndRenameOperationContext moveAndRenameContext ) throws LdapException |
| { |
| Dn oldDn = moveAndRenameContext.getDn(); |
| Dn newSuperiorDn = moveAndRenameContext.getNewSuperiorDn(); |
| |
| Entry entry = moveAndRenameContext.getOriginalEntry(); |
| |
| if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) |
| { |
| Subentry subentry = directoryService.getSubentryCache().removeSubentry( oldDn ); |
| SubtreeSpecification ss = subentry.getSubtreeSpecification(); |
| Dn apName = oldDn.getParent(); |
| Dn baseDn = apName; |
| baseDn = baseDn.add( ss.getBase() ); |
| Dn newName = newSuperiorDn.getParent(); |
| |
| newName = newName.add( moveAndRenameContext.getNewRdn() ); |
| newName.apply( schemaManager ); |
| |
| directoryService.getSubentryCache().addSubentry( newName, subentry ); |
| |
| next( moveAndRenameContext ); |
| |
| subentry = directoryService.getSubentryCache().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 |
| { |
| while ( subentries.next() ) |
| { |
| Entry candidate = subentries.get(); |
| Dn dn = candidate.getDn(); |
| dn.apply( schemaManager ); |
| |
| if ( directoryService.getEvaluator().evaluate( ss, apName, dn, candidate ) ) |
| { |
| nexus.modify( new ModifyOperationContext( moveAndRenameContext.getSession(), dn, |
| getOperationalModsForReplace( |
| oldDn, newName, subentry, candidate ) ) ); |
| } |
| } |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapOperationException( e.getMessage(), e ); |
| } |
| finally |
| { |
| try |
| { |
| subentries.close(); |
| } |
| catch ( Exception e ) |
| { |
| LOG.error( I18n.err( I18n.ERR_168 ), e ); |
| } |
| } |
| } |
| 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( 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<Modification> mods = getModsOnEntryRdnChange( oldDn, newDn, entry ); |
| |
| if ( mods.size() > 0 ) |
| { |
| nexus.modify( new ModifyOperationContext( moveAndRenameContext.getSession(), newDn, mods ) ); |
| } |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void rename( RenameOperationContext renameContext ) throws LdapException |
| { |
| Dn oldDn = renameContext.getDn(); |
| |
| Entry entry = ( ( ClonedServerEntry ) renameContext.getEntry() ).getClonedEntry(); |
| |
| if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) |
| { |
| // @Todo To be reviewed !!! |
| Subentry subentry = directoryService.getSubentryCache().removeSubentry( oldDn ); |
| SubtreeSpecification ss = subentry.getSubtreeSpecification(); |
| Dn apName = oldDn.getParent(); |
| Dn baseDn = apName; |
| baseDn = baseDn.add( ss.getBase() ); |
| Dn newName = oldDn.getParent(); |
| |
| newName = newName.add( renameContext.getNewRdn() ); |
| newName.apply( schemaManager ); |
| |
| directoryService.getSubentryCache().addSubentry( newName, subentry ); |
| next( renameContext ); |
| |
| subentry = directoryService.getSubentryCache().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.apply( schemaManager ); |
| |
| if ( directoryService.getEvaluator().evaluate( ss, apName, dn, candidate ) ) |
| { |
| nexus.modify( new ModifyOperationContext( renameContext.getSession(), dn, |
| getOperationalModsForReplace( |
| oldDn, newName, subentry, candidate ) ) ); |
| } |
| } |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapOperationException( e.getMessage(), e ); |
| } |
| finally |
| { |
| try |
| { |
| subentries.close(); |
| } |
| catch ( Exception e ) |
| { |
| LOG.error( I18n.err( I18n.ERR_168 ), e ); |
| } |
| } |
| } |
| else |
| { |
| if ( hasAdministrativeDescendant( renameContext, oldDn ) ) |
| { |
| String msg = I18n.err( I18n.ERR_308 ); |
| LOG.warn( msg ); |
| throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg ); |
| } |
| |
| next( 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<Modification> mods = getModsOnEntryRdnChange( oldDn, newName, entry ); |
| |
| if ( mods.size() > 0 ) |
| { |
| nexus.modify( new ModifyOperationContext( renameContext.getSession(), newName, mods ) ); |
| } |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public EntryFilteringCursor search( SearchOperationContext searchContext ) throws LdapException |
| { |
| EntryFilteringCursor cursor = next( searchContext ); |
| |
| // object scope searches by default return subentries |
| if ( searchContext.getScope() == SearchScope.OBJECT ) |
| { |
| return cursor; |
| } |
| |
| // DO NOT hide subentries for replication operations |
| if ( searchContext.isSyncreplSearch() ) |
| { |
| return cursor; |
| } |
| |
| // for subtree and one level scope we filter |
| if ( !isSubentryVisible( searchContext ) ) |
| { |
| cursor.addEntryFilter( new HideSubentriesFilter() ); |
| } |
| else |
| { |
| cursor.addEntryFilter( new HideEntriesFilter() ); |
| } |
| |
| return cursor; |
| } |
| } |