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 java.io.IOException;
21 import java.io.FileFilter;
22 import java.io.File;
23 import java.net.InetSocketAddress;
24 import java.util.Hashtable;
25 import java.util.Iterator;
26
27 import javax.naming.NamingException;
28 import javax.naming.Context;
29 import javax.naming.directory.DirContext;
30 import javax.naming.directory.Attributes;
31 import javax.naming.directory.BasicAttributes;
32
33 import org.apache.kerberos.kdc.KdcConfiguration;
34 import org.apache.kerberos.kdc.KerberosServer;
35 import org.apache.kerberos.store.JndiPrincipalStoreImpl;
36 import org.apache.kerberos.store.PrincipalStore;
37 import org.apache.ldap.common.exception.LdapConfigurationException;
38 import org.apache.ldap.server.DirectoryService;
39 import org.apache.ldap.server.configuration.ServerStartupConfiguration;
40 import org.apache.ldap.server.protocol.ExtendedOperationHandler;
41 import org.apache.ldap.server.protocol.LdapProtocolProvider;
42 import org.apache.mina.common.TransportType;
43 import org.apache.mina.registry.Service;
44 import org.apache.mina.registry.ServiceRegistry;
45 import org.apache.ntp.NtpServer;
46 import org.apache.ntp.NtpConfiguration;
47 import org.apache.protocol.common.LoadStrategy;
48 import org.apache.protocol.common.store.LdifFileLoader;
49 import org.apache.changepw.ChangePasswordServer;
50 import org.apache.changepw.ChangePasswordConfiguration;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54
55 /***
56 * Adds additional bootstrapping for server socket listeners when firing
57 * up the server.
58 *
59 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
60 * @version $Rev: 329711 $
61 * @see javax.naming.spi.InitialContextFactory
62 */
63 public class ServerContextFactory extends CoreContextFactory
64 {
65 private static final Logger log = LoggerFactory.getLogger( ServerContextFactory.class.getName() );
66 private static final String LDIF_FILES_DN = "ou=loadedLdifFiles,ou=configuration,ou=system";
67
68 private static Service ldapService;
69 private static KerberosServer kdcServer;
70 private static ChangePasswordServer changePasswordServer;
71 private static NtpServer ntpServer;
72 private static ServiceRegistry minaRegistry;
73
74
75 protected ServiceRegistry getMinaRegistry()
76 {
77 return minaRegistry;
78 }
79
80
81 public void afterShutdown( DirectoryService service )
82 {
83 if ( minaRegistry != null )
84 {
85 if ( ldapService != null )
86 {
87 minaRegistry.unbind( ldapService );
88 if ( log.isInfoEnabled() )
89 {
90 log.info( "Unbind of LDAP Service complete: " + ldapService );
91 }
92 ldapService = null;
93 }
94
95 if ( kdcServer != null )
96 {
97 kdcServer.destroy();
98 if ( log.isInfoEnabled() )
99 {
100 log.info( "Unbind of KRB5 Service complete: " + kdcServer );
101 }
102 kdcServer = null;
103 }
104
105 if ( changePasswordServer != null )
106 {
107 changePasswordServer.destroy();
108 if ( log.isInfoEnabled() )
109 {
110 log.info( "Unbind of Change Password Service complete: " + changePasswordServer );
111 }
112 changePasswordServer = null;
113 }
114
115 if ( ntpServer != null )
116 {
117 ntpServer.destroy();
118 if ( log.isInfoEnabled() )
119 {
120 log.info( "Unbind of NTP Service complete: " + ntpServer );
121 }
122 ntpServer = null;
123 }
124 }
125 }
126
127
128 public void afterStartup( DirectoryService service ) throws NamingException
129 {
130 ServerStartupConfiguration cfg =
131 ( ServerStartupConfiguration ) service.getConfiguration().getStartupConfiguration();
132 Hashtable env = service.getConfiguration().getEnvironment();
133
134 loadLdifs( service );
135
136 if ( cfg.isEnableNetworking() )
137 {
138 setupRegistry( cfg );
139 startLdapProtocol( cfg, env );
140
141 if ( cfg.isEnableKerberos() )
142 {
143 try
144 {
145 KdcConfiguration kdcConfiguration = new KdcConfiguration( env, LoadStrategy.PROPS );
146 PrincipalStore kdcStore = new JndiPrincipalStoreImpl( kdcConfiguration, this );
147 kdcServer = new KerberosServer( kdcConfiguration, minaRegistry, kdcStore );
148 }
149 catch ( Throwable t )
150 {
151 log.error( "Failed to start the Kerberos service", t );
152 }
153 }
154
155 if ( cfg.isEnableChangePassword() )
156 {
157 try
158 {
159 ChangePasswordConfiguration changePasswordConfiguration = new ChangePasswordConfiguration( env, LoadStrategy.PROPS );
160 PrincipalStore store = new JndiPrincipalStoreImpl( changePasswordConfiguration, this );
161 changePasswordServer = new ChangePasswordServer( changePasswordConfiguration, minaRegistry, store );
162 }
163 catch ( Throwable t )
164 {
165 log.error( "Failed to start the Change Password service", t );
166 }
167 }
168
169 if ( cfg.isEnableNtp() )
170 {
171 try
172 {
173 NtpConfiguration ntpConfig = new NtpConfiguration( env, LoadStrategy.PROPS );
174 ntpServer = new NtpServer( ntpConfig, minaRegistry );
175 }
176 catch ( Throwable t )
177 {
178 log.error( "Failed to start the NTP service", t );
179 }
180 }
181 }
182 }
183
184
185 private void ensureLdifFileBase( DirContext root ) throws NamingException
186 {
187 Attributes entry = new BasicAttributes( "ou", "loadedLdifFiles", true );
188 entry.put( "objectClass", "top" );
189 entry.get( "objectClass" ).add( "organizationalUnit" );
190 try
191 {
192 root.createSubcontext( LDIF_FILES_DN, entry );
193 log.info( "Creating " + LDIF_FILES_DN );
194 }
195 catch( NamingException e ) { log.info( LDIF_FILES_DN + " exists" );}
196 }
197
198
199 private final static String WINDOWSFILE_ATTR = "windowsFilePath";
200 private final static String UNIXFILE_ATTR = "unixFilePath";
201 private final static String WINDOWSFILE_OC = "windowsFile";
202 private final static String UNIXFILE_OC = "unixFile";
203 private void addFileEntry( DirContext root, File ldif ) throws NamingException
204 {
205 String rdnAttr = File.pathSeparatorChar == '//' ? WINDOWSFILE_ATTR : UNIXFILE_ATTR;
206 String oc = File.pathSeparatorChar == '//' ? WINDOWSFILE_OC : UNIXFILE_OC;
207 StringBuffer buf = new StringBuffer();
208 buf.append( rdnAttr );
209 buf.append( "=" );
210 buf.append( getCanonical( ldif ) );
211 buf.append( "," );
212 buf.append( LDIF_FILES_DN );
213
214 Attributes entry = new BasicAttributes( rdnAttr, getCanonical( ldif ), true );
215 entry.put( "objectClass", "top" );
216 entry.get( "objectClass" ).add( oc );
217 root.createSubcontext( buf.toString(), entry );
218 }
219
220
221 private Attributes getLdifFileEntry( DirContext root, File ldif ) throws NamingException
222 {
223 String rdnAttr = File.pathSeparatorChar == '//' ? WINDOWSFILE_ATTR : UNIXFILE_ATTR;
224 StringBuffer buf = new StringBuffer();
225 buf.append( rdnAttr );
226 buf.append( "=" );
227 buf.append( getCanonical( ldif ) );
228 buf.append( "," );
229 buf.append( LDIF_FILES_DN );
230
231 try
232 {
233 return root.getAttributes( buf.toString(), new String[]{ "createTimestamp" });
234 }
235 catch ( NamingException e )
236 {
237 return null;
238 }
239 }
240
241
242 private String getCanonical( File file ) throws NamingException
243 {
244 String canonical = null;
245 try
246 {
247 canonical = file.getCanonicalPath();
248 }
249 catch (IOException e)
250 {
251 log.error( "could not get canonical path", e );
252 return null;
253 }
254 return canonical;
255 }
256
257
258 private void loadLdifs( DirectoryService service ) throws NamingException
259 {
260 ServerStartupConfiguration cfg =
261 ( ServerStartupConfiguration ) service.getConfiguration().getStartupConfiguration();
262
263
264 if ( cfg.getLdifDirectory() == null )
265 {
266 log.info( "LDIF load directory not specified. No LDIF files will be loaded." );
267 return;
268 }
269
270
271 if ( !cfg.getLdifDirectory().exists() )
272 {
273 log.warn( "LDIF load directory '" + getCanonical( cfg.getLdifDirectory() )
274 + "' does not exist. No LDIF files will be loaded.");
275 return;
276 }
277
278
279 Hashtable env = ( Hashtable ) service.getConfiguration().getEnvironment().clone();
280 env.put( Context.PROVIDER_URL, "" );
281 DirContext root = ( DirContext ) this.getInitialContext( env );
282
283
284 ensureLdifFileBase( root );
285
286
287 if ( !cfg.getLdifDirectory().isDirectory() )
288 {
289 log.info( "LDIF load directory '" + getCanonical( cfg.getLdifDirectory() )
290 + "' is a file. Will attempt to load as LDIF." );
291 Attributes fileEntry = getLdifFileEntry( root, cfg.getLdifDirectory() );
292 if ( fileEntry != null )
293 {
294 String time = ( String ) fileEntry.get( "createTimestamp" ).get();
295 log.info( "Load of LDIF file '" + getCanonical( cfg.getLdifDirectory() )
296 + "' skipped. It has already been loaded on " + time + "." );
297 return;
298 }
299 LdifFileLoader loader = new LdifFileLoader( root, cfg.getLdifDirectory(), cfg.getLdifFilters() );
300 loader.execute();
301
302 addFileEntry( root, cfg.getLdifDirectory() );
303 return;
304 }
305
306
307 File[] ldifFiles = cfg.getLdifDirectory().listFiles( new FileFilter()
308 {
309 public boolean accept( File pathname )
310 {
311 boolean isLdif = pathname.getName().toLowerCase().endsWith( ".ldif" );
312 return pathname.isFile() && pathname.canRead() && isLdif;
313 }
314 });
315
316
317 if ( ldifFiles == null || ldifFiles.length == 0 )
318 {
319 log.warn( "LDIF load directory '" + getCanonical( cfg.getLdifDirectory() )
320 + "' does not contain any LDIF files. No LDIF files will be loaded.");
321 return;
322 }
323
324
325 for ( int ii = 0; ii < ldifFiles.length; ii++ )
326 {
327 Attributes fileEntry = getLdifFileEntry( root, ldifFiles[ii] );
328 if ( fileEntry != null )
329 {
330 String time = ( String ) fileEntry.get( "createTimestamp" ).get();
331 log.info( "Load of LDIF file '" + getCanonical( ldifFiles[ii] )
332 + "' skipped. It has already been loaded on " + time + "." );
333 continue;
334 }
335 LdifFileLoader loader = new LdifFileLoader( root, ldifFiles[ii], cfg.getLdifFilters() );
336 int count = loader.execute();
337 log.info( "Loaded " + count + " entries from LDIF file '" + getCanonical( ldifFiles[ii] ) + "'" );
338 if ( fileEntry == null )
339 {
340 addFileEntry( root, ldifFiles[ii] );
341 }
342 }
343 }
344
345
346 /***
347 * Starts up the MINA registry so various protocol providers can be started.
348 */
349 private void setupRegistry( ServerStartupConfiguration cfg )
350 {
351 minaRegistry = cfg.getMinaServiceRegistry();
352 }
353
354
355 /***
356 * Starts up the LDAP protocol provider to service LDAP requests
357 *
358 * @throws NamingException if there are problems starting the LDAP provider
359 */
360 private void startLdapProtocol( ServerStartupConfiguration cfg, Hashtable env ) throws NamingException
361 {
362 int port = cfg.getLdapPort();
363 Service service = new Service( "ldap", TransportType.SOCKET, new InetSocketAddress( port ) );
364
365
366 LdapProtocolProvider protocolProvider = new LdapProtocolProvider( ( Hashtable ) env.clone() );
367 for( Iterator i = cfg.getExtendedOperationHandlers().iterator(); i.hasNext(); )
368 {
369 ExtendedOperationHandler h = ( ExtendedOperationHandler ) i.next();
370 protocolProvider.addExtendedOperationHandler( h );
371 }
372
373 try
374 {
375 minaRegistry.bind( service, protocolProvider );
376 ldapService = service;
377 if ( log.isInfoEnabled() )
378 {
379 log.info( "Successful bind of LDAP Service completed: " + ldapService );
380 }
381 }
382 catch ( IOException e )
383 {
384 String msg = "Failed to bind the LDAP protocol service to the service registry: " + service;
385 LdapConfigurationException lce = new LdapConfigurationException( msg );
386 lce.setRootCause( e );
387 log.error( msg, e );
388 throw lce;
389 }
390 }
391 }