This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
commit f682d99264e752092187ec6c2d9a7ae8d925261e Author: Guillaume Nodet <[email protected]> AuthorDate: Wed Jul 15 13:54:37 2020 +0200 [CAMEL-11807] Upgrade camel-ldif to junit5 --- components/camel-ldif/pom.xml | 2 +- .../apache/camel/component/ldif/LdifRouteTest.java | 24 +- .../server/core/integ5/DSAnnotationProcessor.java | 481 +++++++++++++++++++++ .../directory/server/core/integ5/Description.java | 45 ++ .../server/core/integ5/DirectoryExtension.java | 281 ++++++++++++ .../core/integ5/ServerAnnotationProcessor.java | 459 ++++++++++++++++++++ 6 files changed, 1279 insertions(+), 13 deletions(-) diff --git a/components/camel-ldif/pom.xml b/components/camel-ldif/pom.xml index 72f473e..a8cc8af 100644 --- a/components/camel-ldif/pom.xml +++ b/components/camel-ldif/pom.xml @@ -56,7 +56,7 @@ <!-- test dependencies --> <dependency> <groupId>org.apache.camel</groupId> - <artifactId>camel-test</artifactId> + <artifactId>camel-test-junit5</artifactId> <scope>test</scope> </dependency> <dependency> diff --git a/components/camel-ldif/src/test/java/org/apache/camel/component/ldif/LdifRouteTest.java b/components/camel-ldif/src/test/java/org/apache/camel/component/ldif/LdifRouteTest.java index 91feefe..6084495 100644 --- a/components/camel-ldif/src/test/java/org/apache/camel/component/ldif/LdifRouteTest.java +++ b/components/camel-ldif/src/test/java/org/apache/camel/component/ldif/LdifRouteTest.java @@ -40,21 +40,21 @@ import org.apache.directory.server.annotations.CreateLdapServer; import org.apache.directory.server.annotations.CreateTransport; import org.apache.directory.server.core.annotations.ApplyLdifFiles; import org.apache.directory.server.core.integ.AbstractLdapTestUnit; -import org.apache.directory.server.core.integ.FrameworkRunner; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.apache.directory.server.core.integ5.DirectoryExtension; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import static org.apache.directory.server.integ.ServerIntegrationUtils.getWiredConnection; import static org.apache.directory.server.integ.ServerIntegrationUtils.getWiredContext; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; -@RunWith(FrameworkRunner.class) +@ExtendWith(DirectoryExtension.class) @CreateLdapServer(transports = {@CreateTransport(protocol = "LDAP")}) public class LdifRouteTest extends AbstractLdapTestUnit { // Constants @@ -67,8 +67,8 @@ public class LdifRouteTest extends AbstractLdapTestUnit { private CamelContext camel; private ProducerTemplate template; private LdapContext ldapContext; - - @Before + + @BeforeEach public void setup() throws Exception { // Create the LDAPConnection ldapContext = getWiredContext(ldapServer); @@ -79,7 +79,7 @@ public class LdifRouteTest extends AbstractLdapTestUnit { template = camel.createProducerTemplate(); } - @After + @AfterEach public void tearDown() throws Exception { camel.stop(); } @@ -326,7 +326,7 @@ public class LdifRouteTest extends AbstractLdapTestUnit { assertNotNull(out); assertNotNull(out.getMessage()); List<String> data = out.getMessage().getBody(List.class); - assertNotNull("out body could not be converted to a List - was: " + out.getMessage().getBody(), data); + assertNotNull(data, "out body could not be converted to a List - was: " + out.getMessage().getBody()); return data; } diff --git a/components/camel-ldif/src/test/java/org/apache/directory/server/core/integ5/DSAnnotationProcessor.java b/components/camel-ldif/src/test/java/org/apache/directory/server/core/integ5/DSAnnotationProcessor.java new file mode 100644 index 0000000..68b64b0 --- /dev/null +++ b/components/camel-ldif/src/test/java/org/apache/directory/server/core/integ5/DSAnnotationProcessor.java @@ -0,0 +1,481 @@ +/* + * 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.core.integ5; + + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.directory.api.ldap.model.entry.DefaultEntry; +import org.apache.directory.api.ldap.model.exception.LdapException; +import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException; +import org.apache.directory.api.ldap.model.ldif.LdifEntry; +import org.apache.directory.api.ldap.model.ldif.LdifReader; +import org.apache.directory.api.ldap.model.name.Dn; +import org.apache.directory.api.ldap.model.schema.SchemaManager; +import org.apache.directory.api.util.Network; +import org.apache.directory.api.util.Strings; +import org.apache.directory.server.core.annotations.AnnotationUtils; +import org.apache.directory.server.core.annotations.ApplyLdifFiles; +import org.apache.directory.server.core.annotations.ApplyLdifs; +import org.apache.directory.server.core.annotations.ContextEntry; +import org.apache.directory.server.core.annotations.CreateAuthenticator; +import org.apache.directory.server.core.annotations.CreateDS; +import org.apache.directory.server.core.annotations.CreateIndex; +import org.apache.directory.server.core.annotations.CreatePartition; +import org.apache.directory.server.core.annotations.LoadSchema; +import org.apache.directory.server.core.api.DirectoryService; +import org.apache.directory.server.core.api.DnFactory; +import org.apache.directory.server.core.api.interceptor.Interceptor; +import org.apache.directory.server.core.api.partition.Partition; +import org.apache.directory.server.core.authn.AuthenticationInterceptor; +import org.apache.directory.server.core.authn.Authenticator; +import org.apache.directory.server.core.authn.DelegatingAuthenticator; +import org.apache.directory.server.core.factory.DirectoryServiceFactory; +import org.apache.directory.server.core.factory.PartitionFactory; +import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition; +import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex; +import org.apache.directory.server.core.partition.impl.btree.mavibot.MavibotIndex; +import org.apache.directory.server.i18n.I18n; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * A Helper class used to create a DS from the annotations + * + * @author <a href="mailto:[email protected]">Apache Directory Project</a> + */ +public final class DSAnnotationProcessor { + + /** + * A logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(DSAnnotationProcessor.class); + + private DSAnnotationProcessor() { + } + + /** + * Create the DirectoryService + * + * @param dsBuilder The DirectoryService builder + * @return an instance of DirectoryService + * @throws Exception If the DirectoryService cannot be created + */ + public static DirectoryService createDS(CreateDS dsBuilder) + throws Exception { + LOG.debug("Starting DS {}...", dsBuilder.name()); + Class<?> factory = dsBuilder.factory(); + DirectoryServiceFactory dsf = (DirectoryServiceFactory) factory + .getDeclaredConstructor().newInstance(); + + DirectoryService service = dsf.getDirectoryService(); + service.setAccessControlEnabled(dsBuilder.enableAccessControl()); + service.setAllowAnonymousAccess(dsBuilder.allowAnonAccess()); + service.getChangeLog().setEnabled(dsBuilder.enableChangeLog()); + + dsf.init(dsBuilder.name()); + + for (Class<?> interceptorClass : dsBuilder.additionalInterceptors()) { + service.addLast((Interceptor) interceptorClass.getDeclaredConstructor().newInstance()); + } + + List<Interceptor> interceptorList = service.getInterceptors(); + + if (dsBuilder.authenticators().length != 0) { + AuthenticationInterceptor authenticationInterceptor = null; + + for (Interceptor interceptor : interceptorList) { + if (interceptor instanceof AuthenticationInterceptor) { + authenticationInterceptor = (AuthenticationInterceptor) interceptor; + break; + } + } + + if (authenticationInterceptor == null) { + throw new IllegalStateException( + "authentication interceptor not found"); + } + + Set<Authenticator> authenticators = new HashSet<Authenticator>(); + + for (CreateAuthenticator createAuthenticator : dsBuilder.authenticators()) { + Authenticator auth = createAuthenticator.type().getDeclaredConstructor().newInstance(); + + if (auth instanceof DelegatingAuthenticator) { + DelegatingAuthenticator dauth = (DelegatingAuthenticator) auth; + + String host = createAuthenticator.delegateHost(); + + if (Strings.isEmpty(host)) { + host = Network.LOOPBACK_HOSTNAME; + } + + dauth.setDelegateHost(host); + dauth.setDelegatePort(createAuthenticator.delegatePort()); + dauth.setDelegateSsl(createAuthenticator.delegateSsl()); + dauth.setDelegateTls(createAuthenticator.delegateTls()); + dauth.setBaseDn(service.getDnFactory().create(createAuthenticator.baseDn())); + dauth.setDelegateSslTrustManagerFQCN(createAuthenticator.delegateSslTrustManagerFQCN()); + dauth.setDelegateTlsTrustManagerFQCN(createAuthenticator.delegateTlsTrustManagerFQCN()); + } + + authenticators.add(auth); + } + + authenticationInterceptor.setAuthenticators(authenticators); + authenticationInterceptor.init(service); + } + + service.setInterceptors(interceptorList); + + SchemaManager schemaManager = service.getSchemaManager(); + + // process the schemas + for (LoadSchema loadedSchema : dsBuilder.loadedSchemas()) { + String schemaName = loadedSchema.name(); + Boolean enabled = loadedSchema.enabled(); + + // Check if the schema is loaded or not + boolean isLoaded = schemaManager.isSchemaLoaded(schemaName); + + if (!isLoaded) { + // We have to load the schema, if it exists + try { + isLoaded = schemaManager.load(schemaName); + } catch (LdapUnwillingToPerformException lutpe) { + // Cannot load the schema, it does not exists + LOG.error(lutpe.getMessage()); + continue; + } + } + + if (isLoaded) { + if (enabled) { + schemaManager.enable(schemaName); + + if (schemaManager.isDisabled(schemaName)) { + LOG.error("Cannot enable " + schemaName); + } + } else { + schemaManager.disable(schemaName); + + if (schemaManager.isEnabled(schemaName)) { + LOG.error("Cannot disable " + schemaName); + } + } + } + + LOG.debug("Loading schema {}, enabled= {}", schemaName, enabled); + } + + // Process the Partition, if any. + for (CreatePartition createPartition : dsBuilder.partitions()) { + Partition partition; + + // Determine the partition type + if (createPartition.type() == Partition.class) { + // The annotation does not specify a specific partition type. + // We use the partition factory to create partition and index + // instances. + PartitionFactory partitionFactory = dsf.getPartitionFactory(); + partition = partitionFactory.createPartition( + schemaManager, + service.getDnFactory(), + createPartition.name(), + createPartition.suffix(), + createPartition.cacheSize(), + new File(service.getInstanceLayout().getPartitionsDirectory(), createPartition.name())); + + partition.setCacheService(service.getCacheService()); + + CreateIndex[] indexes = createPartition.indexes(); + + for (CreateIndex createIndex : indexes) { + partitionFactory.addIndex(partition, + createIndex.attribute(), createIndex.cacheSize()); + } + + partition.initialize(); + } else { + // The annotation contains a specific partition type, we use + // that type. + Class<?>[] partypes = new Class[] { + SchemaManager.class, DnFactory.class }; + Constructor<?> constructor = createPartition.type().getConstructor(partypes); + partition = (Partition) constructor.newInstance(new Object[] { + schemaManager, service.getDnFactory() }); + partition.setId(createPartition.name()); + partition.setSuffixDn(new Dn(schemaManager, createPartition.suffix())); + + if (partition instanceof AbstractBTreePartition) { + AbstractBTreePartition btreePartition = (AbstractBTreePartition) partition; + btreePartition.setCacheService(service.getCacheService()); + btreePartition.setCacheSize(createPartition.cacheSize()); + btreePartition.setPartitionPath(new File(service + .getInstanceLayout().getPartitionsDirectory(), + createPartition.name()).toURI()); + + // Process the indexes if any + CreateIndex[] indexes = createPartition.indexes(); + + for (CreateIndex createIndex : indexes) { + if (createIndex.type() == JdbmIndex.class) { + // JDBM index + JdbmIndex index = new JdbmIndex(createIndex.attribute(), false); + + btreePartition.addIndexedAttributes(index); + } else if (createIndex.type() == MavibotIndex.class) { + // Mavibot index + MavibotIndex index = new MavibotIndex(createIndex.attribute(), false); + + btreePartition.addIndexedAttributes(index); + } else { + // The annotation does not specify a specific index + // type. + // We use the generic index implementation. + JdbmIndex index = new JdbmIndex(createIndex.attribute(), false); + + btreePartition.addIndexedAttributes(index); + } + } + } + } + + partition.setSchemaManager(schemaManager); + + // Inject the partition into the DirectoryService + service.addPartition(partition); + + // Last, process the context entry + ContextEntry contextEntry = createPartition.contextEntry(); + + if (contextEntry != null) { + injectEntries(service, contextEntry.entryLdif()); + } + } + + return service; + } + + + /** + * Create a DirectoryService from a Unit test annotation + * + * @param description The annotations containing the info from which we will create + * the DS + * @return A valid DirectoryService + * @throws Exception If the DirectoryService instance can't be returned + */ + public static DirectoryService getDirectoryService(Description description) + throws Exception { + CreateDS dsBuilder = description.getAnnotation(CreateDS.class); + + if (dsBuilder != null) { + return createDS(dsBuilder); + } else { + LOG.debug("No {} DS.", description.getDisplayName()); + return null; + } + } + + + /** + * Create a DirectoryService from an annotation. The @CreateDS annotation + * must be associated with either the method or the encapsulating class. We + * will first try to get the annotation from the method, and if there is + * none, then we try at the class level. + * + * @return A valid DS + * @throws Exception If the DirectoryService instance can't be returned + */ + public static DirectoryService getDirectoryService() throws Exception { + Object instance = AnnotationUtils.getInstance(CreateDS.class); + CreateDS dsBuilder = null; + + if (instance != null) { + dsBuilder = (CreateDS) instance; + + // Ok, we have found a CreateDS annotation. Process it now. + return createDS(dsBuilder); + } + + throw new LdapException(I18n.err(I18n.ERR_114)); + } + + + /** + * injects an LDIF entry in the given DirectoryService + * + * @param entry the LdifEntry to be injected + * @param service the DirectoryService + * @throws Exception If the entry cannot be injected + */ + private static void injectEntry(LdifEntry entry, DirectoryService service) + throws LdapException { + if (entry.isChangeAdd() || entry.isLdifContent()) { + service.getAdminSession().add( + new DefaultEntry(service.getSchemaManager(), entry + .getEntry())); + } else if (entry.isChangeModify()) { + service.getAdminSession().modify(entry.getDn(), + entry.getModifications()); + } else { + String message = I18n.err(I18n.ERR_117, entry.getChangeType()); + throw new LdapException(message); + } + } + + + /** + * injects the LDIF entries present in a LDIF file + * + * @param clazz The class which classLoaded will be use to retrieve the resources + * @param service the DirectoryService + * @param ldifFiles array of LDIF file names (only ) + * @throws Exception If we weren't able to inject LdifFiles + */ + public static void injectLdifFiles(Class<?> clazz, + DirectoryService service, String[] ldifFiles) throws Exception { + if ((ldifFiles != null) && (ldifFiles.length > 0)) { + for (String ldifFile : ldifFiles) { + InputStream is = clazz.getClassLoader().getResourceAsStream( + ldifFile); + if (is == null) { + throw new FileNotFoundException("LDIF file '" + ldifFile + + "' not found."); + } else { + LdifReader ldifReader = new LdifReader(is); + + for (LdifEntry entry : ldifReader) { + injectEntry(entry, service); + } + + ldifReader.close(); + } + } + } + } + + + /** + * Inject an ldif String into the server. Dn must be relative to the root. + * + * @param service the directory service to use + * @param ldif the ldif containing entries to add to the server. + * @throws Exception if there is a problem adding the entries from the LDIF + */ + public static void injectEntries(DirectoryService service, String ldif) + throws Exception { + LdifReader reader = new LdifReader(); + List<LdifEntry> entries = reader.parseLdif(ldif); + + for (LdifEntry entry : entries) { + injectEntry(entry, service); + } + + // And close the reader + reader.close(); + } + + + /** + * Load the schemas, and enable/disable them. + * + * @param desc The description + * @param service The DirectoryService instance + */ + public static void loadSchemas(Description desc, DirectoryService service) { + if (desc == null) { + return; + } + + /*for ( Class<?> loadSchema : dsBuilder.additionalInterceptors() ) + { + service.addLast( ( Interceptor ) interceptorClass.newInstance() ); + }*/ + LoadSchema loadSchema = desc + .getAnnotation(LoadSchema.class); + + if (loadSchema != null) { + System.out.println(loadSchema); + } + } + + + /** + * Apply the LDIF entries to the given service + * + * @param desc The description + * @param service The DirectoryService instance + * @throws Exception If we can't apply the ldifs + */ + public static void applyLdifs(Description desc, DirectoryService service) + throws Exception { + if (desc == null) { + return; + } + + ApplyLdifFiles applyLdifFiles = desc + .getAnnotation(ApplyLdifFiles.class); + + if (applyLdifFiles != null) { + LOG.debug("Applying {} to {}", applyLdifFiles.value(), + desc.getDisplayName()); + injectLdifFiles(applyLdifFiles.clazz(), service, applyLdifFiles.value()); + } + + ApplyLdifs applyLdifs = desc.getAnnotation(ApplyLdifs.class); + + if ((applyLdifs != null) && (applyLdifs.value() != null)) { + String[] ldifs = applyLdifs.value(); + + String dnStart = "dn:"; + + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < ldifs.length;) { + String s = ldifs[i++].trim(); + if (s.startsWith(dnStart)) { + sb.append(s).append('\n'); + + // read the rest of lines till we encounter Dn again + while (i < ldifs.length) { + s = ldifs[i++]; + if (!s.startsWith(dnStart)) { + sb.append(s).append('\n'); + } else { + break; + } + } + + LOG.debug("Applying {} to {}", sb, desc.getDisplayName()); + injectEntries(service, sb.toString()); + sb.setLength(0); + + i--; // step up a line + } + } + } + } +} diff --git a/components/camel-ldif/src/test/java/org/apache/directory/server/core/integ5/Description.java b/components/camel-ldif/src/test/java/org/apache/directory/server/core/integ5/Description.java new file mode 100644 index 0000000..f8c1b11 --- /dev/null +++ b/components/camel-ldif/src/test/java/org/apache/directory/server/core/integ5/Description.java @@ -0,0 +1,45 @@ +/* + * 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.core.integ5; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; + +public class Description { + + private final String displayName; + private final AnnotatedElement annotated; + + public Description(Method method) { + annotated = method; + displayName = String.format("%s(%s)", method.getName(), method.getDeclaringClass().getName()); + } + + public Description(Class clazz) { + annotated = clazz; + displayName = clazz.getName(); + } + + public <A extends Annotation> A getAnnotation(Class<A> annotation) { + return annotated.getAnnotation(annotation); + } + + public String getDisplayName() { + return displayName; + } +} diff --git a/components/camel-ldif/src/test/java/org/apache/directory/server/core/integ5/DirectoryExtension.java b/components/camel-ldif/src/test/java/org/apache/directory/server/core/integ5/DirectoryExtension.java new file mode 100644 index 0000000..3b8c2cd --- /dev/null +++ b/components/camel-ldif/src/test/java/org/apache/directory/server/core/integ5/DirectoryExtension.java @@ -0,0 +1,281 @@ +/* + * 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.core.integ5; + + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.UUID; + +import org.apache.directory.api.ldap.model.entry.DefaultModification; +import org.apache.directory.api.ldap.model.entry.Entry; +import org.apache.directory.api.ldap.model.entry.Modification; +import org.apache.directory.api.ldap.model.entry.ModificationOperation; +import org.apache.directory.api.ldap.model.exception.LdapException; +import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException; +import org.apache.directory.api.ldap.model.name.Dn; +import org.apache.directory.api.util.FileUtils; +import org.apache.directory.server.constants.ServerDNConstants; +import org.apache.directory.server.core.api.DirectoryService; +import org.apache.directory.server.core.api.changelog.ChangeLog; +import org.apache.directory.server.core.factory.DefaultDirectoryServiceFactory; +import org.apache.directory.server.core.factory.DirectoryServiceFactory; +import org.apache.directory.server.core.factory.PartitionFactory; +import org.apache.directory.server.core.security.TlsKeyGenerator; +import org.apache.directory.server.kerberos.kdc.KdcServer; +import org.apache.directory.server.ldap.LdapServer; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * The class responsible for running all the tests. t read the annotations, + * initialize the DirectoryService, call each test and do the cleanup at the end. + * + * @author <a href="mailto:[email protected]">Apache Directory Project</a> + */ +public class DirectoryExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback { + /** + * A logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(DirectoryExtension.class); + + private static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace.create(DirectoryExtension.class); + + /** + * The 'service' field in the run tests + */ + private static final String SET_SERVICE_METHOD_NAME = "setService"; + + /** + * The 'ldapServer' field in the run tests + */ + private static final String SET_LDAP_SERVER_METHOD_NAME = "setLdapServer"; + + /** + * The 'kdcServer' field in the run tests + */ + private static final String SET_KDC_SERVER_METHOD_NAME = "setKdcServer"; + + public static class State { + DirectoryService classDirectoryService; + DirectoryService methodDirectoryService; + DirectoryService directoryService; + LdapServer classLdapServer; + LdapServer methodLdapServer; + LdapServer ldapServer; + KdcServer classKdcServer; + KdcServer methodKdcServer; + KdcServer kdcServer; + DirectoryService oldLdapServerDirService; + DirectoryService oldKdcServerDirService; + long revision; + + public void beforeAll(ExtensionContext context) throws Exception { + Description description = new Description(context.getRequiredTestClass()); + + // Before running any test, check to see if we must create a class DS + // Get the LdapServerBuilder, if any + + classDirectoryService = DSAnnotationProcessor.getDirectoryService(description); + if (classDirectoryService == null) { + // define a default class DS then + DirectoryServiceFactory dsf = new DefaultDirectoryServiceFactory(); + classDirectoryService = dsf.getDirectoryService(); + // enable CL explicitly cause we are not using DSAnnotationProcessor + classDirectoryService.getChangeLog().setEnabled(true); + dsf.init("default" + UUID.randomUUID().toString()); + // Load the schemas + DSAnnotationProcessor.loadSchemas(description, classDirectoryService); + } + // Apply the class LDIFs + DSAnnotationProcessor.applyLdifs(description, classDirectoryService); + updateTlsKey(classDirectoryService); + + // check if it has a LdapServerBuilder, then use the DS created above + classLdapServer = ServerAnnotationProcessor.createLdapServer(description, classDirectoryService); + + classKdcServer = ServerAnnotationProcessor.getKdcServer(description, classDirectoryService); + + // print out information which partition factory we use + DirectoryServiceFactory dsFactory = new DefaultDirectoryServiceFactory(); + PartitionFactory partitionFactory = dsFactory.getPartitionFactory(); + LOG.debug("Using partition factory {}", partitionFactory.getClass().getSimpleName()); + } + + public void beforeEach(ExtensionContext context) throws Exception { + Description classDescription = new Description(context.getRequiredTestClass()); + Description methodDescription = new Description(context.getRequiredTestMethod()); + + // Check if this method has a dedicated DSBuilder + methodDirectoryService = DSAnnotationProcessor.getDirectoryService(methodDescription); + // give #1 priority to method level DS if present + if (methodDirectoryService != null) { + // Apply all the LDIFs + DSAnnotationProcessor.applyLdifs(classDescription, methodDirectoryService); + updateTlsKey(methodDirectoryService); + directoryService = methodDirectoryService; + } else if (classDirectoryService != null) { + directoryService = classDirectoryService; + } else if (classLdapServer != null) { + directoryService = classLdapServer.getDirectoryService(); + } else if (classKdcServer != null) { + directoryService = classKdcServer.getDirectoryService(); + } + // apply the method LDIFs, and tag for reversion + revision = getCurrentRevision(directoryService); + DSAnnotationProcessor.applyLdifs(methodDescription, directoryService); + + methodLdapServer = ServerAnnotationProcessor.createLdapServer(methodDescription, directoryService); + if (methodLdapServer != null) { + ldapServer = methodLdapServer; + } else if (classLdapServer != null) { + ldapServer = classLdapServer; + } + if (ldapServer != null) { + oldLdapServerDirService = ldapServer.getDirectoryService(); + ldapServer.setDirectoryService(directoryService); + } + + methodKdcServer = ServerAnnotationProcessor.getKdcServer(methodDescription, directoryService); + if (methodKdcServer != null) { + kdcServer = methodKdcServer; + } else if (classKdcServer != null) { + kdcServer = classKdcServer; + } + if (kdcServer != null) { + oldKdcServerDirService = kdcServer.getDirectoryService(); + kdcServer.setDirectoryService(directoryService); + } + + // At this point, we know which services to use, so inject them into the test instance + inject(context, SET_SERVICE_METHOD_NAME, DirectoryService.class, directoryService); + inject(context, SET_LDAP_SERVER_METHOD_NAME, LdapServer.class, ldapServer); + inject(context, SET_KDC_SERVER_METHOD_NAME, KdcServer.class, kdcServer); + } + + public void afterEach(ExtensionContext context) throws Exception { + if (oldLdapServerDirService != null) { + ldapServer.setDirectoryService(oldLdapServerDirService); + } + if (oldKdcServerDirService != null) { + kdcServer.setDirectoryService(oldKdcServerDirService); + } + if (methodLdapServer != null) { + methodLdapServer.stop(); + } + if (methodKdcServer != null) { + methodKdcServer.stop(); + } + // Cleanup the methodDS if it has been created + if (methodDirectoryService != null) { + LOG.debug("Shuting down DS for {}", methodDirectoryService.getInstanceId()); + methodDirectoryService.shutdown(); + FileUtils.deleteDirectory(methodDirectoryService.getInstanceLayout().getInstanceDirectory()); + } else { + // We use a class or suite DS, just revert the current test's modifications + revert(classDirectoryService, revision); + } + } + + public void afterAll(ExtensionContext context) throws Exception { + if (classLdapServer != null) { + classLdapServer.stop(); + } + if (classKdcServer != null) { + classKdcServer.stop(); + } + if (classDirectoryService != null) { + LOG.debug("Shuting down DS for {}", classDirectoryService.getInstanceId()); + classDirectoryService.shutdown(); + FileUtils.deleteDirectory(classDirectoryService.getInstanceLayout().getInstanceDirectory()); + } + } + + private <T> void inject(ExtensionContext context, String name, Class<T> type, T instance) throws InvocationTargetException, IllegalAccessException { + try { + Method setter = context.getRequiredTestClass().getMethod(name, type); + setter.invoke(context.getRequiredTestInstance(), instance); + } catch (NoSuchMethodException nsme) { + // Do nothing + } + } + + private long getCurrentRevision(DirectoryService dirService) throws Exception { + if ((dirService != null) && (dirService.getChangeLog().isEnabled())) { + long revision = dirService.getChangeLog().getCurrentRevision(); + LOG.debug("Create revision {}", revision); + return revision; + } + return 0; + } + + private void revert(DirectoryService dirService, long revision) throws Exception { + if (dirService == null) { + return; + } + ChangeLog cl = dirService.getChangeLog(); + if (cl.isEnabled() && (revision < cl.getCurrentRevision())) { + LOG.debug("Revert revision {}", revision); + dirService.revert(revision); + } + } + + + private void updateTlsKey(DirectoryService ds) throws LdapException, LdapInvalidDnException { + // Update TLS key for tests. Newer Java 8 releases consider RSA keys + // with less than 1024 bits as insecure and such are disabled by default, see + // http://www.oracle.com/technetwork/java/javase/8-compatibility-guide-2156366.html + Entry adminEntry = ds.getAdminSession().lookup(new Dn(ServerDNConstants.ADMIN_SYSTEM_DN)); + TlsKeyGenerator.addKeyPair(adminEntry, TlsKeyGenerator.CERTIFICATE_PRINCIPAL_DN, + TlsKeyGenerator.CERTIFICATE_PRINCIPAL_DN, "RSA", 1024); + Modification mod1 = new DefaultModification(ModificationOperation.REPLACE_ATTRIBUTE, + adminEntry.get(TlsKeyGenerator.PRIVATE_KEY_AT)); + Modification mod2 = new DefaultModification(ModificationOperation.REPLACE_ATTRIBUTE, + adminEntry.get(TlsKeyGenerator.PUBLIC_KEY_AT)); + Modification mod3 = new DefaultModification(ModificationOperation.REPLACE_ATTRIBUTE, + adminEntry.get(TlsKeyGenerator.USER_CERTIFICATE_AT)); + ds.getAdminSession().modify(adminEntry.getDn(), mod1, mod2, mod3); + } + } + + @Override + public void beforeAll(ExtensionContext context) throws Exception { + context.getStore(NAMESPACE).getOrComputeIfAbsent(State.class).beforeAll(context); + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + context.getStore(NAMESPACE).getOrComputeIfAbsent(State.class).beforeEach(context); + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + context.getStore(NAMESPACE).getOrComputeIfAbsent(State.class).afterEach(context); + } + + @Override + public void afterAll(ExtensionContext context) throws Exception { + context.getStore(NAMESPACE).getOrComputeIfAbsent(State.class).afterAll(context); + } + +} diff --git a/components/camel-ldif/src/test/java/org/apache/directory/server/core/integ5/ServerAnnotationProcessor.java b/components/camel-ldif/src/test/java/org/apache/directory/server/core/integ5/ServerAnnotationProcessor.java new file mode 100644 index 0000000..bd918a5 --- /dev/null +++ b/components/camel-ldif/src/test/java/org/apache/directory/server/core/integ5/ServerAnnotationProcessor.java @@ -0,0 +1,459 @@ +/* + * 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.core.integ5; + + +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.Collections; +import java.util.List; + +import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms; +import org.apache.directory.api.util.Network; +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; + + +/** + * Annotation processor for creating LDAP and Kerberos servers. + * + * @author <a href="mailto:[email protected]">Apache Directory Project</a> + */ +public final class ServerAnnotationProcessor { + + private ServerAnnotationProcessor() { + } + + 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)} + * + * @param createLdapServer The LdapServer to create + * @param directoryService the directory service + * @return The created LdapServer + * @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 + .getDeclaredConstructor().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() + .getDeclaredConstructor().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 + .getDeclaredConstructor().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 + * + * @param directoryService the directory service + * @return a running LdapServer instance + * @throws ClassNotFoundException If the CreateLdapServer class cannot be loaded + */ + 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(); + + String remoteHost = createConsumer.remoteHost(); + + if (Strings.isEmpty(remoteHost)) { + remoteHost = Network.LOOPBACK_HOSTNAME; + } + + config.setRemoteHost(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 + * @throws ClassNotFoundException If the CreateConsumer class cannot be loaded + */ + 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 + * @return An LdapServer instance + */ + public static LdapServer createLdapServer(Description description, DirectoryService directoryService) { + 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); + } + + + 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 (Strings.isEmpty(address)) { + address = Network.LOOPBACK_HOSTNAME; + } + + 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; + } + + public static KdcServer getKdcServer(Description description, DirectoryService directoryService) + throws Exception { + CreateKdcServer createLdapServer = description.getAnnotation(CreateKdcServer.class); + + return createKdcServer(createLdapServer, directoryService); + } + +}
