blob: 647d1ef9c21b1533e3a134f8caaa65ff7d451f01 [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 org.apache.directory.server.core.interceptor.BaseInterceptor;
import org.apache.directory.server.constants.ApacheSchemaConstants;
import org.apache.directory.server.core.DirectoryService;
import org.apache.directory.server.core.entry.DefaultServerAttribute;
import org.apache.directory.server.core.entry.DefaultServerEntry;
import org.apache.directory.server.core.entry.ServerAttribute;
import org.apache.directory.server.core.entry.ServerEntry;
import org.apache.directory.server.core.entry.ServerModification;
import org.apache.directory.server.core.entry.ServerSearchResult;
import org.apache.directory.server.core.enumeration.SearchResultFilter;
import org.apache.directory.server.core.enumeration.SearchResultFilteringEnumeration;
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.MoveAndRenameOperationContext;
import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
import org.apache.directory.server.core.invocation.Invocation;
import org.apache.directory.server.core.invocation.InvocationStack;
import org.apache.directory.server.core.partition.PartitionNexus;
import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
import org.apache.directory.server.schema.registries.OidRegistry;
import org.apache.directory.server.schema.registries.Registries;
import org.apache.directory.shared.ldap.constants.SchemaConstants;
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.Value;
import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException;
import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
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.message.AliasDerefMode;
import org.apache.directory.shared.ldap.message.ResultCodeEnum;
import org.apache.directory.shared.ldap.message.SubentriesControl;
import org.apache.directory.shared.ldap.name.LdapDN;
import org.apache.directory.shared.ldap.schema.AttributeType;
import org.apache.directory.shared.ldap.schema.NormalizerMappingResolver;
import org.apache.directory.shared.ldap.schema.OidNormalizer;
import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
import org.apache.directory.shared.ldap.subtree.SubtreeSpecificationParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.ldap.Control;
import javax.naming.ldap.LdapContext;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* The Subentry interceptor service which is responsible for filtering
* out subentries on search operations and injecting operational attributes
*
* @org.apache.xbean.XBean
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$
*/
public class SubentryInterceptor extends BaseInterceptor
{
/** the subentry control OID */
private static final String SUBENTRY_CONTROL = SubentriesControl.CONTROL_OID;
public static final String AC_AREA = "accessControlSpecificArea";
public static final String AC_INNERAREA = "accessControlInnerArea";
public static final String SCHEMA_AREA = "subschemaAdminSpecificArea";
public static final String COLLECTIVE_AREA = "collectiveAttributeSpecificArea";
public static final String COLLECTIVE_INNERAREA = "collectiveAttributeInnerArea";
public static final String TRIGGER_AREA = "triggerExecutionSpecificArea";
public static final String TRIGGER_INNERAREA = "triggerExecutionInnerArea";
public static final String[] SUBENTRY_OPATTRS =
{ SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, SchemaConstants.SUBSCHEMA_SUBENTRY_AT,
SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT };
private static final Logger LOG = LoggerFactory.getLogger( SubentryInterceptor.class );
/** the hash mapping the DN of a subentry to its SubtreeSpecification/types */
private final SubentryCache subentryCache = new SubentryCache();
private SubtreeSpecificationParser ssParser;
private SubtreeEvaluator evaluator;
private PartitionNexus nexus;
/** The global registries */
private Registries registries;
/** The AttributeType registry */
private AttributeTypeRegistry atRegistry;
/** The OID registry */
private OidRegistry oidRegistry;
private AttributeType objectClassType;
public void init( DirectoryService directoryService ) throws NamingException
{
super.init( directoryService );
nexus = directoryService.getPartitionNexus();
registries = directoryService.getRegistries();
atRegistry = registries.getAttributeTypeRegistry();
oidRegistry = registries.getOidRegistry();
// setup various attribute type values
objectClassType = atRegistry.lookup( oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT ) );
ssParser = new SubtreeSpecificationParser( new NormalizerMappingResolver()
{
public Map<String, OidNormalizer> getNormalizerMapping() throws NamingException
{
return atRegistry.getNormalizerMapping();
}
}, atRegistry.getNormalizerMapping() );
evaluator = new SubtreeEvaluator( oidRegistry, atRegistry );
// prepare to find all subentries in all namingContexts
Iterator<String> suffixes = this.nexus.listSuffixes( null );
ExprNode filter = new EqualityNode( SchemaConstants.OBJECT_CLASS_AT, new ClientStringValue(
SchemaConstants.SUBENTRY_OC ) );
SearchControls controls = new SearchControls();
controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
controls.setReturningAttributes( new String[]
{ SchemaConstants.SUBTREE_SPECIFICATION_AT, SchemaConstants.OBJECT_CLASS_AT } );
// search each namingContext for subentries
while ( suffixes.hasNext() )
{
LdapDN suffix = new LdapDN( suffixes.next() );
//suffix = LdapDN.normalize( suffix, registry.getNormalizerMapping() );
suffix.normalize( atRegistry.getNormalizerMapping() );
NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
suffix, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
while ( subentries.hasMore() )
{
ServerSearchResult result = subentries.next();
LdapDN dnName = new LdapDN( result.getDn() );
ServerEntry subentry = result.getServerEntry();
String subtree = subentry.get( SchemaConstants.SUBTREE_SPECIFICATION_AT ).getString();
SubtreeSpecification ss;
try
{
ss = ssParser.parse( subtree );
}
catch ( Exception e )
{
LOG.warn( "Failed while parsing subtreeSpecification for " + dnName );
continue;
}
dnName.normalize( atRegistry.getNormalizerMapping() );
subentryCache.setSubentry( dnName.toString(), ss, getSubentryTypes( subentry ) );
}
}
}
private int getSubentryTypes( ServerEntry subentry ) throws NamingException
{
int types = 0;
EntryAttribute oc = subentry.get( SchemaConstants.OBJECT_CLASS_AT );
if ( oc == null )
{
throw new LdapSchemaViolationException( "A subentry must have an objectClass attribute",
ResultCodeEnum.OBJECT_CLASS_VIOLATION );
}
if ( oc.contains( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) )
{
types |= Subentry.ACCESS_CONTROL_SUBENTRY;
}
if ( oc.contains( "subschema" ) )
{
types |= Subentry.SCHEMA_SUBENTRY;
}
if ( oc.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC ) )
{
types |= Subentry.COLLECTIVE_SUBENTRY;
}
if ( oc.contains( ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) )
{
types |= Subentry.TRIGGER_SUBENTRY;
}
return types;
}
// -----------------------------------------------------------------------
// Methods/Code dealing with Subentry Visibility
// -----------------------------------------------------------------------
public NamingEnumeration<ServerSearchResult> list( NextInterceptor nextInterceptor, ListOperationContext opContext )
throws NamingException
{
NamingEnumeration<ServerSearchResult> result = nextInterceptor.list( opContext );
Invocation invocation = InvocationStack.getInstance().peek();
if ( !isSubentryVisible( invocation ) )
{
return new SearchResultFilteringEnumeration( result, new SearchControls(), invocation,
new HideSubentriesFilter(), "List Subentry filter" );
}
return result;
}
public NamingEnumeration<ServerSearchResult> search( NextInterceptor nextInterceptor,
SearchOperationContext opContext ) throws NamingException
{
NamingEnumeration<ServerSearchResult> result = nextInterceptor.search( opContext );
Invocation invocation = InvocationStack.getInstance().peek();
SearchControls searchCtls = opContext.getSearchControls();
// object scope searches by default return subentries
if ( searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE )
{
return result;
}
// for subtree and one level scope we filter
if ( !isSubentryVisible( invocation ) )
{
return new SearchResultFilteringEnumeration( result, searchCtls, invocation, new HideSubentriesFilter(),
"Search Subentry filter hide subentries" );
}
else
{
return new SearchResultFilteringEnumeration( result, searchCtls, invocation, new HideEntriesFilter(),
"Search Subentry filter hide entries" );
}
}
/**
* 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 NamingException if there are problems accessing request controls
*/
private boolean isSubentryVisible( Invocation invocation ) throws NamingException
{
Control[] reqControls = ( ( LdapContext ) invocation.getCaller() ).getRequestControls();
if ( reqControls == null || reqControls.length <= 0 )
{
return false;
}
// check all request controls to see if subentry control is present
for ( Control reqControl : reqControls )
{
// found the subentry request control so we return its value
if ( reqControl.getID().equals( SUBENTRY_CONTROL ) )
{
SubentriesControl subentriesControl = ( SubentriesControl ) reqControl;
return subentriesControl.isVisible();
}
}
return false;
}
// -----------------------------------------------------------------------
// Methods dealing with entry and subentry addition
// -----------------------------------------------------------------------
/**
* Evaluates the set of subentry subtrees upon an entry and returns the
* operational subentry attributes that will be added to the entry if
* added at the dn specified.
*
* @param dn the normalized distinguished name of the entry
* @param entryAttrs the entry attributes are generated for
* @return the set of subentry op attrs for an entry
* @throws NamingException if there are problems accessing entry information
*/
public ServerEntry getSubentryAttributes( LdapDN dn, ServerEntry entryAttrs ) throws NamingException
{
ServerEntry subentryAttrs = new DefaultServerEntry( registries, dn );
Iterator<String> list = subentryCache.nameIterator();
while ( list.hasNext() )
{
String subentryDnStr = list.next();
LdapDN subentryDn = new LdapDN( subentryDnStr );
LdapDN apDn = ( LdapDN ) subentryDn.clone();
apDn.remove( apDn.size() - 1 );
Subentry subentry = subentryCache.getSubentry( subentryDnStr );
SubtreeSpecification ss = subentry.getSubtreeSpecification();
if ( evaluator.evaluate( ss, apDn, dn, entryAttrs ) )
{
EntryAttribute operational;
if ( subentry.isAccessControlSubentry() )
{
operational = subentryAttrs.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT );
if ( operational == null )
{
operational = new DefaultServerAttribute( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT,
atRegistry.lookup( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) );
subentryAttrs.put( operational );
}
operational.add( subentryDn.toString() );
}
if ( subentry.isSchemaSubentry() )
{
operational = subentryAttrs.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT );
if ( operational == null )
{
operational = new DefaultServerAttribute( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, atRegistry
.lookup( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) );
subentryAttrs.put( operational );
}
operational.add( subentryDn.toString() );
}
if ( subentry.isCollectiveSubentry() )
{
operational = subentryAttrs.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
if ( operational == null )
{
operational = new DefaultServerAttribute( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT,
atRegistry.lookup( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
subentryAttrs.put( operational );
}
operational.add( subentryDn.toString() );
}
if ( subentry.isTriggerSubentry() )
{
operational = subentryAttrs.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
if ( operational == null )
{
operational = new DefaultServerAttribute( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT,
atRegistry.lookup( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
subentryAttrs.put( operational );
}
operational.add( subentryDn.toString() );
}
}
}
return subentryAttrs;
}
public void add( NextInterceptor next, AddOperationContext addContext ) throws NamingException
{
LdapDN name = addContext.getDn();
ServerEntry entry = addContext.getEntry();
EntryAttribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT );
if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
{
// get the name of the administrative point and its administrativeRole attributes
LdapDN apName = ( LdapDN ) name.clone();
apName.remove( name.size() - 1 );
ServerEntry ap = nexus.lookup( new LookupOperationContext( registries, apName ) );
EntryAttribute administrativeRole = ap.get( "administrativeRole" );
// check that administrativeRole has something valid in it for us
if ( administrativeRole == null || administrativeRole.size() <= 0 )
{
throw new LdapNoSuchAttributeException( "Administration point " + apName
+ " does not contain an administrativeRole attribute! An"
+ " administrativeRole attribute in the administrative point is"
+ " required to add a subordinate subentry." );
}
/* ----------------------------------------------------------------
* Build the set of operational attributes to be injected into
* entries that are contained within the subtree repesented by this
* new subentry. In the process we make sure the proper roles are
* supported by the administrative point to allow the addition of
* this new subentry.
* ----------------------------------------------------------------
*/
Subentry subentry = new Subentry();
subentry.setTypes( getSubentryTypes( entry ) );
ServerEntry operational = getSubentryOperatationalAttributes( name, subentry );
/* ----------------------------------------------------------------
* Parse the subtreeSpecification of the subentry and add it to the
* SubtreeSpecification cache. If the parse succeeds we continue
* to add the entry to the DIT. Thereafter we search out entries
* to modify the subentry operational attributes of.
* ----------------------------------------------------------------
*/
String subtree = entry.get( SchemaConstants.SUBTREE_SPECIFICATION_AT ).getString();
SubtreeSpecification ss;
try
{
ss = ssParser.parse( subtree );
}
catch ( Exception e )
{
String msg = "Failed while parsing subtreeSpecification for " + name.getUpName();
LOG.warn( msg );
throw new LdapInvalidAttributeValueException( msg, ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
}
subentryCache.setSubentry( name.getNormName(), ss, getSubentryTypes( entry ) );
next.add( addContext );
/* ----------------------------------------------------------------
* Find the baseDn for the subentry and use that to search the tree
* while testing each entry returned for inclusion within the
* subtree of the subentry's subtreeSpecification. All included
* entries will have their operational attributes merged with the
* operational attributes calculated above.
* ----------------------------------------------------------------
*/
LdapDN baseDn = ( LdapDN ) apName.clone();
baseDn.addAll( ss.getBase() );
ExprNode filter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT_OID ); // (objectClass=*)
SearchControls controls = new SearchControls();
controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
controls.setReturningAttributes( new String[]
{ SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
baseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
while ( subentries.hasMore() )
{
ServerSearchResult result = subentries.next();
LdapDN dn = result.getDn();
dn.normalize( atRegistry.getNormalizerMapping() );
ServerEntry candidate = result.getServerEntry();
if ( evaluator.evaluate( ss, apName, dn, candidate ) )
{
nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForAdd( candidate,
operational ) ) );
}
}
addContext.setEntry( entry );
}
else
{
Iterator<String> list = subentryCache.nameIterator();
while ( list.hasNext() )
{
String subentryDnStr = list.next();
LdapDN subentryDn = new LdapDN( subentryDnStr );
LdapDN apDn = ( LdapDN ) subentryDn.clone();
apDn.remove( apDn.size() - 1 );
Subentry subentry = subentryCache.getSubentry( subentryDnStr );
SubtreeSpecification ss = subentry.getSubtreeSpecification();
if ( evaluator.evaluate( ss, apDn, name, entry ) )
{
EntryAttribute operational;
if ( subentry.isAccessControlSubentry() )
{
operational = entry.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT );
if ( operational == null )
{
operational = new DefaultServerAttribute( atRegistry
.lookup( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) );
entry.put( operational );
}
operational.add( subentryDn.toString() );
}
if ( subentry.isSchemaSubentry() )
{
operational = entry.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT );
if ( operational == null )
{
operational = new DefaultServerAttribute( atRegistry
.lookup( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) );
entry.put( operational );
}
operational.add( subentryDn.toString() );
}
if ( subentry.isCollectiveSubentry() )
{
operational = entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
if ( operational == null )
{
operational = new DefaultServerAttribute( atRegistry
.lookup( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
entry.put( operational );
}
operational.add( subentryDn.toString() );
}
if ( subentry.isTriggerSubentry() )
{
operational = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
if ( operational == null )
{
operational = new DefaultServerAttribute( atRegistry
.lookup( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
entry.put( operational );
}
operational.add( subentryDn.toString() );
}
}
}
addContext.setEntry( entry );
next.add( addContext );
}
}
// -----------------------------------------------------------------------
// Methods dealing subentry deletion
// -----------------------------------------------------------------------
public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws NamingException
{
LdapDN name = opContext.getDn();
ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, name ) );
EntryAttribute objectClasses = entry.get( objectClassType );
if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
{
SubtreeSpecification ss = subentryCache.removeSubentry( name.toNormName() ).getSubtreeSpecification();
next.delete( opContext );
/* ----------------------------------------------------------------
* Find the baseDn for the subentry and use that to search the tree
* for all entries included by the subtreeSpecification. Then we
* check the entry for subentry operational attribute that contain
* the DN of the subentry. These are the subentry operational
* attributes we remove from the entry in a modify operation.
* ----------------------------------------------------------------
*/
LdapDN apName = ( LdapDN ) name.clone();
apName.remove( name.size() - 1 );
LdapDN baseDn = ( LdapDN ) apName.clone();
baseDn.addAll( ss.getBase() );
ExprNode filter = new PresenceNode( oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT ) );
SearchControls controls = new SearchControls();
controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
controls.setReturningAttributes( new String[]
{ SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
baseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
while ( subentries.hasMore() )
{
ServerSearchResult result = subentries.next();
LdapDN dn = new LdapDN( result.getDn() );
dn.normalize( atRegistry.getNormalizerMapping() );
ServerEntry candidate = result.getServerEntry();
if ( evaluator.evaluate( ss, apName, dn, candidate ) )
{
nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForRemove( name,
candidate ) ) );
}
}
}
else
{
next.delete( opContext );
}
}
// -----------------------------------------------------------------------
// Methods dealing subentry name changes
// -----------------------------------------------------------------------
/**
* 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 NamingException if there are errors while searching the directory
*/
private boolean hasAdministrativeDescendant( LdapDN name ) throws NamingException
{
ExprNode filter = new PresenceNode( "administrativeRole" );
SearchControls controls = new SearchControls();
controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
NamingEnumeration<ServerSearchResult> aps = nexus.search( new SearchOperationContext( registries, name,
AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
if ( aps.hasMore() )
{
aps.close();
return true;
}
return false;
}
private List<Modification> getModsOnEntryRdnChange( Name oldName, Name newName, ServerEntry entry )
throws NamingException
{
List<Modification> modList = new ArrayList<Modification>();
/*
* There are two different situations warranting action. Firt if
* an ss evalutating to true with the old name no longer evalutates
* to true with the new name. This would be caused by specific chop
* exclusions that effect the new name but did not effect the old
* name. In this case we must remove subentry operational attribute
* values associated with the dn of that subentry.
*
* In the second case an ss selects the entry with the new name when
* it did not previously with the old name. Again this situation
* would be caused by chop exclusions. In this case we must add subentry
* operational attribute values with the dn of this subentry.
*/
Iterator<String> subentries = subentryCache.nameIterator();
while ( subentries.hasNext() )
{
String subentryDn = subentries.next();
Name apDn = new LdapDN( subentryDn );
apDn.remove( apDn.size() - 1 );
SubtreeSpecification ss = subentryCache.getSubentry( subentryDn ).getSubtreeSpecification();
boolean isOldNameSelected = evaluator.evaluate( ss, apDn, oldName, entry );
boolean isNewNameSelected = evaluator.evaluate( ss, apDn, newName, entry );
if ( isOldNameSelected == isNewNameSelected )
{
continue;
}
// need to remove references to the subentry
if ( isOldNameSelected && !isNewNameSelected )
{
for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS )
{
ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE;
EntryAttribute opAttr = entry.get( aSUBENTRY_OPATTRS );
if ( opAttr != null )
{
opAttr = ( ServerAttribute ) opAttr.clone();
opAttr.remove( subentryDn );
if ( opAttr.size() < 1 )
{
op = ModificationOperation.REMOVE_ATTRIBUTE;
}
modList.add( new ServerModification( op, opAttr ) );
}
}
}
// need to add references to the subentry
else if ( isNewNameSelected && !isOldNameSelected )
{
for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS )
{
ModificationOperation op = ModificationOperation.ADD_ATTRIBUTE;
ServerAttribute opAttr = new DefaultServerAttribute( aSUBENTRY_OPATTRS, atRegistry
.lookup( aSUBENTRY_OPATTRS ) );
opAttr.add( subentryDn );
modList.add( new ServerModification( op, opAttr ) );
}
}
}
return modList;
}
public void rename( NextInterceptor next, RenameOperationContext opContext ) throws NamingException
{
LdapDN name = opContext.getDn();
ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, name ) );
EntryAttribute objectClasses = entry.get( objectClassType );
if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
{
Subentry subentry = subentryCache.getSubentry( name.toNormName() );
SubtreeSpecification ss = subentry.getSubtreeSpecification();
LdapDN apName = ( LdapDN ) name.clone();
apName.remove( apName.size() - 1 );
LdapDN baseDn = ( LdapDN ) apName.clone();
baseDn.addAll( ss.getBase() );
LdapDN newName = ( LdapDN ) name.clone();
newName.remove( newName.size() - 1 );
newName.add( opContext.getNewRdn() );
String newNormName = newName.toNormName();
subentryCache.setSubentry( newNormName, ss, subentry.getTypes() );
next.rename( opContext );
subentry = subentryCache.getSubentry( newNormName );
ExprNode filter = new PresenceNode( oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT ) );
SearchControls controls = new SearchControls();
controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
controls.setReturningAttributes( new String[]
{ SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
baseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
while ( subentries.hasMore() )
{
ServerSearchResult result = subentries.next();
LdapDN dn = result.getDn();
dn.normalize( atRegistry.getNormalizerMapping() );
ServerEntry candidate = result.getServerEntry();
if ( evaluator.evaluate( ss, apName, dn, candidate ) )
{
nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForReplace( name,
newName, subentry, candidate ) ) );
}
}
}
else
{
if ( hasAdministrativeDescendant( name ) )
{
String msg = "Will not allow rename operation on entries with administrative descendants.";
LOG.warn( msg );
throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
}
next.rename( opContext );
// calculate the new DN now for use below to modify subentry operational
// attributes contained within this regular entry with name changes
LdapDN newName = ( LdapDN ) name.clone();
newName.remove( newName.size() - 1 );
newName.add( opContext.getNewRdn() );
newName.normalize( atRegistry.getNormalizerMapping() );
List<Modification> mods = getModsOnEntryRdnChange( name, newName, entry );
if ( mods.size() > 0 )
{
nexus.modify( new ModifyOperationContext( registries, newName, mods ) );
}
}
}
public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext ) throws NamingException
{
LdapDN oriChildName = opContext.getDn();
LdapDN parent = opContext.getParent();
ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, oriChildName ) );
EntryAttribute objectClasses = entry.get( objectClassType );
if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
{
Subentry subentry = subentryCache.getSubentry( oriChildName.toNormName() );
SubtreeSpecification ss = subentry.getSubtreeSpecification();
LdapDN apName = ( LdapDN ) oriChildName.clone();
apName.remove( apName.size() - 1 );
LdapDN baseDn = ( LdapDN ) apName.clone();
baseDn.addAll( ss.getBase() );
LdapDN newName = ( LdapDN ) parent.clone();
newName.remove( newName.size() - 1 );
newName.add( opContext.getNewRdn() );
String newNormName = newName.toNormName();
subentryCache.setSubentry( newNormName, ss, subentry.getTypes() );
next.moveAndRename( opContext );
subentry = subentryCache.getSubentry( newNormName );
ExprNode filter = new PresenceNode( oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT ) );
SearchControls controls = new SearchControls();
controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
controls.setReturningAttributes( new String[]
{ SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
baseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
while ( subentries.hasMore() )
{
ServerSearchResult result = subentries.next();
LdapDN dn = result.getDn();
dn.normalize( atRegistry.getNormalizerMapping() );
ServerEntry candidate = result.getServerEntry();
if ( evaluator.evaluate( ss, apName, dn, candidate ) )
{
nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForReplace(
oriChildName, newName, subentry, candidate ) ) );
}
}
}
else
{
if ( hasAdministrativeDescendant( oriChildName ) )
{
String msg = "Will not allow rename operation on entries with administrative descendants.";
LOG.warn( msg );
throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
}
next.moveAndRename( opContext );
// calculate the new DN now for use below to modify subentry operational
// attributes contained within this regular entry with name changes
LdapDN newName = ( LdapDN ) parent.clone();
newName.add( opContext.getNewRdn() );
newName.normalize( atRegistry.getNormalizerMapping() );
List<Modification> mods = getModsOnEntryRdnChange( oriChildName, newName, entry );
if ( mods.size() > 0 )
{
nexus.modify( new ModifyOperationContext( registries, newName, mods ) );
}
}
}
public void move( NextInterceptor next, MoveOperationContext opContext ) throws NamingException
{
LdapDN oriChildName = opContext.getDn();
LdapDN newParentName = opContext.getParent();
ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, oriChildName ) );
EntryAttribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT );
if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
{
Subentry subentry = subentryCache.getSubentry( oriChildName.toString() );
SubtreeSpecification ss = subentry.getSubtreeSpecification();
LdapDN apName = ( LdapDN ) oriChildName.clone();
apName.remove( apName.size() - 1 );
LdapDN baseDn = ( LdapDN ) apName.clone();
baseDn.addAll( ss.getBase() );
LdapDN newName = ( LdapDN ) newParentName.clone();
newName.remove( newName.size() - 1 );
newName.add( newParentName.get( newParentName.size() - 1 ) );
String newNormName = newName.toNormName();
subentryCache.setSubentry( newNormName, ss, subentry.getTypes() );
next.move( opContext );
subentry = subentryCache.getSubentry( newNormName );
ExprNode filter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT );
SearchControls controls = new SearchControls();
controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
controls.setReturningAttributes( new String[]
{ SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
baseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
while ( subentries.hasMore() )
{
ServerSearchResult result = subentries.next();
LdapDN dn = result.getDn();
dn.normalize( atRegistry.getNormalizerMapping() );
ServerEntry candidate = result.getServerEntry();
if ( evaluator.evaluate( ss, apName, dn, candidate ) )
{
nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForReplace(
oriChildName, newName, subentry, candidate ) ) );
}
}
}
else
{
if ( hasAdministrativeDescendant( oriChildName ) )
{
String msg = "Will not allow rename operation on entries with administrative descendants.";
LOG.warn( msg );
throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
}
next.move( opContext );
// calculate the new DN now for use below to modify subentry operational
// attributes contained within this regular entry with name changes
LdapDN newName = ( LdapDN ) newParentName.clone();
newName.add( oriChildName.get( oriChildName.size() - 1 ) );
List<Modification> mods = getModsOnEntryRdnChange( oriChildName, newName, entry );
if ( mods.size() > 0 )
{
nexus.modify( new ModifyOperationContext( registries, newName, mods ) );
}
}
}
// -----------------------------------------------------------------------
// Methods dealing subentry modification
// -----------------------------------------------------------------------
private int getSubentryTypes( ServerEntry entry, List<Modification> mods ) throws NamingException
{
ServerAttribute ocFinalState = ( ServerAttribute ) entry.get( SchemaConstants.OBJECT_CLASS_AT ).clone();
for ( Modification mod : mods )
{
if ( mod.getAttribute().getId().equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT ) )
{
switch ( mod.getOperation() )
{
case ADD_ATTRIBUTE:
for ( Value<?> value : ( ServerAttribute ) mod.getAttribute() )
{
ocFinalState.add( ( String ) value.get() );
}
break;
case REMOVE_ATTRIBUTE:
for ( Value<?> value : ( ServerAttribute ) mod.getAttribute() )
{
ocFinalState.remove( ( String ) value.get() );
}
break;
case REPLACE_ATTRIBUTE:
ocFinalState = ( ServerAttribute ) mod.getAttribute();
break;
}
}
}
ServerEntry attrs = new DefaultServerEntry( registries, LdapDN.EMPTY_LDAPDN );
attrs.put( ocFinalState );
return getSubentryTypes( attrs );
}
public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
{
LdapDN name = opContext.getDn();
List<Modification> mods = opContext.getModItems();
ServerEntry entry = nexus.lookup( new LookupOperationContext( registries, name ) );
ServerEntry oldEntry = ( ServerEntry ) entry.clone();
EntryAttribute objectClasses = entry.get( objectClassType );
boolean isSubtreeSpecificationModification = false;
Modification subtreeMod = null;
for ( Modification mod : mods )
{
if ( SchemaConstants.SUBTREE_SPECIFICATION_AT.equalsIgnoreCase( mod.getAttribute().getId() ) )
{
isSubtreeSpecificationModification = true;
subtreeMod = mod;
}
}
if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) && isSubtreeSpecificationModification )
{
SubtreeSpecification ssOld = subentryCache.removeSubentry( name.toString() ).getSubtreeSpecification();
SubtreeSpecification ssNew;
try
{
ssNew = ssParser.parse( ( ( ServerAttribute ) subtreeMod.getAttribute() ).getString() );
}
catch ( Exception e )
{
String msg = "failed to parse the new subtreeSpecification";
LOG.error( msg, e );
throw new LdapInvalidAttributeValueException( msg, ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
}
subentryCache.setSubentry( name.toNormName(), ssNew, getSubentryTypes( entry, mods ) );
next.modify( opContext );
// search for all entries selected by the old SS and remove references to subentry
LdapDN apName = ( LdapDN ) name.clone();
apName.remove( apName.size() - 1 );
LdapDN oldBaseDn = ( LdapDN ) apName.clone();
oldBaseDn.addAll( ssOld.getBase() );
ExprNode filter = new PresenceNode( oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT ) );
SearchControls controls = new SearchControls();
controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
controls.setReturningAttributes( new String[]
{ SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
NamingEnumeration<ServerSearchResult> subentries = nexus.search( new SearchOperationContext( registries,
oldBaseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
while ( subentries.hasMore() )
{
ServerSearchResult result = subentries.next();
LdapDN dn = result.getDn();
dn.normalize( atRegistry.getNormalizerMapping() );
ServerEntry candidate = result.getServerEntry();
if ( evaluator.evaluate( ssOld, apName, dn, candidate ) )
{
nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForRemove( name,
candidate ) ) );
}
}
// search for all selected entries by the new SS and add references to subentry
Subentry subentry = subentryCache.getSubentry( name.toNormName() );
ServerEntry operational = getSubentryOperatationalAttributes( name, subentry );
LdapDN newBaseDn = ( LdapDN ) apName.clone();
newBaseDn.addAll( ssNew.getBase() );
subentries = nexus.search( new SearchOperationContext( registries, newBaseDn,
AliasDerefMode.NEVER_DEREF_ALIASES, filter, controls ) );
while ( subentries.hasMore() )
{
ServerSearchResult result = subentries.next();
LdapDN dn = result.getDn();
dn.normalize( atRegistry.getNormalizerMapping() );
ServerEntry candidate = result.getServerEntry();
if ( evaluator.evaluate( ssNew, apName, dn, candidate ) )
{
nexus.modify( new ModifyOperationContext( registries, dn, getOperationalModsForAdd( candidate,
operational ) ) );
}
}
}
else
{
next.modify( opContext );
if ( !objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
{
ServerEntry newEntry = nexus.lookup( new LookupOperationContext( registries, name ) );
List<Modification> subentriesOpAttrMods = getModsOnEntryModification( name, oldEntry, newEntry );
if ( subentriesOpAttrMods.size() > 0 )
{
nexus.modify( new ModifyOperationContext( registries, name, subentriesOpAttrMods ) );
}
}
}
}
// -----------------------------------------------------------------------
// Utility Methods
// -----------------------------------------------------------------------
private List<Modification> getOperationalModsForReplace( Name oldName, Name newName, Subentry subentry,
ServerEntry entry ) throws NamingException
{
List<Modification> modList = new ArrayList<Modification>();
ServerAttribute operational;
if ( subentry.isAccessControlSubentry() )
{
operational = ( ServerAttribute ) entry.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ).clone();
if ( operational == null )
{
operational = new DefaultServerAttribute( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, atRegistry
.lookup( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) );
operational.add( newName.toString() );
}
else
{
operational.remove( oldName.toString() );
operational.add( newName.toString() );
}
modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) );
}
if ( subentry.isSchemaSubentry() )
{
operational = ( ServerAttribute ) entry.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).clone();
if ( operational == null )
{
operational = new DefaultServerAttribute( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, atRegistry
.lookup( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) );
operational.add( newName.toString() );
}
else
{
operational.remove( oldName.toString() );
operational.add( newName.toString() );
}
modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) );
}
if ( subentry.isCollectiveSubentry() )
{
operational = ( ServerAttribute ) entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ).clone();
if ( operational == null )
{
operational = new DefaultServerAttribute( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT,
atRegistry.lookup( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
operational.add( newName.toString() );
}
else
{
operational.remove( oldName.toString() );
operational.add( newName.toString() );
}
modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) );
}
if ( subentry.isTriggerSubentry() )
{
operational = ( ServerAttribute ) entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ).clone();
if ( operational == null )
{
operational = new DefaultServerAttribute( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT, atRegistry
.lookup( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
operational.add( newName.toString() );
}
else
{
operational.remove( oldName.toString() );
operational.add( newName.toString() );
}
modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) );
}
return modList;
}
/**
* Gets the subschema operational attributes to be added to or removed from
* an entry selected by a subentry's subtreeSpecification.
*
* @param name the normalized distinguished name of the subentry (the value of op attrs)
* @param subentry the subentry to get attributes from
* @return the set of attributes to be added or removed from entries
*/
private ServerEntry getSubentryOperatationalAttributes( LdapDN name, Subentry subentry ) throws NamingException
{
ServerEntry operational = new DefaultServerEntry( registries, name );
if ( subentry.isAccessControlSubentry() )
{
if ( operational.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) == null )
{
operational.put( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, name.toString() );
}
else
{
operational.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ).add( name.toString() );
}
}
if ( subentry.isSchemaSubentry() )
{
if ( operational.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) == null )
{
operational.put( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, name.toString() );
}
else
{
operational.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).add( name.toString() );
}
}
if ( subentry.isCollectiveSubentry() )
{
if ( operational.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) == null )
{
operational.put( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, name.toString() );
}
else
{
operational.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ).add( name.toString() );
}
}
if ( subentry.isTriggerSubentry() )
{
if ( operational.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) == null )
{
operational.put( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT, name.toString() );
}
else
{
operational.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ).add( name.toString() );
}
}
return operational;
}
/**
* Calculates the subentry operational attributes to remove from a candidate
* entry selected by a subtreeSpecification. When we remove a subentry we
* must remove the operational attributes in the entries that were once selected
* by the subtree specification of that subentry. To do so we must perform
* a modify operation with the set of modifications to perform. This method
* calculates those modifications.
*
* @param subentryDn the distinguished name of the subentry
* @param candidate the candidate entry to removed from the
* @return the set of modifications required to remove an entry's reference to
* a subentry
*/
private List<Modification> getOperationalModsForRemove( LdapDN subentryDn, ServerEntry candidate )
throws NamingException
{
List<Modification> modList = new ArrayList<Modification>();
String dn = subentryDn.toNormName();
for ( String opAttrId : SUBENTRY_OPATTRS )
{
EntryAttribute opAttr = candidate.get( opAttrId );
if ( ( opAttr != null ) && opAttr.contains( dn ) )
{
AttributeType attributeType = atRegistry.lookup( opAttrId );
ServerAttribute attr = new DefaultServerAttribute( opAttrId, attributeType, dn );
modList.add( new ServerModification( ModificationOperation.REMOVE_ATTRIBUTE, attr ) );
}
}
return modList;
}
/**
* Calculates the subentry operational attributes to add or replace from
* a candidate entry selected by a subtree specification. When a subentry
* is added or it's specification is modified some entries must have new
* operational attributes added to it to point back to the associated
* subentry. To do so a modify operation must be performed on entries
* selected by the subtree specification. This method calculates the
* modify operation to be performed on the entry.
*
* @param entry the entry being modified
* @param operational the set of operational attributes supported by the AP
* of the subentry
* @return the set of modifications needed to update the entry
* @throws NamingException if there are probelms accessing modification items
*/
public List<Modification> getOperationalModsForAdd( ServerEntry entry, ServerEntry operational )
throws NamingException
{
List<Modification> modList = new ArrayList<Modification>();
for ( AttributeType attributeType : operational.getAttributeTypes() )
{
ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE;
EntryAttribute result = new DefaultServerAttribute( attributeType );
EntryAttribute opAttrAdditions = operational.get( attributeType );
EntryAttribute opAttrInEntry = entry.get( attributeType );
for ( Value<?> value : opAttrAdditions )
{
result.add( value );
}
if ( opAttrInEntry != null && opAttrInEntry.size() > 0 )
{
for ( Value<?> value : opAttrInEntry )
{
result.add( value );
}
}
else
{
op = ModificationOperation.ADD_ATTRIBUTE;
}
modList.add( new ServerModification( op, result ) );
}
return modList;
}
/**
* SearchResultFilter used to filter out subentries based on objectClass values.
*/
public class HideSubentriesFilter implements SearchResultFilter
{
public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
throws NamingException
{
String dn = result.getDn().getNormName();
// see if we can get a match without normalization
if ( subentryCache.hasSubentry( dn ) )
{
return false;
}
// see if we can use objectclass if present
EntryAttribute objectClasses = result.getServerEntry().get( SchemaConstants.OBJECT_CLASS_AT );
if ( objectClasses != null )
{
return !objectClasses.contains( SchemaConstants.SUBENTRY_OC );
}
if ( !result.isRelative() )
{
LdapDN ndn = new LdapDN( dn );
ndn.normalize( atRegistry.getNormalizerMapping() );
String normalizedDn = ndn.toString();
return !subentryCache.hasSubentry( normalizedDn );
}
LdapDN name = new LdapDN( invocation.getCaller().getNameInNamespace() );
name.normalize( atRegistry.getNormalizerMapping() );
LdapDN rest = result.getDn();
rest.normalize( atRegistry.getNormalizerMapping() );
name.addAll( rest );
return !subentryCache.hasSubentry( name.toString() );
}
}
/**
* SearchResultFilter used to filter out normal entries but shows subentries based on
* objectClass values.
*/
public class HideEntriesFilter implements SearchResultFilter
{
public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
throws NamingException
{
String dn = result.getDn().getNormName();
// see if we can get a match without normalization
if ( subentryCache.hasSubentry( dn ) )
{
return true;
}
// see if we can use objectclass if present
EntryAttribute objectClasses = result.getServerEntry().get( SchemaConstants.OBJECT_CLASS_AT );
if ( objectClasses != null )
{
return objectClasses.contains( SchemaConstants.SUBENTRY_OC );
}
if ( !result.isRelative() )
{
LdapDN ndn = new LdapDN( dn );
ndn.normalize( atRegistry.getNormalizerMapping() );
return subentryCache.hasSubentry( ndn.toNormName() );
}
LdapDN name = new LdapDN( invocation.getCaller().getNameInNamespace() );
name.normalize( atRegistry.getNormalizerMapping() );
LdapDN rest = result.getDn();
rest.normalize( atRegistry.getNormalizerMapping() );
name.addAll( rest );
return subentryCache.hasSubentry( name.toNormName() );
}
}
private List<Modification> getModsOnEntryModification( LdapDN name, ServerEntry oldEntry, ServerEntry newEntry )
throws NamingException
{
List<Modification> modList = new ArrayList<Modification>();
Iterator<String> subentries = subentryCache.nameIterator();
while ( subentries.hasNext() )
{
String subentryDn = subentries.next();
Name apDn = new LdapDN( subentryDn );
apDn.remove( apDn.size() - 1 );
SubtreeSpecification ss = subentryCache.getSubentry( subentryDn ).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 ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS )
{
ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE;
EntryAttribute opAttr = oldEntry.get( aSUBENTRY_OPATTRS );
if ( opAttr != null )
{
opAttr = ( ServerAttribute ) opAttr.clone();
opAttr.remove( subentryDn );
if ( opAttr.size() < 1 )
{
op = ModificationOperation.REMOVE_ATTRIBUTE;
}
modList.add( new ServerModification( op, opAttr ) );
}
}
}
// need to add references to the subentry
else if ( isNewEntrySelected && !isOldEntrySelected )
{
for ( String attribute : SUBENTRY_OPATTRS )
{
ModificationOperation op = ModificationOperation.ADD_ATTRIBUTE;
AttributeType type = atRegistry.lookup( attribute );
ServerAttribute opAttr = new DefaultServerAttribute( attribute, type );
opAttr.add( subentryDn );
modList.add( new ServerModification( op, opAttr ) );
}
}
}
return modList;
}
}