blob: d887b691c1e3e5565ef6c81aa2a506c4208d4947 [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.collective;
import org.apache.directory.server.core.DirectoryService;
import org.apache.directory.server.core.entry.DefaultServerAttribute;
import org.apache.directory.server.core.entry.ServerEntry;
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.BaseInterceptor;
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.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.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.Registries;
import org.apache.directory.shared.ldap.constants.SchemaConstants;
import org.apache.directory.shared.ldap.entry.EntryAttribute;
import org.apache.directory.shared.ldap.entry.Value;
import org.apache.directory.shared.ldap.name.LdapDN;
import org.apache.directory.shared.ldap.schema.AttributeType;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import java.util.HashSet;
import java.util.Set;
/**
* An interceptor based service dealing with collective attribute
* management. This service intercepts read operations on entries to
* inject collective attribute value pairs into the response based on
* the entires inclusion within collectiveAttributeSpecificAreas and
* collectiveAttributeInnerAreas.
*
* @org.apache.xbean.XBean
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$
*/
public class CollectiveAttributeInterceptor extends BaseInterceptor
{
/** The global registries */
private Registries registries;
/** The attributeType registry */
private AttributeTypeRegistry atRegistry;
private PartitionNexus nexus;
private CollectiveAttributesSchemaChecker collectiveAttributesSchemaChecker;
/**
* the search result filter to use for collective attribute injection
*/
private final SearchResultFilter SEARCH_FILTER = new SearchResultFilter()
{
public boolean accept( Invocation invocation, ServerSearchResult result, SearchControls controls )
throws NamingException
{
LdapDN name = ((ServerSearchResult)result).getDn();
if ( name.isNormalized() == false )
{
name = LdapDN.normalize( name, atRegistry.getNormalizerMapping() );
}
ServerEntry entry = result.getServerEntry();
String[] retAttrs = controls.getReturningAttributes();
addCollectiveAttributes( name, entry, retAttrs );
result.setServerEntry( entry );
return true;
}
};
public void init( DirectoryService directoryService ) throws NamingException
{
super.init( directoryService );
nexus = directoryService.getPartitionNexus();
atRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
collectiveAttributesSchemaChecker = new CollectiveAttributesSchemaChecker( nexus, atRegistry );
registries = directoryService.getRegistries();
}
/**
* Adds the set of collective attributes requested in the returning attribute list
* and contained in subentries referenced by the entry. Excludes collective
* attributes that are specified to be excluded via the 'collectiveExclusions'
* attribute in the entry.
*
* @param normName name of the entry being processed
* @param entry the entry to have the collective attributes injected
* @param retAttrs array or attribute type to be specifically included in the result entry(s)
* @throws NamingException if there are problems accessing subentries
*/
private void addCollectiveAttributes( LdapDN normName, ServerEntry entry, String[] retAttrs ) throws NamingException
{
EntryAttribute caSubentries;
//noinspection StringEquality
if ( ( retAttrs == null ) || ( retAttrs.length != 1 ) || ( retAttrs[0] != SchemaConstants.ALL_USER_ATTRIBUTES ) )
{
ServerEntry entryWithCAS = nexus.lookup( new LookupOperationContext( registries, normName, new String[] {
SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT_OID } ) );
caSubentries = entryWithCAS.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
}
else
{
caSubentries = entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
}
/*
* If there are no collective attribute subentries referenced
* then we have no collective attributes to inject to this entry.
*/
if ( caSubentries == null )
{
return;
}
/*
* Before we proceed we need to lookup the exclusions within the
* entry and build a set of exclusions for rapid lookup. We use
* OID values in the exclusions set instead of regular names that
* may have case variance.
*/
EntryAttribute collectiveExclusions = entry.get( SchemaConstants.COLLECTIVE_EXCLUSIONS_AT );
Set<String> exclusions = new HashSet<String>();
if ( collectiveExclusions != null )
{
if ( collectiveExclusions.contains( SchemaConstants.EXCLUDE_ALL_COLLECTIVE_ATTRIBUTES_AT_OID )
|| collectiveExclusions.contains( SchemaConstants.EXCLUDE_ALL_COLLECTIVE_ATTRIBUTES_AT ) )
{
/*
* This entry does not allow any collective attributes
* to be injected into itself.
*/
return;
}
exclusions = new HashSet<String>();
for ( Value<?> value:collectiveExclusions )
{
AttributeType attrType = atRegistry.lookup( ( String ) value.get() );
exclusions.add( attrType.getOid() );
}
}
/*
* If no attributes are requested specifically
* then it means all user attributes are requested.
* So populate the array with all user attributes indicator: "*".
*/
if ( retAttrs == null )
{
retAttrs = SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY;
}
/*
* Construct a set of requested attributes for easier tracking.
*/
Set<String> retIdsSet = new HashSet<String>( retAttrs.length );
for ( String retAttr:retAttrs )
{
if ( retAttr.equals( SchemaConstants.ALL_USER_ATTRIBUTES ) ||
retAttr.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
{
retIdsSet.add( retAttr );
}
else
{
retIdsSet.add( atRegistry.lookup( retAttr ).getOid() );
}
}
/*
* For each collective subentry referenced by the entry we lookup the
* attributes of the subentry and copy collective attributes from the
* subentry into the entry.
*/
for ( Value<?> value:caSubentries )
{
String subentryDnStr = ( String ) value.get();
LdapDN subentryDn = new LdapDN( subentryDnStr );
ServerEntry subentry = nexus.lookup( new LookupOperationContext( registries, subentryDn ) );
for ( AttributeType attributeType:subentry.getAttributeTypes() )
{
String attrId = attributeType.getName();
if ( !attributeType.isCollective() )
{
continue;
}
/*
* Skip the addition of this collective attribute if it is excluded
* in the 'collectiveAttributes' attribute.
*/
if ( exclusions.contains( attributeType.getOid() ) )
{
continue;
}
Set<AttributeType> allSuperTypes = getAllSuperTypes( attributeType );
for ( String retId : retIdsSet )
{
if ( retId.equals( SchemaConstants.ALL_USER_ATTRIBUTES ) || retId.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
{
continue;
}
AttributeType retType = atRegistry.lookup( retId );
if ( allSuperTypes.contains( retType ) )
{
retIdsSet.add( atRegistry.lookup( attrId ).getOid() );
break;
}
}
/*
* If not all attributes or this collective attribute requested specifically
* then bypass the inclusion process.
*/
if ( !( retIdsSet.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) ||
retIdsSet.contains( atRegistry.lookup( attrId ).getOid() ) ) )
{
continue;
}
EntryAttribute subentryColAttr = subentry.get( attrId );
EntryAttribute entryColAttr = entry.get( attrId );
/*
* If entry does not have attribute for collective attribute then create it.
*/
if ( entryColAttr == null )
{
entryColAttr = new DefaultServerAttribute( attrId, atRegistry.lookup( attrId ) );
entry.put( entryColAttr );
}
/*
* Add all the collective attribute values in the subentry
* to the currently processed collective attribute in the entry.
*/
for ( Value<?> subentryColVal:subentryColAttr )
{
entryColAttr.add( (String)subentryColVal.get() );
}
}
}
}
private Set<AttributeType> getAllSuperTypes( AttributeType id ) throws NamingException
{
Set<AttributeType> allSuperTypes = new HashSet<AttributeType>();
AttributeType superType = id;
while ( superType != null )
{
superType = superType.getSuperior();
if ( superType != null )
{
allSuperTypes.add( superType );
}
}
return allSuperTypes;
}
// ------------------------------------------------------------------------
// Interceptor Method Overrides
// ------------------------------------------------------------------------
public ServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext ) throws NamingException
{
ServerEntry result = nextInterceptor.lookup( opContext );
if ( result == null )
{
return null;
}
if ( ( opContext.getAttrsId() == null ) || ( opContext.getAttrsId().size() == 0 ) )
{
addCollectiveAttributes( opContext.getDn(), result, SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY );
}
else
{
addCollectiveAttributes( opContext.getDn(), result, opContext.getAttrsIdArray() );
}
return result;
}
public NamingEnumeration<ServerSearchResult> list( NextInterceptor nextInterceptor, ListOperationContext opContext ) throws NamingException
{
NamingEnumeration<ServerSearchResult> result = nextInterceptor.list( opContext );
Invocation invocation = InvocationStack.getInstance().peek();
return new SearchResultFilteringEnumeration( result, new SearchControls(), invocation, SEARCH_FILTER, "List collective Filter" );
}
public NamingEnumeration<ServerSearchResult> search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) throws NamingException
{
NamingEnumeration<ServerSearchResult> result = nextInterceptor.search( opContext );
Invocation invocation = InvocationStack.getInstance().peek();
return new SearchResultFilteringEnumeration(
result, opContext.getSearchControls(), invocation, SEARCH_FILTER, "Search collective Filter" );
}
// ------------------------------------------------------------------------
// Partial Schema Checking
// ------------------------------------------------------------------------
public void add( NextInterceptor next, AddOperationContext opContext ) throws NamingException
{
collectiveAttributesSchemaChecker.checkAdd( opContext.getDn(), opContext.getEntry() );
next.add( opContext );
}
public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws NamingException
{
collectiveAttributesSchemaChecker.checkModify( opContext.getRegistries(),opContext.getDn(), opContext.getModItems() );
next.modify( opContext );
}
}