Skip to content

Commit

Permalink
Merge pull request smallrye#5 from ozangunalp/main
Browse files Browse the repository at this point in the history
Return `CertificateFiles` from generate and make those injectable in Junit5 extension
  • Loading branch information
cescoffier authored Feb 28, 2024
2 parents e5831c7 + 0ac3dd9 commit 6d01e94
Show file tree
Hide file tree
Showing 9 changed files with 430 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package me.escoffier.certs.junit5;

import java.util.Arrays;
import java.util.stream.Stream;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.AnnotationBasedArgumentsProvider;
import org.junit.jupiter.params.provider.Arguments;

import me.escoffier.certs.CertificateFiles;

public class CertificateFilesArgumentsProvider extends AnnotationBasedArgumentsProvider<CertificatesSource> {
@Override
protected Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext, CertificatesSource certificatesSource) {
CertificateGenerationExtension extension = CertificateGenerationExtension.getInstance(extensionContext);
Stream<CertificateFiles> stream = extension.certificateFiles.stream();
if (certificatesSource.names().length > 0) {
stream = stream.filter(certificateFiles -> Arrays.stream(certificatesSource.names())
.anyMatch(name -> name.equals(certificateFiles.name())));
}
if (certificatesSource.formats().length > 0) {
stream = stream.filter(certificateFiles -> Arrays.stream(certificatesSource.formats())
.anyMatch(name -> name.equals(certificateFiles.format())));
}
return stream.map(Arguments::of);
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
package me.escoffier.certs.junit5;

import me.escoffier.certs.CertificateFiles;
import me.escoffier.certs.CertificateGenerator;
import me.escoffier.certs.CertificateRequest;

import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.platform.commons.util.AnnotationUtils;

import java.io.File;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

public class CertificateGenerationExtension implements BeforeAllCallback, ParameterResolver {

public static CertificateGenerationExtension getInstance(ExtensionContext extensionContext) {
return extensionContext.getStore(ExtensionContext.Namespace.GLOBAL)
.get(CertificateGenerationExtension.class, CertificateGenerationExtension.class);
}

List<CertificateFiles> certificateFiles = new ArrayList<>();

public class CertificateGenerationExtension implements BeforeAllCallback {
@Override
public void beforeAll(ExtensionContext extensionContext) throws Exception {
extensionContext.getStore(ExtensionContext.Namespace.GLOBAL)
.getOrComputeIfAbsent(CertificateGenerationExtension.class, c -> this);
var maybe = AnnotationUtils.findAnnotation(extensionContext.getRequiredTestClass(), Certificates.class);
if (maybe.isEmpty()) {
return;
Expand All @@ -32,9 +53,44 @@ public void beforeAll(ExtensionContext extensionContext) throws Exception {
.withPassword(certificate.password().isEmpty() ? null : certificate.password())
.withDuration(Duration.ofDays(certificate.duration()));

generator.generate(request);
certificateFiles.addAll(generator.generate(request));
}
}

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
if (extensionContext.getRequiredTestMethod().isAnnotationPresent(CertificatesSource.class)) {
return false;
}
if (parameterContext.getParameter().getParameterizedType() instanceof ParameterizedType type) {
if (((Class<?>) type.getRawType()).isAssignableFrom(List.class)) {
Type argument = type.getActualTypeArguments()[0];
return CertificateFiles.class.isAssignableFrom((Class<?>) argument);
} else {
return false;
}
} else {
return CertificateFiles.class.isAssignableFrom(parameterContext.getParameter().getType());
}
}

@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
if (parameterContext.getParameter().getParameterizedType() instanceof ParameterizedType type) {
if (((Class<?>) type.getRawType()).isAssignableFrom(List.class)) {
Type argument = type.getActualTypeArguments()[0];
return certificateFiles.stream()
.filter(f -> ((Class<?>) argument).isAssignableFrom(f.getClass()))
.collect(Collectors.toList());
} else {
return null;
}
} else {
return certificateFiles.stream()
.filter(f -> parameterContext.getParameter().getType().isAssignableFrom(f.getClass()))
.findFirst()
.orElse(null);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package me.escoffier.certs.junit5;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.jupiter.params.provider.ArgumentsSource;

import me.escoffier.certs.Format;

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(CertificateFilesArgumentsProvider.class)
public @interface CertificatesSource {
String[] names() default {};

Format[] formats() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package me.escoffier.certs;

import java.nio.file.Path;

public sealed interface CertificateFiles permits JksCertificateFiles, PemCertificateFiles, Pkcs12CertificateFiles {

String name();

Format format();

Path root();

boolean client();

String password();

Path trustStore();

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.nio.file.Path;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

Expand All @@ -26,8 +27,9 @@ public CertificateGenerator() {
replaceIfExists = false;
}

public void generate(CertificateRequest request) throws Exception {
public List<CertificateFiles> generate(CertificateRequest request) throws Exception {
request.validate();
List<CertificateFiles> certificateFiles = new ArrayList<>();
try {
KeyAndCertificateHolder server = new KeyAndCertificateHolder(request.getCN(), request.getDuration());
KeyPair keyPair = server.keys();
Expand All @@ -44,20 +46,23 @@ public void generate(CertificateRequest request) throws Exception {

for (Format format : request.formats()) {
if (format == Format.PEM) {
File certFile = new File(root, request.name() + ".crt");
File keyFile = new File(root, request.name() + ".key");
File trustfile = new File(root, request.name() + (client != null ? "-client" : "") + "-ca.crt");
File clientCertFile = new File(root, request.name() + "-client.crt");
File clientKeyFile = new File(root, request.name() + "-client.key");
File serverTrustfile = new File(root, request.name() + "-server-ca.crt");

if (replaceIfExists || !certFile.isFile()) {
PemCertificateFiles files = new PemCertificateFiles(root.toPath(), request.name(), request.client());
certificateFiles.add(files);

File certFile = files.certFile().toFile();
File keyFile = files.keyFile().toFile();
File trustfile = files.trustFile().toFile();
File clientCertFile = files.clientCertFile().toFile();
File clientKeyFile = files.clientKeyFile().toFile();
File serverTrustfile = files.serverTrustFile().toFile();

if (replaceIfExists || !certFile.isFile()) {
writeCertificateToPEM(certificate, certFile);
}
if (replaceIfExists || ! keyFile.isFile()) {
if (replaceIfExists || !keyFile.isFile()) {
writePrivateKeyToPem(keyPair.getPrivate(), keyFile);
}
if (replaceIfExists || ! trustfile.isFile()) {
if (replaceIfExists || !trustfile.isFile()) {
writeTruststoreToPem(List.of(certificate), trustfile);
}

Expand All @@ -73,7 +78,7 @@ public void generate(CertificateRequest request) throws Exception {
}
}

LOGGER.log(System.Logger.Level.INFO, "⭐ PEM Certificates, keystore, and truststore generated successfully!");
LOGGER.log(System.Logger.Level.INFO, "⭐ PEM Certificates, keystore, and truststore generated successfully!");
LOGGER.log(System.Logger.Level.INFO, "\uD83D\uDD11 Key File: " + keyFile.getAbsolutePath());
LOGGER.log(System.Logger.Level.INFO, "\uD83D\uDCDC Cert File: " + certFile.getAbsolutePath());
if (client != null) {
Expand All @@ -86,11 +91,14 @@ public void generate(CertificateRequest request) throws Exception {
}

} else if (format == Format.JKS) {
File keyStoreFile = new File(root, request.name() + "-keystore." + format.extension());
File trustStoreFile = new File(root, request.name() + (client != null ? "-client" : "") + "-truststore." + format.extension());
JksCertificateFiles files = new JksCertificateFiles(root.toPath(), request.name(), request.client(), request.password());
certificateFiles.add(files);

File clientKeyStoreFile = new File(root, request.name() + "-client-keystore." + format.extension());
File serverTrustStoreFile = new File(root, request.name() + "-server-truststore." + format.extension());
File keyStoreFile = files.keyStoreFile().toFile();
File trustStoreFile = files.trustStoreFile().toFile();

File clientKeyStoreFile = files.clientKeyStoreFile().toFile();
File serverTrustStoreFile = files.serverTrustStoreFile().toFile();

if (replaceIfExists || !keyStoreFile.isFile()) {
writePrivateKeyAndCertificateToJKS(certificate, keyPair, keyStoreFile, request.password().toCharArray(), request.getAlias());
Expand Down Expand Up @@ -119,11 +127,14 @@ public void generate(CertificateRequest request) throws Exception {
LOGGER.log(System.Logger.Level.INFO, "\uD83D\uDD13 Trust Store File: " + trustStoreFile.getAbsolutePath());
}
} else if (format == Format.PKCS12) {
File keyStoreFile = new File(root, request.name() + "-keystore." + format.extension());
File trustStoreFile = new File(root, request.name() + (client != null ? "-client" : "") + "-truststore." + format.extension());
Pkcs12CertificateFiles files = new Pkcs12CertificateFiles(root.toPath(), request.name(), request.client(), request.password());
certificateFiles.add(files);

File keyStoreFile = files.keyStoreFile().toFile();
File trustStoreFile = files.trustStoreFile().toFile();

File clientKeyStoreFile = new File(root, request.name() + "-client-keystore." + format.extension());
File serverTrustStoreFile = new File(root, request.name() + "-server-truststore." + format.extension());
File clientKeyStoreFile = files.clientKeyStoreFile().toFile();
File serverTrustStoreFile = files.serverTrustStoreFile().toFile();

if (replaceIfExists || !keyStoreFile.isFile()) {
writePrivateKeyAndCertificateToPKCS12(certificate, keyPair, keyStoreFile, request.password().toCharArray(), request.getAlias());
Expand Down Expand Up @@ -159,6 +170,7 @@ public void generate(CertificateRequest request) throws Exception {
LOGGER.log(System.Logger.Level.ERROR, "Error while generating the certificates", e);
throw e;
}
return certificateFiles;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package me.escoffier.certs;

import java.nio.file.Path;

public final class JksCertificateFiles implements CertificateFiles {

private final Path root;
private final String name;
private final boolean client;
private final String password;

private final Path keyStoreFile;
private final Path trustStoreFile;
private final Path clientKeyStoreFile;
private final Path serverTrustStoreFile;

public JksCertificateFiles(Path root, String name, boolean client, String password) {
this.root = root;
this.name = name;
this.client = client;
this.password = password;
this.keyStoreFile = root.resolve(name + "-keystore." + format().extension());
this.trustStoreFile = root.resolve(name + (client ? "-client" : "") + "-truststore." + format().extension());
this.clientKeyStoreFile = root.resolve(name + "-client-keystore." + format().extension());
this.serverTrustStoreFile = root.resolve(name + "-server-truststore." + format().extension());
}

@Override
public Format format() {
return Format.JKS;
}

@Override
public String name() {
return name;
}

@Override
public Path root() {
return root;
}

@Override
public boolean client() {
return client;
}

@Override
public String password() {
return password;
}

@Override
public Path trustStore() {
return client ? serverTrustStoreFile: trustStoreFile;
}

@Override
public String toString() {
return "JksCertificateFiles{" +
"root=" + root +
", name='" + name + '\'' +
", client=" + client +
", password='" + password + '\'' +
'}';
}

public Path keyStoreFile() {
return keyStoreFile;
}

public Path trustStoreFile() {
return trustStoreFile;
}

public Path clientKeyStoreFile() {
return clientKeyStoreFile;
}

public Path serverTrustStoreFile() {
return serverTrustStoreFile;
}

}
Loading

0 comments on commit 6d01e94

Please sign in to comment.