| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.directory.server.factory; |
| |
| |
| import java.io.IOException; |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.Method; |
| import java.net.ServerSocket; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms; |
| import org.apache.directory.api.util.Strings; |
| import org.apache.directory.server.annotations.CreateChngPwdServer; |
| import org.apache.directory.server.annotations.CreateConsumer; |
| import org.apache.directory.server.annotations.CreateKdcServer; |
| import org.apache.directory.server.annotations.CreateLdapServer; |
| import org.apache.directory.server.annotations.CreateTransport; |
| import org.apache.directory.server.annotations.SaslMechanism; |
| import org.apache.directory.server.core.annotations.AnnotationUtils; |
| import org.apache.directory.server.core.api.DirectoryService; |
| import org.apache.directory.server.i18n.I18n; |
| import org.apache.directory.server.kerberos.ChangePasswordConfig; |
| import org.apache.directory.server.kerberos.KerberosConfig; |
| import org.apache.directory.server.kerberos.changepwd.ChangePasswordServer; |
| import org.apache.directory.server.kerberos.kdc.KdcServer; |
| import org.apache.directory.server.ldap.ExtendedOperationHandler; |
| import org.apache.directory.server.ldap.LdapServer; |
| import org.apache.directory.server.ldap.handlers.sasl.MechanismHandler; |
| import org.apache.directory.server.ldap.handlers.sasl.ntlm.NtlmMechanismHandler; |
| import org.apache.directory.server.ldap.handlers.sasl.ntlm.NtlmProvider; |
| import org.apache.directory.server.ldap.replication.SyncReplConfiguration; |
| import org.apache.directory.server.ldap.replication.consumer.ReplicationConsumer; |
| import org.apache.directory.server.ldap.replication.consumer.ReplicationConsumerImpl; |
| import org.apache.directory.server.protocol.shared.transport.TcpTransport; |
| import org.apache.directory.server.protocol.shared.transport.Transport; |
| import org.apache.directory.server.protocol.shared.transport.UdpTransport; |
| import org.apache.mina.util.AvailablePortFinder; |
| import org.junit.runner.Description; |
| |
| |
| /** |
| * |
| * Annotation processor for creating LDAP and Kerberos servers. |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| */ |
| public class ServerAnnotationProcessor |
| { |
| private static void createTransports( LdapServer ldapServer, CreateTransport[] transportBuilders ) |
| { |
| if ( transportBuilders.length != 0 ) |
| { |
| for ( CreateTransport transportBuilder : transportBuilders ) |
| { |
| String protocol = transportBuilder.protocol(); |
| int port = transportBuilder.port(); |
| int nbThreads = transportBuilder.nbThreads(); |
| int backlog = transportBuilder.backlog(); |
| String address = transportBuilder.address(); |
| |
| if ( port <= 0 ) |
| { |
| try |
| { |
| ServerSocket ss = new ServerSocket( 0 ); |
| |
| port = ss.getLocalPort(); |
| |
| ss.close(); |
| } |
| catch ( IOException ioe ) |
| { |
| // Don't know what to do here... |
| } |
| } |
| |
| if ( protocol.equalsIgnoreCase( "LDAP" ) ) |
| { |
| Transport ldap = new TcpTransport( address, port, nbThreads, backlog ); |
| ldapServer.addTransports( ldap ); |
| } |
| else if ( protocol.equalsIgnoreCase( "LDAPS" ) ) |
| { |
| Transport ldaps = new TcpTransport( address, port, nbThreads, backlog ); |
| ldaps.setEnableSSL( true ); |
| ldapServer.addTransports( ldaps ); |
| } |
| else |
| { |
| throw new IllegalArgumentException( I18n.err( I18n.ERR_689, protocol ) ); |
| } |
| } |
| } |
| else |
| { |
| // Create default LDAP and LDAPS transports |
| int port = AvailablePortFinder.getNextAvailable( 1024 ); |
| Transport ldap = new TcpTransport( port ); |
| ldapServer.addTransports( ldap ); |
| |
| port = AvailablePortFinder.getNextAvailable( port ); |
| Transport ldaps = new TcpTransport( port ); |
| ldaps.setEnableSSL( true ); |
| ldapServer.addTransports( ldaps ); |
| } |
| } |
| |
| |
| /** |
| * Just gives an instance of {@link LdapServer} without starting it. |
| * For getting a running LdapServer instance see {@link #createLdapServer(CreateLdapServer, DirectoryService)} |
| * @see #createLdapServer(CreateLdapServer, DirectoryService) |
| */ |
| public static LdapServer instantiateLdapServer( CreateLdapServer createLdapServer, DirectoryService directoryService ) |
| { |
| if ( createLdapServer != null ) |
| { |
| LdapServer ldapServer = new LdapServer(); |
| |
| ldapServer.setServiceName( createLdapServer.name() ); |
| |
| // Read the transports |
| createTransports( ldapServer, createLdapServer.transports() ); |
| |
| // Associate the DS to this LdapServer |
| ldapServer.setDirectoryService( directoryService ); |
| |
| // Propagate the anonymous flag to the DS |
| directoryService.setAllowAnonymousAccess( createLdapServer.allowAnonymousAccess() ); |
| |
| ldapServer.setSaslHost( createLdapServer.saslHost() ); |
| |
| ldapServer.setSaslPrincipal( createLdapServer.saslPrincipal() ); |
| |
| if ( !Strings.isEmpty( createLdapServer.keyStore() ) ) |
| { |
| ldapServer.setKeystoreFile( createLdapServer.keyStore() ); |
| ldapServer.setCertificatePassword( createLdapServer.certificatePassword() ); |
| } |
| |
| for ( Class<?> extOpClass : createLdapServer.extendedOpHandlers() ) |
| { |
| try |
| { |
| ExtendedOperationHandler extOpHandler = ( ExtendedOperationHandler ) extOpClass.newInstance(); |
| ldapServer.addExtendedOperationHandler( extOpHandler ); |
| } |
| catch ( Exception e ) |
| { |
| throw new RuntimeException( I18n.err( I18n.ERR_690, extOpClass.getName() ), e ); |
| } |
| } |
| |
| for ( SaslMechanism saslMech : createLdapServer.saslMechanisms() ) |
| { |
| try |
| { |
| MechanismHandler handler = ( MechanismHandler ) saslMech.implClass().newInstance(); |
| ldapServer.addSaslMechanismHandler( saslMech.name(), handler ); |
| } |
| catch ( Exception e ) |
| { |
| throw new RuntimeException( |
| I18n.err( I18n.ERR_691, saslMech.name(), saslMech.implClass().getName() ), e ); |
| } |
| } |
| |
| NtlmMechanismHandler ntlmHandler = ( NtlmMechanismHandler ) ldapServer.getSaslMechanismHandlers().get( |
| SupportedSaslMechanisms.NTLM ); |
| |
| if ( ntlmHandler != null ) |
| { |
| Class<?> ntlmProviderClass = createLdapServer.ntlmProvider(); |
| // default value is a invalid Object.class |
| if ( ( ntlmProviderClass != null ) && ( ntlmProviderClass != Object.class ) ) |
| { |
| try |
| { |
| ntlmHandler.setNtlmProvider( ( NtlmProvider ) ntlmProviderClass.newInstance() ); |
| } |
| catch ( Exception e ) |
| { |
| throw new RuntimeException( I18n.err( I18n.ERR_692 ), e ); |
| } |
| } |
| } |
| |
| List<String> realms = new ArrayList<String>(); |
| for ( String s : createLdapServer.saslRealms() ) |
| { |
| realms.add( s ); |
| } |
| |
| ldapServer.setSaslRealms( realms ); |
| |
| return ldapServer; |
| } |
| else |
| { |
| return null; |
| } |
| } |
| |
| |
| /** |
| * Returns an LdapServer instance and starts it before returning the instance, infering |
| * the configuration from the Stack trace |
| * |
| * @return a running LdapServer instance |
| */ |
| public static LdapServer getLdapServer( DirectoryService directoryService ) throws ClassNotFoundException |
| { |
| Object instance = AnnotationUtils.getInstance( CreateLdapServer.class ); |
| LdapServer ldapServer = null; |
| |
| if ( instance != null ) |
| { |
| CreateLdapServer createLdapServer = ( CreateLdapServer ) instance; |
| |
| ldapServer = createLdapServer( createLdapServer, directoryService ); |
| } |
| |
| return ldapServer; |
| } |
| |
| |
| /** |
| * Create a replication consumer |
| */ |
| private static ReplicationConsumer createConsumer( CreateConsumer createConsumer ) |
| { |
| ReplicationConsumer consumer = new ReplicationConsumerImpl(); |
| |
| SyncReplConfiguration config = new SyncReplConfiguration(); |
| config.setRemoteHost( createConsumer.remoteHost() ); |
| config.setRemotePort( createConsumer.remotePort() ); |
| config.setReplUserDn( createConsumer.replUserDn() ); |
| config.setReplUserPassword( Strings.getBytesUtf8( createConsumer.replUserPassword() ) ); |
| config.setUseTls( createConsumer.useTls() ); |
| config.setBaseDn( createConsumer.baseDn() ); |
| config.setRefreshInterval( createConsumer.refreshInterval() ); |
| |
| consumer.setConfig( config ); |
| |
| return consumer; |
| } |
| |
| |
| /** |
| * creates an LdapServer and starts before returning the instance, infering |
| * the configuration from the Stack trace |
| * |
| * @return a running LdapServer instance |
| */ |
| public static ReplicationConsumer createConsumer() throws ClassNotFoundException |
| { |
| Object instance = AnnotationUtils.getInstance( CreateConsumer.class ); |
| ReplicationConsumer consumer = null; |
| |
| if ( instance != null ) |
| { |
| CreateConsumer createConsumer = ( CreateConsumer ) instance; |
| |
| consumer = createConsumer( createConsumer ); |
| } |
| |
| return consumer; |
| } |
| |
| |
| /** |
| * creates an LdapServer and starts before returning the instance |
| * |
| * @param createLdapServer the annotation containing the custom configuration |
| * @param directoryService the directory service |
| * @return a running LdapServer instance |
| */ |
| private static LdapServer createLdapServer( CreateLdapServer createLdapServer, DirectoryService directoryService ) |
| { |
| LdapServer ldapServer = instantiateLdapServer( createLdapServer, directoryService ); |
| |
| if ( ldapServer == null ) |
| { |
| return null; |
| } |
| |
| // Launch the server |
| try |
| { |
| ldapServer.start(); |
| } |
| catch ( Exception e ) |
| { |
| e.printStackTrace(); |
| } |
| |
| return ldapServer; |
| } |
| |
| |
| /** |
| * Create a new instance of LdapServer |
| * |
| * @param description A description for the created LdapServer |
| * @param directoryService The associated DirectoryService |
| * @param startPort The port used by the server |
| * @return An LdapServer instance |
| * @throws Exception If the server cannot be started |
| */ |
| public static LdapServer createLdapServer( Description description, DirectoryService directoryService ) |
| throws Exception |
| { |
| CreateLdapServer createLdapServer = description.getAnnotation( CreateLdapServer.class ); |
| |
| // Ok, we have found a CreateLdapServer annotation. Process it now. |
| return createLdapServer( createLdapServer, directoryService ); |
| } |
| |
| |
| @SuppressWarnings("unchecked") |
| private static Annotation getAnnotation( Class annotationClass ) throws Exception |
| { |
| // Get the caller by inspecting the stackTrace |
| StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); |
| |
| // In Java5 the 0th stacktrace element is: java.lang.Thread.dumpThreads(Native Method) |
| int index = stackTrace[0].getMethodName().equals( "dumpThreads" ) ? 4 : 3; |
| |
| // Get the enclosing class |
| Class<?> classCaller = Class.forName( stackTrace[index].getClassName() ); |
| |
| // Get the current method |
| String methodCaller = stackTrace[index].getMethodName(); |
| |
| // Check if we have any annotation associated with the method |
| Method[] methods = classCaller.getMethods(); |
| |
| for ( Method method : methods ) |
| { |
| if ( methodCaller.equals( method.getName() ) ) |
| { |
| Annotation annotation = method.getAnnotation( annotationClass ); |
| |
| if ( annotation != null ) |
| { |
| return annotation; |
| } |
| } |
| } |
| |
| // No : look at the class level |
| return classCaller.getAnnotation( annotationClass ); |
| } |
| |
| |
| public static KdcServer getKdcServer( DirectoryService directoryService, int startPort ) throws Exception |
| { |
| CreateKdcServer createKdcServer = ( CreateKdcServer ) getAnnotation( CreateKdcServer.class ); |
| |
| return createKdcServer( createKdcServer, directoryService, startPort ); |
| } |
| |
| |
| private static KdcServer createKdcServer( CreateKdcServer createKdcServer, DirectoryService directoryService, |
| int startPort ) |
| { |
| if ( createKdcServer == null ) |
| { |
| return null; |
| } |
| |
| KerberosConfig kdcConfig = new KerberosConfig(); |
| kdcConfig.setServicePrincipal( createKdcServer.kdcPrincipal() ); |
| kdcConfig.setPrimaryRealm( createKdcServer.primaryRealm() ); |
| kdcConfig.setMaximumTicketLifetime( createKdcServer.maxTicketLifetime() ); |
| kdcConfig.setMaximumRenewableLifetime( createKdcServer.maxRenewableLifetime() ); |
| |
| KdcServer kdcServer = new KdcServer( kdcConfig ); |
| |
| kdcServer.setSearchBaseDn( createKdcServer.searchBaseDn() ); |
| |
| CreateTransport[] transportBuilders = createKdcServer.transports(); |
| |
| if ( transportBuilders == null ) |
| { |
| // create only UDP transport if none specified |
| UdpTransport defaultTransport = new UdpTransport( AvailablePortFinder.getNextAvailable( startPort ) ); |
| kdcServer.addTransports( defaultTransport ); |
| } |
| else if ( transportBuilders.length > 0 ) |
| { |
| for ( CreateTransport transportBuilder : transportBuilders ) |
| { |
| Transport t = createTransport( transportBuilder, startPort ); |
| startPort = t.getPort() + 1; |
| kdcServer.addTransports( t ); |
| } |
| } |
| |
| CreateChngPwdServer[] createChngPwdServers = createKdcServer.chngPwdServer(); |
| |
| if ( createChngPwdServers.length > 0 ) |
| { |
| |
| CreateChngPwdServer createChngPwdServer = createChngPwdServers[0]; |
| ChangePasswordConfig config = new ChangePasswordConfig( kdcConfig ); |
| config.setServicePrincipal( createChngPwdServer.srvPrincipal() ); |
| |
| ChangePasswordServer chngPwdServer = new ChangePasswordServer( config ); |
| |
| for ( CreateTransport transportBuilder : createChngPwdServer.transports() ) |
| { |
| Transport t = createTransport( transportBuilder, startPort ); |
| startPort = t.getPort() + 1; |
| chngPwdServer.addTransports( t ); |
| } |
| |
| chngPwdServer.setDirectoryService( directoryService ); |
| |
| kdcServer.setChangePwdServer( chngPwdServer ); |
| } |
| |
| kdcServer.setDirectoryService( directoryService ); |
| |
| // Launch the server |
| try |
| { |
| kdcServer.start(); |
| } |
| catch ( Exception e ) |
| { |
| e.printStackTrace(); |
| } |
| |
| return kdcServer; |
| } |
| |
| |
| public static Transport createTransport( CreateTransport transportBuilder, int startPort ) |
| { |
| String protocol = transportBuilder.protocol(); |
| int port = transportBuilder.port(); |
| int nbThreads = transportBuilder.nbThreads(); |
| int backlog = transportBuilder.backlog(); |
| String address = transportBuilder.address(); |
| |
| if ( port == -1 ) |
| { |
| port = AvailablePortFinder.getNextAvailable( startPort ); |
| startPort = port + 1; |
| } |
| |
| if ( protocol.equalsIgnoreCase( "TCP" ) ) |
| { |
| Transport tcp = new TcpTransport( address, port, nbThreads, backlog ); |
| return tcp; |
| } |
| else if ( protocol.equalsIgnoreCase( "UDP" ) ) |
| { |
| UdpTransport udp = new UdpTransport( address, port ); |
| return udp; |
| } |
| else |
| { |
| throw new IllegalArgumentException( I18n.err( I18n.ERR_689, protocol ) ); |
| } |
| } |
| |
| |
| public static KdcServer getKdcServer( Description description, DirectoryService directoryService, int startPort ) |
| throws Exception |
| { |
| CreateKdcServer createLdapServer = description.getAnnotation( CreateKdcServer.class ); |
| |
| return createKdcServer( createLdapServer, directoryService, startPort ); |
| } |
| |
| } |