blob: ff5a2a2491381720831d7eb9a50527a7a6b7603c [file] [log] [blame]
/*
* 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;
}
}