/*
* 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.infinispan.test.integration.security.utils;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Collections;
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.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.protocol.shared.transport.TcpTransport;
import org.apache.directory.server.protocol.shared.transport.Transport;
import org.apache.directory.server.protocol.shared.transport.UdpTransport;
/**
* Annotation processor for creating LDAP and Kerberos servers.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @author <a href="mailto:vchepeli@redhat.com">Vitalii Chepeliuk</a>
*/
public class KdcServerAnnotationProcessor {
private static void createTransports(LdapServer ldapServer, CreateTransport[] transportBuilders) {
if (transportBuilders.length != 0) {
for (CreateTransport transportBuilder : transportBuilders) {
List<Transport> transports = createTransports(transportBuilder);
for (Transport t : transports) {
ldapServer.addTransports(t);
}
}
} else {
// Create default LDAP and LDAPS transports
try {
int port = getFreePort();
Transport ldap = new TcpTransport(port);
ldapServer.addTransports(ldap);
} catch (IOException ioe) {
// Don't know what to do here...
}
try {
int port = getFreePort();
Transport ldaps = new TcpTransport(port);
ldaps.setEnableSSL(true);
ldapServer.addTransports(ldaps);
} catch (IOException ioe) {
// Don't know what to do here...
}
}
}
/**
* 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;
}
/**
* 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;
}
public static KdcServer getKdcServer(DirectoryService directoryService) throws Exception {
CreateKdcServer createKdcServer = (CreateKdcServer) AnnotationUtils.getInstance(CreateKdcServer.class);
return createKdcServer(createKdcServer, directoryService);
}
private static KdcServer createKdcServer(CreateKdcServer createKdcServer, DirectoryService directoryService) {
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
int port = 0;
try {
port = getFreePort();
} catch (IOException ioe) {
// Don't know what to do here...
}
UdpTransport defaultTransport = new UdpTransport(port);
kdcServer.addTransports(defaultTransport);
} else if (transportBuilders.length > 0) {
for (CreateTransport transportBuilder : transportBuilders) {
List<Transport> transports = createTransports(transportBuilder);
for (Transport t : transports) {
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()) {
List<Transport> transports = createTransports(transportBuilder);
for (Transport t : transports) {
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;
}
private static List<Transport> createTransports(CreateTransport transportBuilder) {
String protocol = transportBuilder.protocol();
int port = transportBuilder.port();
int nbThreads = transportBuilder.nbThreads();
int backlog = transportBuilder.backlog();
String address = transportBuilder.address();
if (port <= 0) {
try {
port = getFreePort();
} catch (IOException ioe) {
// Don't know what to do here...
}
}
if (protocol.equalsIgnoreCase("TCP") || protocol.equalsIgnoreCase("LDAP")) {
Transport tcp = new TcpTransport(address, port, nbThreads, backlog);
return Collections.singletonList(tcp);
} else if (protocol.equalsIgnoreCase("LDAPS")) {
Transport tcp = new TcpTransport(address, port, nbThreads, backlog);
tcp.setEnableSSL(true);
return Collections.singletonList(tcp);
} else if (protocol.equalsIgnoreCase("UDP")) {
Transport udp = new UdpTransport(address, port);
return Collections.singletonList(udp);
} else if (protocol.equalsIgnoreCase("KRB") || protocol.equalsIgnoreCase("CPW")) {
Transport tcp = new TcpTransport(address, port, nbThreads, backlog);
List<Transport> transports = new ArrayList<Transport>();
transports.add(tcp);
Transport udp = new UdpTransport(address, port);
transports.add(udp);
return transports;
}
throw new IllegalArgumentException(I18n.err(I18n.ERR_689, protocol));
}
private static int getFreePort() throws IOException {
ServerSocket ss = new ServerSocket(0);
int port = ss.getLocalPort();
ss.close();
return port;
}
}