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 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.name.DnParser;
35 import org.apache.ldap.common.name.LdapName;
36 import org.apache.ldap.common.name.NameComponentNormalizer;
37 import org.apache.ldap.common.util.DateUtils;
38 import org.apache.ldap.server.configuration.Configuration;
39 import org.apache.ldap.server.configuration.ConfigurationException;
40 import org.apache.ldap.server.configuration.StartupConfiguration;
41 import org.apache.ldap.server.interceptor.InterceptorChain;
42 import org.apache.ldap.server.partition.ContextPartitionNexus;
43 import org.apache.ldap.server.partition.DefaultContextPartitionNexus;
44 import org.apache.ldap.server.schema.AttributeTypeRegistry;
45 import org.apache.ldap.server.schema.ConcreteNameComponentNormalizer;
46 import org.apache.ldap.server.schema.GlobalRegistries;
47 import org.apache.ldap.server.schema.bootstrap.BootstrapRegistries;
48 import org.apache.ldap.server.schema.bootstrap.BootstrapSchemaLoader;
49
50
51 /***
52 * Default implementation of {@link ContextFactoryService}.
53 *
54 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
55 * @version $Rev: 226451 $
56 */
57 class DefaultContextFactoryService extends ContextFactoryService
58 {
59 private final String instanceId;
60
61 private final ContextFactoryConfiguration configuration = new DefaultContextFactoryConfiguration( this );
62
63 private ContextFactoryServiceListener serviceListener;
64
65 /*** the initial context environment that fired up the backend subsystem */
66 private Hashtable environment;
67
68 /*** the configuration */
69 private StartupConfiguration startupConfiguration;
70
71 /*** the registries for system schema objects */
72 private GlobalRegistries globalRegistries;
73
74 /*** the root nexus */
75 private DefaultContextPartitionNexus partitionNexus;
76
77 /*** whether or not server is started for the first time */
78 private boolean firstStart;
79
80 /*** The interceptor (or interceptor chain) for this service */
81 private InterceptorChain interceptorChain;
82
83 /*** whether or not this instance has been shutdown */
84 private boolean started = false;
85
86
87
88
89
90
91 /***
92 * Creates a new instance.
93 */
94 public DefaultContextFactoryService( String instanceId )
95 {
96 if( instanceId == null )
97 {
98 throw new NullPointerException( "instanceId" );
99 }
100
101 this.instanceId = instanceId;
102
103
104 Runtime.getRuntime().addShutdownHook( new Thread( new Runnable() {
105 public void run()
106 {
107 try
108 {
109 shutdown();
110 }
111 catch( NamingException e )
112 {
113 e.printStackTrace();
114 }
115 }
116 }, "ApacheDS Shutdown Hook" ) );
117 }
118
119
120
121
122
123 public Context getJndiContext( String rootDN ) throws NamingException
124 {
125 return this.getJndiContext( null, null, "none", rootDN );
126 }
127
128 public synchronized Context getJndiContext( String principal, byte[] credential, String authentication, String rootDN ) throws NamingException
129 {
130 checkSecuritySettings( principal, credential, authentication );
131
132 if ( !started )
133 {
134 return new DeadContext();
135 }
136
137 Hashtable environment = getEnvironment();
138 environment.remove( Context.SECURITY_PRINCIPAL );
139 environment.remove( Context.SECURITY_CREDENTIALS );
140 environment.remove( Context.SECURITY_AUTHENTICATION );
141
142 if( principal != null )
143 {
144 environment.put( Context.SECURITY_PRINCIPAL, principal );
145 }
146
147 if( credential != null )
148 {
149 environment.put( Context.SECURITY_CREDENTIALS, credential );
150 }
151
152 if( authentication != null )
153 {
154 environment.put( Context.SECURITY_AUTHENTICATION, authentication );
155 }
156
157 if( rootDN == null )
158 {
159 rootDN = "";
160 }
161 environment.put( Context.PROVIDER_URL, rootDN );
162
163 return new ServerLdapContext( this, environment );
164 }
165
166 public synchronized void startup( ContextFactoryServiceListener listener, Hashtable env ) throws NamingException
167 {
168 Hashtable envCopy = ( Hashtable ) env.clone();
169
170 if( started )
171 {
172 return;
173 }
174
175 StartupConfiguration cfg = ( StartupConfiguration ) Configuration.toConfiguration( env );
176 envCopy.put( Context.PROVIDER_URL, "" );
177
178 try
179 {
180 cfg.validate();
181 }
182 catch( ConfigurationException e )
183 {
184 NamingException ne = new LdapConfigurationException( "Invalid configuration." );
185 ne.initCause( e );
186 throw ne;
187 }
188
189 this.environment = envCopy;
190 this.startupConfiguration = cfg;
191
192 listener.beforeStartup( this );
193 try
194 {
195 initialize();
196 firstStart = createBootstrapEntries();
197 createTestEntries();
198 this.serviceListener = listener;
199 started = true;
200 }
201 finally
202 {
203 listener.afterStartup( this );
204 }
205 }
206
207 public synchronized void sync() throws NamingException
208 {
209 if ( !started )
210 {
211 return;
212 }
213
214 serviceListener.beforeSync( this );
215 try
216 {
217 this.partitionNexus.sync();
218 }
219 finally
220 {
221 serviceListener.afterSync( this );
222 }
223 }
224
225
226 public synchronized void shutdown() throws NamingException
227 {
228 if ( !started )
229 {
230 return;
231 }
232
233 serviceListener.beforeShutdown( this );
234 try
235 {
236 this.partitionNexus.sync();
237 this.partitionNexus.destroy();
238 this.interceptorChain.destroy();
239 this.started = false;
240 }
241 finally
242 {
243 environment = null;
244 interceptorChain = null;
245 startupConfiguration = null;
246 serviceListener.afterShutdown( this );
247 }
248 }
249
250 public String getInstanceId()
251 {
252 return instanceId;
253 }
254
255 public ContextFactoryConfiguration getConfiguration()
256 {
257 return configuration;
258 }
259
260
261 public Hashtable getEnvironment()
262 {
263 return ( Hashtable ) environment.clone();
264 }
265
266 public ContextFactoryServiceListener getServiceListener()
267 {
268 return serviceListener;
269 }
270
271 public StartupConfiguration getStartupConfiguration()
272 {
273 return startupConfiguration;
274 }
275
276 public GlobalRegistries getGlobalRegistries()
277 {
278 return globalRegistries;
279 }
280
281 public ContextPartitionNexus getPartitionNexus()
282 {
283 return partitionNexus;
284 }
285
286 public InterceptorChain getInterceptorChain()
287 {
288 return interceptorChain;
289 }
290
291 public boolean isFirstStart()
292 {
293 return firstStart;
294 }
295
296 public boolean isStarted()
297 {
298 return started;
299 }
300
301 /***
302 * Checks to make sure security environment parameters are set correctly.
303 *
304 * @throws javax.naming.NamingException if the security settings are not correctly configured.
305 */
306 private void checkSecuritySettings( String principal, byte[] credential, String authentication ) throws NamingException
307 {
308 if( authentication == null )
309 {
310 authentication = "";
311 }
312
313
314
315
316
317 if ( "simple".equalsIgnoreCase( authentication ) )
318 {
319 if ( credential == null )
320 {
321 throw new LdapConfigurationException( "missing required "
322 + Context.SECURITY_CREDENTIALS + " property for simple authentication" );
323 }
324
325 if ( principal == null )
326 {
327 throw new LdapConfigurationException( "missing required "
328 + Context.SECURITY_PRINCIPAL + " property for simple authentication" );
329 }
330 }
331
332
333
334
335 else if ( "none".equalsIgnoreCase( authentication ) )
336 {
337 if ( credential != null )
338 {
339 throw new LdapConfigurationException( "ambiguous bind "
340 + "settings encountered where bind is anonymous yet "
341 + Context.SECURITY_CREDENTIALS + " property is set" );
342 }
343 if ( principal != null )
344 {
345 throw new LdapConfigurationException( "ambiguous bind "
346 + "settings encountered where bind is anonymous yet "
347 + Context.SECURITY_PRINCIPAL + " property is set" );
348 }
349
350 if( !startupConfiguration.isAllowAnonymousAccess() )
351 {
352 throw new LdapNoPermissionException( "Anonymous access disabled." );
353 }
354 }
355 else
356 {
357
358
359
360
361 throw new LdapAuthenticationNotSupportedException( "Unknown authentication type: '" + authentication + "'", ResultCodeEnum.AUTHMETHODNOTSUPPORTED );
362 }
363 }
364
365
366 /***
367 * Returns true if we had to create the bootstrap entries on the first
368 * start of the server. Otherwise if all entries exist, meaning none
369 * had to be created, then we are not starting for the first time.
370 *
371 * @throws javax.naming.NamingException
372 */
373 private boolean createBootstrapEntries() throws NamingException
374 {
375 boolean firstStart = false;
376
377
378
379
380
381
382
383
384 if ( !partitionNexus.hasEntry( ContextPartitionNexus.getAdminName() ) )
385 {
386 checkPermissionToCreateBootstrapEntries();
387 firstStart = true;
388
389 Attributes attributes = new LockableAttributesImpl();
390 attributes.put( "objectClass", "top" );
391 attributes.put( "objectClass", "person" );
392 attributes.put( "objectClass", "organizationalPerson" );
393 attributes.put( "objectClass", "inetOrgPerson" );
394 attributes.put( "uid", ContextPartitionNexus.ADMIN_UID );
395 attributes.put( "userPassword", environment.get( Context.SECURITY_CREDENTIALS ) );
396 attributes.put( "displayName", "Directory Superuser" );
397 attributes.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
398 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
399 attributes.put( "displayName", "Directory Superuser" );
400
401 partitionNexus.add( ContextPartitionNexus.ADMIN_PRINCIPAL, ContextPartitionNexus.getAdminName(), attributes );
402 }
403
404
405
406
407
408 if ( !partitionNexus.hasEntry( new LdapName( "ou=users,ou=system" ) ) )
409 {
410 firstStart = true;
411 checkPermissionToCreateBootstrapEntries();
412
413 Attributes attributes = new LockableAttributesImpl();
414 attributes.put( "objectClass", "top" );
415 attributes.put( "objectClass", "organizationalUnit" );
416 attributes.put( "ou", "users" );
417 attributes.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
418 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
419
420 partitionNexus.add( "ou=users,ou=system", new LdapName( "ou=users,ou=system" ), attributes );
421 }
422
423
424
425
426
427 if ( !partitionNexus.hasEntry( new LdapName( "ou=groups,ou=system" ) ) )
428 {
429 firstStart = true;
430 checkPermissionToCreateBootstrapEntries();
431
432 Attributes attributes = new LockableAttributesImpl();
433 attributes.put( "objectClass", "top" );
434 attributes.put( "objectClass", "organizationalUnit" );
435 attributes.put( "ou", "groups" );
436 attributes.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
437 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
438
439 partitionNexus.add( "ou=groups,ou=system", new LdapName( "ou=groups,ou=system" ), attributes );
440 }
441
442
443
444
445
446 if ( !partitionNexus.hasEntry( new LdapName( "prefNodeName=sysPrefRoot,ou=system" ) ) )
447 {
448 firstStart = true;
449 checkPermissionToCreateBootstrapEntries();
450
451 Attributes attributes = new LockableAttributesImpl();
452 attributes.put( "objectClass", "top" );
453 attributes.put( "objectClass", "prefNode" );
454 attributes.put( "objectClass", "extensibleObject" );
455 attributes.put( "prefNodeName", "sysPrefRoot" );
456 attributes.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
457 attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
458
459 LdapName dn = new LdapName( "prefNodeName=sysPrefRoot,ou=system" );
460
461 partitionNexus.add( "prefNodeName=sysPrefRoot,ou=system", dn, attributes );
462 }
463
464 return firstStart;
465 }
466
467 private void checkPermissionToCreateBootstrapEntries() throws NamingException
468 {
469 String principal = ( String ) environment.get( Context.SECURITY_PRINCIPAL );
470 if( principal == null || !ContextPartitionNexus.ADMIN_PRINCIPAL.equals( principal ) )
471 {
472 throw new NoPermissionException(
473 "Only '" + ContextPartitionNexus.ADMIN_PRINCIPAL + "' can initiate the first run." );
474 }
475 }
476
477
478
479 private void createTestEntries() throws NamingException
480 {
481
482
483
484
485
486
487 Iterator i = startupConfiguration.getTestEntries().iterator();
488 while( i.hasNext() )
489 {
490 Attributes entry = ( Attributes ) i.next();
491 entry.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL );
492 entry.put( "createTimestamp", DateUtils.getGeneralizedTime() );
493
494 Attribute dn = ( Attribute ) entry.get( "dn" ).clone();
495 AttributeTypeRegistry registry = globalRegistries.getAttributeTypeRegistry();
496 NameComponentNormalizer ncn = new ConcreteNameComponentNormalizer( registry );
497 DnParser parser = new DnParser( ncn );
498 Name ndn = parser.parse( ( String ) dn.get() );
499
500 partitionNexus.add( ( String ) dn.get(), ndn, entry );
501 }
502 }
503
504 /***
505 * Kicks off the initialization of the entire system.
506 *
507 * @throws javax.naming.NamingException if there are problems along the way
508 */
509 private void initialize() throws NamingException
510 {
511
512
513
514
515 BootstrapRegistries bootstrapRegistries = new BootstrapRegistries();
516
517 BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
518 loader.load( startupConfiguration.getBootstrapSchemas(), bootstrapRegistries );
519
520 java.util.List errors = bootstrapRegistries.checkRefInteg();
521 if ( !errors.isEmpty() )
522 {
523 NamingException e = new NamingException();
524
525 e.setRootCause( ( Throwable ) errors.get( 0 ) );
526
527 throw e;
528 }
529
530 globalRegistries = new GlobalRegistries( bootstrapRegistries );
531
532 partitionNexus = new DefaultContextPartitionNexus( new LockableAttributesImpl() );
533 partitionNexus.init( configuration, null );
534
535 interceptorChain = new InterceptorChain();
536 interceptorChain.init( configuration );
537 }
538 }