1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.ldap.server.jndi;
18
19
20 import org.apache.ldap.common.exception.LdapAuthenticationNotSupportedException;
21 import org.apache.ldap.common.exception.LdapConfigurationException;
22 import org.apache.ldap.common.exception.LdapNoPermissionException;
23 import org.apache.ldap.common.message.LockableAttributesImpl;
24 import org.apache.ldap.common.message.ResultCodeEnum;
25 import org.apache.ldap.common.name.LdapName;
26 import org.apache.ldap.common.schema.AttributeType;
27 import org.apache.ldap.common.schema.Normalizer;
28 import org.apache.ldap.common.util.DateUtils;
29 import org.apache.ldap.common.util.StringTools;
30 import org.apache.ldap.server.*;
31 import org.apache.ldap.server.db.*;
32 import org.apache.ldap.server.db.jdbm.JdbmDatabase;
33 import org.apache.ldap.server.interceptor.InterceptorChain;
34 import org.apache.ldap.server.interceptor.InterceptorConfigBuilder;
35 import org.apache.ldap.server.interceptor.InterceptorContext;
36 import org.apache.ldap.server.schema.AttributeTypeRegistry;
37 import org.apache.ldap.server.schema.GlobalRegistries;
38 import org.apache.ldap.server.schema.MatchingRuleRegistry;
39 import org.apache.ldap.server.schema.OidRegistry;
40 import org.apache.ldap.server.schema.bootstrap.BootstrapRegistries;
41 import org.apache.ldap.server.schema.bootstrap.BootstrapSchemaLoader;
42
43 import javax.naming.Context;
44 import javax.naming.Name;
45 import javax.naming.NamingException;
46 import javax.naming.directory.Attributes;
47 import javax.naming.directory.Attribute;
48 import javax.naming.spi.InitialContextFactory;
49 import java.io.File;
50 import java.lang.reflect.Constructor;
51 import java.util.ArrayList;
52 import java.util.Hashtable;
53 import java.util.List;
54
55
56 /***
57 * A server-side provider implementation of a InitialContextFactory. Can be
58 * utilized via JNDI API in the standard fashion:
59 *
60 * <code>
61 * Hashtable env = new Hashtable();
62 * env.put( Context.PROVIDER_URL, "ou=system" );
63 * env.put(
64 * Context.INITIAL_CONTEXT_FACTORY, "org.apache.ldap.server.jndi.CoreContextFactory" );
65 * InitialContext initialContext = new InitialContext( env );
66 * </code>
67 *
68 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
69 * @version $Rev$
70 * @see javax.naming.spi.InitialContextFactory
71 */
72 public class CoreContextFactory implements InitialContextFactory
73 {
74
75
76
77
78
79 /*** shorthand reference to the authentication type property */
80 private static final String TYPE = Context.SECURITY_AUTHENTICATION;
81
82 /*** shorthand reference to the authentication credentials property */
83 private static final String CREDS = Context.SECURITY_CREDENTIALS;
84
85 /*** shorthand reference to the authentication principal property */
86 protected static final String PRINCIPAL = Context.SECURITY_PRINCIPAL;
87
88 /*** shorthand reference to the admin principal name */
89 protected static final String ADMIN = SystemPartition.ADMIN_PRINCIPAL;
90
91 /*** shorthand reference to the admin principal distinguished name */
92 protected static final Name ADMIN_NAME = SystemPartition.getAdminDn();
93
94 /*** default path to working directory if WKDIR_ENV property is not set */
95 public static final String DEFAULT_WKDIR = "server-work";
96
97 /*** default schema classes for the SCHEMAS_ENV property if not set */
98 protected static final String[] DEFAULT_SCHEMAS = new String[]
99 {
100 "org.apache.ldap.server.schema.bootstrap.CoreSchema",
101 "org.apache.ldap.server.schema.bootstrap.CosineSchema",
102 "org.apache.ldap.server.schema.bootstrap.ApacheSchema",
103 "org.apache.ldap.server.schema.bootstrap.InetorgpersonSchema",
104 "org.apache.ldap.server.schema.bootstrap.JavaSchema",
105 "org.apache.ldap.server.schema.bootstrap.SystemSchema"
106 };
107
108
109
110
111
112 /*** The singleton JndiProvider instance */
113 protected JndiProvider provider = null;
114
115 /*** the initial context environment that fired up the backend subsystem */
116 protected Hashtable initialEnv;
117
118 /*** the system partition used by the context factory */
119 protected SystemPartition system;
120
121 /*** the registries for system schema objects */
122 protected GlobalRegistries globalRegistries;
123
124 /*** the root nexus */
125 protected RootNexus nexus;
126
127 /*** whether or not server is started for the first time */
128 protected boolean createMode;
129
130
131 /***
132 * Default constructor that sets the provider of this ServerContextFactory.
133 */
134 public CoreContextFactory()
135 {
136 JndiProvider.setProviderOn( this );
137 }
138
139
140 /***
141 * Enables this ServerContextFactory with a handle to the JndiProvider singleton.
142 *
143 * @param provider the system's singleton BackendSubsystem service.
144 */
145 void setProvider( JndiProvider provider )
146 {
147 this.provider = provider;
148 }
149
150
151 public Context getInitialContext( Hashtable env ) throws NamingException
152 {
153 env = ( Hashtable ) env.clone();
154
155 Context ctx = null;
156
157 if ( env.containsKey( EnvKeys.SHUTDOWN ) )
158 {
159 if ( this.provider == null )
160 {
161 return new DeadContext();
162 }
163
164 try
165 {
166 this.provider.shutdown();
167 }
168 catch ( Throwable t )
169 {
170 t.printStackTrace();
171 }
172 finally
173 {
174 ctx = new DeadContext();
175
176 provider = null;
177
178 initialEnv = null;
179 }
180
181 return ctx;
182 }
183
184 if ( env.containsKey( EnvKeys.SYNC ) )
185 {
186 provider.sync();
187
188 return provider.getLdapContext( env );
189 }
190
191 checkSecuritySettings( env );
192
193 if ( isAnonymous( env ) )
194 {
195 env.put( PRINCIPAL, "" );
196 }
197
198
199 if ( null == provider )
200 {
201
202
203 if ( isAnonymous( env ) && env.containsKey( EnvKeys.DISABLE_ANONYMOUS ) )
204 {
205 throw new LdapNoPermissionException( "cannot bind as anonymous "
206 + "on startup while disabling anonymous binds w/ property: "
207 + EnvKeys.DISABLE_ANONYMOUS );
208 }
209
210 this.initialEnv = env;
211
212 initialize();
213
214 createMode = createBootstrapEntries();
215
216
217
218
219
220
221
222
223 if ( createMode && env.containsKey( EnvKeys.TEST_ENTRIES ) )
224 {
225 ArrayList list = ( ArrayList ) initialEnv.get( EnvKeys.TEST_ENTRIES );
226
227 if ( list != null )
228 {
229 for ( int ii = 0; ii < list.size(); ii++ )
230 {
231 Attributes attributes = ( Attributes ) list.get( ii );
232
233 attributes.put( "creatorsName", ADMIN );
234
235 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
236
237 Attribute dn = attributes.remove( "dn" );
238
239 nexus.add( ( String ) dn.get(), new LdapName( ( String ) dn.get() ), attributes );
240 }
241 }
242 }
243 }
244
245 ctx = ( ServerContext ) provider.getLdapContext( env );
246
247 return ctx;
248 }
249
250
251 /***
252 * Checks to make sure security environment parameters are set correctly.
253 *
254 * @throws javax.naming.NamingException if the security settings are not correctly configured.
255 */
256 protected void checkSecuritySettings( Hashtable env ) throws NamingException
257 {
258 if ( env.containsKey( TYPE ) && env.get( TYPE ) != null )
259 {
260
261
262
263
264 if ( env.get( TYPE ).equals( "simple" ) )
265 {
266 if ( !env.containsKey( CREDS ) )
267 {
268 throw new LdapConfigurationException( "missing required "
269 + CREDS + " property for simple authentication" );
270 }
271
272 if ( !env.containsKey( PRINCIPAL ) )
273 {
274 throw new LdapConfigurationException( "missing required "
275 + PRINCIPAL + " property for simple authentication" );
276 }
277 }
278
279
280
281
282 else if ( env.get( TYPE ).equals( "none" ) )
283 {
284 if ( env.containsKey( CREDS ) )
285 {
286 throw new LdapConfigurationException( "ambiguous bind "
287 + "settings encountered where bind is anonymous yet "
288 + CREDS + " property is set" );
289 }
290 if ( env.containsKey( PRINCIPAL ) )
291 {
292 throw new LdapConfigurationException( "ambiguous bind "
293 + "settings encountered where bind is anonymous yet "
294 + PRINCIPAL + " property is set" );
295 }
296 }
297
298
299
300
301 else
302 {
303 throw new LdapAuthenticationNotSupportedException( ResultCodeEnum.AUTHMETHODNOTSUPPORTED );
304 }
305 }
306 else if ( env.containsKey( CREDS ) )
307 {
308 if ( !env.containsKey( PRINCIPAL ) )
309 {
310 throw new LdapConfigurationException( "credentials provided "
311 + "without principal name property: " + PRINCIPAL );
312 }
313 }
314 }
315
316
317 /***
318 * Checks to see if an anonymous bind is being attempted.
319 *
320 * @return true if bind is anonymous, false otherwise
321 */
322 protected boolean isAnonymous( Hashtable env )
323 {
324
325 if ( env.containsKey( TYPE ) && env.get( TYPE ) != null )
326 {
327 if ( env.get( TYPE ).equals( "none" ) )
328 {
329 return true;
330 }
331
332 return false;
333 }
334
335 if ( env.containsKey( CREDS ) )
336 {
337 return false;
338 }
339
340 return true;
341 }
342
343
344 /***
345 * Returns true if we had to create the bootstrap entries on the first
346 * start of the server. Otherwise if all entries exist, meaning none
347 * had to be created, then we are not starting for the first time.
348 *
349 * @throws javax.naming.NamingException
350 */
351 private boolean createBootstrapEntries() throws NamingException
352 {
353 boolean isFirstStart = false;
354
355
356
357
358
359
360
361
362 if ( nexus.hasEntry( ADMIN_NAME ) )
363 {
364 isFirstStart = false;
365 }
366 else
367 {
368 isFirstStart = true;
369
370 Attributes attributes = new LockableAttributesImpl();
371
372 attributes.put( "objectClass", "top" );
373
374 attributes.put( "objectClass", "person" );
375
376 attributes.put( "objectClass", "organizationalPerson" );
377
378 attributes.put( "objectClass", "inetOrgPerson" );
379
380 attributes.put( "uid", SystemPartition.ADMIN_UID );
381
382 attributes.put( "userPassword", SystemPartition.ADMIN_PW );
383
384 attributes.put( "displayName", "Directory Superuser" );
385
386 attributes.put( "creatorsName", ADMIN );
387
388 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
389
390 attributes.put( "displayName", "Directory Superuser" );
391
392 nexus.add( ADMIN, ADMIN_NAME, attributes );
393 }
394
395
396
397
398
399 if ( nexus.hasEntry( new LdapName( "ou=users,ou=system" ) ) )
400 {
401 isFirstStart = false;
402 }
403 else
404 {
405 isFirstStart = true;
406
407 Attributes attributes = new LockableAttributesImpl();
408
409 attributes.put( "objectClass", "top" );
410
411 attributes.put( "objectClass", "organizationalUnit" );
412
413 attributes.put( "ou", "users" );
414
415 attributes.put( "creatorsName", ADMIN );
416
417 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
418
419 nexus.add( "ou=users,ou=system", new LdapName( "ou=users,ou=system" ), attributes );
420 }
421
422
423
424
425
426 if ( nexus.hasEntry( new LdapName( "ou=groups,ou=system" ) ) )
427 {
428 isFirstStart = false;
429 }
430 else
431 {
432 isFirstStart = true;
433
434 Attributes attributes = new LockableAttributesImpl();
435
436 attributes.put( "objectClass", "top" );
437
438 attributes.put( "objectClass", "organizationalUnit" );
439
440 attributes.put( "ou", "groups" );
441
442 attributes.put( "creatorsName", ADMIN );
443
444 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
445
446 nexus.add( "ou=groups,ou=system", new LdapName( "ou=groups,ou=system" ), attributes );
447 }
448
449
450
451
452
453 if ( nexus.hasEntry( new LdapName( "prefNodeName=sysPrefRoot,ou=system" ) ) )
454 {
455 isFirstStart = false;
456 }
457 else
458 {
459 isFirstStart = true;
460
461 Attributes attributes = new LockableAttributesImpl();
462
463 attributes.put( "objectClass", "top" );
464
465 attributes.put( "objectClass", "prefNode" );
466
467 attributes.put( "objectClass", "extensibleObject" );
468
469 attributes.put( "prefNodeName", "sysPrefRoot" );
470
471 attributes.put( "creatorsName", ADMIN );
472
473 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
474
475 LdapName dn = new LdapName( "prefNodeName=sysPrefRoot,ou=system" );
476
477 nexus.add( "prefNodeName=sysPrefRoot,ou=system", dn, attributes );
478 }
479
480 return isFirstStart;
481 }
482
483
484 /***
485 * Kicks off the initialization of the entire system.
486 *
487 * @throws javax.naming.NamingException if there are problems along the way
488 */
489 protected void initialize() throws NamingException
490 {
491
492
493
494
495 BootstrapRegistries bootstrapRegistries = new BootstrapRegistries();
496
497 BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
498
499 String[] schemas = DEFAULT_SCHEMAS;
500
501 if ( initialEnv.containsKey( EnvKeys.SCHEMAS ) )
502 {
503 String schemaList = ( String ) initialEnv.get( EnvKeys.SCHEMAS );
504
505 schemaList = StringTools.deepTrim( schemaList );
506
507 schemas = schemaList.split( " " );
508
509 for ( int ii = 0; ii < schemas.length; ii++ )
510 {
511 schemas[ii] = schemas[ii].trim();
512 }
513 }
514
515 loader.load( schemas, bootstrapRegistries );
516
517 List errors = bootstrapRegistries.checkRefInteg();
518
519 if ( !errors.isEmpty() )
520 {
521 NamingException e = new NamingException();
522
523 e.setRootCause( ( Throwable ) errors.get( 0 ) );
524
525 throw e;
526 }
527
528
529
530
531
532 String wkdir = DEFAULT_WKDIR;
533
534 if ( initialEnv.containsKey( EnvKeys.WKDIR ) )
535 {
536 wkdir = ( ( String ) initialEnv.get( EnvKeys.WKDIR ) ).trim();
537 }
538
539 File wkdirFile = new File( wkdir );
540
541 if ( wkdirFile.isAbsolute() )
542 {
543 if ( !wkdirFile.exists() )
544 {
545 throw new NamingException( "working directory " + wkdir + " does not exist" );
546 }
547 }
548 else
549 {
550 File current = new File( "." );
551
552 mkdirs( current.getAbsolutePath(), wkdir );
553 }
554
555 LdapName suffix = new LdapName();
556
557 suffix.add( SystemPartition.SUFFIX );
558
559 Database db = new JdbmDatabase( suffix, suffix, wkdir );
560
561 AttributeTypeRegistry attributeTypeRegistry;
562
563 attributeTypeRegistry = bootstrapRegistries .getAttributeTypeRegistry();
564
565 OidRegistry oidRegistry;
566
567 oidRegistry = bootstrapRegistries.getOidRegistry();
568
569 ExpressionEvaluator evaluator;
570
571 evaluator = new ExpressionEvaluator( db, oidRegistry, attributeTypeRegistry );
572
573 ExpressionEnumerator enumerator;
574
575 enumerator = new ExpressionEnumerator( db, attributeTypeRegistry, evaluator );
576
577 SearchEngine eng = new DefaultSearchEngine( db, evaluator, enumerator );
578
579 AttributeType[] attributes = new AttributeType[]
580 {
581 attributeTypeRegistry.lookup( SystemPartition.ALIAS_OID ),
582
583 attributeTypeRegistry.lookup( SystemPartition.EXISTANCE_OID ),
584
585 attributeTypeRegistry.lookup( SystemPartition.HIERARCHY_OID ),
586
587 attributeTypeRegistry.lookup( SystemPartition.NDN_OID ),
588
589 attributeTypeRegistry.lookup( SystemPartition.ONEALIAS_OID ),
590
591 attributeTypeRegistry.lookup( SystemPartition.SUBALIAS_OID ),
592
593 attributeTypeRegistry.lookup( SystemPartition.UPDN_OID )
594 };
595
596 system = new SystemPartition( db, eng, attributes );
597
598 globalRegistries = new GlobalRegistries( system, bootstrapRegistries );
599
600 nexus = new RootNexus( system, new LockableAttributesImpl() );
601
602 provider = new JndiProvider( nexus );
603
604
605
606
607 InterceptorChain interceptor = ( InterceptorChain ) initialEnv.get( EnvKeys.INTERCEPTORS );
608
609 if( interceptor == null )
610 {
611
612
613 interceptor = InterceptorChain.newDefaultChain();
614 }
615
616 interceptor.init( new InterceptorContext( initialEnv, system, globalRegistries, nexus,
617 InterceptorConfigBuilder.build( initialEnv, EnvKeys.INTERCEPTORS ) ) );
618
619 provider.setInterceptor( interceptor );
620
621
622 if ( initialEnv.get( EnvKeys.PARTITIONS ) != null )
623 {
624 startUpAppPartitions( wkdir );
625 }
626 }
627
628 /***
629 * Starts up all the application partitions that will be attached to naming contexts in the system. Partition
630 * database files are created within a subdirectory immediately under the Eve working directory base.
631 *
632 * @param eveWkdir the base Eve working directory
633 * @throws javax.naming.NamingException if there are problems creating and starting these new application
634 * partitions
635 */
636 protected void startUpAppPartitions( String eveWkdir ) throws NamingException
637 {
638 OidRegistry oidRegistry = globalRegistries.getOidRegistry();
639
640 AttributeTypeRegistry attributeTypeRegistry;
641
642 attributeTypeRegistry = globalRegistries.getAttributeTypeRegistry();
643
644 MatchingRuleRegistry reg = globalRegistries.getMatchingRuleRegistry();
645
646
647 ContextPartitionConfig[] configs = null;
648
649 configs = PartitionConfigBuilder.getContextPartitionConfigs( initialEnv );
650
651 for ( int ii = 0; ii < configs.length; ii++ )
652 {
653
654
655
656
657 String wkdir = eveWkdir + File.separator + configs[ii].getId();
658
659 mkdirs( eveWkdir, configs[ii].getId() );
660
661
662
663
664
665 Name upSuffix = new LdapName( configs[ii].getSuffix() );
666
667 Normalizer dnNorm = reg.lookup( "distinguishedNameMatch" ) .getNormalizer();
668
669 Name normSuffix = new LdapName( ( String ) dnNorm.normalize( configs[ii].getSuffix() ) );
670
671 Database db = new JdbmDatabase( upSuffix, normSuffix, wkdir );
672
673
674
675
676
677 ExpressionEvaluator evaluator;
678
679 evaluator = new ExpressionEvaluator( db, oidRegistry, attributeTypeRegistry );
680
681 ExpressionEnumerator enumerator;
682
683 enumerator = new ExpressionEnumerator( db, attributeTypeRegistry, evaluator );
684
685 SearchEngine eng = new DefaultSearchEngine( db, evaluator, enumerator );
686
687
688
689
690
691 ArrayList attributeTypeList = new ArrayList();
692
693 attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.ALIAS_OID ) );
694
695 attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.EXISTANCE_OID ) );
696
697 attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.HIERARCHY_OID ) );
698
699 attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.NDN_OID ) );
700
701 attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.ONEALIAS_OID ) );
702
703 attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.SUBALIAS_OID ) );
704
705 attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.UPDN_OID ) );
706
707
708
709
710
711 for ( int jj = 0; jj < configs[ii].getIndices().length; jj++ )
712 {
713 attributeTypeList.add( attributeTypeRegistry
714 .lookup( configs[ii].getIndices()[jj] ) );
715 }
716
717
718
719
720
721 AttributeType[] indexTypes = ( AttributeType[] ) attributeTypeList
722 .toArray( new AttributeType[attributeTypeList.size()] );
723
724 String partitionClass = configs[ii].getPartitionClass();
725
726 String properties = configs[ii].getProperties();
727
728 ContextPartition partition = null;
729
730 if ( partitionClass == null )
731 {
732
733 partition = new ApplicationPartition( upSuffix, normSuffix, db, eng, indexTypes );
734
735 }
736 else
737 {
738
739 try
740 {
741 Class clazz = Class.forName( partitionClass );
742
743 Constructor constructor = clazz.getConstructor(
744 new Class[] { Name.class, Name.class, String.class } );
745
746 partition = ( ContextPartition ) constructor.newInstance(
747 new Object[] { upSuffix, normSuffix, properties } );
748 }
749 catch ( Exception e )
750 {
751 e.printStackTrace();
752 }
753 }
754
755 if ( partition != null )
756 {
757 nexus.register( partition );
758 }
759
760
761
762
763
764 partition.add( configs[ii].getSuffix(), normSuffix, configs[ii].getAttributes() );
765 }
766 }
767
768
769 /***
770 * Recursively creates a bunch of directories from a base down to a path.
771 *
772 * @param base the base directory to start at
773 * @param path the path to recursively create if we have to
774 * @return true if the target directory has been created or exists, false if we fail along the way somewhere
775 */
776 protected boolean mkdirs( String base, String path )
777 {
778 String[] comps = path.split( "/" );
779
780 File file = new File( base );
781
782 if ( !file.exists() )
783 {
784 file.mkdirs();
785 }
786
787 for ( int ii = 0; ii < comps.length; ii++ )
788 {
789 file = new File( file, comps[ii] );
790
791 if ( !file.exists() )
792 {
793 file.mkdirs();
794 }
795 }
796
797 return file.exists();
798 }
799 }