| /* |
| * 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; |
| |
| |
| import org.apache.directory.server.constants.ServerDNConstants; |
| import org.apache.directory.server.core.authn.AuthenticationInterceptor; |
| import org.apache.directory.server.core.authn.LdapPrincipal; |
| import org.apache.directory.server.core.authz.AciAuthorizationInterceptor; |
| import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor; |
| import org.apache.directory.server.core.changelog.ChangeLog; |
| import org.apache.directory.server.core.changelog.ChangeLogEvent; |
| import org.apache.directory.server.core.changelog.ChangeLogInterceptor; |
| import org.apache.directory.server.core.changelog.DefaultChangeLog; |
| import org.apache.directory.server.core.changelog.Tag; |
| import org.apache.directory.server.core.collective.CollectiveAttributeInterceptor; |
| import org.apache.directory.server.core.cursor.Cursor; |
| import org.apache.directory.server.core.entry.DefaultServerEntry; |
| import org.apache.directory.server.core.entry.ServerEntry; |
| import org.apache.directory.server.core.entry.ServerEntryUtils; |
| import org.apache.directory.server.core.event.EventInterceptor; |
| import org.apache.directory.server.core.exception.ExceptionInterceptor; |
| import org.apache.directory.server.core.interceptor.Interceptor; |
| import org.apache.directory.server.core.interceptor.InterceptorChain; |
| import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext; |
| import org.apache.directory.server.core.interceptor.context.AddOperationContext; |
| import org.apache.directory.server.core.interceptor.context.EntryOperationContext; |
| import org.apache.directory.server.core.interceptor.context.LookupOperationContext; |
| import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext; |
| import org.apache.directory.server.core.jndi.DeadContext; |
| import org.apache.directory.server.core.jndi.ServerLdapContext; |
| import org.apache.directory.server.core.normalization.NormalizationInterceptor; |
| import org.apache.directory.server.core.operational.OperationalAttributeInterceptor; |
| import org.apache.directory.server.core.partition.DefaultPartitionNexus; |
| import org.apache.directory.server.core.partition.Partition; |
| import org.apache.directory.server.core.partition.PartitionNexus; |
| import org.apache.directory.server.core.partition.impl.btree.BTreePartition; |
| import org.apache.directory.server.core.partition.impl.btree.Index; |
| import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex; |
| import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition; |
| import org.apache.directory.server.core.referral.ReferralInterceptor; |
| import org.apache.directory.server.core.schema.PartitionSchemaLoader; |
| import org.apache.directory.server.core.schema.SchemaInterceptor; |
| import org.apache.directory.server.core.schema.SchemaOperationControl; |
| import org.apache.directory.server.core.schema.SchemaPartitionDao; |
| import org.apache.directory.server.core.schema.SchemaService; |
| import org.apache.directory.server.core.security.TlsKeyGenerator; |
| import org.apache.directory.server.core.subtree.SubentryInterceptor; |
| import org.apache.directory.server.core.trigger.TriggerInterceptor; |
| import org.apache.directory.server.schema.SerializableComparator; |
| import org.apache.directory.server.schema.bootstrap.ApacheSchema; |
| import org.apache.directory.server.schema.bootstrap.ApachemetaSchema; |
| import org.apache.directory.server.schema.bootstrap.BootstrapSchemaLoader; |
| import org.apache.directory.server.schema.bootstrap.CoreSchema; |
| import org.apache.directory.server.schema.bootstrap.Schema; |
| import org.apache.directory.server.schema.bootstrap.SystemSchema; |
| import org.apache.directory.server.schema.bootstrap.partition.DbFileListing; |
| import org.apache.directory.server.schema.bootstrap.partition.SchemaPartitionExtractor; |
| import org.apache.directory.server.schema.registries.DefaultOidRegistry; |
| import org.apache.directory.server.schema.registries.DefaultRegistries; |
| import org.apache.directory.server.schema.registries.OidRegistry; |
| import org.apache.directory.server.schema.registries.Registries; |
| import org.apache.directory.shared.ldap.NotImplementedException; |
| import org.apache.directory.shared.ldap.constants.AuthenticationLevel; |
| import org.apache.directory.shared.ldap.constants.SchemaConstants; |
| import org.apache.directory.shared.ldap.exception.LdapAuthenticationNotSupportedException; |
| import org.apache.directory.shared.ldap.exception.LdapConfigurationException; |
| import org.apache.directory.shared.ldap.exception.LdapNamingException; |
| import org.apache.directory.shared.ldap.exception.LdapNoPermissionException; |
| import org.apache.directory.shared.ldap.ldif.ChangeType; |
| import org.apache.directory.shared.ldap.ldif.LdifEntry; |
| import org.apache.directory.shared.ldap.ldif.LdifReader; |
| import org.apache.directory.shared.ldap.message.AttributesImpl; |
| import org.apache.directory.shared.ldap.message.ResultCodeEnum; |
| import org.apache.directory.shared.ldap.name.LdapDN; |
| import org.apache.directory.shared.ldap.schema.OidNormalizer; |
| import org.apache.directory.shared.ldap.util.DateUtils; |
| import org.apache.directory.shared.ldap.util.StringTools; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import javax.naming.Context; |
| import javax.naming.NamingException; |
| import javax.naming.directory.Attribute; |
| import javax.naming.directory.Attributes; |
| import javax.naming.directory.DirContext; |
| import javax.naming.ldap.LdapContext; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.StringReader; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| |
| /** |
| * Default implementation of {@link DirectoryService}. |
| * |
| * @org.apache.xbean.XBean |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| */ |
| public class DefaultDirectoryService implements DirectoryService |
| { |
| private static final Logger LOG = LoggerFactory.getLogger( DefaultDirectoryService.class ); |
| |
| private static final String ILLEGAL_STATE_MSG = "Something has got to be severely " + |
| "wrong with the core packaging\nor the build to have " + |
| "resulted in this exception."; |
| |
| private SchemaService schemaService; |
| |
| /** the registries for system schema objects */ |
| private Registries registries; |
| |
| /** the root nexus */ |
| private DefaultPartitionNexus partitionNexus; |
| |
| /** whether or not server is started for the first time */ |
| private boolean firstStart; |
| |
| /** The interceptor (or interceptor chain) for this service */ |
| private InterceptorChain interceptorChain; |
| |
| /** whether or not this instance has been shutdown */ |
| private boolean started; |
| |
| /** the change log service */ |
| private ChangeLog changeLog; |
| |
| private LdapDN adminDn; |
| |
| /** remove me after implementation is completed */ |
| private static final String PARTIAL_IMPL_WARNING = |
| "WARNING: the changelog is only partially operational and will revert\n" + |
| "state without consideration of who made the original change. All reverting " + |
| "changes are made by the admin user.\n Furthermore the used controls are not at " + |
| "all taken into account"; |
| |
| // ------------------------------------------------------------------------ |
| // Constructor |
| // ------------------------------------------------------------------------ |
| |
| |
| /** |
| * Creates a new instance. |
| */ |
| public DefaultDirectoryService() |
| { |
| setDefaultInterceptorConfigurations(); |
| changeLog = new DefaultChangeLog(); |
| |
| // -------------------------------------------------------------------- |
| // Load the bootstrap schemas to start up the schema partition |
| // -------------------------------------------------------------------- |
| |
| // setup temporary loader and temp registry |
| BootstrapSchemaLoader loader = new BootstrapSchemaLoader(); |
| OidRegistry oidRegistry = new DefaultOidRegistry(); |
| registries = new DefaultRegistries( "bootstrap", loader, oidRegistry ); |
| |
| // load essential bootstrap schemas |
| Set<Schema> bootstrapSchemas = new HashSet<Schema>(); |
| bootstrapSchemas.add( new ApachemetaSchema() ); |
| bootstrapSchemas.add( new ApacheSchema() ); |
| bootstrapSchemas.add( new CoreSchema() ); |
| bootstrapSchemas.add( new SystemSchema() ); |
| |
| |
| try |
| { |
| loader.loadWithDependencies( bootstrapSchemas, registries ); |
| } |
| catch ( NamingException e ) |
| { |
| throw new IllegalStateException( ILLEGAL_STATE_MSG, e ); |
| } |
| |
| // run referential integrity tests |
| List<Throwable> errors = registries.checkRefInteg(); |
| if ( !errors.isEmpty() ) |
| { |
| NamingException e = new NamingException(); |
| e.setRootCause( errors.get( 0 ) ); |
| throw new IllegalStateException( ILLEGAL_STATE_MSG, e ); |
| } |
| |
| SerializableComparator.setRegistry( registries.getComparatorRegistry() ); |
| } |
| |
| |
| // ------------------------------------------------------------------------ |
| // C O N F I G U R A T I O N M E T H O D S |
| // ------------------------------------------------------------------------ |
| |
| |
| public static final int MAX_SIZE_LIMIT_DEFAULT = 100; |
| public static final int MAX_TIME_LIMIT_DEFAULT = 10000; |
| |
| private String instanceId; |
| private File workingDirectory = new File( "server-work" ); |
| private boolean exitVmOnShutdown = true; // allow by default |
| private boolean shutdownHookEnabled = true; // allow by default |
| private boolean allowAnonymousAccess = true; // allow by default |
| private boolean accessControlEnabled; // off by default |
| private boolean denormalizeOpAttrsEnabled; // off by default |
| private int maxSizeLimit = MAX_SIZE_LIMIT_DEFAULT; // set to default value |
| private int maxTimeLimit = MAX_TIME_LIMIT_DEFAULT; // set to default value (milliseconds) |
| private List<Interceptor> interceptors; |
| private Partition systemPartition; |
| private Set<Partition> partitions = new HashSet<Partition>(); |
| private List<? extends LdifEntry> testEntries = new ArrayList<LdifEntry>(); // List<Attributes> |
| |
| |
| |
| public void setInstanceId( String instanceId ) |
| { |
| this.instanceId = instanceId; |
| } |
| |
| |
| public String getInstanceId() |
| { |
| return instanceId; |
| } |
| |
| |
| /** |
| * Gets the {@link Partition}s used by this DirectoryService. |
| * |
| * @org.apache.xbean.Property nestedType="org.apache.directory.server.core.partition.Partition" |
| * @return the set of partitions used |
| */ |
| public Set<? extends Partition> getPartitions() |
| { |
| Set<Partition> cloned = new HashSet<Partition>(); |
| cloned.addAll( partitions ); |
| return cloned; |
| } |
| |
| |
| /** |
| * Sets {@link Partition}s used by this DirectoryService. |
| * |
| * @org.apache.xbean.Property nestedType="org.apache.directory.server.core.partition.Partition" |
| * @param partitions the partitions to used |
| */ |
| public void setPartitions( Set<? extends Partition> partitions ) |
| { |
| Set<Partition> cloned = new HashSet<Partition>(); |
| cloned.addAll( partitions ); |
| Set<String> names = new HashSet<String>(); |
| for ( Partition partition : cloned ) |
| { |
| String id = partition.getId(); |
| if ( names.contains( id ) ) |
| { |
| LOG.warn( "Encountered duplicate partition {} identifier.", id ); |
| } |
| names.add( id ); |
| } |
| |
| this.partitions = cloned; |
| } |
| |
| |
| /** |
| * Returns <tt>true</tt> if access control checks are enabled. |
| * |
| * @return true if access control checks are enabled, false otherwise |
| */ |
| public boolean isAccessControlEnabled() |
| { |
| return accessControlEnabled; |
| } |
| |
| |
| /** |
| * Sets whether to enable basic access control checks or not. |
| * |
| * @param accessControlEnabled true to enable access control checks, false otherwise |
| */ |
| public void setAccessControlEnabled( boolean accessControlEnabled ) |
| { |
| this.accessControlEnabled = accessControlEnabled; |
| } |
| |
| |
| /** |
| * Returns <tt>true</tt> if anonymous access is allowed on entries besides the RootDSE. |
| * If the access control subsystem is enabled then access to some entries may not be |
| * allowed even when full anonymous access is enabled. |
| * |
| * @return true if anonymous access is allowed on entries besides the RootDSE, false |
| * if anonymous access is allowed to all entries. |
| */ |
| public boolean isAllowAnonymousAccess() |
| { |
| return allowAnonymousAccess; |
| } |
| |
| |
| /** |
| * Sets whether to allow anonymous access to entries other than the RootDSE. If the |
| * access control subsystem is enabled then access to some entries may not be allowed |
| * even when full anonymous access is enabled. |
| * |
| * @param enableAnonymousAccess true to enable anonymous access, false to disable it |
| */ |
| public void setAllowAnonymousAccess( boolean enableAnonymousAccess ) |
| { |
| this.allowAnonymousAccess = enableAnonymousAccess; |
| } |
| |
| |
| /** |
| * Returns interceptors in the server. |
| * |
| * @return the interceptors in the server. |
| */ |
| public List<Interceptor> getInterceptors() |
| { |
| List<Interceptor> cloned = new ArrayList<Interceptor>(); |
| cloned.addAll( interceptors ); |
| return cloned; |
| } |
| |
| |
| /** |
| * Sets the interceptors in the server. |
| * |
| * @org.apache.xbean.Property nestedType="org.apache.directory.server.core.interceptor.Interceptor" |
| * @param interceptors the interceptors to be used in the server. |
| */ |
| public void setInterceptors( List<Interceptor> interceptors ) |
| { |
| Set<String> names = new HashSet<String>(); |
| for ( Interceptor interceptor : interceptors ) |
| { |
| String name = interceptor.getName(); |
| if ( names.contains( name ) ) |
| { |
| LOG.warn( "Encountered duplicate definitions for {} interceptor", interceptor.getName() ); |
| } |
| names.add( name ); |
| } |
| |
| this.interceptors = interceptors; |
| } |
| |
| |
| /** |
| * Returns test directory entries({@link LdifEntry}) to be loaded while |
| * bootstrapping. |
| * |
| * @org.apache.xbean.Property nestedType="org.apache.directory.shared.ldap.ldif.Entry" |
| * @return test entries to load during bootstrapping |
| */ |
| public List<LdifEntry> getTestEntries() |
| { |
| List<LdifEntry> cloned = new ArrayList<LdifEntry>(); |
| cloned.addAll( testEntries ); |
| return cloned; |
| } |
| |
| |
| /** |
| * Sets test directory entries({@link Attributes}) to be loaded while |
| * bootstrapping. |
| * |
| * @org.apache.xbean.Property nestedType="org.apache.directory.shared.ldap.ldif.Entry" |
| * @param testEntries the test entries to load while bootstrapping |
| */ |
| public void setTestEntries( List<? extends LdifEntry> testEntries ) |
| { |
| //noinspection MismatchedQueryAndUpdateOfCollection |
| List<LdifEntry> cloned = new ArrayList<LdifEntry>(); |
| cloned.addAll( testEntries ); |
| this.testEntries = testEntries; |
| } |
| |
| |
| /** |
| * Returns working directory (counterpart of <tt>var/lib</tt>) where partitions are |
| * stored by default. |
| * |
| * @return the directory where partition's are stored. |
| */ |
| public File getWorkingDirectory() |
| { |
| return workingDirectory; |
| } |
| |
| |
| /** |
| * Sets working directory (counterpart of <tt>var/lib</tt>) where partitions are stored |
| * by default. |
| * |
| * @param workingDirectory the directory where the server's partitions are stored by default. |
| */ |
| public void setWorkingDirectory( File workingDirectory ) |
| { |
| this.workingDirectory = workingDirectory; |
| } |
| |
| |
| public void setShutdownHookEnabled( boolean shutdownHookEnabled ) |
| { |
| this.shutdownHookEnabled = shutdownHookEnabled; |
| } |
| |
| |
| public boolean isShutdownHookEnabled() |
| { |
| return shutdownHookEnabled; |
| } |
| |
| |
| public void setExitVmOnShutdown( boolean exitVmOnShutdown ) |
| { |
| this.exitVmOnShutdown = exitVmOnShutdown; |
| } |
| |
| |
| public boolean isExitVmOnShutdown() |
| { |
| return exitVmOnShutdown; |
| } |
| |
| |
| public void setMaxSizeLimit( int maxSizeLimit ) |
| { |
| this.maxSizeLimit = maxSizeLimit; |
| } |
| |
| |
| public int getMaxSizeLimit() |
| { |
| return maxSizeLimit; |
| } |
| |
| |
| public void setMaxTimeLimit( int maxTimeLimit ) |
| { |
| this.maxTimeLimit = maxTimeLimit; |
| } |
| |
| |
| public int getMaxTimeLimit() |
| { |
| return maxTimeLimit; |
| } |
| |
| public void setSystemPartition( Partition systemPartition ) |
| { |
| this.systemPartition = systemPartition; |
| } |
| |
| |
| public Partition getSystemPartition() |
| { |
| return systemPartition; |
| } |
| |
| |
| public boolean isDenormalizeOpAttrsEnabled() |
| { |
| return denormalizeOpAttrsEnabled; |
| } |
| |
| |
| public void setDenormalizeOpAttrsEnabled( boolean denormalizeOpAttrsEnabled ) |
| { |
| this.denormalizeOpAttrsEnabled = denormalizeOpAttrsEnabled; |
| } |
| |
| |
| public ChangeLog getChangeLog() |
| { |
| return changeLog; |
| } |
| |
| |
| public void setChangeLog( ChangeLog changeLog ) |
| { |
| this.changeLog = changeLog; |
| } |
| |
| |
| public void addPartition( Partition parition ) throws NamingException |
| { |
| partitions.add( parition ); |
| |
| if ( ! started ) |
| { |
| return; |
| } |
| |
| AddContextPartitionOperationContext addPartitionCtx = new AddContextPartitionOperationContext( registries, parition ); |
| partitionNexus.addContextPartition( addPartitionCtx ); |
| } |
| |
| |
| public void removePartition( Partition partition ) throws NamingException |
| { |
| partitions.remove( partition ); |
| |
| if ( ! started ) |
| { |
| return; |
| } |
| |
| RemoveContextPartitionOperationContext removePartitionCtx = |
| new RemoveContextPartitionOperationContext( registries, partition.getSuffixDn() ); |
| partitionNexus.removeContextPartition( removePartitionCtx ); |
| } |
| |
| |
| // ------------------------------------------------------------------------ |
| // BackendSubsystem Interface Method Implemetations |
| // ------------------------------------------------------------------------ |
| |
| |
| private void setDefaultInterceptorConfigurations() |
| { |
| // Set default interceptor chains |
| List<Interceptor> list = new ArrayList<Interceptor>(); |
| |
| list.add( new NormalizationInterceptor() ); |
| list.add( new AuthenticationInterceptor() ); |
| list.add( new ReferralInterceptor() ); |
| list.add( new AciAuthorizationInterceptor() ); |
| list.add( new DefaultAuthorizationInterceptor() ); |
| list.add( new ExceptionInterceptor() ); |
| list.add( new ChangeLogInterceptor() ); |
| list.add( new OperationalAttributeInterceptor() ); |
| list.add( new SchemaInterceptor() ); |
| list.add( new SubentryInterceptor() ); |
| list.add( new CollectiveAttributeInterceptor() ); |
| list.add( new EventInterceptor() ); |
| list.add( new TriggerInterceptor() ); |
| |
| setInterceptors( list ); |
| } |
| |
| |
| public LdapContext getJndiContext() throws NamingException |
| { |
| return this.getJndiContext( null, null, null, AuthenticationLevel.NONE.toString(), "" ); |
| } |
| |
| |
| public LdapContext getJndiContext( String dn ) throws NamingException |
| { |
| return this.getJndiContext( null, null, null, AuthenticationLevel.NONE.toString(), dn ); |
| } |
| |
| |
| public LdapContext getJndiContext( LdapPrincipal principal ) throws NamingException |
| { |
| return new ServerLdapContext( this, principal, new LdapDN() ); |
| } |
| |
| |
| public LdapContext getJndiContext( LdapPrincipal principal, String dn ) throws NamingException |
| { |
| return new ServerLdapContext( this, principal, new LdapDN( dn ) ); |
| } |
| |
| |
| public synchronized LdapContext getJndiContext( LdapDN principalDn, String principal, byte[] credential, |
| String authentication, String rootDN ) throws NamingException |
| { |
| checkSecuritySettings( principal, credential, authentication ); |
| |
| if ( !started ) |
| { |
| return new DeadContext(); |
| } |
| |
| Hashtable<String, Object> environment = new Hashtable<String, Object>(); |
| |
| if ( principal != null ) |
| { |
| environment.put( Context.SECURITY_PRINCIPAL, principal ); |
| } |
| |
| if ( credential != null ) |
| { |
| environment.put( Context.SECURITY_CREDENTIALS, credential ); |
| } |
| |
| if ( authentication != null ) |
| { |
| environment.put( Context.SECURITY_AUTHENTICATION, authentication ); |
| } |
| |
| if ( rootDN == null ) |
| { |
| rootDN = ""; |
| } |
| environment.put( Context.PROVIDER_URL, rootDN ); |
| environment.put( DirectoryService.JNDI_KEY, this ); |
| |
| return new ServerLdapContext( this, environment ); |
| } |
| |
| |
| public long revert() throws NamingException |
| { |
| if ( changeLog == null || ! changeLog.isEnabled() ) |
| { |
| throw new IllegalStateException( "The change log must be enabled to revert to previous log revisions." ); |
| } |
| |
| Tag latest = changeLog.getLatest(); |
| if ( null != latest ) |
| { |
| if ( latest.getRevision() < changeLog.getCurrentRevision() ) |
| { |
| return revert( latest.getRevision() ); |
| } |
| else |
| { |
| LOG.info( "Ignoring request to revert without changes since the latest tag." ); |
| return changeLog.getCurrentRevision(); |
| } |
| } |
| |
| throw new IllegalStateException( "There must be at least one tag to revert to the latest tag." ); |
| } |
| |
| |
| public long revert( long revision ) throws NamingException |
| { |
| if ( changeLog == null || ! changeLog.isEnabled() ) |
| { |
| throw new IllegalStateException( "The change log must be enabled to revert to previous log revisions." ); |
| } |
| |
| if ( revision < 0 ) |
| { |
| throw new IllegalArgumentException( "revision must be greater than or equal to 0" ); |
| } |
| |
| if ( revision >= changeLog.getChangeLogStore().getCurrentRevision() ) |
| { |
| throw new IllegalArgumentException( "revision must be less than the current revision" ); |
| } |
| |
| DirContext ctx = getJndiContext( new LdapPrincipal( adminDn, AuthenticationLevel.SIMPLE ) ); |
| Cursor<ChangeLogEvent> cursor = changeLog.getChangeLogStore().findAfter( revision ); |
| |
| /* |
| * BAD, BAD, BAD!!! |
| * |
| * No synchronization no nothing. Just getting this to work for now |
| * so we can revert tests. Any production grade use of this feature |
| * needs to synchronize on all changes while the revert is in progress. |
| * |
| * How about making this operation transactional? |
| * |
| * First of all just stop using JNDI and construct the operations to |
| * feed into the interceptor pipeline. |
| */ |
| |
| try |
| { |
| LOG.warn( PARTIAL_IMPL_WARNING ); |
| cursor.afterLast(); |
| while ( cursor.previous() ) // apply ldifs in reverse order |
| { |
| ChangeLogEvent event = cursor.get(); |
| LdifEntry reverse = event.getReverseLdif(); |
| |
| switch( reverse.getChangeType().getChangeType() ) |
| { |
| case( ChangeType.ADD_ORDINAL ): |
| ctx.createSubcontext( reverse.getDn(), reverse.getAttributes() ); |
| break; |
| case( ChangeType.DELETE_ORDINAL ): |
| ctx.destroySubcontext( reverse.getDn() ); |
| break; |
| case( ChangeType.MODIFY_ORDINAL ): |
| ctx.modifyAttributes( reverse.getDn(), reverse.getModificationItemsArray() ); |
| break; |
| case( ChangeType.MODDN_ORDINAL ): |
| // NOT BREAK - both ModDN and ModRDN handling is the same |
| case( ChangeType.MODRDN_ORDINAL ): |
| if ( reverse.isDeleteOldRdn() ) |
| { |
| ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" ); |
| } |
| else |
| { |
| ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "true" ); |
| } |
| |
| ctx.rename( reverse.getDn(), event.getForwardLdif().getDn() ); |
| break; |
| default: |
| throw new NotImplementedException( "Reverts of change type " + reverse.getChangeType() |
| + " has not yet been implemented!"); |
| } |
| } |
| } |
| catch ( IOException e ) |
| { |
| throw new NamingException( "Encountered a failure while trying to revert to a previous revision: " |
| + revision ); |
| } |
| |
| return changeLog.getCurrentRevision(); |
| } |
| |
| |
| /** |
| * @throws NamingException if the LDAP server cannot be started |
| */ |
| public synchronized void startup() throws NamingException |
| { |
| if ( started ) |
| { |
| return; |
| } |
| |
| if ( shutdownHookEnabled ) |
| { |
| Runtime.getRuntime().addShutdownHook( new Thread( new Runnable() |
| { |
| public void run() |
| { |
| try |
| { |
| shutdown(); |
| } |
| catch ( NamingException e ) |
| { |
| LOG.warn( "Failed to shut down the directory service: " |
| + DefaultDirectoryService.this.instanceId, e ); |
| } |
| } |
| }, "ApacheDS Shutdown Hook (" + instanceId + ')' ) ); |
| |
| LOG.info( "ApacheDS shutdown hook has been registered with the runtime." ); |
| } |
| else if ( LOG.isWarnEnabled() ) |
| { |
| LOG.warn( "ApacheDS shutdown hook has NOT been registered with the runtime." |
| + " This default setting for standalone operation has been overriden." ); |
| } |
| |
| initialize(); |
| firstStart = createBootstrapEntries(); |
| showSecurityWarnings(); |
| started = true; |
| |
| adminDn = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| adminDn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() ); |
| |
| if ( !testEntries.isEmpty() ) |
| { |
| createTestEntries(); |
| } |
| } |
| |
| |
| public synchronized void sync() throws NamingException |
| { |
| if ( !started ) |
| { |
| return; |
| } |
| |
| this.changeLog.sync(); |
| this.partitionNexus.sync(); |
| } |
| |
| |
| public synchronized void shutdown() throws NamingException |
| { |
| if ( !started ) |
| { |
| return; |
| } |
| |
| this.changeLog.sync(); |
| this.changeLog.destroy(); |
| |
| this.partitionNexus.sync(); |
| this.partitionNexus.destroy(); |
| this.interceptorChain.destroy(); |
| this.started = false; |
| setDefaultInterceptorConfigurations(); |
| } |
| |
| |
| public Registries getRegistries() |
| { |
| return registries; |
| } |
| |
| |
| public void setRegistries( Registries registries ) |
| { |
| this.registries = registries; |
| } |
| |
| |
| public SchemaService getSchemaService() |
| { |
| return schemaService; |
| } |
| |
| |
| public void setSchemaService( SchemaService schemaService ) |
| { |
| this.schemaService = schemaService; |
| } |
| |
| |
| public PartitionNexus getPartitionNexus() |
| { |
| return partitionNexus; |
| } |
| |
| |
| public InterceptorChain getInterceptorChain() |
| { |
| return interceptorChain; |
| } |
| |
| |
| public boolean isFirstStart() |
| { |
| return firstStart; |
| } |
| |
| |
| public boolean isStarted() |
| { |
| return started; |
| } |
| |
| |
| public ServerEntry newEntry( LdapDN dn ) throws NamingException |
| { |
| return new DefaultServerEntry( registries, dn ); |
| } |
| |
| |
| /** |
| * Checks to make sure security environment parameters are set correctly. |
| * |
| * @throws javax.naming.NamingException if the security settings are not correctly configured. |
| * @param authentication the mechanism for authentication |
| * @param credential the password |
| * @param principal the distinguished name of the principal |
| */ |
| private void checkSecuritySettings( String principal, byte[] credential, String authentication ) |
| throws NamingException |
| { |
| if ( authentication == null ) |
| { |
| authentication = ""; |
| } |
| |
| /* |
| * If bind is strong make sure we have the principal name |
| * set within the environment, otherwise complain |
| */ |
| if ( AuthenticationLevel.STRONG.toString().equalsIgnoreCase( authentication ) ) |
| { |
| if ( principal == null ) |
| { |
| throw new LdapConfigurationException( "missing required " + Context.SECURITY_PRINCIPAL |
| + " property for strong authentication" ); |
| } |
| } |
| /* |
| * If bind is simple make sure we have the credentials and the |
| * principal name set within the environment, otherwise complain |
| */ |
| else if ( AuthenticationLevel.SIMPLE.toString().equalsIgnoreCase( authentication ) ) |
| { |
| if ( credential == null ) |
| { |
| throw new LdapConfigurationException( "missing required " + Context.SECURITY_CREDENTIALS |
| + " property for simple authentication" ); |
| } |
| |
| if ( principal == null ) |
| { |
| throw new LdapConfigurationException( "missing required " + Context.SECURITY_PRINCIPAL |
| + " property for simple authentication" ); |
| } |
| } |
| /* |
| * If bind is none make sure credentials and the principal |
| * name are NOT set within the environment, otherwise complain |
| */ |
| else if ( AuthenticationLevel.NONE.toString().equalsIgnoreCase( authentication ) ) |
| { |
| if ( credential != null ) |
| { |
| throw new LdapConfigurationException( "ambiguous bind " |
| + "settings encountered where bind is anonymous yet " + Context.SECURITY_CREDENTIALS |
| + " property is set" ); |
| } |
| |
| if ( principal != null ) |
| { |
| throw new LdapConfigurationException( "ambiguous bind " |
| + "settings encountered where bind is anonymous yet " + Context.SECURITY_PRINCIPAL |
| + " property is set" ); |
| } |
| |
| if ( !allowAnonymousAccess ) |
| { |
| throw new LdapNoPermissionException( "Anonymous access disabled." ); |
| } |
| } |
| else |
| { |
| /* |
| * If bind is anything other than strong, simple, or none we need to complain |
| */ |
| throw new LdapAuthenticationNotSupportedException( "Unknown authentication type: '" + authentication + "'", |
| ResultCodeEnum.AUTH_METHOD_NOT_SUPPORTED ); |
| } |
| } |
| |
| |
| /** |
| * Returns true if we had to create the bootstrap entries on the first |
| * start of the server. Otherwise if all entries exist, meaning none |
| * had to be created, then we are not starting for the first time. |
| * |
| * @return true if the bootstrap entries had to be created, false otherwise |
| * @throws javax.naming.NamingException if entries cannot be created |
| */ |
| private boolean createBootstrapEntries() throws NamingException |
| { |
| boolean firstStart = false; |
| |
| // ------------------------------------------------------------------- |
| // create admin entry |
| // ------------------------------------------------------------------- |
| |
| /* |
| * If the admin entry is there, then the database was already created |
| */ |
| if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, PartitionNexus.getAdminName() ) ) ) |
| { |
| firstStart = true; |
| |
| ServerEntry serverEntry = new DefaultServerEntry( registries, PartitionNexus.getAdminName() ); |
| |
| serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, |
| SchemaConstants.TOP_OC, |
| SchemaConstants.PERSON_OC, |
| SchemaConstants.ORGANIZATIONAL_PERSON_OC, |
| SchemaConstants.INET_ORG_PERSON_OC ); |
| |
| serverEntry.put( SchemaConstants.UID_AT, PartitionNexus.ADMIN_UID ); |
| serverEntry.put( SchemaConstants.USER_PASSWORD_AT, PartitionNexus.ADMIN_PASSWORD_BYTES ); |
| serverEntry.put( SchemaConstants.DISPLAY_NAME_AT, "Directory Superuser" ); |
| serverEntry.put( SchemaConstants.CN_AT, "system administrator" ); |
| serverEntry.put( SchemaConstants.SN_AT, "administrator" ); |
| serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); |
| serverEntry.put( SchemaConstants.DISPLAY_NAME_AT, "Directory Superuser" ); |
| |
| TlsKeyGenerator.addKeyPair( serverEntry ); |
| partitionNexus.add( new AddOperationContext( registries, serverEntry ) ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // create system users area |
| // ------------------------------------------------------------------- |
| |
| Map<String,OidNormalizer> oidsMap = registries.getAttributeTypeRegistry().getNormalizerMapping(); |
| LdapDN userDn = new LdapDN( ServerDNConstants.USERS_SYSTEM_DN ); |
| userDn.normalize( oidsMap ); |
| |
| if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, userDn ) ) ) |
| { |
| firstStart = true; |
| |
| ServerEntry serverEntry = new DefaultServerEntry( registries, userDn ); |
| |
| serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, |
| SchemaConstants.TOP_OC, |
| SchemaConstants.ORGANIZATIONAL_UNIT_OC ); |
| |
| serverEntry.put( SchemaConstants.OU_AT, "users" ); |
| serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); |
| |
| partitionNexus.add( new AddOperationContext( registries, serverEntry ) ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // create system groups area |
| // ------------------------------------------------------------------- |
| |
| LdapDN groupDn = new LdapDN( ServerDNConstants.GROUPS_SYSTEM_DN ); |
| groupDn.normalize( oidsMap ); |
| |
| if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, groupDn ) ) ) |
| { |
| firstStart = true; |
| |
| ServerEntry serverEntry = new DefaultServerEntry( registries, groupDn ); |
| |
| serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, |
| SchemaConstants.TOP_OC, |
| SchemaConstants.ORGANIZATIONAL_UNIT_OC ); |
| |
| serverEntry.put( SchemaConstants.OU_AT, "groups" ); |
| serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); |
| |
| partitionNexus.add( new AddOperationContext( registries, serverEntry ) ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // create administrator group |
| // ------------------------------------------------------------------- |
| |
| LdapDN name = new LdapDN( ServerDNConstants.ADMINISTRATORS_GROUP_DN ); |
| name.normalize( oidsMap ); |
| |
| if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, name ) ) ) |
| { |
| firstStart = true; |
| |
| ServerEntry serverEntry = new DefaultServerEntry( registries, name ); |
| |
| serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, |
| SchemaConstants.TOP_OC, |
| SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC ); |
| |
| serverEntry.put( SchemaConstants.CN_AT, "Administrators" ); |
| serverEntry.put( SchemaConstants.UNIQUE_MEMBER_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); |
| |
| partitionNexus.add( new AddOperationContext( registries, serverEntry ) ); |
| |
| Interceptor authzInterceptor = interceptorChain.get( AciAuthorizationInterceptor.class.getName() ); |
| |
| if ( authzInterceptor == null ) |
| { |
| LOG.error( "The Authorization service is null : this is not allowed" ); |
| throw new NamingException( "The Authorization service is null" ); |
| } |
| |
| if ( !( authzInterceptor instanceof AciAuthorizationInterceptor ) ) |
| { |
| LOG.error( "The Authorization service is not set correctly : '{}' is an incorect interceptor", |
| authzInterceptor.getClass().getName() ); |
| throw new NamingException( "The Authorization service is incorrectly set" ); |
| |
| } |
| |
| AciAuthorizationInterceptor authzSrvc = ( AciAuthorizationInterceptor ) authzInterceptor; |
| authzSrvc.cacheNewGroup( name, serverEntry ); |
| |
| } |
| |
| // ------------------------------------------------------------------- |
| // create system configuration area |
| // ------------------------------------------------------------------- |
| |
| LdapDN configurationDn = new LdapDN( "ou=configuration,ou=system" ); |
| configurationDn.normalize( oidsMap ); |
| |
| if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, configurationDn ) ) ) |
| { |
| firstStart = true; |
| |
| ServerEntry serverEntry = new DefaultServerEntry( registries, configurationDn ); |
| serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC ); |
| |
| serverEntry.put( SchemaConstants.OU_AT, "configuration" ); |
| serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); |
| |
| partitionNexus.add( new AddOperationContext( registries, serverEntry ) ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // create system configuration area for partition information |
| // ------------------------------------------------------------------- |
| |
| LdapDN partitionsDn = new LdapDN( "ou=partitions,ou=configuration,ou=system" ); |
| partitionsDn.normalize( oidsMap ); |
| |
| if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, partitionsDn ) ) ) |
| { |
| firstStart = true; |
| |
| ServerEntry serverEntry = new DefaultServerEntry( registries, partitionsDn ); |
| serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC ); |
| |
| serverEntry.put( SchemaConstants.OU_AT, "partitions" ); |
| serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); |
| |
| partitionNexus.add( new AddOperationContext( registries, serverEntry ) ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // create system configuration area for services |
| // ------------------------------------------------------------------- |
| |
| LdapDN servicesDn = new LdapDN( "ou=services,ou=configuration,ou=system" ); |
| servicesDn.normalize( oidsMap ); |
| |
| if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, servicesDn ) ) ) |
| { |
| firstStart = true; |
| |
| ServerEntry serverEntry = new DefaultServerEntry( registries, servicesDn ); |
| serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC ); |
| |
| serverEntry.put( SchemaConstants.OU_AT, "services" ); |
| serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); |
| |
| partitionNexus.add( new AddOperationContext( registries, serverEntry ) ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // create system configuration area for interceptors |
| // ------------------------------------------------------------------- |
| |
| LdapDN interceptorsDn = new LdapDN( "ou=interceptors,ou=configuration,ou=system" ); |
| interceptorsDn.normalize( oidsMap ); |
| |
| if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, interceptorsDn ) ) ) |
| { |
| firstStart = true; |
| |
| ServerEntry serverEntry = new DefaultServerEntry( registries, interceptorsDn ); |
| serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC ); |
| |
| serverEntry.put( SchemaConstants.OU_AT, "interceptors" ); |
| serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); |
| |
| partitionNexus.add( new AddOperationContext( registries, serverEntry ) ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // create system preferences area |
| // ------------------------------------------------------------------- |
| |
| LdapDN sysPrefRootDn = new LdapDN( ServerDNConstants.SYSPREFROOT_SYSTEM_DN ); |
| sysPrefRootDn.normalize( oidsMap ); |
| |
| if ( !partitionNexus.hasEntry( new EntryOperationContext( registries, sysPrefRootDn ) ) ) |
| { |
| firstStart = true; |
| |
| ServerEntry serverEntry = new DefaultServerEntry( registries, sysPrefRootDn ); |
| serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, |
| SchemaConstants.TOP_OC, |
| SchemaConstants.ORGANIZATIONAL_UNIT_OC, |
| SchemaConstants.EXTENSIBLE_OBJECT_OC ); |
| |
| serverEntry.put( "prefNodeName", "sysPrefRoot" ); |
| serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); |
| serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); |
| |
| partitionNexus.add( new AddOperationContext( registries, serverEntry ) ); |
| } |
| |
| return firstStart; |
| } |
| |
| |
| /** |
| * Displays security warning messages if any possible secutiry issue is found. |
| * @throws NamingException if there are failures parsing and accessing internal structures |
| */ |
| private void showSecurityWarnings() throws NamingException |
| { |
| // Warn if the default password is not changed. |
| boolean needToChangeAdminPassword = false; |
| |
| LdapDN adminDn = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN ); |
| adminDn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() ); |
| |
| ServerEntry adminEntry = partitionNexus.lookup( new LookupOperationContext( registries, adminDn ) ); |
| Object userPassword = adminEntry.get( SchemaConstants.USER_PASSWORD_AT ).get(); |
| |
| if ( userPassword instanceof byte[] ) |
| { |
| needToChangeAdminPassword = Arrays.equals( PartitionNexus.ADMIN_PASSWORD_BYTES, ( byte[] ) userPassword ); |
| } |
| else if ( userPassword.toString().equals( PartitionNexus.ADMIN_PASSWORD_STRING ) ) |
| { |
| needToChangeAdminPassword = PartitionNexus.ADMIN_PASSWORD_STRING.equals( userPassword.toString() ); |
| } |
| |
| if ( needToChangeAdminPassword ) |
| { |
| LOG.warn( "You didn't change the admin password of directory service " + "instance '" + instanceId + "'. " |
| + "Please update the admin password as soon as possible " + "to prevent a possible security breach." ); |
| } |
| } |
| |
| |
| /** |
| * Adds test entries into the core. |
| * |
| * @todo this may no longer be needed when JNDI is not used for bootstrapping |
| * |
| * @throws NamingException if the creation of test entries fails. |
| */ |
| private void createTestEntries() throws NamingException |
| { |
| LdapPrincipal principal = new LdapPrincipal( adminDn, AuthenticationLevel.SIMPLE ); |
| ServerLdapContext ctx = new ServerLdapContext( this, principal, new LdapDN() ); |
| |
| for ( LdifEntry testEntry : testEntries ) |
| { |
| try |
| { |
| LdifEntry entry = testEntry.clone(); |
| Attributes attributes = entry.getAttributes(); |
| String dn = entry.getDn(); |
| |
| try |
| { |
| ctx.createSubcontext( dn, attributes ); |
| } |
| catch ( Exception e ) |
| { |
| LOG.warn( dn + " test entry already exists.", e ); |
| } |
| } |
| catch ( CloneNotSupportedException cnse ) |
| { |
| LOG.warn( "Cannot clone the entry ", cnse ); |
| } |
| } |
| } |
| |
| |
| /** |
| * Kicks off the initialization of the entire system. |
| * |
| * @throws javax.naming.NamingException if there are problems along the way |
| */ |
| private void initialize() throws NamingException |
| { |
| if ( LOG.isDebugEnabled() ) |
| { |
| LOG.debug( "---> Initializing the DefaultDirectoryService " ); |
| } |
| |
| // -------------------------------------------------------------------- |
| // If not present extract schema partition from jar |
| // -------------------------------------------------------------------- |
| |
| File schemaDirectory = new File( workingDirectory, "schema" ); |
| SchemaPartitionExtractor extractor; |
| if ( ! schemaDirectory.exists() ) |
| { |
| try |
| { |
| extractor = new SchemaPartitionExtractor( workingDirectory ); |
| extractor.extract(); |
| } |
| catch ( IOException e ) |
| { |
| NamingException ne = new NamingException( "Failed to extract pre-loaded schema partition." ); |
| ne.setRootCause( e ); |
| throw ne; |
| } |
| } |
| |
| // -------------------------------------------------------------------- |
| // Initialize schema partition |
| // -------------------------------------------------------------------- |
| |
| JdbmPartition schemaPartition = new JdbmPartition(); |
| schemaPartition.setId( "schema" ); |
| schemaPartition.setCacheSize( 1000 ); |
| |
| DbFileListing listing; |
| |
| try |
| { |
| listing = new DbFileListing(); |
| } |
| catch( IOException e ) |
| { |
| throw new LdapNamingException( "Got IOException while trying to read DBFileListing: " + e.getMessage(), |
| ResultCodeEnum.OTHER ); |
| } |
| |
| Set<Index> indexedAttributes = new HashSet<Index>(); |
| |
| for ( String attributeId : listing.getIndexedAttributes() ) |
| { |
| indexedAttributes.add( new JdbmIndex( attributeId ) ); |
| } |
| |
| schemaPartition.setIndexedAttributes( indexedAttributes ); |
| schemaPartition.setSuffix( ServerDNConstants.OU_SCHEMA_DN ); |
| |
| ServerEntry entry = new DefaultServerEntry( registries, new LdapDN( ServerDNConstants.OU_SCHEMA_DN ) ); |
| entry.put( SchemaConstants.OBJECT_CLASS_AT, |
| SchemaConstants.TOP_OC, |
| SchemaConstants.ORGANIZATIONAL_UNIT_OC ); |
| entry.put( SchemaConstants.OU_AT, "schema" ); |
| schemaPartition.setContextEntry( entry ); |
| schemaPartition.init( this ); |
| |
| // -------------------------------------------------------------------- |
| // Enable schemas of all indices of partition configurations |
| // -------------------------------------------------------------------- |
| |
| /* |
| * We need to make sure that every attribute indexed by a partition is |
| * loaded into the registries on the next step. So here we must enable |
| * the schemas of those attributes so they are loaded into the global |
| * registries. |
| */ |
| |
| SchemaPartitionDao dao = new SchemaPartitionDao( schemaPartition, registries ); |
| Map<String,Schema> schemaMap = dao.getSchemas(); |
| Set<Partition> partitions = new HashSet<Partition>(); |
| partitions.add( systemPartition ); |
| partitions.addAll( this.partitions ); |
| |
| for ( Partition partition : partitions ) |
| { |
| if ( partition instanceof BTreePartition ) |
| { |
| JdbmPartition btpconf = ( JdbmPartition ) partition; |
| for ( Index index : btpconf.getIndexedAttributes() ) |
| { |
| String schemaName = dao.findSchema( index.getAttributeId() ); |
| if ( schemaName == null ) |
| { |
| throw new NamingException( "Index on unidentified attribute: " + index.toString() ); |
| } |
| |
| Schema schema = schemaMap.get( schemaName ); |
| if ( schema.isDisabled() ) |
| { |
| dao.enableSchema( schemaName ); |
| } |
| } |
| } |
| } |
| |
| // -------------------------------------------------------------------- |
| // Initialize schema subsystem and reset registries |
| // -------------------------------------------------------------------- |
| |
| PartitionSchemaLoader schemaLoader = new PartitionSchemaLoader( schemaPartition, registries ); |
| Registries globalRegistries = new DefaultRegistries( "global", schemaLoader, registries.getOidRegistry() ); |
| schemaLoader.loadEnabled( globalRegistries ); |
| registries = globalRegistries; |
| SerializableComparator.setRegistry( globalRegistries.getComparatorRegistry() ); |
| |
| SchemaOperationControl schemaControl = new SchemaOperationControl( registries, schemaLoader, |
| new SchemaPartitionDao( schemaPartition, registries ) ); |
| |
| schemaService = new SchemaService( registries, schemaPartition, schemaControl ); |
| |
| |
| partitionNexus = new DefaultPartitionNexus( new DefaultServerEntry( registries, LdapDN.EMPTY_LDAPDN ) ); |
| partitionNexus.init( this ); |
| partitionNexus.addContextPartition( new AddContextPartitionOperationContext( registries, schemaPartition ) ); |
| |
| interceptorChain = new InterceptorChain(); |
| interceptorChain.init( this ); |
| |
| if ( changeLog.isEnabled() ) |
| { |
| changeLog.init( this ); |
| } |
| |
| if ( LOG.isDebugEnabled() ) |
| { |
| LOG.debug( "<--- DefaultDirectoryService initialized" ); |
| } |
| } |
| |
| /** |
| * Read an entry (without DN) |
| * |
| * @param text The ldif format file |
| * @return An Attributes. |
| */ |
| private Attributes readEntry( String text ) |
| { |
| StringReader strIn = new StringReader( text ); |
| BufferedReader in = new BufferedReader( strIn ); |
| |
| String line = null; |
| Attributes attributes = new AttributesImpl( true ); |
| |
| try |
| { |
| while ( ( line = in.readLine() ) != null ) |
| { |
| if ( line.length() == 0 ) |
| { |
| continue; |
| } |
| |
| String addedLine = line.trim(); |
| |
| if ( StringTools.isEmpty( addedLine ) ) |
| { |
| continue; |
| } |
| |
| Attribute attribute = LdifReader.parseAttributeValue( addedLine ); |
| Attribute oldAttribute = attributes.get( attribute.getID() ); |
| |
| if ( oldAttribute != null ) |
| { |
| try |
| { |
| oldAttribute.add( attribute.get() ); |
| attributes.put( oldAttribute ); |
| } |
| catch (NamingException ne) |
| { |
| // Do nothing |
| } |
| } |
| else |
| { |
| attributes.put( attribute ); |
| } |
| } |
| } |
| catch (IOException ioe) |
| { |
| // Do nothing : we can't reach this point ! |
| } |
| |
| return attributes; |
| } |
| |
| |
| /** |
| * Create a new ServerEntry |
| * |
| * @param ldif The String representing the attributes, as a LDIF file |
| * @param dn The DN for this new entry |
| */ |
| public ServerEntry newEntry( String ldif, String dn ) |
| { |
| try |
| { |
| Attributes entry = readEntry( ldif ); |
| |
| ServerEntry serverEntry = ServerEntryUtils.toServerEntry( entry, new LdapDN( dn ), registries ); |
| return serverEntry; |
| } |
| catch ( Exception e ) |
| { |
| LOG.error( "Cannot build an entry for '{}' and this DN :'{}'", ldif, dn ); |
| // do nothing |
| return null; |
| } |
| } |
| } |