001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.directory.server.factory; 020 021 022import java.io.File; 023import java.io.IOException; 024import java.lang.annotation.Annotation; 025import java.lang.reflect.AnnotatedElement; 026import java.lang.reflect.Method; 027import java.net.ServerSocket; 028import java.util.ArrayList; 029import java.util.Collections; 030import java.util.List; 031 032import javax.net.ssl.TrustManager; 033 034import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms; 035import org.apache.directory.api.util.Network; 036import org.apache.directory.api.util.Strings; 037import org.apache.directory.server.annotations.CreateConsumer; 038import org.apache.directory.server.annotations.CreateLdapServer; 039import org.apache.directory.server.annotations.CreateTransport; 040import org.apache.directory.server.annotations.SaslMechanism; 041import org.apache.directory.server.core.annotations.AnnotationUtils; 042import org.apache.directory.server.core.api.DirectoryService; 043import org.apache.directory.server.core.security.CertificateUtil; 044import org.apache.directory.server.i18n.I18n; 045import org.apache.directory.server.ldap.ExtendedOperationHandler; 046import org.apache.directory.server.ldap.LdapServer; 047import org.apache.directory.server.ldap.handlers.sasl.MechanismHandler; 048import org.apache.directory.server.ldap.handlers.sasl.ntlm.NtlmMechanismHandler; 049import org.apache.directory.server.ldap.handlers.sasl.ntlm.NtlmProvider; 050import org.apache.directory.server.ldap.replication.SyncReplConfiguration; 051import org.apache.directory.server.ldap.replication.consumer.ReplicationConsumer; 052import org.apache.directory.server.ldap.replication.consumer.ReplicationConsumerImpl; 053import org.apache.directory.server.protocol.shared.transport.TcpTransport; 054import org.apache.directory.server.protocol.shared.transport.Transport; 055import org.apache.directory.server.protocol.shared.transport.UdpTransport; 056 057 058/** 059 * 060 * Annotation processor for creating LDAP and Kerberos servers. 061 * 062 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 063 */ 064public final class ServerAnnotationProcessor 065{ 066 private ServerAnnotationProcessor() 067 { 068 } 069 070 071 private static void createTransports( LdapServer ldapServer, CreateTransport[] transportBuilders ) 072 { 073 if ( transportBuilders.length != 0 ) 074 { 075 for ( CreateTransport transportBuilder : transportBuilders ) 076 { 077 List< Transport > transports = createTransports( transportBuilder ); 078 079 for ( Transport t : transports ) 080 { 081 ldapServer.addTransports( t ); 082 } 083 } 084 } 085 else 086 { 087 // Create default LDAP and LDAPS transports 088 try 089 { 090 int port = getFreePort(); 091 Transport ldap = new TcpTransport( port ); 092 ldapServer.addTransports( ldap ); 093 } 094 catch ( IOException ioe ) 095 { 096 // Don't know what to do here... 097 } 098 099 try 100 { 101 int port = getFreePort(); 102 Transport ldaps = new TcpTransport( port ); 103 ldaps.setEnableSSL( true ); 104 ldapServer.addTransports( ldaps ); 105 } 106 catch ( IOException ioe ) 107 { 108 // Don't know what to do here... 109 } 110 } 111 } 112 113 114 /** 115 * Just gives an instance of {@link LdapServer} without starting it. 116 * For getting a running LdapServer instance see {@link #createLdapServer(CreateLdapServer, DirectoryService)} 117 * @see #createLdapServer(CreateLdapServer, DirectoryService) 118 * 119 * @param createLdapServer The LdapServer to create 120 * @param directoryService the directory service 121 * @return The created LdapServer 122 */ 123 public static LdapServer instantiateLdapServer( CreateLdapServer createLdapServer, DirectoryService directoryService ) 124 { 125 if ( createLdapServer != null ) 126 { 127 LdapServer ldapServer = new LdapServer(); 128 129 ldapServer.setServiceName( createLdapServer.name() ); 130 131 // Read the transports 132 createTransports( ldapServer, createLdapServer.transports() ); 133 134 // Associate the DS to this LdapServer 135 ldapServer.setDirectoryService( directoryService ); 136 137 // Propagate the anonymous flag to the DS 138 directoryService.setAllowAnonymousAccess( createLdapServer.allowAnonymousAccess() ); 139 140 ldapServer.setSaslHost( createLdapServer.saslHost() ); 141 142 ldapServer.setSaslPrincipal( createLdapServer.saslPrincipal() ); 143 144 if ( !Strings.isEmpty( createLdapServer.keyStore() ) ) 145 { 146 ldapServer.setKeystoreFile( createLdapServer.keyStore() ); 147 ldapServer.setCertificatePassword( createLdapServer.certificatePassword() ); 148 } 149 else 150 { 151 try 152 { 153 // Create a temporary keystore, be sure to remove it when exiting the test 154 File keyStoreFile = CertificateUtil.createTempKeyStore( "testStore", "secret".toCharArray() ); 155 ldapServer.setKeystoreFile( keyStoreFile.getAbsolutePath() ); 156 ldapServer.setCertificatePassword( "secret" ); 157 } 158 catch ( Exception e ) 159 { 160 161 } 162 } 163 164 for ( Class<?> extOpClass : createLdapServer.extendedOpHandlers() ) 165 { 166 try 167 { 168 ExtendedOperationHandler extOpHandler = ( ExtendedOperationHandler ) extOpClass.newInstance(); 169 ldapServer.addExtendedOperationHandler( extOpHandler ); 170 } 171 catch ( Exception e ) 172 { 173 throw new RuntimeException( I18n.err( I18n.ERR_690, extOpClass.getName() ), e ); 174 } 175 } 176 177 for ( SaslMechanism saslMech : createLdapServer.saslMechanisms() ) 178 { 179 try 180 { 181 MechanismHandler handler = ( MechanismHandler ) saslMech.implClass().newInstance(); 182 ldapServer.addSaslMechanismHandler( saslMech.name(), handler ); 183 } 184 catch ( Exception e ) 185 { 186 throw new RuntimeException( 187 I18n.err( I18n.ERR_691, saslMech.name(), saslMech.implClass().getName() ), e ); 188 } 189 } 190 191 NtlmMechanismHandler ntlmHandler = ( NtlmMechanismHandler ) ldapServer.getSaslMechanismHandlers().get( 192 SupportedSaslMechanisms.NTLM ); 193 194 if ( ntlmHandler != null ) 195 { 196 Class<?> ntlmProviderClass = createLdapServer.ntlmProvider(); 197 // default value is a invalid Object.class 198 if ( ( ntlmProviderClass != null ) && ( ntlmProviderClass != Object.class ) ) 199 { 200 try 201 { 202 ntlmHandler.setNtlmProvider( ( NtlmProvider ) ntlmProviderClass.newInstance() ); 203 } 204 catch ( Exception e ) 205 { 206 throw new RuntimeException( I18n.err( I18n.ERR_692 ), e ); 207 } 208 } 209 } 210 211 List<String> realms = new ArrayList<>(); 212 for ( String s : createLdapServer.saslRealms() ) 213 { 214 realms.add( s ); 215 } 216 217 ldapServer.setSaslRealms( realms ); 218 219 if ( createLdapServer.trustManagers() != null && createLdapServer.trustManagers().length > 0 ) 220 { 221 TrustManager[] trustManagers = new TrustManager[createLdapServer.trustManagers().length]; 222 for ( int i = 0; i < createLdapServer.trustManagers().length; i++ ) 223 { 224 try 225 { 226 trustManagers[i] = ( TrustManager ) createLdapServer.trustManagers()[i].newInstance(); 227 } 228 catch ( InstantiationException | IllegalAccessException e ) 229 { 230 throw new RuntimeException( I18n.err( I18n.ERR_751, createLdapServer.trustManagers()[i].getName() ), e ); 231 } 232 } 233 ldapServer.setTrustManagers( trustManagers ); 234 } 235 236 return ldapServer; 237 } 238 else 239 { 240 return null; 241 } 242 } 243 244 245 /** 246 * Returns an LdapServer instance and starts it before returning the instance, infering 247 * the configuration from the Stack trace 248 * 249 * @param directoryService the directory service 250 * @return a running LdapServer instance 251 * @throws ClassNotFoundException If the CreateLdapServer class cannot be loaded 252 */ 253 public static LdapServer getLdapServer( DirectoryService directoryService ) throws ClassNotFoundException 254 { 255 Object instance = AnnotationUtils.getInstance( CreateLdapServer.class ); 256 LdapServer ldapServer = null; 257 258 if ( instance != null ) 259 { 260 CreateLdapServer createLdapServer = ( CreateLdapServer ) instance; 261 262 ldapServer = createLdapServer( createLdapServer, directoryService ); 263 } 264 265 return ldapServer; 266 } 267 268 269 /** 270 * Create a replication consumer 271 */ 272 private static ReplicationConsumer createConsumer( CreateConsumer createConsumer ) 273 { 274 ReplicationConsumer consumer = new ReplicationConsumerImpl(); 275 276 SyncReplConfiguration config = new SyncReplConfiguration(); 277 278 String remoteHost = createConsumer.remoteHost(); 279 280 if ( Strings.isEmpty( remoteHost ) ) 281 { 282 remoteHost = Network.LOOPBACK_HOSTNAME; 283 } 284 285 config.setRemoteHost( remoteHost ); 286 config.setRemotePort( createConsumer.remotePort() ); 287 config.setReplUserDn( createConsumer.replUserDn() ); 288 config.setReplUserPassword( Strings.getBytesUtf8( createConsumer.replUserPassword() ) ); 289 config.setUseTls( createConsumer.useTls() ); 290 config.setBaseDn( createConsumer.baseDn() ); 291 config.setRefreshInterval( createConsumer.refreshInterval() ); 292 config.setStrictCertVerification( createConsumer.strictCertVerification() ); 293 294 consumer.setConfig( config ); 295 296 return consumer; 297 } 298 299 300 /** 301 * creates an LdapServer and starts before returning the instance, infering 302 * the configuration from the Stack trace 303 * 304 * @return a running LdapServer instance 305 * @throws ClassNotFoundException If the CreateConsumer class cannot be loaded 306 */ 307 public static ReplicationConsumer createConsumer() throws ClassNotFoundException 308 { 309 Object instance = AnnotationUtils.getInstance( CreateConsumer.class ); 310 ReplicationConsumer consumer = null; 311 312 if ( instance != null ) 313 { 314 CreateConsumer createConsumer = ( CreateConsumer ) instance; 315 316 consumer = createConsumer( createConsumer ); 317 } 318 319 return consumer; 320 } 321 322 323 /** 324 * creates an LdapServer and starts before returning the instance 325 * 326 * @param createLdapServer the annotation containing the custom configuration 327 * @param directoryService the directory service 328 * @return a running LdapServer instance 329 */ 330 public static LdapServer createLdapServer( CreateLdapServer createLdapServer, DirectoryService directoryService ) 331 { 332 LdapServer ldapServer = instantiateLdapServer( createLdapServer, directoryService ); 333 334 if ( ldapServer == null ) 335 { 336 return null; 337 } 338 339 // Launch the server 340 try 341 { 342 ldapServer.start(); 343 } 344 catch ( Exception e ) 345 { 346 e.printStackTrace(); 347 } 348 349 return ldapServer; 350 } 351 352 353 /** 354 * Create a new instance of LdapServer 355 * 356 * @param description A description for the created LdapServer 357 * @param directoryService The associated DirectoryService 358 * @return An LdapServer instance 359 */ 360 public static LdapServer createLdapServer( AnnotatedElement annotation, DirectoryService directoryService ) 361 { 362 CreateLdapServer createLdapServer = annotation.getAnnotation( CreateLdapServer.class ); 363 364 // Ok, we have found a CreateLdapServer annotation. Process it now. 365 return createLdapServer( createLdapServer, directoryService ); 366 } 367 368 369 @SuppressWarnings("unchecked") 370 private static Annotation getAnnotation( Class annotationClass ) throws Exception 371 { 372 // Get the caller by inspecting the stackTrace 373 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 374 375 // In Java5 the 0th stacktrace element is: java.lang.Thread.dumpThreads(Native Method) 376 int index = stackTrace[0].getMethodName().equals( "dumpThreads" ) ? 4 : 3; 377 378 // Get the enclosing class 379 Class<?> classCaller = Class.forName( stackTrace[index].getClassName() ); 380 381 // Get the current method 382 String methodCaller = stackTrace[index].getMethodName(); 383 384 // Check if we have any annotation associated with the method 385 Method[] methods = classCaller.getMethods(); 386 387 for ( Method method : methods ) 388 { 389 if ( methodCaller.equals( method.getName() ) ) 390 { 391 Annotation annotation = method.getAnnotation( annotationClass ); 392 393 if ( annotation != null ) 394 { 395 return annotation; 396 } 397 } 398 } 399 400 // No : look at the class level 401 return classCaller.getAnnotation( annotationClass ); 402 } 403 404 private static List< Transport > createTransports( CreateTransport transportBuilder ) 405 { 406 String protocol = transportBuilder.protocol(); 407 int port = transportBuilder.port(); 408 int nbThreads = transportBuilder.nbThreads(); 409 int backlog = transportBuilder.backlog(); 410 String address = transportBuilder.address(); 411 boolean clientAuth = transportBuilder.clientAuth(); 412 413 if ( Strings.isEmpty( address ) ) 414 { 415 address = Network.LOOPBACK_HOSTNAME; 416 } 417 418 if ( port <= 0 ) 419 { 420 try 421 { 422 port = getFreePort(); 423 } 424 catch ( IOException ioe ) 425 { 426 // Don't know what to do here... 427 } 428 } 429 430 if ( protocol.equalsIgnoreCase( "TCP" ) || protocol.equalsIgnoreCase( "LDAP" ) ) 431 { 432 Transport tcp = new TcpTransport( address, port, nbThreads, backlog ); 433 return Collections.singletonList( tcp ); 434 } 435 else if ( protocol.equalsIgnoreCase( "LDAPS" ) ) 436 { 437 Transport tcp = new TcpTransport( address, port, nbThreads, backlog ); 438 tcp.setEnableSSL( true ); 439 ( ( TcpTransport ) tcp ).setWantClientAuth( clientAuth ); 440 return Collections.singletonList( tcp ); 441 } 442 else if ( protocol.equalsIgnoreCase( "UDP" ) ) 443 { 444 Transport udp = new UdpTransport( address, port ); 445 return Collections.singletonList( udp ); 446 } 447 else if ( protocol.equalsIgnoreCase( "KRB" ) || protocol.equalsIgnoreCase( "CPW" ) ) 448 { 449 Transport tcp = new TcpTransport( address, port, nbThreads, backlog ); 450 List< Transport > transports = new ArrayList<>(); 451 transports.add( tcp ); 452 453 Transport udp = new UdpTransport( address, port ); 454 transports.add( udp ); 455 return transports; 456 } 457 458 throw new IllegalArgumentException( I18n.err( I18n.ERR_689, protocol ) ); 459 } 460 461 462 private static int getFreePort() throws IOException 463 { 464 ServerSocket ss = new ServerSocket( 0 ); 465 int port = ss.getLocalPort(); 466 ss.close(); 467 468 return port; 469 } 470 471}