View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.jndi;
18  
19  import java.util.Hashtable;
20  import java.util.Iterator;
21  
22  import javax.naming.Context;
23  import javax.naming.Name;
24  import javax.naming.NamingException;
25  import javax.naming.NoPermissionException;
26  import javax.naming.directory.Attribute;
27  import javax.naming.directory.Attributes;
28  
29  import org.apache.ldap.common.exception.LdapAuthenticationNotSupportedException;
30  import org.apache.ldap.common.exception.LdapConfigurationException;
31  import org.apache.ldap.common.exception.LdapNoPermissionException;
32  import org.apache.ldap.common.message.LockableAttributesImpl;
33  import org.apache.ldap.common.message.ResultCodeEnum;
34  import org.apache.ldap.common.message.LockableAttributeImpl;
35  import org.apache.ldap.common.name.DnParser;
36  import org.apache.ldap.common.name.LdapName;
37  import org.apache.ldap.common.name.NameComponentNormalizer;
38  import org.apache.ldap.common.util.DateUtils;
39  import org.apache.ldap.server.configuration.Configuration;
40  import org.apache.ldap.server.configuration.ConfigurationException;
41  import org.apache.ldap.server.configuration.StartupConfiguration;
42  import org.apache.ldap.server.interceptor.InterceptorChain;
43  import org.apache.ldap.server.partition.ContextPartitionNexus;
44  import org.apache.ldap.server.partition.DefaultContextPartitionNexus;
45  import org.apache.ldap.server.schema.AttributeTypeRegistry;
46  import org.apache.ldap.server.schema.ConcreteNameComponentNormalizer;
47  import org.apache.ldap.server.schema.GlobalRegistries;
48  import org.apache.ldap.server.schema.bootstrap.BootstrapRegistries;
49  import org.apache.ldap.server.schema.bootstrap.BootstrapSchemaLoader;
50  
51  
52  /***
53   * Default implementation of {@link ContextFactoryService}.
54   * 
55   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
56   * @version $Rev: 264732 $
57   */
58  class DefaultContextFactoryService extends ContextFactoryService
59  {
60      private final String instanceId;
61  
62      private final ContextFactoryConfiguration configuration = new DefaultContextFactoryConfiguration( this );
63  
64      private ContextFactoryServiceListener serviceListener;
65      
66      /*** the initial context environment that fired up the backend subsystem */
67      private Hashtable environment;
68      
69      /*** the configuration */
70      private StartupConfiguration startupConfiguration;
71  
72      /*** the registries for system schema objects */
73      private GlobalRegistries globalRegistries;
74  
75      /*** the root nexus */
76      private DefaultContextPartitionNexus partitionNexus;
77  
78      /*** whether or not server is started for the first time */
79      private boolean firstStart;
80  
81      /*** The interceptor (or interceptor chain) for this service */
82      private InterceptorChain interceptorChain;
83      
84      /*** whether or not this instance has been shutdown */
85      private boolean started = false;
86  
87  
88      // ------------------------------------------------------------------------
89      // Constructor
90      // ------------------------------------------------------------------------
91  
92      /***
93       * Creates a new instance.
94       */
95      public DefaultContextFactoryService( String instanceId )
96      {
97          if( instanceId == null )
98          {
99              throw new NullPointerException( "instanceId" );
100         }
101         
102         this.instanceId = instanceId;
103         
104         // Register shutdown hook.
105         Runtime.getRuntime().addShutdownHook( new Thread( new Runnable() {
106             public void run()
107             {
108                 try
109                 {
110                     shutdown();
111                 }
112                 catch( NamingException e )
113                 {
114                     e.printStackTrace();
115                 }
116             }
117         }, "ApacheDS Shutdown Hook" ) );
118     }
119 
120     // ------------------------------------------------------------------------
121     // BackendSubsystem Interface Method Implemetations
122     // ------------------------------------------------------------------------
123 
124     public Context getJndiContext( String rootDN ) throws NamingException
125     {
126         return this.getJndiContext( null, null, "none", rootDN );
127     }
128 
129     public synchronized Context getJndiContext( String principal, byte[] credential, String authentication, String rootDN ) throws NamingException
130     {
131         checkSecuritySettings( principal, credential, authentication );
132         
133         if ( !started )
134         {
135             return new DeadContext();
136         }
137         
138         Hashtable environment = getEnvironment();
139         environment.remove( Context.SECURITY_PRINCIPAL );
140         environment.remove( Context.SECURITY_CREDENTIALS );
141         environment.remove( Context.SECURITY_AUTHENTICATION );
142         
143         if( principal != null )
144         {
145             environment.put( Context.SECURITY_PRINCIPAL, principal );
146         }
147         
148         if( credential != null )
149         {
150             environment.put( Context.SECURITY_CREDENTIALS, credential );
151         }
152         
153         if( authentication != null )
154         {
155             environment.put( Context.SECURITY_AUTHENTICATION, authentication );
156         }
157         
158         if( rootDN == null )
159         {
160             rootDN = "";
161         }
162         environment.put( Context.PROVIDER_URL, rootDN );
163 
164         return new ServerLdapContext( this, environment );
165     }
166 
167     public synchronized void startup( ContextFactoryServiceListener listener, Hashtable env ) throws NamingException
168     {
169         Hashtable envCopy = ( Hashtable ) env.clone();
170 
171         if( started )
172         {
173             return;
174         }
175 
176         StartupConfiguration cfg = ( StartupConfiguration ) Configuration.toConfiguration( env );
177         envCopy.put( Context.PROVIDER_URL, "" );
178         
179         try
180         {
181             cfg.validate();
182         }
183         catch( ConfigurationException e )
184         {
185             NamingException ne = new LdapConfigurationException( "Invalid configuration." );
186             ne.initCause( e );
187             throw ne;
188         }
189 
190         this.environment = envCopy;
191         this.startupConfiguration = cfg;
192         
193         listener.beforeStartup( this );
194         try
195         {
196             initialize();
197             firstStart = createBootstrapEntries();
198             createTestEntries();
199             this.serviceListener = listener;
200             started = true;
201         }
202         finally
203         {
204             listener.afterStartup( this );
205         }
206     }
207 
208     public synchronized void sync() throws NamingException
209     {
210         if ( !started )
211         {
212             return;
213         }
214 
215         serviceListener.beforeSync( this );
216         try
217         {
218             this.partitionNexus.sync();
219         }
220         finally
221         {
222             serviceListener.afterSync( this );
223         }
224     }
225 
226 
227     public synchronized void shutdown() throws NamingException
228     {
229         if ( !started )
230         {
231             return;
232         }
233 
234         serviceListener.beforeShutdown( this );
235         try
236         {
237             this.partitionNexus.sync();
238             this.partitionNexus.destroy();
239             this.interceptorChain.destroy();
240             this.started = false;
241         }
242         finally
243         {
244             environment = null;
245             interceptorChain = null;
246             startupConfiguration = null;
247             serviceListener.afterShutdown( this );
248         }
249     }
250     
251     public String getInstanceId()
252     {
253         return instanceId;
254     }
255     
256     public ContextFactoryConfiguration getConfiguration()
257     {
258         return configuration;
259     }
260     
261     
262     public Hashtable getEnvironment()
263     {
264         return ( Hashtable ) environment.clone();
265     }
266     
267     public ContextFactoryServiceListener getServiceListener()
268     {
269         return serviceListener;
270     }
271     
272     public StartupConfiguration getStartupConfiguration()
273     {
274         return startupConfiguration;
275     }
276     
277     public GlobalRegistries getGlobalRegistries()
278     {
279         return globalRegistries;
280     }
281 
282     public ContextPartitionNexus getPartitionNexus()
283     {
284         return partitionNexus;
285     }
286     
287     public InterceptorChain getInterceptorChain()
288     {
289         return interceptorChain;
290     }
291     
292     public boolean isFirstStart()
293     {
294         return firstStart;
295     }
296     
297     public boolean isStarted()
298     {
299         return started;
300     }
301     
302     /***
303      * Checks to make sure security environment parameters are set correctly.
304      *
305      * @throws javax.naming.NamingException if the security settings are not correctly configured.
306      */
307     private void checkSecuritySettings( String principal, byte[] credential, String authentication ) throws NamingException
308     {
309         if( authentication == null )
310         {
311             authentication = "";
312         }
313         
314         /*
315          * If bind is simple make sure we have the credentials and the
316          * principal name set within the environment, otherwise complain
317          */
318         if ( "simple".equalsIgnoreCase( authentication ) )
319         {
320             if ( credential == null )
321             {
322                 throw new LdapConfigurationException( "missing required "
323                         + Context.SECURITY_CREDENTIALS + " property for simple authentication" );
324             }
325 
326             if ( principal == null )
327             {
328                 throw new LdapConfigurationException( "missing required "
329                         + Context.SECURITY_PRINCIPAL + " property for simple authentication" );
330             }
331         }
332         /*
333          * If bind is none make sure credentials and the principal
334          * name are NOT set within the environment, otherwise complain
335          */
336         else if ( "none".equalsIgnoreCase( authentication ) )
337         {
338             if ( credential != null )
339             {
340                 throw new LdapConfigurationException( "ambiguous bind "
341                         + "settings encountered where bind is anonymous yet "
342                         + Context.SECURITY_CREDENTIALS + " property is set" );
343             }
344             if ( principal != null )
345             {
346                 throw new LdapConfigurationException( "ambiguous bind "
347                         + "settings encountered where bind is anonymous yet "
348                         + Context.SECURITY_PRINCIPAL + " property is set" );
349             }
350             
351             if( !startupConfiguration.isAllowAnonymousAccess() )
352             {
353                 throw new LdapNoPermissionException( "Anonymous access disabled." );
354             }
355         }
356         else
357         {
358             /*
359              * If bind is anything other than simple or none we need to
360              * complain because SASL is not a supported auth method yet
361              */
362             throw new LdapAuthenticationNotSupportedException( "Unknown authentication type: '" + authentication + "'", ResultCodeEnum.AUTHMETHODNOTSUPPORTED );
363         }
364     }
365 
366 
367     /***
368      * Returns true if we had to create the bootstrap entries on the first
369      * start of the server.  Otherwise if all entries exist, meaning none
370      * had to be created, then we are not starting for the first time.
371      *
372      * @throws javax.naming.NamingException
373      */
374     private boolean createBootstrapEntries() throws NamingException
375     {
376         boolean firstStart = false;
377 
378         // -------------------------------------------------------------------
379         // create admin entry
380         // -------------------------------------------------------------------
381 
382         /*
383          * If the admin entry is there, then the database was already created
384          */
385         if ( !partitionNexus.hasEntry( ContextPartitionNexus.getAdminName() ) )
386         {
387             checkPermissionToCreateBootstrapEntries();
388             firstStart = true;
389 
390             Attributes attributes = new LockableAttributesImpl();
391             Attribute objectClass = new LockableAttributeImpl( "objectClass" );
392             objectClass.add( "top" );
393             objectClass.add( "person" );
394             objectClass.add( "organizationalPerson" );
395             objectClass.add( "inetOrgPerson" );
396             attributes.put( objectClass );
397 
398             attributes.put( "uid", ContextPartitionNexus.ADMIN_UID );
399             attributes.put( "userPassword", environment.get( Context.SECURITY_CREDENTIALS ) );
400             attributes.put( "displayName", "Directory Superuser" );
401             attributes.put( "cn", "system administrator" );
402             attributes.put( "sn", "administrator" );
403             attributes.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
404             attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
405             attributes.put( "displayName", "Directory Superuser" );
406             
407             partitionNexus.add( ContextPartitionNexus.ADMIN_PRINCIPAL, ContextPartitionNexus.getAdminName(), attributes );
408         }
409 
410         // -------------------------------------------------------------------
411         // create system users area
412         // -------------------------------------------------------------------
413 
414         if ( !partitionNexus.hasEntry( new LdapName( "ou=users,ou=system" ) ) )
415         {
416             firstStart = true;
417             checkPermissionToCreateBootstrapEntries();
418 
419             Attributes attributes = new LockableAttributesImpl();
420             Attribute objectClass = new LockableAttributeImpl( "objectClass" );
421             objectClass.add( "top" );
422             objectClass.add( "organizationalUnit" );
423             attributes.put( objectClass );
424 
425             attributes.put( "ou", "users" );
426             attributes.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
427             attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
428 
429             partitionNexus.add( "ou=users,ou=system", new LdapName( "ou=users,ou=system" ), attributes );
430         }
431 
432         // -------------------------------------------------------------------
433         // create system groups area
434         // -------------------------------------------------------------------
435 
436         if ( !partitionNexus.hasEntry( new LdapName( "ou=groups,ou=system" ) ) )
437         {
438             firstStart = true;
439             checkPermissionToCreateBootstrapEntries();
440 
441             Attributes attributes = new LockableAttributesImpl();
442             Attribute objectClass = new LockableAttributeImpl( "objectClass" );
443             objectClass.add( "top" );
444             objectClass.add( "organizationalUnit" );
445             attributes.put( objectClass );
446 
447             attributes.put( "ou", "groups" );
448             attributes.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
449             attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
450 
451             partitionNexus.add( "ou=groups,ou=system", new LdapName( "ou=groups,ou=system" ), attributes );
452         }
453 
454         // -------------------------------------------------------------------
455         // create system configuration area
456         // -------------------------------------------------------------------
457 
458         if ( !partitionNexus.hasEntry( new LdapName( "ou=configuration,ou=system" ) ) )
459         {
460             firstStart = true;
461             checkPermissionToCreateBootstrapEntries();
462 
463             Attributes attributes = new LockableAttributesImpl();
464             Attribute objectClass = new LockableAttributeImpl( "objectClass" );
465             objectClass.add( "top" );
466             objectClass.add( "organizationalUnit" );
467             attributes.put( objectClass );
468 
469             attributes.put( "ou", "configuration" );
470             attributes.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
471             attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
472 
473             partitionNexus.add( "ou=configuration,ou=system", new LdapName( "ou=configuration,ou=system" ), attributes );
474         }
475 
476         // -------------------------------------------------------------------
477         // create system configuration area for partition information
478         // -------------------------------------------------------------------
479 
480         if ( !partitionNexus.hasEntry( new LdapName( "ou=partitions,ou=configuration,ou=system" ) ) )
481         {
482             firstStart = true;
483             checkPermissionToCreateBootstrapEntries();
484 
485             Attributes attributes = new LockableAttributesImpl();
486             Attribute objectClass = new LockableAttributeImpl( "objectClass" );
487             objectClass.add( "top" );
488             objectClass.add( "organizationalUnit" );
489             attributes.put( objectClass );
490 
491             attributes.put( "ou", "partitions" );
492             attributes.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
493             attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
494 
495             partitionNexus.add( "ou=partitions,ou=configuration,ou=system",
496                     new LdapName( "ou=partitions,ou=configuration,ou=system" ), attributes );
497         }
498 
499         // -------------------------------------------------------------------
500         // create system configuration area for services
501         // -------------------------------------------------------------------
502 
503         if ( !partitionNexus.hasEntry( new LdapName( "ou=services,ou=configuration,ou=system" ) ) )
504         {
505             firstStart = true;
506             checkPermissionToCreateBootstrapEntries();
507 
508             Attributes attributes = new LockableAttributesImpl();
509             Attribute objectClass = new LockableAttributeImpl( "objectClass" );
510             objectClass.add( "top" );
511             objectClass.add( "organizationalUnit" );
512             attributes.put( objectClass );
513 
514             attributes.put( "ou", "services" );
515             attributes.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
516             attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
517 
518             partitionNexus.add( "ou=services,ou=configuration,ou=system",
519                     new LdapName( "ou=services,ou=configuration,ou=system" ), attributes );
520         }
521 
522         // -------------------------------------------------------------------
523         // create system configuration area for interceptors
524         // -------------------------------------------------------------------
525 
526         if ( !partitionNexus.hasEntry( new LdapName( "ou=interceptors,ou=configuration,ou=system" ) ) )
527         {
528             firstStart = true;
529             checkPermissionToCreateBootstrapEntries();
530 
531             Attributes attributes = new LockableAttributesImpl();
532             Attribute objectClass = new LockableAttributeImpl( "objectClass" );
533             objectClass.add( "top" );
534             objectClass.add( "organizationalUnit" );
535             attributes.put( objectClass );
536 
537             attributes.put( "ou", "interceptors" );
538             attributes.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
539             attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
540 
541             partitionNexus.add( "ou=interceptors,ou=configuration,ou=system",
542                     new LdapName( "ou=interceptors,ou=configuration,ou=system" ), attributes );
543         }
544 
545         // -------------------------------------------------------------------
546         // create system preferences area
547         // -------------------------------------------------------------------
548 
549         if ( !partitionNexus.hasEntry( new LdapName( "prefNodeName=sysPrefRoot,ou=system" ) ) )
550         {
551             firstStart = true;
552             checkPermissionToCreateBootstrapEntries();
553 
554             Attributes attributes = new LockableAttributesImpl();
555             Attribute objectClass = new LockableAttributeImpl( "objectClass" );
556             objectClass.add( "top" );
557             objectClass.add( "organizationalUnit" );
558             attributes.put( objectClass );
559 
560             attributes.put( "objectClass", "extensibleObject" );
561             attributes.put( "prefNodeName", "sysPrefRoot" );
562             attributes.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
563             attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
564 
565             LdapName dn = new LdapName( "prefNodeName=sysPrefRoot,ou=system" );
566 
567             partitionNexus.add( "prefNodeName=sysPrefRoot,ou=system", dn, attributes );
568         }
569 
570         return firstStart;
571     }
572     
573     private void checkPermissionToCreateBootstrapEntries() throws NamingException
574     {
575         String principal = ( String ) environment.get( Context.SECURITY_PRINCIPAL );
576         if( principal == null || !ContextPartitionNexus.ADMIN_PRINCIPAL.equals( principal ) )
577         {
578             throw new NoPermissionException(
579                     "Only '" + ContextPartitionNexus.ADMIN_PRINCIPAL + "' can initiate the first run." );
580         }
581     }
582 
583 
584 
585     private void createTestEntries() throws NamingException
586     {
587         /*
588          * Unfortunately to test non-root user startup of the core and make sure
589          * all the appropriate functionality is there we need to load more user
590          * entries at startup due to a chicken and egg like problem.  The value
591          * of this property is a list of attributes to be added.
592          */
593         Iterator i = startupConfiguration.getTestEntries().iterator();
594         while( i.hasNext() )
595         {
596             Attributes entry = ( Attributes ) i.next();
597             entry.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
598             entry.put( "createTimestamp", DateUtils.getGeneralizedTime() );
599             
600             Attribute dn = ( Attribute ) entry.get( "dn" ).clone();
601             AttributeTypeRegistry registry = globalRegistries.getAttributeTypeRegistry();
602             NameComponentNormalizer ncn = new ConcreteNameComponentNormalizer( registry );
603             DnParser parser = new DnParser( ncn );
604             Name ndn = parser.parse( ( String ) dn.get() );
605             
606             partitionNexus.add( ( String ) dn.get(), ndn, entry );
607         }
608     }
609 
610     /***
611      * Kicks off the initialization of the entire system.
612      *
613      * @throws javax.naming.NamingException if there are problems along the way
614      */
615     private void initialize() throws NamingException
616     {
617         // --------------------------------------------------------------------
618         // Load the schema here and check that it is ok!
619         // --------------------------------------------------------------------
620 
621         BootstrapRegistries bootstrapRegistries = new BootstrapRegistries();
622 
623         BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
624         loader.load( startupConfiguration.getBootstrapSchemas(), bootstrapRegistries );
625 
626         java.util.List errors = bootstrapRegistries.checkRefInteg();
627         if ( !errors.isEmpty() )
628         {
629             NamingException e = new NamingException();
630 
631             e.setRootCause( ( Throwable ) errors.get( 0 ) );
632 
633             throw e;
634         }
635 
636         globalRegistries = new GlobalRegistries( bootstrapRegistries );
637         
638         partitionNexus = new DefaultContextPartitionNexus( new LockableAttributesImpl() );
639         partitionNexus.init( configuration, null );
640         
641         interceptorChain = new InterceptorChain();
642         interceptorChain.init( configuration );
643     }
644 }