/*
* 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.sshd.server.auth;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.directory.server.constants.ServerDNConstants;
import org.apache.directory.server.core.CoreSession;
import org.apache.directory.server.core.DefaultDirectoryService;
import org.apache.directory.server.core.DirectoryService;
import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
import org.apache.directory.server.core.partition.ldif.LdifPartition;
import org.apache.directory.server.core.schema.SchemaPartition;
import org.apache.directory.server.core.schema.SchemaService;
import org.apache.directory.server.ldap.LdapServer;
import org.apache.directory.server.protocol.shared.transport.TcpTransport;
import org.apache.directory.server.protocol.shared.transport.Transport;
import org.apache.directory.shared.ldap.entry.Entry;
import org.apache.directory.shared.ldap.entry.EntryAttribute;
import org.apache.directory.shared.ldap.ldif.ChangeType;
import org.apache.directory.shared.ldap.ldif.LdifEntry;
import org.apache.directory.shared.ldap.ldif.LdifReader;
import org.apache.directory.shared.ldap.message.AddRequestImpl;
import org.apache.directory.shared.ldap.message.internal.InternalAddRequest;
import org.apache.directory.shared.ldap.schema.SchemaManager;
import org.apache.directory.shared.ldap.schema.ldif.extractor.SchemaLdifExtractor;
import org.apache.directory.shared.ldap.schema.ldif.extractor.impl.DefaultSchemaLdifExtractor;
import org.apache.directory.shared.ldap.schema.loader.ldif.LdifSchemaLoader;
import org.apache.directory.shared.ldap.schema.manager.impl.DefaultSchemaManager;
import org.apache.directory.shared.ldap.schema.registries.SchemaLoader;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.Pair;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.util.test.BaseTestSupport;
import org.apache.sshd.util.test.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public abstract class BaseAuthenticatorTest extends BaseTestSupport {
public static final int PORT = Integer.parseInt(System.getProperty("org.apache.sshd.test.ldap.port", "11389"));
public static final String BASE_DN_TEST = "ou=People,dc=sshd,dc=apache,dc=org";
protected BaseAuthenticatorTest() {
super();
}
public static String getHost(Pair<LdapServer, DirectoryService> context) {
return getHost((context == null) ? null : context.getFirst());
}
public static String getHost(LdapServer ldapServer) {
return getHost((ldapServer == null) ? null : ldapServer.getTransports());
}
public static String getHost(Transport... transports) {
return GenericUtils.isEmpty(transports) ? null : transports[0].getAddress();
}
public static int getPort(Pair<LdapServer, DirectoryService> context) {
return getPort((context == null) ? null : context.getFirst());
}
public static int getPort(LdapServer ldapServer) {
return getPort((ldapServer == null) ? null : ldapServer.getTransports());
}
public static int getPort(Transport... transports) {
return GenericUtils.isEmpty(transports) ? -1 : transports[0].getPort();
}
// see http://javlog.cacek.cz/2014/09/speed-up-apacheds-ldap-server.html
// see https://cwiki.apache.org/confluence/display/DIRxSRVx11/4.1.+Embedding+ApacheDS+into+an+application
// see http://stackoverflow.com/questions/1560230/running-apache-ds-embedded-in-my-application
@SuppressWarnings("checkstyle:avoidnestedblocks")
public static Pair<LdapServer, DirectoryService> startApacheDs(Class<?> anchor) throws Exception {
Logger log = LoggerFactory.getLogger(anchor);
File targetFolder = Objects.requireNonNull(Utils.detectTargetFolder(anchor), "Failed to detect target folder");
File workingDirectory = assertHierarchyTargetFolderExists(Utils.deleteRecursive(Utils.resolve(targetFolder, anchor.getSimpleName(), "apacheds-work")));
DirectoryService directoryService = new DefaultDirectoryService();
directoryService.setWorkingDirectory(workingDirectory);
SchemaService schemaService = directoryService.getSchemaService();
SchemaPartition schemaPartition = schemaService.getSchemaPartition();
LdifPartition ldifPartition = new LdifPartition();
// see DefaultSchemaLdifExtractor#SCHEMA...
File schemaRepository = assertHierarchyTargetFolderExists(new File(workingDirectory, "schema"));
ldifPartition.setWorkingDirectory(schemaRepository.getAbsolutePath());
SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor(workingDirectory);
extractor.extractOrCopy(true);
schemaPartition.setWrappedPartition(ldifPartition);
SchemaLoader loader = new LdifSchemaLoader(schemaRepository);
SchemaManager schemaManager = new DefaultSchemaManager(loader);
directoryService.setSchemaManager(schemaManager);
schemaManager.loadAllEnabled();
schemaPartition.setSchemaManager(schemaManager);
List<Throwable> errors = schemaManager.getErrors();
if (GenericUtils.size(errors) > 0) {
log.error("Schema management loading errors found");
for (Throwable t : errors) {
log.error(t.getClass().getSimpleName() + ": " + t.getMessage(), t);
}
throw new Exception("Schema load failed");
}
{
JdbmPartition systemPartition = new JdbmPartition();
systemPartition.setId("system");
systemPartition.setPartitionDir(assertHierarchyTargetFolderExists(Utils.deleteRecursive(new File(workingDirectory, systemPartition.getId()))));
systemPartition.setSuffix(ServerDNConstants.SYSTEM_DN);
systemPartition.setSchemaManager(schemaManager);
directoryService.setSystemPartition(systemPartition);
}
// Create a new partition for the users
{
JdbmPartition partition = new JdbmPartition();
partition.setId("users");
partition.setSuffix(BASE_DN_TEST);
partition.setPartitionDir(assertHierarchyTargetFolderExists(Utils.deleteRecursive(new File(workingDirectory, partition.getId()))));
directoryService.addPartition(partition);
}
directoryService.setShutdownHookEnabled(true);
directoryService.getChangeLog().setEnabled(false);
LdapServer ldapServer = new LdapServer();
ldapServer.setTransports(new TcpTransport(TEST_LOCALHOST, PORT));
ldapServer.setDirectoryService(directoryService);
log.info("Starting directory service ...");
directoryService.startup();
log.info("Directory service started");
log.info("Starting LDAP server on port=" + getPort(ldapServer) + " ...");
try {
ldapServer.start();
log.info("LDAP server started");
} catch (Exception e) {
log.error("Failed (" + e.getClass().getSimpleName() + ") to start LDAP server: " + e.getMessage(), e);
e.printStackTrace(System.err);
stopApacheDs(directoryService);
throw e;
}
return new Pair<>(ldapServer, directoryService);
}
// see http://users.directory.apache.narkive.com/GkyqAkot/how-to-import-ldif-file-programmatically
public static Map<String, String> populateUsers(DirectoryService service, Class<?> anchor, String credentialName) throws Exception {
Logger log = LoggerFactory.getLogger(anchor);
CoreSession session = Objects.requireNonNull(service.getAdminSession(), "No core session");
Map<String, String> usersMap = new HashMap<>();
try (LdifReader reader = new LdifReader(Objects.requireNonNull(anchor.getResourceAsStream("/auth-users.ldif"), "No users ldif"))) {
int id = 1;
for (LdifEntry entry : reader) {
if (log.isDebugEnabled()) {
log.debug("Process LDIF entry={}", entry);
}
Entry data = entry.getEntry();
EntryAttribute userAttr = data.get("uid");
EntryAttribute passAttr = data.get(credentialName);
if ((userAttr != null) && (passAttr != null)) {
String username = userAttr.getString();
ValidateUtils.checkTrue(usersMap.put(username, passAttr.getString()) == null, "Multiple entries for user=%s", username);
}
ChangeType changeType = entry.getChangeType();
try {
switch (changeType) {
case Add: {
InternalAddRequest addRequest = new AddRequestImpl(id++);
addRequest.setEntry(data);
session.add(addRequest);
break;
}
default:
throw new UnsupportedOperationException("Unsupported change type (" + changeType + ") for entry=" + entry);
}
} catch (Exception e) {
log.error("Failed (" + e.getClass().getSimpleName() + ") to add entry=" + entry + ": " + e.getMessage(), e);
throw e;
}
}
}
return usersMap;
}
public static void stopApacheDs(Pair<LdapServer, DirectoryService> context) throws Exception {
stopApacheDs((context == null) ? null : context.getFirst());
stopApacheDs((context == null) ? null : context.getSecond());
}
public static void stopApacheDs(LdapServer ldapServer) throws Exception {
if ((ldapServer == null) || (!ldapServer.isStarted())) {
return;
}
Logger log = LoggerFactory.getLogger(BaseAuthenticatorTest.class);
log.info("Stopping LDAP server...");
ldapServer.stop();
log.info("LDAP server stopped");
}
public static void stopApacheDs(DirectoryService directoryService) throws Exception {
if ((directoryService == null) || (!directoryService.isStarted())) {
return;
}
Logger log = LoggerFactory.getLogger(BaseAuthenticatorTest.class);
File workDir = directoryService.getWorkingDirectory();
log.info("Shutdown directory service ...");
directoryService.shutdown();
log.info("Directory service shut down");
log.info("Deleting " + workDir.getAbsolutePath());
Utils.deleteRecursive(workDir);
log.info(workDir.getAbsolutePath() + " deleted");
}
}