blob: 8bf3820bf6d485214cbb5f8b0c73bdfd822670a8 [file] [log] [blame]
/*
* 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 );
}
}