| /* |
| * 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.text.ParseException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.locks.ReentrantReadWriteLock; |
| |
| import javax.naming.directory.SearchControls; |
| |
| import org.apache.directory.server.constants.ApacheSchemaConstants; |
| import org.apache.directory.server.constants.ServerDNConstants; |
| import org.apache.directory.server.core.CoreSession; |
| import org.apache.directory.server.core.DefaultCoreSession; |
| import org.apache.directory.server.core.DirectoryService; |
| import org.apache.directory.server.core.LdapPrincipal; |
| import org.apache.directory.server.core.admin.AdministrativePointInterceptor; |
| import org.apache.directory.server.core.administrative.AccessControlAAP; |
| import org.apache.directory.server.core.administrative.AccessControlAdministrativePoint; |
| import org.apache.directory.server.core.administrative.AccessControlIAP; |
| import org.apache.directory.server.core.administrative.AccessControlSAP; |
| import org.apache.directory.server.core.administrative.AccessControlSubentry; |
| import org.apache.directory.server.core.administrative.AdministrativePoint; |
| import org.apache.directory.server.core.administrative.AdministrativeRoleEnum; |
| import org.apache.directory.server.core.administrative.CollectiveAttributeAAP; |
| import org.apache.directory.server.core.administrative.CollectiveAttributeAdministrativePoint; |
| import org.apache.directory.server.core.administrative.CollectiveAttributeIAP; |
| import org.apache.directory.server.core.administrative.CollectiveAttributeSAP; |
| import org.apache.directory.server.core.administrative.CollectiveAttributeSubentry; |
| import org.apache.directory.server.core.administrative.SubSchemaSubentry; |
| import org.apache.directory.server.core.administrative.Subentry; |
| import org.apache.directory.server.core.administrative.SubentryCache; |
| import org.apache.directory.server.core.administrative.SubschemaAAP; |
| import org.apache.directory.server.core.administrative.SubschemaAdministrativePoint; |
| import org.apache.directory.server.core.administrative.SubschemaSAP; |
| import org.apache.directory.server.core.administrative.TriggerExecutionAAP; |
| import org.apache.directory.server.core.administrative.TriggerExecutionAdministrativePoint; |
| import org.apache.directory.server.core.administrative.TriggerExecutionIAP; |
| import org.apache.directory.server.core.administrative.TriggerExecutionSAP; |
| import org.apache.directory.server.core.administrative.TriggerExecutionSubentry; |
| import org.apache.directory.server.core.authn.AuthenticationInterceptor; |
| import org.apache.directory.server.core.authz.AciAuthorizationInterceptor; |
| import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor; |
| import org.apache.directory.server.core.collective.CollectiveAttributeInterceptor; |
| import org.apache.directory.server.core.entry.ClonedServerEntry; |
| import org.apache.directory.server.core.event.EventInterceptor; |
| import org.apache.directory.server.core.exception.ExceptionInterceptor; |
| import org.apache.directory.server.core.filtering.EntryFilter; |
| import org.apache.directory.server.core.filtering.EntryFilteringCursor; |
| import org.apache.directory.server.core.interceptor.BaseInterceptor; |
| import org.apache.directory.server.core.interceptor.Interceptor; |
| import org.apache.directory.server.core.interceptor.NextInterceptor; |
| import org.apache.directory.server.core.interceptor.context.AddOperationContext; |
| import org.apache.directory.server.core.interceptor.context.DeleteOperationContext; |
| import org.apache.directory.server.core.interceptor.context.ListOperationContext; |
| import org.apache.directory.server.core.interceptor.context.LookupOperationContext; |
| import org.apache.directory.server.core.interceptor.context.ModifyOperationContext; |
| import org.apache.directory.server.core.interceptor.context.OperationContext; |
| import org.apache.directory.server.core.interceptor.context.RenameOperationContext; |
| import org.apache.directory.server.core.interceptor.context.SearchOperationContext; |
| import org.apache.directory.server.core.interceptor.context.SearchingOperationContext; |
| import org.apache.directory.server.core.normalization.NormalizationInterceptor; |
| import org.apache.directory.server.core.operational.OperationalAttributeInterceptor; |
| import org.apache.directory.server.core.partition.PartitionNexus; |
| import org.apache.directory.server.core.schema.SchemaInterceptor; |
| import org.apache.directory.server.core.trigger.TriggerInterceptor; |
| import org.apache.directory.server.i18n.I18n; |
| import org.apache.directory.shared.ldap.aci.ACIItem; |
| import org.apache.directory.shared.ldap.aci.ACIItemParser; |
| import org.apache.directory.shared.ldap.codec.search.controls.subentries.SubentriesControl; |
| import org.apache.directory.shared.ldap.constants.AuthenticationLevel; |
| import org.apache.directory.shared.ldap.constants.SchemaConstants; |
| import org.apache.directory.shared.ldap.entry.DefaultEntry; |
| import org.apache.directory.shared.ldap.entry.DefaultEntryAttribute; |
| import org.apache.directory.shared.ldap.entry.DefaultModification; |
| import org.apache.directory.shared.ldap.entry.Entry; |
| import org.apache.directory.shared.ldap.entry.EntryAttribute; |
| import org.apache.directory.shared.ldap.entry.Modification; |
| import org.apache.directory.shared.ldap.entry.ModificationOperation; |
| import org.apache.directory.shared.ldap.entry.StringValue; |
| import org.apache.directory.shared.ldap.entry.Value; |
| import org.apache.directory.shared.ldap.exception.LdapException; |
| import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException; |
| import org.apache.directory.shared.ldap.exception.LdapOperationException; |
| import org.apache.directory.shared.ldap.exception.LdapUnwillingToPerformException; |
| import org.apache.directory.shared.ldap.filter.EqualityNode; |
| import org.apache.directory.shared.ldap.filter.ExprNode; |
| import org.apache.directory.shared.ldap.filter.PresenceNode; |
| import org.apache.directory.shared.ldap.filter.SearchScope; |
| import org.apache.directory.shared.ldap.message.AliasDerefMode; |
| import org.apache.directory.shared.ldap.message.ResultCodeEnum; |
| import org.apache.directory.shared.ldap.name.DN; |
| import org.apache.directory.shared.ldap.name.RDN; |
| import org.apache.directory.shared.ldap.schema.AttributeType; |
| import org.apache.directory.shared.ldap.schema.NormalizerMappingResolver; |
| import org.apache.directory.shared.ldap.schema.SchemaManager; |
| import org.apache.directory.shared.ldap.schema.normalizers.ConcreteNameComponentNormalizer; |
| import org.apache.directory.shared.ldap.schema.normalizers.OidNormalizer; |
| import org.apache.directory.shared.ldap.subtree.AdministrativeRole; |
| import org.apache.directory.shared.ldap.subtree.SubtreeSpecification; |
| import org.apache.directory.shared.ldap.subtree.SubtreeSpecificationParser; |
| import org.apache.directory.shared.ldap.trigger.TriggerSpecification; |
| import org.apache.directory.shared.ldap.trigger.TriggerSpecificationParser; |
| import org.apache.directory.shared.ldap.util.StringTools; |
| import org.apache.directory.shared.ldap.util.tree.DnNode; |
| 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 = SubentriesControl.CONTROL_OID; |
| |
| /** The set of Subentry operational attributes */ |
| public static AttributeType[] SUBENTRY_OPATTRS; |
| |
| /** the hash mapping the DN of a subentry to its SubtreeSpecification/types */ |
| private final SubentryCache subentryCache = new SubentryCache(); |
| |
| /** The SubTree specification parser instance */ |
| private SubtreeSpecificationParser ssParser; |
| |
| /** The Subtree evaluator instance */ |
| private SubtreeEvaluator evaluator; |
| |
| /** a normalizing Trigger Specification parser */ |
| private TriggerSpecificationParser triggerParser; |
| |
| /** a normalizing ACIItem parser */ |
| private ACIItemParser aciParser; |
| |
| /** A reference to the nexus for direct backend operations */ |
| private PartitionNexus nexus; |
| |
| /** The SchemManager instance */ |
| private SchemaManager schemaManager; |
| |
| /** A reference to the ObjectClass AT */ |
| private static AttributeType OBJECT_CLASS_AT; |
| |
| /** A reference to the CN AT */ |
| private static AttributeType CN_AT; |
| |
| /** A reference to the EntryUUID AT */ |
| private static AttributeType ENTRY_UUID_AT; |
| |
| /** A reference to the AdministrativeRole AT */ |
| private static AttributeType ADMINISTRATIVE_ROLE_AT; |
| |
| /** A reference to the SubtreeSpecification AT */ |
| private static AttributeType SUBTREE_SPECIFICATION_AT; |
| |
| /** A reference to the AccessControl dedicated AT */ |
| private static AttributeType ACCESS_CONTROL_SUBENTRIES_AT; |
| private static AttributeType ACCESS_CONTROL_SEQ_NUMBER_AT; |
| private static AttributeType ACCESS_CONTROL_SUBENTRIES_UUID_AT; |
| |
| /** A reference to the CollectiveAttribute dedicated AT */ |
| private static AttributeType COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT; |
| private static AttributeType COLLECTIVE_ATTRIBUTE_SEQ_NUMBER_AT; |
| private static AttributeType COLLECTIVE_ATTRIBUTE_SUBENTRIES_UUID_AT; |
| |
| /** A reference to the Subschema dedicated AT */ |
| private static AttributeType SUBSCHEMA_SUBENTRY_AT; |
| private static AttributeType SUBSCHEMA_SUBENTRY_UUID_AT; |
| private static AttributeType SUB_SCHEMA_SEQ_NUMBER_AT; |
| |
| /** A reference to the TriggerExecution dedicated AT */ |
| private static AttributeType TRIGGER_EXECUTION_SUBENTRIES_AT; |
| private static AttributeType TRIGGER_EXECUTION_SEQ_NUMBER_AT; |
| private static AttributeType TRIGGER_EXECUTION_SUBENTRIES_UUID_AT; |
| |
| /** An enum used for the entries update */ |
| private enum OperationEnum |
| { |
| ADD, |
| REMOVE, |
| REPLACE |
| } |
| |
| /** The possible roles */ |
| private static final Set<String> ROLES = new HashSet<String>(); |
| |
| // Initialize the ROLES field |
| static |
| { |
| ROLES.add( SchemaConstants.AUTONOMOUS_AREA.toLowerCase() ); |
| ROLES.add( SchemaConstants.AUTONOMOUS_AREA_OID ); |
| ROLES.add( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA.toLowerCase() ); |
| ROLES.add( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID ); |
| ROLES.add( SchemaConstants.ACCESS_CONTROL_INNER_AREA.toLowerCase() ); |
| ROLES.add( SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID ); |
| ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA.toLowerCase() ); |
| ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID ); |
| ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA.toLowerCase() ); |
| ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID ); |
| ROLES.add( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA.toLowerCase() ); |
| ROLES.add( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID ); |
| ROLES.add( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA.toLowerCase() ); |
| ROLES.add( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID ); |
| ROLES.add( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA.toLowerCase() ); |
| ROLES.add( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID ); |
| } |
| |
| /** A Map to associate a role with it's OID */ |
| private static final Map<String, String> ROLES_OID = new HashMap<String, String>(); |
| |
| // Initialize the roles/oid map |
| static |
| { |
| ROLES_OID.put( SchemaConstants.AUTONOMOUS_AREA.toLowerCase(), SchemaConstants.AUTONOMOUS_AREA_OID ); |
| ROLES_OID.put( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA.toLowerCase(), |
| SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID ); |
| ROLES_OID.put( SchemaConstants.ACCESS_CONTROL_INNER_AREA.toLowerCase(), |
| SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID ); |
| ROLES_OID.put( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA.toLowerCase(), |
| SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID ); |
| ROLES_OID.put( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA.toLowerCase(), |
| SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID ); |
| ROLES_OID.put( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA.toLowerCase(), |
| SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID ); |
| ROLES_OID.put( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA.toLowerCase(), |
| SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID ); |
| ROLES_OID.put( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA.toLowerCase(), |
| SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID ); |
| } |
| |
| /** The possible inner area roles */ |
| private static final Set<String> INNER_AREA_ROLES = new HashSet<String>(); |
| |
| static |
| { |
| INNER_AREA_ROLES.add( SchemaConstants.ACCESS_CONTROL_INNER_AREA.toLowerCase() ); |
| INNER_AREA_ROLES.add( SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID ); |
| INNER_AREA_ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA.toLowerCase() ); |
| INNER_AREA_ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID ); |
| INNER_AREA_ROLES.add( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA.toLowerCase() ); |
| INNER_AREA_ROLES.add( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID ); |
| } |
| |
| /** The possible specific area roles */ |
| private static final Set<String> SPECIFIC_AREA_ROLES = new HashSet<String>(); |
| |
| static |
| { |
| SPECIFIC_AREA_ROLES.add( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA.toLowerCase() ); |
| SPECIFIC_AREA_ROLES.add( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID ); |
| SPECIFIC_AREA_ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA.toLowerCase() ); |
| SPECIFIC_AREA_ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID ); |
| SPECIFIC_AREA_ROLES.add( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA.toLowerCase() ); |
| SPECIFIC_AREA_ROLES.add( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID ); |
| SPECIFIC_AREA_ROLES.add( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA.toLowerCase() ); |
| SPECIFIC_AREA_ROLES.add( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID ); |
| } |
| |
| /** A lock to guarantee the AP cache consistency */ |
| private ReentrantReadWriteLock mutex = new ReentrantReadWriteLock(); |
| |
| /** |
| * the set of interceptors we should *not* go through updating some operational attributes |
| */ |
| private static final Collection<String> BYPASS_INTERCEPTORS; |
| |
| static |
| { |
| Set<String> c = new HashSet<String>(); |
| c.add( NormalizationInterceptor.class.getName() ); |
| c.add( AuthenticationInterceptor.class.getName() ); |
| c.add( AciAuthorizationInterceptor.class.getName() ); |
| c.add( AdministrativePointInterceptor.class.getName() ); |
| c.add( DefaultAuthorizationInterceptor.class.getName() ); |
| c.add( ExceptionInterceptor.class.getName() ); |
| c.add( OperationalAttributeInterceptor.class.getName() ); |
| c.add( SchemaInterceptor.class.getName() ); |
| c.add( CollectiveAttributeInterceptor.class.getName() ); |
| c.add( SubentryInterceptor.class.getName() ); |
| c.add( EventInterceptor.class.getName() ); |
| c.add( TriggerInterceptor.class.getName() ); |
| BYPASS_INTERCEPTORS = Collections.unmodifiableCollection( c ); |
| } |
| |
| //------------------------------------------------------------------------------------------- |
| // Search filter methods |
| //------------------------------------------------------------------------------------------- |
| /** |
| * SearchResultFilter used to filter out subentries based on objectClass values. |
| * A subentry won't be returned if the request is done with a ONE_LEVEL or SUB_LEVEL |
| * scope. |
| */ |
| public class HideSubentriesFilter implements EntryFilter |
| { |
| public boolean accept( SearchingOperationContext searchContext, ClonedServerEntry entry ) throws Exception |
| { |
| // See if the requested entry is a subentry |
| 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 |
| return entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ); |
| } |
| } |
| |
| |
| /** |
| * Filter the returned entries, checking if they depend on some APs, and if so, |
| * update their SeqNumber if it's not uptodate. |
| */ |
| public class SeqNumberUpdateFilter implements EntryFilter |
| { |
| public boolean accept( SearchingOperationContext searchContext, ClonedServerEntry entry ) throws Exception |
| { |
| if ( entry.getOriginalEntry().containsAttribute( ADMINISTRATIVE_ROLE_AT ) || |
| entry.getOriginalEntry().contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) |
| { |
| // No need to update anything |
| return true; |
| } |
| |
| List<Modification> modifications = updateEntry( entry ); |
| |
| if ( modifications != null ) |
| { |
| // The entry has been updated, we have to apply the modifications in the entry |
| applyModifications( entry, modifications ); |
| } |
| |
| return true; |
| } |
| } |
| |
| |
| /** |
| * Apply the modifications done on an entry. We should just have REPLACE operations |
| * @param entry |
| * @param modifications |
| * @throws LdapException |
| */ |
| private void applyModifications( Entry entry, List<Modification> modifications ) throws LdapException |
| { |
| for ( Modification modification : modifications ) |
| { |
| switch ( modification.getOperation() ) |
| { |
| case ADD_ATTRIBUTE : |
| entry.add( modification.getAttribute() ); |
| break; |
| |
| case REMOVE_ATTRIBUTE : |
| entry.remove( modification.getAttribute() ); |
| break; |
| |
| case REPLACE_ATTRIBUTE : |
| entry.remove( modification.getAttribute() ); |
| entry.add( modification.getAttribute() ); |
| |
| break; |
| } |
| } |
| } |
| |
| |
| /** |
| * Creates a specific Subentry instances using the Auxiliary ObjectClasses stored into |
| * the Subentry Entry. We may have more than one instance created, if the entry is managing |
| * more than one role. |
| */ |
| private Subentry[] createSubentry( Entry subentry ) throws LdapException |
| { |
| DN subentryDn = subentry.getDn(); |
| |
| String subtree = subentry.get( SUBTREE_SPECIFICATION_AT ).getString(); |
| |
| EntryAttribute cn = subentry.get( SchemaConstants.CN_AT ); |
| String uuid = subentry.get( SchemaConstants.ENTRY_UUID_AT).getString(); |
| SubtreeSpecification ss; |
| |
| // Process the SubtreeSpecification |
| try |
| { |
| ss = ssParser.parse( subtree ); |
| } |
| catch ( Exception e ) |
| { |
| String message = "Failed while parsing subtreeSpecification for " + subentryDn; |
| LOG.error( message ); |
| |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| |
| // Now, creates all the subentries |
| List<Subentry> subentries = new ArrayList<Subentry>(); |
| |
| // CollectiveAttribute Subentry |
| if ( subentry.contains( OBJECT_CLASS_AT, SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC ) ) |
| { |
| // It's a CA subentry. Collect the list of COLLECTIVE attributes |
| List<EntryAttribute> collectiveAttributes = new ArrayList<EntryAttribute>(); |
| |
| for ( EntryAttribute attribute : subentry ) |
| { |
| if ( attribute.getAttributeType().isCollective() ) |
| { |
| collectiveAttributes.add( attribute ); |
| } |
| } |
| |
| Subentry newSubentry = new CollectiveAttributeSubentry( cn, ss, uuid, collectiveAttributes ); |
| |
| subentries.add( newSubentry ); |
| } |
| |
| // AccessControl Subentry |
| if ( subentry.contains( OBJECT_CLASS_AT, SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) ) |
| { |
| // It's a AC subentry. Collect the list of ACIItem attributes |
| EntryAttribute prescriptiveACI = subentry.get( SchemaConstants.PRESCRIPTIVE_ACI_AT ); |
| |
| AccessControlSubentry newSubentry = new AccessControlSubentry( cn, ss, uuid ); |
| |
| for ( Value<?> value:prescriptiveACI ) |
| { |
| ACIItem aciItem = null; |
| String aciItemStr = value.getString(); |
| |
| try |
| { |
| aciItem = aciParser.parse( aciItemStr ); |
| newSubentry.addAciItem( aciItem ); |
| } |
| catch ( ParseException e ) |
| { |
| String msg = I18n.err( I18n.ERR_73, aciItemStr ); |
| LOG.error( msg, e ); |
| } |
| |
| } |
| |
| subentries.add( newSubentry ); |
| } |
| |
| // SubSchema Subentry |
| if ( subentry.contains( OBJECT_CLASS_AT, SchemaConstants.SUB_SCHEMA_SUBENTRY_AT ) ) |
| { |
| SubSchemaSubentry newSubentry = new SubSchemaSubentry( cn, ss, uuid ); |
| subentries.add( newSubentry ); |
| } |
| |
| // TrggerExecution Subentry |
| if ( subentry.contains( OBJECT_CLASS_AT, SchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) ) |
| { |
| EntryAttribute triggerSpecificationAttr = subentry.get( SchemaConstants.PRESCRIPTIVE_TRIGGER_SPECIFICATION ); |
| TriggerExecutionSubentry newSubentry = new TriggerExecutionSubentry( cn, ss, uuid ); |
| |
| for ( Value<?> value:triggerSpecificationAttr ) |
| { |
| try |
| { |
| TriggerSpecification triggerSpecification = triggerParser.parse( value.getString() ); |
| newSubentry.addTriggerSpecification( triggerSpecification ); |
| } |
| catch ( ParseException e ) |
| { |
| String msg = I18n.err( I18n.ERR_73, value ); |
| LOG.error( msg, e ); |
| } |
| } |
| } |
| |
| return subentries.toArray( new Subentry[]{}); |
| } |
| |
| //------------------------------------------------------------------------------------------- |
| // Interceptor initialization |
| //------------------------------------------------------------------------------------------- |
| /** |
| * Initialize the Subentry Interceptor |
| * |
| * @param directoryService The DirectoryService instance |
| */ |
| public void init( DirectoryService directoryService ) throws LdapException |
| { |
| super.init( directoryService ); |
| |
| this.directoryService = directoryService; |
| nexus = directoryService.getPartitionNexus(); |
| schemaManager = directoryService.getSchemaManager(); |
| |
| // setup various attribute type values |
| 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 ); |
| ENTRY_UUID_AT = schemaManager.getAttributeType( SchemaConstants.ENTRY_UUID_AT ); |
| CN_AT = schemaManager.getAttributeType( SchemaConstants.CN_AT ); |
| |
| ACCESS_CONTROL_SEQ_NUMBER_AT = schemaManager.getAttributeType( ApacheSchemaConstants.ACCESS_CONTROL_SEQ_NUMBER_AT ); |
| ACCESS_CONTROL_SUBENTRIES_AT = schemaManager.getAttributeType( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ); |
| ACCESS_CONTROL_SUBENTRIES_UUID_AT = schemaManager.getAttributeType( ApacheSchemaConstants.ACCESS_CONTROL_SUBENTRIES_UUID_AT ); |
| |
| COLLECTIVE_ATTRIBUTE_SEQ_NUMBER_AT = schemaManager.getAttributeType( ApacheSchemaConstants.COLLECTIVE_ATTRIBUTE_SEQ_NUMBER_AT ); |
| COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT = schemaManager.getAttributeType( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ); |
| COLLECTIVE_ATTRIBUTE_SUBENTRIES_UUID_AT = schemaManager.getAttributeType( ApacheSchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_UUID_AT ); |
| |
| SUB_SCHEMA_SEQ_NUMBER_AT = schemaManager.getAttributeType( ApacheSchemaConstants.SUB_SCHEMA_SEQ_NUMBER_AT ); |
| SUBSCHEMA_SUBENTRY_AT = schemaManager.getAttributeType( SchemaConstants.SUB_SCHEMA_SUBENTRY_AT ); |
| SUBSCHEMA_SUBENTRY_UUID_AT = schemaManager.getAttributeType( ApacheSchemaConstants.SUB_SCHEMA_SUBENTRY_UUID_AT ); |
| |
| TRIGGER_EXECUTION_SEQ_NUMBER_AT = schemaManager.getAttributeType( ApacheSchemaConstants.TRIGGER_EXECUTION_SEQ_NUMBER_AT ); |
| TRIGGER_EXECUTION_SUBENTRIES_AT = schemaManager.getAttributeType( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ); |
| TRIGGER_EXECUTION_SUBENTRIES_UUID_AT = schemaManager.getAttributeType( ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_UUID_AT ); |
| |
| SUBENTRY_OPATTRS = new AttributeType[] |
| { |
| ACCESS_CONTROL_SUBENTRIES_AT, |
| SUBSCHEMA_SUBENTRY_AT, |
| COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, |
| TRIGGER_EXECUTION_SUBENTRIES_AT |
| }; |
| |
| // Initialize the various parsers we use for SubtreeSpecification, ACIItem and TriggerSpecification |
| ssParser = new SubtreeSpecificationParser( schemaManager ); |
| evaluator = new SubtreeEvaluator( schemaManager ); |
| |
| triggerParser = new TriggerSpecificationParser( new NormalizerMappingResolver() |
| { |
| public Map<String, OidNormalizer> getNormalizerMapping() throws Exception |
| { |
| return schemaManager.getNormalizerMapping(); |
| } |
| } ); |
| |
| aciParser = new ACIItemParser( new ConcreteNameComponentNormalizer( schemaManager ), 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 = directoryService.getDNFactory().create( ServerDNConstants.ADMIN_SYSTEM_DN ); |
| |
| // search each namingContext for subentries |
| for ( String suffix : suffixes ) |
| { |
| DN suffixDn = directoryService.getDNFactory().create( suffix ); |
| |
| CoreSession adminSession = new DefaultCoreSession( |
| new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), directoryService ); |
| |
| 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 caches (UUID and DN) |
| try |
| { |
| while ( subentries.next() ) |
| { |
| Entry subentry = subentries.get(); |
| |
| Subentry[] subentryInstances = createSubentry( subentry ); |
| |
| for ( Subentry newSubentry : subentryInstances ) |
| { |
| directoryService.getSubentryCache().addSubentry( subentry.getDn(), newSubentry ); |
| } |
| } |
| |
| subentries.close(); |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapOperationException( e.getMessage() ); |
| } |
| } |
| } |
| |
| |
| //------------------------------------------------------------------------------------------- |
| // Helper methods |
| //------------------------------------------------------------------------------------------- |
| /** |
| * Return the list of AdministrativeRole for a subentry |
| */ |
| private Set<AdministrativeRoleEnum> getSubentryAdminRoles( Entry subentry ) throws LdapException |
| { |
| Set<AdministrativeRoleEnum> adminRoles = new HashSet<AdministrativeRoleEnum>(); |
| |
| if ( subentry.hasObjectClass( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) ) |
| { |
| adminRoles.add( AdministrativeRoleEnum.AccessControl ); |
| } |
| |
| if ( subentry.hasObjectClass( SchemaConstants.SUBSCHEMA_OC ) ) |
| { |
| adminRoles.add( AdministrativeRoleEnum.SubSchema ); |
| } |
| |
| if ( subentry.hasObjectClass( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC ) ) |
| { |
| adminRoles.add( AdministrativeRoleEnum.CollectiveAttribute ); |
| } |
| |
| if ( subentry.hasObjectClass( ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) ) |
| { |
| adminRoles.add( AdministrativeRoleEnum.TriggerExecution ); |
| } |
| |
| 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 invocation 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 isSubentriesControlPresent( OperationContext opContext ) throws LdapException |
| { |
| if ( !opContext.hasRequestControls() ) |
| { |
| return false; |
| } |
| |
| // found the subentry request control so we return its value |
| if ( opContext.hasRequestControl( SUBENTRY_CONTROL ) ) |
| { |
| SubentriesControl subentriesControl = ( SubentriesControl ) opContext.getRequestControl( SUBENTRY_CONTROL ); |
| |
| return subentriesControl.isVisible(); |
| } |
| |
| return false; |
| } |
| |
| |
| /** |
| * Update the seqNumber for each kind of role. The entry will be updated in the backend only |
| * if its seqNumbers are not up to date. |
| */ |
| private List<Modification> updateEntry( Entry entry ) throws LdapException |
| { |
| List<Modification> modificationAcs = updateEntry( entry, directoryService.getAccessControlAPCache(), ACCESS_CONTROL_SEQ_NUMBER_AT, ACCESS_CONTROL_SUBENTRIES_UUID_AT ); |
| List<Modification> modificationCas = updateEntry( entry, directoryService.getCollectiveAttributeAPCache(), COLLECTIVE_ATTRIBUTE_SEQ_NUMBER_AT, COLLECTIVE_ATTRIBUTE_SUBENTRIES_UUID_AT ); |
| List<Modification> modificationSss = updateEntry( entry, directoryService.getSubschemaAPCache(), SUB_SCHEMA_SEQ_NUMBER_AT, SUBSCHEMA_SUBENTRY_UUID_AT ); |
| List<Modification> modificationTes = updateEntry( entry, directoryService.getTriggerExecutionAPCache(), TRIGGER_EXECUTION_SEQ_NUMBER_AT, TRIGGER_EXECUTION_SUBENTRIES_UUID_AT ); |
| List<Modification> modifications = null; |
| |
| if ( ( modificationAcs != null ) || ( modificationCas != null ) || ( modificationSss != null ) || ( modificationTes != null ) ) |
| { |
| modifications = new ArrayList<Modification>(); |
| |
| if ( modificationAcs != null ) |
| { |
| modifications.addAll( modificationAcs ); |
| } |
| |
| if ( modificationCas != null ) |
| { |
| modifications.addAll( modificationCas ); |
| } |
| |
| if ( modificationSss != null ) |
| { |
| modifications.addAll( modificationSss ); |
| } |
| |
| if ( modificationTes != null ) |
| { |
| modifications.addAll( modificationTes ); |
| } |
| |
| ModifyOperationContext modCtx = new ModifyOperationContext( directoryService.getAdminSession() ); |
| modCtx.setByPassed( BYPASS_INTERCEPTORS ); |
| modCtx.setDn( entry.getDn() ); |
| modCtx.setModItems( modifications ); |
| modCtx.setEntry( (ClonedServerEntry)entry ); |
| |
| directoryService.getOperationManager().modify( modCtx ); |
| } |
| |
| return modifications; |
| } |
| |
| |
| /** |
| * Update the CollectiveAttribute seqNumber for each kind of role. The entry will be updated in the backend only |
| * if its seqNumbers are not up to date. |
| */ |
| private List<Modification> updateEntry( Entry entry, DnNode<AdministrativePoint> apCache, AttributeType seqNumberAT, |
| AttributeType subentryUuidAT ) throws LdapException |
| { |
| DN entryDn = entry.getDn(); |
| List<Modification> modifications = null; |
| |
| DnNode<AdministrativePoint> apNode = apCache.getParentWithElement( entryDn ); |
| |
| if ( apNode != null ) |
| { |
| // We have an AdministrativePoint for this entry, get its SeqNumber |
| AdministrativePoint adminPoint = apNode.getElement(); |
| EntryAttribute seqNumberAttribute = entry.get( seqNumberAT ); |
| |
| // We have to recurse : starting from the IAP, we go up the AP tree |
| // until we find the SAP. For each AP we find, we compare the AP's seqNumber |
| // and if it's above the current SeqNumber (initialized to the entry value), |
| // we select this SeqNumber. When we are done, if the selected SeqNumber is |
| // greater than the initial entry seqNumber, then we update the entry |
| |
| // First, init the entry seqNumber. If we have no AT, then we initialize it to -1 |
| long entrySeqNumber = Long.MIN_VALUE; |
| |
| if ( seqNumberAttribute != null ) |
| { |
| entrySeqNumber = Long.parseLong( seqNumberAttribute.getString() ); |
| } |
| |
| boolean sapFound = false; |
| boolean seqNumberUpdated = false; |
| List<String> subentryUuids = null; |
| long initialSeqNumber = entrySeqNumber; |
| |
| do |
| { |
| if ( adminPoint.isSpecific() ) |
| { |
| sapFound = true; |
| } |
| |
| // We update the seqNumber only if it's below the AdminPoint seqNumber |
| // We update the UUID ref only if the initial seqNumber is below the AdminPoint |
| if ( entrySeqNumber < adminPoint.getSeqNumber() ) |
| { |
| seqNumberUpdated = true; |
| subentryUuids = new ArrayList<String>(); |
| entrySeqNumber = adminPoint.getSeqNumber(); |
| } |
| |
| if ( ( initialSeqNumber < adminPoint.getSeqNumber() ) || seqNumberUpdated ) |
| { |
| // Evaluate the current AP on the entry for each subentry |
| for ( Subentry subentry : adminPoint.getSubentries() ) |
| { |
| if ( evaluator.evaluate( subentry.getSubtreeSpecification(), apNode.getDn(), entryDn, entry ) ) |
| { |
| subentryUuids.add( subentry.getUuid() ); |
| } |
| } |
| } |
| |
| // Go down one level |
| apNode = apNode.getParentWithElement(); |
| adminPoint = apNode.getElement(); |
| } while ( !sapFound ); |
| |
| // If we have updated the entry, create the list of modifications to apply |
| if ( seqNumberUpdated ) |
| { |
| // Create the list of modifications : we will inject REPLACE operations. |
| modifications = new ArrayList<Modification>(); |
| |
| // The seqNubmer |
| EntryAttribute newSeqNumberAT = new DefaultEntryAttribute( seqNumberAT, Long.toString( entrySeqNumber ) ); |
| Modification seqNumberModification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, newSeqNumberAT ); |
| |
| modifications.add( seqNumberModification ); |
| |
| // The subentry UUID, if any |
| if ( ( subentryUuids != null ) && ( subentryUuids.size() != 0 ) ) |
| { |
| EntryAttribute newSubentryUuidAT = new DefaultEntryAttribute( subentryUuidAT, subentryUuids.toArray( new String[]{} ) ); |
| Modification subentryUuiMod = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, newSubentryUuidAT ); |
| |
| modifications.add( subentryUuiMod ); |
| } |
| else |
| { |
| // We may have to remove UUID refs from the entry |
| if ( entry.containsAttribute( subentryUuidAT ) ) |
| { |
| EntryAttribute newSubentryUuidAT = new DefaultEntryAttribute( subentryUuidAT ); |
| Modification subentryUuiMod = new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, newSubentryUuidAT ); |
| |
| modifications.add( subentryUuiMod ); |
| } |
| } |
| } |
| } |
| |
| return modifications; |
| } |
| |
| |
| /** |
| * 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 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() ) |
| { |
| aps.close(); |
| return true; |
| } |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapOperationException( e.getMessage() ); |
| } |
| |
| |
| return false; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| * |
| 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. |
| // |
| for ( String uuid : subentryCache ) |
| { |
| DN apDn = null; //subentryDn.getParent(); |
| DN subentryDn = null; |
| |
| SubtreeSpecification ss = directoryService.getSubentryCache().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; |
| EntryAttribute 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; |
| EntryAttribute opAttr = new DefaultEntryAttribute( operationalAttribute ); |
| opAttr.add( subentryDn.getNormName() ); |
| modifications.add( new DefaultModification( op, opAttr ) ); |
| } |
| } |
| } |
| |
| return modifications; |
| } |
| |
| |
| /** |
| * Check that the AT contains the AccessControl SAP role |
| */ |
| private boolean hasAccessControlSpecificRole( EntryAttribute adminPoint ) |
| { |
| return adminPoint.contains( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA ) || |
| adminPoint.contains( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID ); |
| } |
| |
| |
| /** |
| * Check that the AT contains the CollectiveAttribute SAP role |
| */ |
| private boolean hasCollectiveAttributeSpecificRole( EntryAttribute adminPoint ) |
| { |
| return adminPoint.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA ) || |
| adminPoint.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID ); |
| } |
| |
| |
| /** |
| * Check that the AT contains the TriggerExecution SAP role |
| */ |
| private boolean hasTriggerExecutionSpecificRole( EntryAttribute adminPoint ) |
| { |
| return adminPoint.contains( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA ) || |
| adminPoint.contains( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID ); |
| } |
| |
| |
| /** |
| * Check that the AT contains the SubSchema SAP role |
| */ |
| private boolean hasSubSchemaSpecificRole( EntryAttribute adminPoint ) |
| { |
| return adminPoint.contains( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA ) || |
| adminPoint.contains( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID ); |
| } |
| |
| |
| /** |
| * Tells if the role is an AC IAP |
| */ |
| private boolean isAccessControlInnerRole( String role ) |
| { |
| return role.equalsIgnoreCase( SchemaConstants.ACCESS_CONTROL_INNER_AREA ) || |
| role.equals( SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID ); |
| } |
| |
| |
| /** |
| * Tells if the role is an AC SAP |
| */ |
| private boolean isAccessControlSpecificRole( String role ) |
| { |
| return role.equalsIgnoreCase( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA ) || |
| role.equals( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID ); |
| } |
| |
| |
| /** |
| * Tells if the role is a CA IAP |
| */ |
| private boolean isCollectiveAttributeInnerRole( String role ) |
| { |
| return role.equalsIgnoreCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA ) || |
| role.equals( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID ); |
| } |
| |
| |
| /** |
| * Tells if the role is a CA SAP |
| */ |
| private boolean isCollectiveAttributeSpecificRole( String role ) |
| { |
| return role.equalsIgnoreCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA ) || |
| role.equals( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID ); |
| } |
| |
| |
| /** |
| * Tells if the role is a TE IAP |
| */ |
| private boolean isTriggerExecutionInnerRole( String role ) |
| { |
| return role.equalsIgnoreCase( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA ) || |
| role.equals( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID ); |
| } |
| |
| |
| /** |
| * Tells if the role is a TE SAP |
| */ |
| private boolean isTriggerExecutionSpecificRole( String role ) |
| { |
| return role.equalsIgnoreCase( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA ) || |
| role.equals( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID ); |
| } |
| |
| |
| /** |
| * Tells if the role is a SS SAP |
| */ |
| private boolean isSubschemaSpecificRole( String role ) |
| { |
| return role.equalsIgnoreCase( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA ) || |
| role.equals( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID ); |
| } |
| |
| |
| /** |
| * Tells if the role is an AAP |
| */ |
| private boolean isAutonomousAreaRole( String role ) |
| { |
| return role.equalsIgnoreCase( SchemaConstants.AUTONOMOUS_AREA ) || |
| role.equals( SchemaConstants.AUTONOMOUS_AREA_OID ); |
| } |
| |
| |
| /** |
| * Tells if the Administrative Point role is an AAP |
| */ |
| private boolean isAAP( EntryAttribute adminPoint ) |
| { |
| return ( adminPoint.contains( SchemaConstants.AUTONOMOUS_AREA ) || adminPoint |
| .contains( SchemaConstants.AUTONOMOUS_AREA_OID ) ); |
| } |
| |
| |
| /** |
| * Tells if the Administrative Point role is an IAP |
| */ |
| private boolean isIAP( EntryAttribute adminPoint ) |
| { |
| return ( adminPoint.contains( SchemaConstants.ACCESS_CONTROL_INNER_AREA ) || |
| adminPoint.contains( SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID ) || |
| adminPoint.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA ) || |
| adminPoint.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID ) || |
| adminPoint.contains( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA ) || |
| adminPoint.contains( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID ) ); |
| } |
| |
| |
| /** |
| * Check that we don't have an IAP and a SAP with the same family |
| */ |
| private void checkInnerSpecificMix( String role, EntryAttribute adminPoint ) throws LdapUnwillingToPerformException |
| { |
| if ( isAccessControlInnerRole( role ) ) |
| { |
| if ( hasAccessControlSpecificRole( adminPoint ) ) |
| { |
| // This is inconsistent |
| String message = "Cannot add a specific Administrative Point and the same" |
| + " inner Administrative point at the same time : " + adminPoint; |
| LOG.error( message ); |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| else |
| { |
| return; |
| } |
| } |
| |
| if ( isCollectiveAttributeInnerRole( role ) ) |
| { |
| if ( hasCollectiveAttributeSpecificRole( adminPoint ) ) |
| { |
| // This is inconsistent |
| String message = "Cannot add a specific Administrative Point and the same" |
| + " inner Administrative point at the same time : " + adminPoint; |
| LOG.error( message ); |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| else |
| { |
| return; |
| } |
| } |
| |
| if ( isTriggerExecutionInnerRole( role ) ) |
| { |
| if ( hasTriggerExecutionSpecificRole( adminPoint ) ) |
| { |
| // This is inconsistent |
| String message = "Cannot add a specific Administrative Point and the same" |
| + " inner Administrative point at the same time : " + adminPoint; |
| LOG.error( message ); |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| else |
| { |
| return; |
| } |
| } |
| } |
| |
| |
| private boolean isIAP( String role ) |
| { |
| return INNER_AREA_ROLES.contains( role ); |
| } |
| |
| |
| /** |
| * Check that the IAPs (if any) have a parent. We will check for each kind or role : |
| * AC, CA and TE. |
| */ |
| private void checkIAPHasParent( String role, EntryAttribute adminPoint, DN dn ) |
| throws LdapUnwillingToPerformException |
| { |
| // Check for the AC role |
| if ( isAccessControlInnerRole( role ) ) |
| { |
| DnNode<AdministrativePoint> acCache = directoryService.getAccessControlAPCache(); |
| |
| DnNode<AdministrativePoint> parent = acCache.getNode( dn ); |
| |
| if ( parent == null ) |
| { |
| // We don't have any AC administrativePoint in the tree, this is an error |
| String message = "Cannot add an IAP with no parent : " + adminPoint; |
| LOG.error( message ); |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| } |
| else if ( isCollectiveAttributeInnerRole( role ) ) |
| { |
| DnNode<AdministrativePoint> caCache = directoryService.getCollectiveAttributeAPCache(); |
| |
| boolean hasAP = caCache.hasParentElement( dn ); |
| |
| if ( !hasAP ) |
| { |
| // We don't have any AC administrativePoint in the tree, this is an error |
| String message = "Cannot add an IAP with no parent : " + adminPoint; |
| LOG.error( message ); |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| } |
| else if ( isTriggerExecutionInnerRole( role ) ) |
| { |
| DnNode<AdministrativePoint> caCache = directoryService.getTriggerExecutionAPCache(); |
| |
| DnNode<AdministrativePoint> parent = caCache.getNode( dn ); |
| |
| if ( parent == null ) |
| { |
| // We don't have any AC administrativePoint in the tree, this is an error |
| String message = "Cannot add an IAP with no parent : " + adminPoint; |
| LOG.error( message ); |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| } |
| else |
| { |
| // Wtf ? We *must* have an IAP here... |
| String message = "This is not an IAP : " + role; |
| LOG.error( message ); |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| } |
| |
| |
| /** |
| * Check if we can safely add a role. If it's an AAP, we have to be sure that |
| * all the 4 SAPs are present. |
| */ |
| private void checkAddRole( Value<?> role, EntryAttribute adminPoint, DN dn ) throws LdapException |
| { |
| String roleStr = StringTools.toLowerCase( StringTools.trim( role.getString() ) ); |
| |
| // Check that the added AdministrativeRole is valid |
| if ( !ROLES.contains( roleStr ) ) |
| { |
| String message = "Cannot add the given role, it's not a valid one :" + role; |
| LOG.error( message ); |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| |
| // If we are trying to add an AAP, we have to check that |
| // all the SAP roles are present. If nit, we add them. |
| int nbRoles = adminPoint.size(); |
| |
| if ( isAutonomousAreaRole( roleStr ) ) |
| { |
| nbRoles--; |
| |
| if ( !hasAccessControlSpecificRole( adminPoint ) ) |
| { |
| adminPoint.add( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA ); |
| } |
| else |
| { |
| nbRoles--; |
| } |
| |
| if ( !hasCollectiveAttributeSpecificRole( adminPoint ) ) |
| { |
| adminPoint.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA ); |
| } |
| else |
| { |
| nbRoles--; |
| } |
| |
| if ( !hasTriggerExecutionSpecificRole( adminPoint ) ) |
| { |
| adminPoint.add( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA ); |
| } |
| else |
| { |
| nbRoles--; |
| } |
| |
| if ( !hasSubSchemaSpecificRole( adminPoint ) ) |
| { |
| adminPoint.add( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA ); |
| } |
| else |
| { |
| nbRoles--; |
| } |
| |
| if ( nbRoles != 0 ) |
| { |
| // Check that we don't have any other role : we should have only 5 roles max |
| String message = "Cannot add an Autonomous Administrative Point if we have some IAP roles : " |
| + adminPoint; |
| LOG.error( message ); |
| |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| |
| // Fine, we have an AAP and the 4 SAPs |
| return; |
| } |
| |
| // check that we can't mix Inner and Specific areas |
| checkInnerSpecificMix( roleStr, adminPoint ); |
| |
| // Check that we don't add an IAP with no parent. The IAP must be added under |
| // either a AAP, or a SAP/IAP within the same family |
| if ( isIAP( roleStr ) ) |
| { |
| checkIAPHasParent( roleStr, adminPoint, dn ); |
| } |
| } |
| |
| |
| /** |
| * Check if an AP being added is valid or not : |
| * - it's not under a subentry |
| * - it cannot be a subentry |
| * - the roles must be consistent (ie, AAP is coming with the 4 SAPs, we can't have a SAP and a IAP for the same role) |
| * - it can't be the rootDSE or a NamingContext |
| */ |
| private boolean checkIsValidAP( Entry entry ) throws LdapException |
| { |
| DN dn = entry.getDn(); |
| |
| // Not rootDSE nor a namingContext |
| if ( dn.isRootDSE() || isNamingContext( dn ) ) |
| { |
| return false; |
| } |
| |
| // Not a subentry |
| if ( entry.hasObjectClass( SchemaConstants.SUBENTRY_OC ) ) |
| { |
| return false; |
| } |
| |
| // Not under a subentry |
| if ( directoryService.getSubentryCache().hasSubentry( dn.getParent() ) ) |
| { |
| return false; |
| } |
| |
| // Check the roles |
| EntryAttribute adminPoint = entry.get( ADMINISTRATIVE_ROLE_AT ); |
| |
| for ( Value<?> role : adminPoint ) |
| { |
| checkAddRole( role, adminPoint, dn ); |
| } |
| |
| return true; |
| } |
| |
| |
| // ----------------------------------------------------------------------- |
| // Methods dealing with subentry modification |
| // ----------------------------------------------------------------------- |
| |
| private Set<AdministrativeRoleEnum> getSubentryTypes( Entry entry, List<Modification> mods ) throws LdapException |
| { |
| EntryAttribute 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.EMPTY_DN ); |
| 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 ) |
| { |
| String oldDnStr = oldDn.getNormName(); |
| String newDnStr = newDn.getNormName(); |
| |
| if ( hasRole ) |
| { |
| EntryAttribute operational = entry.get( attributeType ).clone(); |
| |
| if ( operational == null ) |
| { |
| operational = new DefaultEntryAttribute( 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<EntryAttribute> getSubentryOperationalAttributes( DN dn, Subentry subentry ) throws LdapException |
| { |
| List<EntryAttribute> attributes = new ArrayList<EntryAttribute>(); |
| |
| 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 triggerExecutionSubentries = new DefaultEntryAttribute( TRIGGER_EXECUTION_SUBENTRIES_AT, dn.getNormName() ); |
| attributes.add( triggerExecutionSubentries ); |
| } |
| |
| 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 ) |
| { |
| EntryAttribute opAttr = candidate.get( operationalAttribute ); |
| |
| if ( ( opAttr != null ) && opAttr.contains( dn ) ) |
| { |
| EntryAttribute attr = new DefaultEntryAttribute( 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<EntryAttribute> operationalAttributes ) throws LdapException |
| { |
| List<Modification> modifications = new ArrayList<Modification>(); |
| |
| for ( EntryAttribute operationalAttribute : operationalAttributes ) |
| { |
| EntryAttribute opAttrInEntry = entry.get( operationalAttribute.getAttributeType() ); |
| |
| if ( ( opAttrInEntry != null ) && ( opAttrInEntry.size() > 0 ) ) |
| { |
| EntryAttribute 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 ( String uuid : subentryCache ) |
| { |
| DN apDn = null; //subentryDn.getParent(); |
| DN subentryDn = null; |
| |
| SubtreeSpecification ss = directoryService.getSubentryCache().getSubentry( uuid ).getSubtreeSpecification(); |
| boolean isOldEntrySelected = evaluator.evaluate( ss, apDn, name, oldEntry ); |
| boolean isNewEntrySelected = evaluator.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; |
| EntryAttribute 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; |
| EntryAttribute opAttr = new DefaultEntryAttribute( operationalAttribute ); |
| opAttr.add( subentryDn.getNormName() ); |
| modList.add( new DefaultModification( op, opAttr ) ); |
| } |
| } |
| } |
| |
| return modList; |
| } |
| |
| |
| /** |
| * Get a read-lock on the AP cache. |
| * No read operation can be done on the AP cache if this |
| * method is not called before. |
| */ |
| public void lockRead() |
| { |
| mutex.readLock().lock(); |
| } |
| |
| |
| /** |
| * Get a write-lock on the AP cache. |
| * No write operation can be done on the apCache if this |
| * method is not called before. |
| */ |
| public void lockWrite() |
| { |
| mutex.writeLock().lock(); |
| } |
| |
| |
| /** |
| * Release the read-write lock on the AP cache. |
| * This method must be called after having read or modified the |
| * AP cache |
| */ |
| public void unlock() |
| { |
| if ( mutex.isWriteLockedByCurrentThread() ) |
| { |
| mutex.writeLock().unlock(); |
| } |
| else |
| { |
| mutex.readLock().unlock(); |
| } |
| } |
| |
| |
| /** |
| * Create the list of AP for a given entry. |
| */ |
| private void createAdministrativePoints( EntryAttribute adminPoint, DN dn, String uuid, long seqNumber ) throws LdapException |
| { |
| if ( isAAP( adminPoint ) ) |
| { |
| // The AC AAP |
| AccessControlAdministrativePoint acAap = new AccessControlAAP( uuid, seqNumber ); |
| directoryService.getAccessControlAPCache().add( dn, acAap ); |
| |
| // The CA AAP |
| CollectiveAttributeAdministrativePoint caAap = new CollectiveAttributeAAP( uuid, seqNumber ); |
| directoryService.getCollectiveAttributeAPCache().add( dn, caAap ); |
| |
| // The TE AAP |
| TriggerExecutionAdministrativePoint teAap = new TriggerExecutionAAP( uuid, seqNumber ); |
| directoryService.getTriggerExecutionAPCache().add( dn, teAap ); |
| |
| // The SS AAP |
| SubschemaAdministrativePoint ssAap = new SubschemaAAP( uuid, seqNumber ); |
| directoryService.getSubschemaAPCache().add( dn, ssAap ); |
| |
| // If it's an AAP, we can get out immediately |
| return; |
| } |
| |
| // Not an AAP |
| for ( Value<?> value : adminPoint ) |
| { |
| String role = value.getString(); |
| |
| // Deal with AccessControl AP |
| if ( isAccessControlSpecificRole( role ) ) |
| { |
| AccessControlAdministrativePoint sap = new AccessControlSAP( uuid, seqNumber ); |
| directoryService.getAccessControlAPCache().add( dn, sap ); |
| |
| continue; |
| } |
| |
| if ( isAccessControlInnerRole( role ) ) |
| { |
| AccessControlAdministrativePoint iap = new AccessControlIAP( uuid, seqNumber ); |
| directoryService.getAccessControlAPCache().add( dn, iap ); |
| |
| continue; |
| } |
| |
| // Deal with CollectiveAttribute AP |
| if ( isCollectiveAttributeSpecificRole( role ) ) |
| { |
| CollectiveAttributeAdministrativePoint sap = new CollectiveAttributeSAP( uuid, seqNumber ); |
| directoryService.getCollectiveAttributeAPCache().add( dn, sap ); |
| |
| continue; |
| } |
| |
| if ( isCollectiveAttributeInnerRole( role ) ) |
| { |
| CollectiveAttributeAdministrativePoint iap = new CollectiveAttributeIAP( uuid, seqNumber ); |
| directoryService.getCollectiveAttributeAPCache().add( dn, iap ); |
| |
| continue; |
| } |
| |
| // Deal with SubSchema AP |
| if ( isSubschemaSpecificRole( role ) ) |
| { |
| SubschemaAdministrativePoint sap = new SubschemaSAP( uuid, seqNumber ); |
| directoryService.getSubschemaAPCache().add( dn, sap ); |
| |
| continue; |
| } |
| |
| // Deal with TriggerExecution AP |
| if ( isTriggerExecutionSpecificRole( role ) ) |
| { |
| TriggerExecutionAdministrativePoint sap = new TriggerExecutionSAP( uuid, seqNumber ); |
| directoryService.getTriggerExecutionAPCache().add( dn, sap ); |
| |
| continue; |
| } |
| |
| if ( isTriggerExecutionInnerRole( role ) ) |
| { |
| TriggerExecutionAdministrativePoint iap = new TriggerExecutionIAP( uuid, seqNumber ); |
| directoryService.getTriggerExecutionAPCache().add( dn, iap ); |
| |
| continue; |
| } |
| } |
| |
| return; |
| } |
| |
| |
| /** |
| * Delete the list of AP for a given entry. We can update the cache for each role, |
| * as if the AP doe snot have such a role, it won't do anythig anyway |
| */ |
| private void deleteAdministrativePoints( EntryAttribute adminPoint, DN dn ) throws LdapException |
| { |
| // The AC SAP |
| directoryService.getAccessControlAPCache().remove( dn ); |
| |
| // The CA SAP |
| directoryService.getCollectiveAttributeAPCache().remove( dn ); |
| |
| // The TE SAP |
| directoryService.getTriggerExecutionAPCache().remove( dn ); |
| |
| // The SS SAP |
| directoryService.getSubschemaAPCache().remove( dn ); |
| // If it's an AAP, we can get out immediately |
| return; |
| } |
| |
| |
| /** |
| * Get the AdministrativePoint associated with a subentry |
| * @param apDn |
| * @return |
| */ |
| private List<AdministrativePoint> getAdministrativePoints( DN apDn ) |
| { |
| List<AdministrativePoint> administrativePoints = new ArrayList<AdministrativePoint>(); |
| |
| AdministrativePoint administrativePoint = directoryService.getAccessControlAPCache().getElement( apDn ); |
| |
| if ( administrativePoint != null ) |
| { |
| administrativePoints.add( administrativePoint ); |
| } |
| |
| administrativePoint = directoryService.getCollectiveAttributeAPCache().getElement( apDn ); |
| |
| if ( administrativePoint != null ) |
| { |
| administrativePoints.add( administrativePoint ); |
| } |
| |
| administrativePoint = directoryService.getTriggerExecutionAPCache().getElement( apDn ); |
| |
| if ( administrativePoint != null ) |
| { |
| administrativePoints.add( administrativePoint ); |
| } |
| |
| administrativePoint = directoryService.getSubschemaAPCache().getElement( apDn ); |
| |
| if ( administrativePoint != null ) |
| { |
| administrativePoints.add( administrativePoint ); |
| } |
| |
| return administrativePoints; |
| } |
| |
| |
| /** |
| * Tells if the subentry's roles point to an AP |
| */ |
| private boolean isValidSubentry( Entry subentry, DN apDn ) |
| { |
| boolean isValid = true; |
| |
| // Check that the ACAP exists if the AC subentry OC is present in the subentry |
| if ( subentry.hasObjectClass( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) || |
| subentry.hasObjectClass( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC_OID ) ) |
| { |
| isValid &= directoryService.getAccessControlAPCache().hasElement( apDn ); |
| } |
| |
| // Check that the CAAP exists if the CA subentry OC is present in the subentry |
| if ( subentry.hasObjectClass( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC ) || |
| subentry.hasObjectClass( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC_OID ) ) |
| { |
| isValid &= directoryService.getCollectiveAttributeAPCache().hasElement( apDn ); |
| } |
| |
| // Check that the TEAP exists if the TE subentry OC is present in the subentry |
| if ( subentry.hasObjectClass( SchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) || |
| subentry.hasObjectClass( SchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC_OID ) ) |
| { |
| isValid &= directoryService.getTriggerExecutionAPCache().hasElement( apDn ); |
| } |
| |
| |
| // Check that the SSAP exists if the SS subentry OC is present in the subentry |
| if ( subentry.hasObjectClass( SchemaConstants.SUBSCHEMA_OC ) || |
| subentry.hasObjectClass( SchemaConstants.SUBSCHEMA_OC_OID ) ) |
| { |
| isValid &= directoryService.getSubschemaAPCache().hasElement( apDn ); |
| } |
| |
| return isValid; |
| } |
| |
| |
| /** |
| * Inject the seqNumbers in an AP |
| */ |
| private long updateAPSeqNumbers( OperationEnum operation, DN apDn, Entry entry, Subentry[] subentries ) throws LdapException |
| { |
| long seqNumber = directoryService.getNewApSeqNumber(); |
| String seqNumberStr = Long.toString( seqNumber ); |
| List<Modification> modifications = new ArrayList<Modification>(); |
| |
| EntryAttribute newSeqNumber = null; |
| |
| for ( Subentry subentry : subentries ) |
| { |
| if ( subentry == null ) |
| { |
| continue; |
| } |
| |
| AdministrativePoint adminPoint = null; |
| |
| switch ( subentry.getAdministrativeRole() ) |
| { |
| case AccessControl : |
| newSeqNumber = new DefaultEntryAttribute( ACCESS_CONTROL_SEQ_NUMBER_AT, seqNumberStr ); |
| adminPoint = directoryService.getAccessControlAPCache().getElement( apDn ); |
| break; |
| |
| case CollectiveAttribute : |
| newSeqNumber = new DefaultEntryAttribute( COLLECTIVE_ATTRIBUTE_SEQ_NUMBER_AT, seqNumberStr ); |
| adminPoint = directoryService.getCollectiveAttributeAPCache().getElement( apDn ); |
| break; |
| |
| case SubSchema : |
| newSeqNumber = new DefaultEntryAttribute( SUB_SCHEMA_SEQ_NUMBER_AT, seqNumberStr ); |
| adminPoint = directoryService.getSubschemaAPCache().getElement( apDn ); |
| break; |
| |
| case TriggerExecution : |
| newSeqNumber = new DefaultEntryAttribute( TRIGGER_EXECUTION_SEQ_NUMBER_AT, seqNumberStr ); |
| adminPoint = directoryService.getTriggerExecutionAPCache().getElement( apDn ); |
| break; |
| |
| } |
| |
| Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, newSeqNumber ); |
| modifications.add( modification ); |
| |
| // Get back the subentry entryUUID and store it in the subentry if this is an addition |
| switch ( operation ) |
| { |
| case ADD : |
| String subentryUuid = entry.get( SchemaConstants.ENTRY_UUID_AT ).getString(); |
| subentry.setUuid( subentryUuid ); |
| adminPoint.addSubentry( subentry ); |
| adminPoint.setSeqNumber( seqNumber ); |
| break; |
| |
| case REMOVE : |
| adminPoint.setSeqNumber( seqNumber ); |
| break; |
| } |
| } |
| |
| // Inject the seqNumbers into the parent AP |
| ModifyOperationContext modCtx = new ModifyOperationContext( directoryService.getAdminSession() ); |
| modCtx.setByPassed( BYPASS_INTERCEPTORS ); |
| modCtx.setDn( apDn ); |
| modCtx.setModItems( modifications ); |
| |
| directoryService.getOperationManager().modify( modCtx ); |
| |
| return seqNumber; |
| } |
| |
| |
| /** |
| * Process the addition of a standard entry, adding the SeqNumber and references |
| * to the subentries. |
| */ |
| private void processAddEntry( Entry entry ) throws LdapException |
| { |
| List<Modification> modificationAcs = updateEntry( entry, directoryService.getAccessControlAPCache(), ACCESS_CONTROL_SEQ_NUMBER_AT, ACCESS_CONTROL_SUBENTRIES_UUID_AT ); |
| List<Modification> modificationCas = updateEntry( entry, directoryService.getCollectiveAttributeAPCache(), COLLECTIVE_ATTRIBUTE_SEQ_NUMBER_AT, COLLECTIVE_ATTRIBUTE_SUBENTRIES_UUID_AT ); |
| List<Modification> modificationSss = updateEntry( entry, directoryService.getSubschemaAPCache(), SUB_SCHEMA_SEQ_NUMBER_AT, SUBSCHEMA_SUBENTRY_UUID_AT ); |
| List<Modification> modificationTes = updateEntry( entry, directoryService.getTriggerExecutionAPCache(), TRIGGER_EXECUTION_SEQ_NUMBER_AT, TRIGGER_EXECUTION_SUBENTRIES_UUID_AT ); |
| |
| if ( ( modificationAcs != null ) || ( modificationCas != null ) || ( modificationSss != null ) || (modificationTes != null ) ) |
| { |
| List<Modification> modifications = new ArrayList<Modification>(); |
| |
| if ( modificationAcs != null ) |
| { |
| modifications.addAll( modificationAcs ); |
| } |
| |
| if ( modificationCas != null ) |
| { |
| modifications.addAll( modificationCas ); |
| } |
| |
| if ( modificationSss != null ) |
| { |
| modifications.addAll( modificationSss ); |
| } |
| |
| if ( modificationTes != null ) |
| { |
| modifications.addAll( modificationTes ); |
| } |
| |
| for ( Modification modification : modifications ) |
| { |
| entry.add( modification.getAttribute() ); |
| } |
| } |
| } |
| |
| |
| /** |
| * Add a reference to the added subentry in each of the AP cache for which the |
| * subentry has a role. |
| */ |
| private void addSubentry( DN apDn, Entry entry, Subentry subentry, long seqNumber ) throws LdapException |
| { |
| for ( AdministrativeRoleEnum role : getSubentryAdminRoles( entry ) ) |
| { |
| switch ( role ) |
| { |
| case AccessControl : |
| AdministrativePoint apAC = directoryService.getAccessControlAPCache().getElement( apDn ); |
| apAC.addSubentry( subentry ); |
| apAC.setSeqNumber( seqNumber ); |
| break; |
| |
| case CollectiveAttribute : |
| AdministrativePoint apCA = directoryService.getCollectiveAttributeAPCache().getElement( apDn ); |
| apCA.addSubentry( subentry ); |
| apCA.setSeqNumber( seqNumber ); |
| break; |
| |
| case SubSchema : |
| AdministrativePoint apSS = directoryService.getSubschemaAPCache().getElement( apDn ); |
| apSS.addSubentry( subentry ); |
| apSS.setSeqNumber( seqNumber ); |
| break; |
| |
| case TriggerExecution : |
| AdministrativePoint apTE = directoryService.getTriggerExecutionAPCache().getElement( apDn ); |
| apTE.addSubentry( subentry ); |
| apTE.setSeqNumber( seqNumber ); |
| break; |
| } |
| } |
| } |
| |
| |
| /** |
| * Remove the reference to the deleted subentry in each of the AP cache for which the |
| * subentry has a role. |
| */ |
| private void deleteSubentry( DN apDn, Entry entry, Subentry subentry ) throws LdapException |
| { |
| for ( AdministrativeRoleEnum role : getSubentryAdminRoles( entry ) ) |
| { |
| switch ( role ) |
| { |
| case AccessControl : |
| directoryService.getAccessControlAPCache().getElement( apDn ).deleteSubentry( subentry ); |
| break; |
| |
| case CollectiveAttribute : |
| directoryService.getCollectiveAttributeAPCache().getElement( apDn ).deleteSubentry( subentry ); |
| break; |
| |
| case SubSchema : |
| directoryService.getSubschemaAPCache().getElement( apDn ).deleteSubentry( subentry ); |
| break; |
| |
| case TriggerExecution : |
| directoryService.getTriggerExecutionAPCache().getElement( apDn ).deleteSubentry( subentry ); |
| break; |
| |
| } |
| } |
| } |
| |
| |
| private void initSeqNumber( Entry entry, EntryAttribute adminPoint ) throws LdapException |
| { |
| String initSeqNumber = Long.toString( -1L ); |
| |
| if ( adminPoint.contains( AdministrativeRole.AutonomousArea.getRole() ) ) |
| { |
| entry.add( ApacheSchemaConstants.ACCESS_CONTROL_SEQ_NUMBER_AT, initSeqNumber); |
| entry.add( ApacheSchemaConstants.COLLECTIVE_ATTRIBUTE_SEQ_NUMBER_AT, initSeqNumber); |
| entry.add( ApacheSchemaConstants.SUB_SCHEMA_SEQ_NUMBER_AT, initSeqNumber); |
| entry.add( ApacheSchemaConstants.TRIGGER_EXECUTION_SEQ_NUMBER_AT, initSeqNumber); |
| |
| return; |
| } |
| |
| if ( adminPoint.contains( AdministrativeRole.AccessControlSpecificArea.getRole() ) || |
| adminPoint.contains( AdministrativeRole.AccessControlInnerArea.getRole() ) ) |
| { |
| entry.add( ApacheSchemaConstants.ACCESS_CONTROL_SEQ_NUMBER_AT, initSeqNumber); |
| } |
| |
| if ( adminPoint.contains( AdministrativeRole.CollectiveAttributeSpecificArea.getRole() ) || |
| adminPoint.contains( AdministrativeRole.CollectiveAttributeInnerArea.getRole() ) ) |
| { |
| entry.add( ApacheSchemaConstants.COLLECTIVE_ATTRIBUTE_SEQ_NUMBER_AT, initSeqNumber); |
| } |
| |
| if ( adminPoint.contains( AdministrativeRole.SubSchemaSpecificArea.getRole() ) ) |
| { |
| entry.add( ApacheSchemaConstants.SUB_SCHEMA_SEQ_NUMBER_AT, initSeqNumber); |
| } |
| |
| if ( adminPoint.contains( AdministrativeRole.TriggerExecutionSpecificArea.getRole() ) || |
| adminPoint.contains( AdministrativeRole.TriggerExecutionInnerArea.getRole() )) |
| { |
| entry.add( ApacheSchemaConstants.TRIGGER_EXECUTION_SEQ_NUMBER_AT, initSeqNumber); |
| } |
| } |
| |
| |
| //------------------------------------------------------------------------------------------- |
| // Interceptor API methods |
| //------------------------------------------------------------------------------------------- |
| /** |
| * Add a new entry into the DIT. We deal with the Administrative aspects. |
| * We have to manage the three kind of added element : |
| * <ul> |
| * <li>APs</li> |
| * <li>SubEntries</li> |
| * <li>Entries</li> |
| * </ul> |
| * |
| * @param next The next {@link Interceptor} in the chain |
| * @param addContext The {@link AddOperationContext} instance |
| * @throws LdapException If we had some error while processing the Add operation |
| */ |
| public void add( NextInterceptor next, AddOperationContext addContext ) throws LdapException |
| { |
| LOG.debug( "Entering into the Subtree Interceptor, addRequest : {}", addContext ); |
| |
| DN dn = addContext.getDn(); |
| Entry entry = addContext.getEntry(); |
| |
| boolean isAdmin = addContext.getSession().getAuthenticatedPrincipal().getName().equals( |
| ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| |
| // Check if we are adding an Administrative Point |
| EntryAttribute adminPointAT = entry.get( ADMINISTRATIVE_ROLE_AT ); |
| |
| // First, deal with an AP addition |
| if ( adminPointAT != null ) |
| { |
| // Only admin can add an AP |
| if ( !isAdmin ) |
| { |
| String message = "Cannot add the given AdministrativePoint, user is not an Admin"; |
| LOG.error( message ); |
| |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| |
| LOG.debug( "Addition of an administrative point at {} for the roles {}", dn, adminPointAT ); |
| |
| try |
| { |
| // Protect the AP caches against concurrent access |
| lockWrite(); |
| |
| // This is an AP, do the initial check |
| if ( !checkIsValidAP( entry ) ) |
| { |
| String message = "Cannot add the given AP, it's not a valid one :" + entry; |
| LOG.error( message ); |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| |
| // Add a negative seqNumber for each role this AP manage |
| initSeqNumber( entry, adminPointAT ); |
| |
| // Ok, we are golden. |
| next.add( addContext ); |
| |
| String apUuid = entry.get( ENTRY_UUID_AT ).getString(); |
| |
| // Now, update the AdminPoint cache |
| createAdministrativePoints( adminPointAT, dn, apUuid, -1 ); |
| } |
| finally |
| { |
| // Release the APCaches lock |
| unlock(); |
| } |
| |
| LOG.debug( "Added an Administrative Point at {}", dn ); |
| } |
| else if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) |
| { |
| // It's a subentry |
| if ( !isAdmin ) |
| { |
| String message = "Cannot add the given Subentry, user is not an Admin"; |
| LOG.error( message ); |
| |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| |
| // Get the administrativePoint role : we must have one immediately |
| // upper |
| DN apDn = dn.getParent(); |
| |
| try |
| { |
| // Protect the AP caches while checking the subentry |
| lockRead(); |
| |
| if ( !isValidSubentry( entry, apDn ) ) |
| { |
| String message = "Cannot add the given Subentry, it does not have a parent AP"; |
| LOG.error( message ); |
| |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| |
| // Create the Subentry |
| Subentry[] subentries = createSubentry( entry ); |
| |
| // Now inject the subentry into the backend |
| next.add( addContext ); |
| |
| // Update the seqNumber and update the parent AP |
| updateAPSeqNumbers( OperationEnum.ADD, apDn, entry, subentries ); |
| |
| // Update the subentry cache |
| for ( Subentry subentry : subentries ) |
| { |
| if ( subentry != null ) |
| { |
| directoryService.getSubentryCache().addSubentry( dn, subentry ); |
| } |
| } |
| } |
| finally |
| { |
| // Release the APCaches lock |
| unlock(); |
| } |
| |
| LOG.debug( "Added a subentry at {}", dn ); |
| } |
| else |
| { |
| // The added entry is a normal entry |
| // We have to process the addition for each role |
| processAddEntry( entry ); |
| |
| // Propagate the addition down to the backend. |
| next.add( addContext ); |
| } |
| } |
| |
| |
| /** |
| * Remove the subentry from the associated AP, and update the AP seqNumber |
| */ |
| private void deleteAPSubentry(DnNode<AdministrativePoint> cache, DN apDn, Subentry subentry ) |
| { |
| AdministrativePoint adminPoint = cache.getElement( apDn ); |
| |
| Set<Subentry> apSubentries = adminPoint.getSubentries( subentry.getAdministrativeRole() ); |
| |
| for ( Subentry apSubentry : apSubentries ) |
| { |
| if ( apSubentry.equals( subentry ) ) |
| { |
| adminPoint.deleteSubentry( subentry ); |
| break; |
| } |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void delete( NextInterceptor next, DeleteOperationContext deleteContext ) throws LdapException |
| { |
| DN dn = deleteContext.getDn(); |
| Entry entry = deleteContext.getEntry(); |
| |
| // Check if we are deleting an Administrative Point |
| EntryAttribute adminPointAT = entry.get( ADMINISTRATIVE_ROLE_AT ); |
| |
| boolean isAdmin = deleteContext.getSession().getAuthenticatedPrincipal().getName().equals( |
| ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| |
| // First, deal with an AP deletion |
| if ( adminPointAT != null ) |
| { |
| if ( !isAdmin ) |
| { |
| String message = "Cannot delete the given AdministrativePoint, user is not an Admin"; |
| LOG.error( message ); |
| |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| |
| // It's an AP : we can delete the entry, and if done successfully, |
| // we can update the APCache for each role |
| next.delete( deleteContext ); |
| |
| // Now, update the AP cache |
| deleteAdministrativePoints( adminPointAT, dn ); |
| } |
| else if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) |
| { |
| // It's a subentry. We must be admin to remove it |
| if ( !isAdmin ) |
| { |
| String message = "Cannot delete the given Subentry, user is not an Admin"; |
| LOG.error( message ); |
| |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| |
| // Now delete the subentry itself |
| next.delete( deleteContext ); |
| |
| // Get the administrativePoint role : we must have one immediately |
| // upper |
| DN apDn = dn.getParent(); |
| |
| // As the deleted entry subentry can handle more than one role, we have to get all |
| // the subentries and delete their associated AP (those with the same role) |
| Subentry[] subentries = directoryService.getSubentryCache().getSubentries( dn ); |
| |
| for ( Subentry subentry : subentries ) |
| { |
| if ( subentry == null ) |
| { |
| continue; |
| } |
| |
| switch ( subentry.getAdministrativeRole() ) |
| { |
| case AccessControl : |
| deleteAPSubentry( directoryService.getAccessControlAPCache(), apDn, subentry ); |
| break; |
| |
| case CollectiveAttribute : |
| deleteAPSubentry( directoryService.getCollectiveAttributeAPCache(), apDn, subentry ); |
| break; |
| |
| case SubSchema : |
| deleteAPSubentry( directoryService.getSubschemaAPCache(), apDn, subentry ); |
| break; |
| |
| case TriggerExecution : |
| deleteAPSubentry( directoryService.getTriggerExecutionAPCache(), apDn, subentry ); |
| break; |
| |
| } |
| |
| // Cleanup the subentry cache |
| directoryService.getSubentryCache().removeSubentry( dn ); |
| |
| } |
| |
| // And finally, update the parent AP SeqNumber for each role the subentry manage |
| //Set<AdministrativeRoleEnum> subentryRoles = subentry.getAdministrativeRoles(); |
| updateAPSeqNumbers( OperationEnum.REMOVE, apDn, entry, subentries ); |
| } |
| else |
| { |
| // This is a normal entry : propagate the deletion down to the backend |
| next.delete( deleteContext ); |
| } |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Entry lookup( NextInterceptor nextInterceptor, LookupOperationContext lookupContext ) |
| throws LdapException |
| { |
| Entry entry = nextInterceptor.lookup( lookupContext ); |
| |
| // We have to update the entry now. At this point, we have no idea if the |
| // returned entry is an AP or a subentry. Check that now |
| Entry originalEntry = ((ClonedServerEntry)entry).getOriginalEntry(); |
| |
| if ( originalEntry.containsAttribute( ADMINISTRATIVE_ROLE_AT ) || |
| originalEntry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) |
| { |
| // No need to update anything |
| return entry; |
| } |
| |
| // This is a normal entry, update its seqNumbers if needed |
| List<Modification> modifications = updateEntry( entry ); |
| |
| if ( modifications != null ) |
| { |
| // update the entry |
| applyModifications( entry, modifications ); |
| } |
| |
| return entry; |
| } |
| |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public EntryFilteringCursor list( NextInterceptor nextInterceptor, ListOperationContext listContext ) |
| throws LdapException |
| { |
| EntryFilteringCursor cursor = nextInterceptor.list( listContext ); |
| |
| if ( !isSubentriesControlPresent( listContext ) ) |
| { |
| cursor.addEntryFilter( new HideSubentriesFilter() ); |
| } |
| |
| return cursor; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| * |
| public void modify( NextInterceptor next, 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 = null; //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[0].setSubtreeSpecification( ssNew ); |
| directoryService.getSubentryCache().addSubentry( dn, subentry[0]); |
| |
| next.modify( modifyContext ); |
| |
| // search for all entries selected by the old SS and remove references to subentry |
| DN apName = dn.getParent(); |
| DN oldBaseDn = apName; |
| oldBaseDn = oldBaseDn.addAll( 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 ( evaluator.evaluate( ssOld, apName, candidateDn, candidate ) ) |
| { |
| nexus.modify( new ModifyOperationContext( modifyContext.getSession(), candidateDn, |
| getOperationalModsForRemove( dn, candidate ) ) ); |
| } |
| } |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapOperationErrorException( e.getMessage() ); |
| } |
| |
| // search for all selected entries by the new SS and add references to subentry |
| subentry = directoryService.getSubentryUuidCache().getSubentry( dn.toString() ); |
| List<EntryAttribute> operationalAttributes = getSubentryOperationalAttributes( dn, subentry ); |
| DN newBaseDn = apName; |
| newBaseDn = newBaseDn.addAll( 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 ( evaluator.evaluate( ssNew, apName, candidateDn, candidate ) ) |
| { |
| nexus.modify( new ModifyOperationContext( modifyContext.getSession(), candidateDn, |
| getOperationalModsForAdd( candidate, operationalAttributes ) ) ); |
| } |
| } |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapOperationErrorException( e.getMessage() ); |
| } |
| } |
| else |
| { |
| next.modify( 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( NextInterceptor next, 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.getSubentryUuidCache().removeSubentry( oldDn.toString() ); |
| SubtreeSpecification ss = subentry.getSubtreeSpecification(); |
| DN apName = oldDn.getParent(); |
| DN baseDn = apName; |
| baseDn = baseDn.addAll( ss.getBase() ); |
| DN newName = newSuperiorDn; |
| newName = newName.add( oldDn.getRdn() ); |
| newName.normalize( schemaManager ); |
| |
| directoryService.getSubentryUuidCache().addSubentry( subentry ); |
| |
| next.move( moveContext ); |
| |
| subentry = directoryService.getSubentryUuidCache().getSubentry( newName.toString() ); |
| |
| 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.normalize( schemaManager ); |
| |
| if ( evaluator.evaluate( ss, apName, dn, candidate ) ) |
| { |
| nexus.modify( new ModifyOperationContext( moveContext.getSession(), dn, getOperationalModsForReplace( |
| oldDn, newName, subentry, candidate ) ) ); |
| } |
| } |
| |
| subentries.close(); |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapOperationException( e.getMessage() ); |
| } |
| } |
| 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.move( 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( NextInterceptor next, 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.getSubentryUuidCache().removeSubentry( oldDn.toString() ); |
| SubtreeSpecification ss = subentry.getSubtreeSpecification(); |
| DN apName = oldDn.getParent(); |
| DN baseDn = apName; |
| baseDn = baseDn.addAll( ss.getBase() ); |
| DN newName = newSuperiorDn.getParent(); |
| |
| newName = newName.add( moveAndRenameContext.getNewRdn() ); |
| newName.normalize( schemaManager ); |
| |
| directoryService.getSubentryUuidCache().addSubentry( subentry ); |
| |
| next.moveAndRename( moveAndRenameContext ); |
| |
| subentry = subentryCache.getSubentry( newName.toString() ); |
| |
| 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.normalize( schemaManager ); |
| |
| if ( evaluator.evaluate( ss, apName, dn, candidate ) ) |
| { |
| nexus.modify( new ModifyOperationContext( moveAndRenameContext.getSession(), dn, getOperationalModsForReplace( |
| oldDn, newName, subentry, candidate ) ) ); |
| } |
| } |
| |
| 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<Modification> mods = getModsOnEntryRdnChange( oldDn, newDn, entry ); |
| |
| if ( mods.size() > 0 ) |
| { |
| nexus.modify( new ModifyOperationContext( moveAndRenameContext.getSession(), newDn, mods ) ); |
| } |
| } |
| } */ |
| |
| |
| /** |
| * Update the AP caches when renaming an AP |
| */ |
| private void applyRenameApCache( DN oldDn, DN newDn ) throws LdapException |
| { |
| List<AdministrativePoint> adminpoints = getAdministrativePoints( oldDn ); |
| |
| for ( AdministrativePoint adminPoint : adminpoints ) |
| { |
| switch ( adminPoint.getRole() ) |
| { |
| case AccessControlSpecificArea : |
| case AccessControlInnerArea : |
| directoryService.getAccessControlAPCache().remove( oldDn ); |
| directoryService.getAccessControlAPCache().add( newDn, adminPoint ); |
| break; |
| |
| case CollectiveAttributeSpecificArea : |
| case CollectiveAttributeInnerArea : |
| directoryService.getCollectiveAttributeAPCache().remove( oldDn ); |
| directoryService.getCollectiveAttributeAPCache().add( newDn, adminPoint ); |
| break; |
| |
| case SubSchemaSpecificArea : |
| directoryService.getSubschemaAPCache().remove( oldDn ); |
| directoryService.getSubschemaAPCache().add( newDn, adminPoint ); |
| break; |
| |
| case TriggerExecutionSpecificArea : |
| case TriggerExecutionInnerArea : |
| directoryService.getTriggerExecutionAPCache().remove( oldDn ); |
| directoryService.getTriggerExecutionAPCache().add( newDn, adminPoint ); |
| break; |
| } |
| } |
| } |
| |
| |
| /** |
| * Get the set of roles this AP is handling |
| */ |
| private Set<AdministrativeRoleEnum> getRoles( EntryAttribute adminPointAT ) |
| { |
| Set<AdministrativeRoleEnum> roles = new HashSet<AdministrativeRoleEnum>(); |
| |
| for ( Value<?> attributeRole : adminPointAT ) |
| { |
| String role = attributeRole.getString(); |
| |
| if ( isAccessControlInnerRole( role ) || isAccessControlSpecificRole( role ) ) |
| { |
| roles.add( AdministrativeRoleEnum.AccessControl ); |
| } |
| else if ( isCollectiveAttributeInnerRole( role ) || isAccessControlSpecificRole( role ) ) |
| { |
| roles.add( AdministrativeRoleEnum.CollectiveAttribute ); |
| } |
| else if ( isSubschemaSpecificRole( role ) ) |
| { |
| roles.add( AdministrativeRoleEnum.SubSchema ); |
| } |
| else if ( isTriggerExecutionInnerRole( role ) || isTriggerExecutionSpecificRole( role ) ) |
| { |
| roles.add( AdministrativeRoleEnum.TriggerExecution ); |
| } |
| } |
| |
| return roles; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void rename( NextInterceptor next, RenameOperationContext renameContext ) throws LdapException |
| { |
| LOG.debug( "Entering into the Subtree Interceptor, renameRequest : {}", renameContext ); |
| DN oldDn = renameContext.getDn(); |
| RDN oldRdn = oldDn.getRdn(); |
| RDN newRdn = renameContext.getNewRdn(); |
| DN newDn = oldDn.getParent().add( newRdn ); |
| |
| Entry entry = renameContext.getEntry().getClonedEntry(); |
| |
| boolean isAdmin = renameContext.getSession().getAuthenticatedPrincipal().getName().equals( |
| ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| |
| // Check if we are adding an Administrative Point |
| EntryAttribute adminPointAT = entry.get( ADMINISTRATIVE_ROLE_AT ); |
| |
| // First, deal with an AP addition |
| if ( adminPointAT != null ) |
| { |
| // This is an AP. If it's a SAP, we have nothing to do, as a rename does not modify |
| // the subtree evaluations, nor does it impact any underlying entries. We just have to |
| // update the AP caches. |
| // If the AP is an IAP, then as the underlying entries will be modified, then |
| // we have to update the IAP seqNumber : the underlying entries might be impacted |
| // as the parent's AP for the renamed IAP may have a base or some chopBefore/chopAfter |
| // specificExclusion that depend on the old name. |
| if ( isIAP( adminPointAT) ) |
| { |
| // First apply the rename |
| next.rename( renameContext ); |
| |
| // Update the caches |
| applyRenameApCache( oldDn, newDn ); |
| |
| // And check if we have to change the parent's AP seqNumbers |
| List<Modification> modifications = new ArrayList<Modification>(); |
| long newSeqNumber = -1L; |
| |
| for ( AdministrativeRoleEnum role : getRoles( adminPointAT ) ) |
| { |
| switch ( role ) |
| { |
| case AccessControl : |
| break; |
| |
| case CollectiveAttribute : |
| DnNode<AdministrativePoint> apCache = directoryService.getCollectiveAttributeAPCache(); |
| DnNode<AdministrativePoint> apNode = apCache.getNode( newDn ); |
| |
| // We have an AdministrativePoint for this entry, get its SeqNumber |
| AdministrativePoint adminPoint = apNode.getElement(); |
| AdministrativePoint currentAP = apNode.getElement(); |
| EntryAttribute seqNumberAttribute = entry.get( COLLECTIVE_ATTRIBUTE_SEQ_NUMBER_AT ); |
| |
| // We have to recurse : starting from the IAP, we go up the AP tree |
| // until we find the SAP. For each AP we find, we check the Subentries |
| // to see if any of them have a localname containing the oldDn. if so, we |
| // will update the AP seqNumber for this role. |
| |
| // First, init the entry seqNumber. If we have no AT, then we initialize it to -1 |
| boolean sapFound = false; |
| boolean updateSN = false; |
| |
| do |
| { |
| if ( currentAP.isSpecific() ) |
| { |
| sapFound = true; |
| } |
| |
| Set<Subentry> subentries = currentAP.getSubentries(); |
| |
| if ( ( subentries != null ) && ( subentries.size() != 0 ) ) |
| { |
| for ( Subentry subentry : subentries ) |
| { |
| SubtreeSpecification ss = subentry.getSubtreeSpecification(); |
| |
| Set<DN> dns = new HashSet<DN>(); |
| |
| if ( ss.getBase() != null ) |
| { |
| dns.add( ss.getBase() ); |
| } |
| |
| dns.addAll( ss.getChopBeforeExclusions() ); |
| dns.addAll( ss.getChopAfterExclusions() ); |
| |
| for ( DN dn : dns ) |
| { |
| DN fullDn = apNode.getDn().addAll( dn ); |
| |
| if ( oldDn.isParentOf( fullDn ) ) |
| { |
| // We have to update this AP's seqNumber |
| updateSN = true; |
| break; |
| } |
| } |
| |
| if ( updateSN ) |
| { |
| break; |
| } |
| } |
| } |
| |
| // Go down one level |
| apNode = apNode.getParentWithElement(); |
| currentAP = apNode.getElement(); |
| } while ( !sapFound && !updateSN ); |
| |
| if ( updateSN ) |
| { |
| if ( newSeqNumber == -1L ) |
| { |
| newSeqNumber = directoryService.getNewApSeqNumber(); |
| |
| adminPoint.setSeqNumber( newSeqNumber ); |
| |
| EntryAttribute newSeqNumberAT = new DefaultEntryAttribute( COLLECTIVE_ATTRIBUTE_SEQ_NUMBER_AT, Long.toString( newSeqNumber ) ); |
| Modification seqNumberModification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, newSeqNumberAT ); |
| |
| modifications.add( seqNumberModification ); |
| } |
| } |
| break; |
| |
| case SubSchema : |
| case TriggerExecution : |
| } |
| |
| // Update the AdminPoint |
| |
| |
| // Now, update the AP entry |
| // If we have updated the entry, create the list of modifications to apply |
| if ( modifications.size() > 0 ) |
| { |
| ModifyOperationContext modCtx = new ModifyOperationContext( directoryService.getAdminSession() ); |
| modCtx.setByPassed( BYPASS_INTERCEPTORS ); |
| modCtx.setDn( entry.getDn() ); |
| modCtx.setModItems( modifications ); |
| modCtx.setEntry( renameContext.getEntry() ); |
| |
| directoryService.getOperationManager().modify( modCtx ); |
| } |
| } |
| } |
| else |
| { |
| next.rename( renameContext ); |
| |
| // Now, update the caches by removing the old reference to AP and adding the new one |
| applyRenameApCache( oldDn, newDn ); |
| } |
| } |
| else if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) ) |
| { |
| // First check that the rename is legal : the new RDN must be a valid CN |
| AttributeType newAT = directoryService.getSchemaManager().getAttributeType( newRdn.getNormType() ); |
| |
| if ( !CN_AT.equals( newAT ) ) |
| { |
| String message = "Cannot rename a subentry using an AttributeType which is not CN : " + renameContext; |
| LOG.error( message ); |
| throw new LdapUnwillingToPerformException( message ); |
| } |
| |
| // Get the new name |
| EntryAttribute newCn = new DefaultEntryAttribute( CN_AT, newRdn.getUpValue() ); |
| |
| // It's a subentry : we just have to update the subentryCache |
| next.rename( renameContext ); |
| |
| // We can update the Subentry cache, removing the old subentry and |
| // adding the new subentry with the new CN |
| Subentry[] subentries = directoryService.getSubentryCache().removeSubentry( oldDn ); |
| |
| for ( Subentry subentry : subentries ) |
| { |
| if ( subentry != null ) |
| { |
| subentry.setCn( newCn ); |
| directoryService.getSubentryCache().addSubentry( newDn, subentry ); |
| } |
| } |
| } |
| else |
| { |
| // A normal entry |
| next.rename( renameContext ); |
| } |
| /* |
| Subentry subentry = directoryService.getSubentryUuidCache().removeSubentry( oldDn.toString() ); |
| SubtreeSpecification ss = subentry.getSubtreeSpecification(); |
| DN apName = oldDn.getParent(); |
| DN baseDn = apName; |
| baseDn = baseDn.addAll( ss.getBase() ); |
| DN newName = oldDn.getParent(); |
| |
| newName = newName.add( renameContext.getNewRdn() ); |
| newName.normalize( schemaManager ); |
| |
| directoryService.getSubentryUuidCache().addSubentry( subentry ); |
| next.rename( renameContext ); |
| |
| subentry = directoryService.getSubentryUuidCache().getSubentry( newName.toString() ); |
| 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 ); |
| |
| 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 |
| { |
| if ( hasAdministrativeDescendant( renameContext, oldDn ) ) |
| { |
| String msg = I18n.err( I18n.ERR_308 ); |
| LOG.warn( msg ); |
| throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg ); |
| } |
| |
| 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<Modification> mods = getModsOnEntryRdnChange( oldDn, newName, entry ); |
| |
| if ( mods.size() > 0 ) |
| { |
| nexus.modify( new ModifyOperationContext( renameContext.getSession(), newName, mods ) ); |
| } |
| } |
| */ |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public EntryFilteringCursor search( NextInterceptor nextInterceptor, SearchOperationContext searchContext ) |
| throws LdapException |
| { |
| EntryFilteringCursor cursor = nextInterceptor.search( searchContext ); |
| |
| // If the Subentries control is present, we return only the subentries |
| if ( isSubentriesControlPresent( searchContext ) ) |
| { |
| cursor.addEntryFilter( new HideEntriesFilter() ); |
| } |
| else |
| { |
| // If the scope is not OBJECT, we don't return the subentries |
| if ( searchContext.getScope() != SearchScope.OBJECT ) |
| { |
| cursor.addEntryFilter( new HideSubentriesFilter() ); |
| cursor.addEntryFilter( new SeqNumberUpdateFilter() ); |
| } |
| else |
| { |
| // Return everything |
| cursor.addEntryFilter( new SeqNumberUpdateFilter() ); |
| } |
| } |
| |
| return cursor; |
| } |
| |
| |
| //------------------------------------------------------------------------------------------- |
| // Shared method |
| //------------------------------------------------------------------------------------------- |
| /** |
| * 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 |
| */ |
| public Entry getSubentryAttributes( DN dn, Entry entryAttrs ) throws LdapException |
| { |
| Entry subentryAttrs = new DefaultEntry( schemaManager, dn ); |
| |
| for ( String subentryUuid : subentryCache ) |
| { |
| DN subentryDn = null; |
| DN apDn = null; //subentryDn.getParent(); |
| Subentry subentry = null; //subentryCache.getSubentry( subentryDn ); |
| SubtreeSpecification ss = subentry.getSubtreeSpecification(); |
| |
| if ( evaluator.evaluate( ss, apDn, dn, entryAttrs ) ) |
| { |
| EntryAttribute operational; |
| |
| if ( subentry.isAccessControlAdminRole() ) |
| { |
| operational = subentryAttrs.get( ACCESS_CONTROL_SUBENTRIES_AT ); |
| |
| if ( operational == null ) |
| { |
| operational = new DefaultEntryAttribute( ACCESS_CONTROL_SUBENTRIES_AT ); |
| subentryAttrs.put( operational ); |
| } |
| |
| operational.add( subentryDn.getNormName() ); |
| } |
| |
| if ( subentry.isSchemaAdminRole() ) |
| { |
| operational = subentryAttrs.get( SUBSCHEMA_SUBENTRY_AT ); |
| |
| if ( operational == null ) |
| { |
| operational = new DefaultEntryAttribute( SUBSCHEMA_SUBENTRY_AT ); |
| subentryAttrs.put( operational ); |
| } |
| |
| operational.add( subentryDn.getNormName() ); |
| } |
| |
| if ( subentry.isCollectiveAdminRole() ) |
| { |
| operational = subentryAttrs.get( COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ); |
| |
| if ( operational == null ) |
| { |
| operational = new DefaultEntryAttribute( COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ); |
| subentryAttrs.put( operational ); |
| } |
| |
| operational.add( subentryDn.getNormName() ); |
| } |
| |
| if ( subentry.isTriggersAdminRole() ) |
| { |
| operational = subentryAttrs.get( TRIGGER_EXECUTION_SUBENTRIES_AT ); |
| |
| if ( operational == null ) |
| { |
| operational = new DefaultEntryAttribute( TRIGGER_EXECUTION_SUBENTRIES_AT ); |
| subentryAttrs.put( operational ); |
| } |
| |
| operational.add( subentryDn.getNormName() ); |
| } |
| } |
| } |
| |
| return subentryAttrs; |
| } |
| } |