| /* |
| * 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.operational; |
| |
| |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.UUID; |
| |
| import org.apache.directory.api.ldap.model.constants.SchemaConstants; |
| import org.apache.directory.api.ldap.model.entry.Attribute; |
| import org.apache.directory.api.ldap.model.entry.DefaultAttribute; |
| import org.apache.directory.api.ldap.model.entry.DefaultModification; |
| import org.apache.directory.api.ldap.model.entry.Entry; |
| import org.apache.directory.api.ldap.model.entry.Modification; |
| import org.apache.directory.api.ldap.model.entry.ModificationOperation; |
| import org.apache.directory.api.ldap.model.entry.Value; |
| import org.apache.directory.api.ldap.model.exception.LdapException; |
| import org.apache.directory.api.ldap.model.exception.LdapNoPermissionException; |
| import org.apache.directory.api.ldap.model.name.Ava; |
| import org.apache.directory.api.ldap.model.name.Dn; |
| import org.apache.directory.api.ldap.model.name.Rdn; |
| import org.apache.directory.api.ldap.model.schema.AttributeType; |
| import org.apache.directory.api.ldap.model.schema.AttributeTypeOptions; |
| import org.apache.directory.api.ldap.model.schema.ObjectClass; |
| import org.apache.directory.api.ldap.model.schema.SchemaManager; |
| import org.apache.directory.api.util.DateUtils; |
| import org.apache.directory.server.constants.ApacheSchemaConstants; |
| import org.apache.directory.server.constants.ServerDNConstants; |
| import org.apache.directory.server.core.api.DirectoryService; |
| import org.apache.directory.server.core.api.InterceptorEnum; |
| import org.apache.directory.server.core.api.entry.ClonedServerEntry; |
| import org.apache.directory.server.core.api.filtering.EntryFilter; |
| import org.apache.directory.server.core.api.filtering.EntryFilteringCursor; |
| import org.apache.directory.server.core.api.interceptor.BaseInterceptor; |
| import org.apache.directory.server.core.api.interceptor.Interceptor; |
| import org.apache.directory.server.core.api.interceptor.context.AddOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.OperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext; |
| import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext; |
| import org.apache.directory.server.core.api.partition.Partition; |
| import org.apache.directory.server.core.api.partition.Subordinates; |
| import org.apache.directory.server.core.shared.SchemaService; |
| import org.apache.directory.server.i18n.I18n; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| |
| /** |
| * An {@link Interceptor} that adds or modifies the default attributes |
| * of entries. There are six default attributes for now; |
| * <tt>'creatorsName'</tt>, <tt>'createTimestamp'</tt>, <tt>'modifiersName'</tt>, |
| * <tt>'modifyTimestamp'</tt>, <tt>entryUUID</tt> and <tt>entryCSN</tt>. |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| */ |
| public class OperationalAttributeInterceptor extends BaseInterceptor |
| { |
| /** The LoggerFactory used by this Interceptor */ |
| private static final Logger LOG = LoggerFactory.getLogger( OperationalAttributeInterceptor.class ); |
| |
| /** The denormalizer filter */ |
| private final EntryFilter denormalizingSearchFilter = new OperationalAttributeDenormalizingSearchFilter(); |
| |
| /** The filter that add the mandatory operational attributes */ |
| private final EntryFilter operationalAttributeSearchFilter = new OperationalAttributeSearchFilter(); |
| |
| /** The filter that add the subordinates operational attributes */ |
| private final EntryFilter subordinatesSearchFilter = new SubordinatesSearchFilter(); |
| |
| /** The subschemasubentry Dn */ |
| private Dn subschemaSubentryDn; |
| |
| /** The admin Dn */ |
| private Dn adminDn; |
| |
| /** |
| * the search result filter to use for collective attribute injection |
| */ |
| private class OperationalAttributeDenormalizingSearchFilter implements EntryFilter |
| { |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean accept( SearchOperationContext operation, Entry entry ) throws LdapException |
| { |
| if ( operation.getReturningAttributesString() == null ) |
| { |
| return true; |
| } |
| |
| // Denormalize the operational Attributes |
| denormalizeEntryOpAttrs( entry ); |
| |
| return true; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public String toString( String tabs ) |
| { |
| return tabs + "OperationalAttributeDenormalizingSearchFilter"; |
| } |
| } |
| |
| |
| /** |
| * the search result filter to use for the addition of mandatory operational attributes |
| */ |
| private class OperationalAttributeSearchFilter implements EntryFilter |
| { |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean accept( SearchOperationContext operation, Entry entry ) throws LdapException |
| { |
| if ( operation.getReturningAttributesString() == null ) |
| { |
| return true; |
| } |
| |
| // Add the SubschemaSubentry AttributeType if it's requested |
| SchemaManager schemaManager = operation.getSession().getDirectoryService().getSchemaManager(); |
| |
| if ( operation.isAllOperationalAttributes() |
| || operation.getReturningAttributes().contains( |
| new AttributeTypeOptions( schemaManager.getAttributeType( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) ) ) ) |
| { |
| AttributeType subschemaSubentryAt = schemaManager.getAttributeType( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ); |
| entry.add( new DefaultAttribute( subschemaSubentryAt, |
| directoryService.getPartitionNexus().getRootDseValue( subschemaSubentryAt ) ) ); |
| } |
| |
| return true; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public String toString( String tabs ) |
| { |
| return tabs + "OperationalAttributeSearchFilter"; |
| } |
| } |
| |
| |
| /** |
| * The search result filter to use for the addition of the subordinates attributes, if requested |
| */ |
| private class SubordinatesSearchFilter implements EntryFilter |
| { |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean accept( SearchOperationContext searchOperationContext, Entry entry ) throws LdapException |
| { |
| // Add the nbChildren/nbSubordinates attributes if required |
| processSubordinates( searchOperationContext, searchOperationContext.getReturningAttributes(), |
| searchOperationContext.isAllOperationalAttributes(), entry ); |
| |
| return true; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public String toString( String tabs ) |
| { |
| return tabs + "SubordinatesSearchFilter"; |
| } |
| } |
| |
| |
| /** |
| * Creates the operational attribute management service interceptor. |
| */ |
| public OperationalAttributeInterceptor() |
| { |
| super( InterceptorEnum.OPERATIONAL_ATTRIBUTE_INTERCEPTOR ); |
| } |
| |
| |
| @Override |
| public void init( DirectoryService directoryService ) throws LdapException |
| { |
| super.init( directoryService ); |
| |
| // stuff for dealing with subentries (garbage for now) |
| Value subschemaSubentry = directoryService.getPartitionNexus().getRootDseValue( |
| directoryService.getAtProvider().getSubschemaSubentry() ); |
| subschemaSubentryDn = dnFactory.create( subschemaSubentry.getString() ); |
| |
| // Create the Admin Dn |
| adminDn = dnFactory.create( ServerDNConstants.ADMIN_SYSTEM_DN ); |
| } |
| |
| |
| @Override |
| public void destroy() |
| { |
| } |
| |
| |
| /** |
| * Check if we have to add an operational attribute, or if the admin has injected one |
| */ |
| private boolean checkAddOperationalAttribute( boolean isAdmin, Entry entry, AttributeType attribute ) |
| throws LdapException |
| { |
| if ( entry.containsAttribute( attribute ) ) |
| { |
| if ( !isAdmin ) |
| { |
| // Wrong ! |
| String message = I18n.err( I18n.ERR_30, attribute ); |
| LOG.error( message ); |
| throw new LdapNoPermissionException( message ); |
| } |
| else |
| { |
| return true; |
| } |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| |
| /** |
| * Adds extra operational attributes to the entry before it is added. |
| * |
| * We add those attributes : |
| * - creatorsName |
| * - createTimestamp |
| * - entryCSN |
| * - entryUUID |
| */ |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void add( AddOperationContext addContext ) throws LdapException |
| { |
| String principal = getPrincipal( addContext ).getName(); |
| |
| Entry entry = addContext.getEntry(); |
| |
| // If we are using replication, the below four OAs may already be present and we retain |
| // those values if the user is admin. |
| boolean isAdmin = addContext.getSession().getAuthenticatedPrincipal().getDn().equals( adminDn ); |
| |
| // The EntryUUID attribute |
| if ( !checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getEntryUUID() ) ) |
| { |
| entry.put( directoryService.getAtProvider().getEntryUUID(), UUID.randomUUID().toString() ); |
| } |
| |
| // The EntryCSN attribute |
| if ( !checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getEntryCSN() ) ) |
| { |
| entry.put( directoryService.getAtProvider().getEntryCSN(), directoryService.getCSN().toString() ); |
| } |
| |
| // The CreatorsName attribute |
| if ( !checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getCreatorsName() ) ) |
| { |
| entry.put( directoryService.getAtProvider().getCreatorsName(), principal ); |
| } |
| |
| // The CreateTimeStamp attribute |
| if ( !checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getCreateTimestamp() ) ) |
| { |
| entry.put( directoryService.getAtProvider().getCreateTimestamp(), DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) ); |
| } |
| |
| // Now, check that the user does not add operational attributes |
| // The accessControlSubentries attribute |
| checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getAccessControlSubentries() ); |
| |
| // The CollectiveAttributeSubentries attribute |
| checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider() |
| .getCollectiveAttributeSubentries() ); |
| |
| // The TriggerExecutionSubentries attribute |
| checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getTriggerExecutionSubentries() ); |
| |
| // The SubSchemaSybentry attribute |
| checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getSubschemaSubentry() ); |
| |
| next( addContext ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public Entry lookup( LookupOperationContext lookupContext ) throws LdapException |
| { |
| Dn dn = lookupContext.getDn(); |
| |
| if ( dn.getNormName().equals( subschemaSubentryDn.getNormName() ) ) |
| { |
| Entry serverEntry = SchemaService.getSubschemaEntry( directoryService, lookupContext ); |
| serverEntry.setDn( dn ); |
| |
| return serverEntry; |
| } |
| |
| Entry entry = next( lookupContext ); |
| |
| denormalizeEntryOpAttrs( entry ); |
| |
| // Add the nbChildren/nbSubordinates attributes if required |
| processSubordinates( lookupContext, lookupContext.getReturningAttributes(), lookupContext.isAllOperationalAttributes(), entry ); |
| |
| return entry; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void modify( ModifyOperationContext modifyContext ) throws LdapException |
| { |
| // We must check that the user hasn't injected either the modifiersName |
| // or the modifyTimestamp operational attributes : they are not supposed to be |
| // added at this point EXCEPT in cases of replication by a admin user. |
| // If so, remove them, and if there are no more attributes, simply return. |
| // otherwise, inject those values into the list of modifications |
| List<Modification> mods = modifyContext.getModItems(); |
| |
| boolean isAdmin = modifyContext.getSession().getAuthenticatedPrincipal().getDn().equals( adminDn ); |
| |
| boolean modifierAtPresent = false; |
| boolean modifiedTimeAtPresent = false; |
| boolean entryCsnAtPresent = false; |
| Dn dn = modifyContext.getDn(); |
| |
| for ( Modification modification : mods ) |
| { |
| AttributeType attributeType = modification.getAttribute().getAttributeType(); |
| |
| if ( attributeType.equals( directoryService.getAtProvider().getModifiersName() ) ) |
| { |
| if ( !isAdmin ) |
| { |
| String message = I18n.err( I18n.ERR_31 ); |
| LOG.error( message ); |
| throw new LdapNoPermissionException( message ); |
| } |
| else |
| { |
| modifierAtPresent = true; |
| } |
| } |
| |
| if ( attributeType.equals( directoryService.getAtProvider().getModifyTimestamp() ) ) |
| { |
| if ( !isAdmin ) |
| { |
| String message = I18n.err( I18n.ERR_30, attributeType ); |
| LOG.error( message ); |
| throw new LdapNoPermissionException( message ); |
| } |
| else |
| { |
| modifiedTimeAtPresent = true; |
| } |
| } |
| |
| if ( attributeType.equals( directoryService.getAtProvider().getEntryCSN() ) ) |
| { |
| if ( !isAdmin ) |
| { |
| String message = I18n.err( I18n.ERR_30, attributeType ); |
| LOG.error( message ); |
| throw new LdapNoPermissionException( message ); |
| } |
| else |
| { |
| entryCsnAtPresent = true; |
| } |
| } |
| |
| if ( PWD_POLICY_STATE_ATTRIBUTE_TYPES.contains( attributeType ) && !isAdmin ) |
| { |
| String message = I18n.err( I18n.ERR_30, attributeType ); |
| LOG.error( message ); |
| throw new LdapNoPermissionException( message ); |
| } |
| } |
| |
| // Add the modification AT only if we are not trying to modify the SubentrySubschema |
| if ( !dn.equals( subschemaSubentryDn ) ) |
| { |
| if ( !modifierAtPresent ) |
| { |
| // Inject the ModifiersName AT if it's not present |
| Attribute attribute = new DefaultAttribute( directoryService.getAtProvider().getModifiersName(), |
| getPrincipal( modifyContext ).getName() ); |
| |
| Modification modifiersName = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, |
| attribute ); |
| |
| mods.add( modifiersName ); |
| } |
| |
| if ( !modifiedTimeAtPresent ) |
| { |
| // Inject the ModifyTimestamp AT if it's not present |
| Attribute attribute = new DefaultAttribute( directoryService.getAtProvider().getModifyTimestamp(), |
| DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) ); |
| |
| Modification timestamp = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attribute ); |
| |
| mods.add( timestamp ); |
| } |
| |
| if ( !entryCsnAtPresent ) |
| { |
| String csn = directoryService.getCSN().toString(); |
| Attribute attribute = new DefaultAttribute( directoryService.getAtProvider().getEntryCSN(), csn ); |
| Modification updatedCsn = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attribute ); |
| mods.add( updatedCsn ); |
| } |
| } |
| |
| // Go down in the chain |
| next( modifyContext ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void move( MoveOperationContext moveContext ) throws LdapException |
| { |
| Entry modifiedEntry = moveContext.getOriginalEntry().clone(); |
| modifiedEntry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal( moveContext ).getName() ); |
| modifiedEntry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) ); |
| |
| Attribute csnAt = new DefaultAttribute( directoryService.getAtProvider().getEntryCSN(), directoryService |
| .getCSN().toString() ); |
| modifiedEntry.put( csnAt ); |
| |
| modifiedEntry.setDn( moveContext.getNewDn() ); |
| moveContext.setModifiedEntry( modifiedEntry ); |
| |
| next( moveContext ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void moveAndRename( MoveAndRenameOperationContext moveAndRenameContext ) throws LdapException |
| { |
| Entry modifiedEntry = moveAndRenameContext.getModifiedEntry(); |
| modifiedEntry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal( moveAndRenameContext ).getName() ); |
| modifiedEntry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) ); |
| modifiedEntry.setDn( moveAndRenameContext.getNewDn() ); |
| |
| Attribute csnAt = new DefaultAttribute( directoryService.getAtProvider().getEntryCSN(), directoryService |
| .getCSN().toString() ); |
| modifiedEntry.put( csnAt ); |
| |
| moveAndRenameContext.setModifiedEntry( modifiedEntry ); |
| |
| next( moveAndRenameContext ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void rename( RenameOperationContext renameContext ) throws LdapException |
| { |
| Entry entry = ( ( ClonedServerEntry ) renameContext.getEntry() ).getClonedEntry(); |
| entry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal( renameContext ).getName() ); |
| entry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) ); |
| |
| Entry modifiedEntry = renameContext.getOriginalEntry().clone(); |
| modifiedEntry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal( renameContext ).getName() ); |
| modifiedEntry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) ); |
| |
| Attribute csnAt = new DefaultAttribute( directoryService.getAtProvider().getEntryCSN(), directoryService |
| .getCSN().toString() ); |
| modifiedEntry.put( csnAt ); |
| |
| renameContext.setModifiedEntry( modifiedEntry ); |
| |
| next( renameContext ); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public EntryFilteringCursor search( SearchOperationContext searchContext ) throws LdapException |
| { |
| EntryFilteringCursor cursor = next( searchContext ); |
| |
| if ( searchContext.isAllOperationalAttributes() |
| || ( ( searchContext.getReturningAttributes() != null ) && !searchContext.getReturningAttributes().isEmpty() ) ) |
| { |
| if ( directoryService.isDenormalizeOpAttrsEnabled() ) |
| { |
| cursor.addEntryFilter( denormalizingSearchFilter ); |
| } |
| |
| cursor.addEntryFilter( operationalAttributeSearchFilter ); |
| cursor.addEntryFilter( subordinatesSearchFilter ); |
| |
| return cursor; |
| } |
| |
| return cursor; |
| } |
| |
| |
| @Override |
| public void delete( DeleteOperationContext deleteContext ) throws LdapException |
| { |
| // insert a new CSN into the entry, this is for replication |
| Entry entry = deleteContext.getEntry(); |
| Attribute csnAt = new DefaultAttribute( directoryService.getAtProvider().getEntryCSN(), directoryService |
| .getCSN().toString() ); |
| entry.put( csnAt ); |
| |
| next( deleteContext ); |
| } |
| |
| |
| private void denormalizeEntryOpAttrs( Entry entry ) throws LdapException |
| { |
| if ( directoryService.isDenormalizeOpAttrsEnabled() ) |
| { |
| Attribute attr = entry.get( SchemaConstants.CREATORS_NAME_AT ); |
| |
| if ( attr != null ) |
| { |
| Dn creatorsName = dnFactory.create( attr.getString() ); |
| |
| attr.clear(); |
| attr.add( denormalizeTypes( creatorsName ).getName() ); |
| } |
| |
| attr = entry.get( SchemaConstants.MODIFIERS_NAME_AT ); |
| |
| if ( attr != null ) |
| { |
| Dn modifiersName = dnFactory.create( attr.getString() ); |
| |
| attr.clear(); |
| attr.add( denormalizeTypes( modifiersName ).getName() ); |
| } |
| |
| attr = entry.get( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT ); |
| |
| if ( attr != null ) |
| { |
| Dn modifiersName = dnFactory.create( attr.getString() ); |
| |
| attr.clear(); |
| attr.add( denormalizeTypes( modifiersName ).getName() ); |
| } |
| } |
| } |
| |
| |
| /** |
| * Does not create a new Dn but alters existing Dn by using the first |
| * short name for an attributeType definition. |
| * |
| * @param dn the normalized distinguished name |
| * @return the distinguished name denormalized |
| * @throws Exception if there are problems denormalizing |
| */ |
| private Dn denormalizeTypes( Dn dn ) throws LdapException |
| { |
| Dn newDn = new Dn( schemaManager ); |
| int size = dn.size(); |
| |
| for ( int pos = 0; pos < size; pos++ ) |
| { |
| Rdn rdn = dn.getRdn( size - 1 - pos ); |
| |
| if ( rdn.size() == 0 ) |
| { |
| newDn = newDn.add( new Rdn() ); |
| continue; |
| } |
| else if ( rdn.size() == 1 ) |
| { |
| String name = schemaManager.lookupAttributeTypeRegistry( rdn.getNormType() ).getName(); |
| String value = rdn.getValue(); |
| newDn = newDn.add( new Rdn( name, value ) ); |
| continue; |
| } |
| |
| // below we only process multi-valued rdns |
| StringBuilder buf = new StringBuilder(); |
| |
| for ( Iterator<Ava> atavs = rdn.iterator(); atavs.hasNext(); /**/) |
| { |
| Ava atav = atavs.next(); |
| String type = schemaManager.lookupAttributeTypeRegistry( rdn.getNormType() ).getName(); |
| buf.append( type ).append( '=' ).append( atav.getValue().getString() ); |
| |
| if ( atavs.hasNext() ) |
| { |
| buf.append( '+' ); |
| } |
| } |
| |
| newDn = newDn.add( new Rdn( buf.toString() ) ); |
| } |
| |
| return newDn; |
| } |
| |
| |
| private void processSubordinates( OperationContext operationContext, Set<AttributeTypeOptions> returningAttributes, |
| boolean allAttributes, Entry entry ) throws LdapException |
| { |
| // Bypass the rootDSE : we won't get the nbChildren and nbSubordiantes for this special entry |
| if ( Dn.isNullOrEmpty( entry.getDn() ) ) |
| { |
| return; |
| } |
| |
| // Add the Subordinates AttributeType if it's requested |
| AttributeType nbChildrenAt = directoryService.getAtProvider().getNbChildren(); |
| AttributeTypeOptions nbChildrenAto = new AttributeTypeOptions( nbChildrenAt ); |
| AttributeType nbSubordinatesAt = directoryService.getAtProvider().getNbSubordinates(); |
| AttributeTypeOptions nbSubordinatesAto = new AttributeTypeOptions( nbSubordinatesAt ); |
| AttributeType hasSubordinatesAt = directoryService.getAtProvider().getHasSubordinates(); |
| AttributeTypeOptions hasSubordinatesAto = new AttributeTypeOptions( hasSubordinatesAt ); |
| AttributeType structuralObjectClassAt = directoryService.getAtProvider().getStructuralObjectClass(); |
| AttributeTypeOptions structuralObjectClassAto = new AttributeTypeOptions( structuralObjectClassAt ); |
| |
| if ( returningAttributes != null ) |
| { |
| boolean nbChildrenRequested = returningAttributes.contains( nbChildrenAto ) || allAttributes; |
| boolean nbSubordinatesRequested = returningAttributes.contains( nbSubordinatesAto ) || allAttributes; |
| boolean hasSubordinatesRequested = returningAttributes.contains( hasSubordinatesAto ) || allAttributes; |
| boolean structuralObjectClassRequested = returningAttributes.contains( structuralObjectClassAto ) || allAttributes; |
| |
| if ( nbChildrenRequested || nbSubordinatesRequested || hasSubordinatesRequested |
| || structuralObjectClassRequested ) |
| { |
| Partition partition = directoryService.getPartitionNexus().getPartition( entry.getDn() ); |
| Subordinates subordinates = partition.getSubordinates( operationContext.getTransaction(), entry ); |
| |
| long nbChildren = subordinates.getNbChildren(); |
| long nbSubordinates = subordinates.getNbSubordinates(); |
| |
| // Inject the nbChildren OpAttr if needed |
| if ( nbChildrenRequested ) |
| { |
| entry.add( new DefaultAttribute( nbChildrenAt, |
| Long.toString( nbChildren ) ) ); |
| } |
| |
| // Inject the nbSubordinates OpAttr if needed |
| if ( nbSubordinatesRequested ) |
| { |
| entry.add( new DefaultAttribute( nbSubordinatesAt, |
| Long.toString( nbSubordinates ) ) ); |
| } |
| |
| // Inject the hasSubordinates OpAttr if needed |
| if ( hasSubordinatesRequested ) |
| { |
| if ( nbSubordinates > 0 ) |
| { |
| entry.add( new DefaultAttribute( hasSubordinatesAt, "TRUE" ) ); |
| } |
| else |
| { |
| entry.add( new DefaultAttribute( hasSubordinatesAt, "FALSE" ) ); |
| } |
| } |
| |
| // Inject the structuralObjectclass OpAttr if needed |
| if ( structuralObjectClassRequested ) |
| { |
| Attribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT ); |
| Map<String, ObjectClass> superiors = new HashMap<>(); |
| ObjectClass[] objectClassArray = new ObjectClass[objectClasses.size()]; |
| int nbStructural = 0; |
| |
| // First get all the structural objectClasses |
| for ( Value objectClassValue : objectClasses ) |
| { |
| ObjectClass objectClass = |
| schemaManager.getObjectClassRegistry().get( objectClassValue.getNormalized() ); |
| |
| if ( objectClass.isStructural() ) |
| { |
| objectClassArray[nbStructural++] = objectClass; |
| |
| // We can only have one superior objectClass for Structural ObjectClass |
| superiors.put( objectClass.getSuperiors().get( 0 ).getOid(), objectClass ); |
| } |
| } |
| |
| // Then find the top of them |
| if ( nbStructural == 1 ) |
| { |
| entry.add( new DefaultAttribute( structuralObjectClassAt, |
| objectClassArray[0].getName() ) ); |
| } |
| else |
| { |
| ObjectClass topStructural = objectClassArray[0]; |
| |
| for ( ObjectClass oc : objectClassArray ) |
| { |
| if ( !superiors.containsKey( oc.getOid() ) ) |
| { |
| // We are done : the current OC is not the superior of any other |
| // OC, this is necessarily the top level one |
| entry.add( new DefaultAttribute( structuralObjectClassAt, oc.getName() ) ); |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |