code simplification
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
open module net.woggioni.gbcs.test {
|
||||
requires net.woggioni.gbcs;
|
||||
requires net.woggioni.gbcs.api;
|
||||
requires java.naming;
|
||||
requires org.bouncycastle.pkix;
|
||||
requires org.bouncycastle.provider;
|
||||
requires io.netty.codec.http;
|
||||
requires net.woggioni.gbcs.base;
|
||||
requires java.net.http;
|
||||
requires static lombok;
|
||||
requires org.junit.jupiter.params;
|
||||
|
||||
exports net.woggioni.gbcs.test to org.junit.platform.commons;
|
||||
// opens net.woggioni.gbcs.test to org.junit.platform.commons;
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
package net.woggioni.gbcs.test;
|
||||
|
||||
|
||||
import net.woggioni.gbcs.GradleBuildCacheServer;
|
||||
import net.woggioni.gbcs.api.Configuration;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public abstract class AbstractServerTest {
|
||||
|
||||
protected Configuration cfg;
|
||||
protected Path testDir;
|
||||
private GradleBuildCacheServer.ServerHandle serverHandle;
|
||||
|
||||
@BeforeAll
|
||||
public void setUp0(@TempDir Path tmpDir) {
|
||||
this.testDir = tmpDir;
|
||||
setUp();
|
||||
startServer(cfg);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public void tearDown0() {
|
||||
tearDown();
|
||||
stopServer();
|
||||
}
|
||||
|
||||
protected abstract void setUp();
|
||||
|
||||
protected abstract void tearDown();
|
||||
|
||||
private void startServer(Configuration cfg) {
|
||||
this.serverHandle = new GradleBuildCacheServer(cfg).run();
|
||||
}
|
||||
|
||||
private void stopServer() {
|
||||
if (serverHandle != null) {
|
||||
try (GradleBuildCacheServer.ServerHandle handle = serverHandle) {
|
||||
handle.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,229 +0,0 @@
|
||||
package net.woggioni.gbcs.test;
|
||||
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.gbcs.AbstractNettyHttpAuthenticator;
|
||||
import net.woggioni.gbcs.api.Role;
|
||||
import net.woggioni.gbcs.base.Xml;
|
||||
import net.woggioni.gbcs.api.Configuration;
|
||||
import net.woggioni.gbcs.cache.FileSystemCacheConfiguration;
|
||||
import net.woggioni.gbcs.configuration.Serializer;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.ServerSocket;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.Deflater;
|
||||
import java.io.IOException;
|
||||
|
||||
public class BasicAuthServerTest extends AbstractServerTest {
|
||||
|
||||
private static final String PASSWORD = "password";
|
||||
private Path cacheDir;
|
||||
private final Random random = new Random(101325);
|
||||
private final Map.Entry<String, byte[]> keyValuePair;
|
||||
|
||||
public BasicAuthServerTest() {
|
||||
this.keyValuePair = newEntry(random);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
protected void setUp() {
|
||||
this.cacheDir = testDir.resolve("cache");
|
||||
Configuration.Group readersGroup = new Configuration.Group("readers", Set.of(Role.Reader));
|
||||
Configuration.Group writersGroup = new Configuration.Group("writers", Set.of(Role.Writer));
|
||||
|
||||
List<Configuration.User> users = Arrays.asList(
|
||||
new Configuration.User("user1", AbstractNettyHttpAuthenticator.Companion.hashPassword(PASSWORD, null), Set.of(readersGroup)),
|
||||
new Configuration.User("user2", AbstractNettyHttpAuthenticator.Companion.hashPassword(PASSWORD, null), Set.of(writersGroup)),
|
||||
new Configuration.User("user3", AbstractNettyHttpAuthenticator.Companion.hashPassword(PASSWORD, null), Set.of(readersGroup, writersGroup))
|
||||
);
|
||||
|
||||
Map<String, Configuration.User> usersMap = users.stream()
|
||||
.collect(Collectors.toMap(user -> user.getName(), user -> user));
|
||||
|
||||
Map<String, Configuration.Group> groupsMap = Stream.of(writersGroup, readersGroup)
|
||||
.collect(Collectors.toMap(group -> group.getName(), group -> group));
|
||||
|
||||
cfg = new Configuration(
|
||||
"127.0.0.1",
|
||||
new ServerSocket(0).getLocalPort() + 1,
|
||||
"/",
|
||||
usersMap,
|
||||
groupsMap,
|
||||
new FileSystemCacheConfiguration(
|
||||
this.cacheDir,
|
||||
Duration.ofSeconds(3600 * 24),
|
||||
"MD5",
|
||||
false,
|
||||
Deflater.DEFAULT_COMPRESSION
|
||||
),
|
||||
new Configuration.BasicAuthentication(),
|
||||
null,
|
||||
true
|
||||
);
|
||||
|
||||
Xml.Companion.write(Serializer.INSTANCE.serialize(cfg), System.out);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() {
|
||||
// Empty implementation
|
||||
}
|
||||
|
||||
private String buildAuthorizationHeader(Configuration.User user, String password) {
|
||||
String credentials = user.getName() + ":" + password;
|
||||
byte[] encodedCredentials = Base64.getEncoder().encode(credentials.getBytes(StandardCharsets.UTF_8));
|
||||
return "Basic " + new String(encodedCredentials, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private HttpRequest.Builder newRequestBuilder(String key) {
|
||||
return HttpRequest.newBuilder()
|
||||
.uri(URI.create(String.format("http://%s:%d/%s", cfg.getHost(), cfg.getPort(), key)));
|
||||
}
|
||||
|
||||
private Map.Entry<String, byte[]> newEntry(Random random) {
|
||||
byte[] keyBytes = new byte[0x10];
|
||||
random.nextBytes(keyBytes);
|
||||
String key = Base64.getUrlEncoder().encodeToString(keyBytes);
|
||||
|
||||
byte[] value = new byte[0x1000];
|
||||
random.nextBytes(value);
|
||||
|
||||
return Map.entry(key, value);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void putWithNoAuthorizationHeader() throws IOException, InterruptedException {
|
||||
try(HttpClient client = HttpClient.newHttpClient()) {
|
||||
String key = keyValuePair.getKey();
|
||||
byte[] value = keyValuePair.getValue();
|
||||
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.PUT(HttpRequest.BodyPublishers.ofByteArray(value));
|
||||
|
||||
HttpResponse<String> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString());
|
||||
Assertions.assertEquals(HttpResponseStatus.UNAUTHORIZED.code(), response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void putAsAReaderUser() throws IOException, InterruptedException {
|
||||
try(HttpClient client = HttpClient.newHttpClient()) {
|
||||
String key = keyValuePair.getKey();
|
||||
byte[] value = keyValuePair.getValue();
|
||||
|
||||
Configuration.User user = cfg.getUsers().values().stream()
|
||||
.filter(u -> u.getRoles().contains(Role.Reader) && !u.getRoles().contains(Role.Writer))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Reader user not found"));
|
||||
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.header("Authorization", buildAuthorizationHeader(user, PASSWORD))
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.PUT(HttpRequest.BodyPublishers.ofByteArray(value));
|
||||
|
||||
HttpResponse<String> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString());
|
||||
Assertions.assertEquals(HttpResponseStatus.FORBIDDEN.code(), response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void getAsAWriterUser() throws IOException, InterruptedException {
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
String key = keyValuePair.getKey();
|
||||
|
||||
Configuration.User user = cfg.getUsers().values().stream()
|
||||
.filter(u -> u.getRoles().contains(Role.Writer))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Writer user not found"));
|
||||
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.header("Authorization", buildAuthorizationHeader(user, PASSWORD))
|
||||
.GET();
|
||||
|
||||
HttpResponse<String> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString());
|
||||
Assertions.assertEquals(HttpResponseStatus.FORBIDDEN.code(), response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void putAsAWriterUser() throws IOException, InterruptedException {
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
String key = keyValuePair.getKey();
|
||||
byte[] value = keyValuePair.getValue();
|
||||
|
||||
Configuration.User user = cfg.getUsers().values().stream()
|
||||
.filter(u -> u.getRoles().contains(Role.Writer))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Writer user not found"));
|
||||
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.header("Authorization", buildAuthorizationHeader(user, PASSWORD))
|
||||
.PUT(HttpRequest.BodyPublishers.ofByteArray(value));
|
||||
|
||||
HttpResponse<String> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString());
|
||||
Assertions.assertEquals(HttpResponseStatus.CREATED.code(), response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void getAsAReaderUser() throws IOException, InterruptedException {
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
String key = keyValuePair.getKey();
|
||||
byte[] value = keyValuePair.getValue();
|
||||
|
||||
Configuration.User user = cfg.getUsers().values().stream()
|
||||
.filter(u -> u.getRoles().contains(Role.Reader))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Reader user not found"));
|
||||
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.header("Authorization", buildAuthorizationHeader(user, PASSWORD))
|
||||
.GET();
|
||||
|
||||
HttpResponse<byte[]> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray());
|
||||
Assertions.assertEquals(HttpResponseStatus.OK.code(), response.statusCode());
|
||||
Assertions.assertArrayEquals(value, response.body());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(6)
|
||||
public void getMissingKeyAsAReaderUser() throws IOException, InterruptedException {
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
Map.Entry<String, byte[]> entry = newEntry(random);
|
||||
String key = entry.getKey();
|
||||
|
||||
Configuration.User user = cfg.getUsers().values().stream()
|
||||
.filter(u -> u.getRoles().contains(Role.Reader))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Reader user not found"));
|
||||
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.header("Authorization", buildAuthorizationHeader(user, PASSWORD))
|
||||
.GET();
|
||||
|
||||
HttpResponse<byte[]> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray());
|
||||
Assertions.assertEquals(HttpResponseStatus.NOT_FOUND.code(), response.statusCode());
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,48 +0,0 @@
|
||||
package net.woggioni.gbcs.test;
|
||||
|
||||
import net.woggioni.gbcs.GradleBuildCacheServer;
|
||||
import net.woggioni.gbcs.base.GBCS;
|
||||
import net.woggioni.gbcs.base.Xml;
|
||||
import net.woggioni.gbcs.configuration.Parser;
|
||||
import net.woggioni.gbcs.configuration.Serializer;
|
||||
import net.woggioni.gbcs.url.ClasspathUrlStreamHandlerFactoryProvider;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
class ConfigurationTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
// "classpath:net/woggioni/gbcs/gbcs-default.xml",
|
||||
"classpath:net/woggioni/gbcs/test/gbcs-memcached.xml"
|
||||
})
|
||||
void test(String configurationUrl, @TempDir Path testDir) throws IOException {
|
||||
URL.setURLStreamHandlerFactory(new ClasspathUrlStreamHandlerFactoryProvider());
|
||||
// DocumentBuilderFactory dbf = Xml.newDocumentBuilderFactory(GradleBuildCacheServer.CONFIGURATION_SCHEMA_URL);
|
||||
// DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
// URL configurationUrl = GradleBuildCacheServer.DEFAULT_CONFIGURATION_URL;
|
||||
|
||||
var doc = Xml.Companion.parseXml(GBCS.INSTANCE.toUrl(configurationUrl), null, null);
|
||||
var cfg = Parser.INSTANCE.parse(doc);
|
||||
Path configFile = testDir.resolve("gbcs.xml");
|
||||
|
||||
try (var outputStream = Files.newOutputStream(configFile)) {
|
||||
Xml.Companion.write(Serializer.INSTANCE.serialize(cfg), outputStream);
|
||||
}
|
||||
|
||||
Xml.Companion.write(Serializer.INSTANCE.serialize(cfg), System.out);
|
||||
|
||||
var parsed = Parser.INSTANCE.parse(Xml.Companion.parseXml(
|
||||
configFile.toUri().toURL(), null, null
|
||||
));
|
||||
|
||||
Assertions.assertEquals(cfg, parsed);
|
||||
}
|
||||
}
|
@@ -1,128 +0,0 @@
|
||||
package net.woggioni.gbcs.test;
|
||||
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.gbcs.base.Xml;
|
||||
import net.woggioni.gbcs.api.Configuration;
|
||||
import net.woggioni.gbcs.cache.FileSystemCacheConfiguration;
|
||||
import net.woggioni.gbcs.configuration.Serializer;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.ServerSocket;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.zip.Deflater;
|
||||
import java.io.IOException;
|
||||
|
||||
public class NoAuthServerTest extends AbstractServerTest {
|
||||
|
||||
private Path cacheDir;
|
||||
private final Random random = new Random(101325);
|
||||
private final Map.Entry<String, byte[]> keyValuePair;
|
||||
|
||||
public NoAuthServerTest() {
|
||||
this.keyValuePair = newEntry(random);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
protected void setUp() {
|
||||
this.cacheDir = testDir.resolve("cache");
|
||||
cfg = new Configuration(
|
||||
"127.0.0.1",
|
||||
new ServerSocket(0).getLocalPort() + 1,
|
||||
"/",
|
||||
Collections.emptyMap(),
|
||||
Collections.emptyMap(),
|
||||
new FileSystemCacheConfiguration(
|
||||
this.cacheDir,
|
||||
Duration.ofSeconds(3600 * 24),
|
||||
"MD5",
|
||||
true,
|
||||
Deflater.DEFAULT_COMPRESSION
|
||||
),
|
||||
null,
|
||||
null,
|
||||
true
|
||||
);
|
||||
Xml.Companion.write(Serializer.INSTANCE.serialize(cfg), System.out);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() {
|
||||
// Empty implementation
|
||||
}
|
||||
|
||||
private HttpRequest.Builder newRequestBuilder(String key) {
|
||||
return HttpRequest.newBuilder()
|
||||
.uri(URI.create(String.format("http://%s:%d/%s", cfg.getHost(), cfg.getPort(), key)));
|
||||
}
|
||||
|
||||
private Map.Entry<String, byte[]> newEntry(Random random) {
|
||||
byte[] keyBytes = new byte[0x10];
|
||||
random.nextBytes(keyBytes);
|
||||
String key = Base64.getUrlEncoder().encodeToString(keyBytes);
|
||||
|
||||
byte[] value = new byte[0x1000];
|
||||
random.nextBytes(value);
|
||||
|
||||
return Map.entry(key, value);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void putWithNoAuthorizationHeader() throws IOException, InterruptedException {
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
String key = keyValuePair.getKey();
|
||||
byte[] value = keyValuePair.getValue();
|
||||
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.PUT(HttpRequest.BodyPublishers.ofByteArray(value));
|
||||
|
||||
HttpResponse<String> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString());
|
||||
Assertions.assertEquals(HttpResponseStatus.CREATED.code(), response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void getWithNoAuthorizationHeader() throws IOException, InterruptedException {
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
String key = keyValuePair.getKey();
|
||||
byte[] value = keyValuePair.getValue();
|
||||
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.GET();
|
||||
|
||||
HttpResponse<byte[]> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray());
|
||||
Assertions.assertEquals(HttpResponseStatus.OK.code(), response.statusCode());
|
||||
Assertions.assertArrayEquals(value, response.body());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void getMissingKey() throws IOException, InterruptedException {
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
|
||||
Map.Entry<String, byte[]> entry = newEntry(random);
|
||||
String key = entry.getKey();
|
||||
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key).GET();
|
||||
|
||||
HttpResponse<byte[]> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray());
|
||||
Assertions.assertEquals(HttpResponseStatus.NOT_FOUND.code(), response.statusCode());
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,357 +0,0 @@
|
||||
package net.woggioni.gbcs.test;
|
||||
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.gbcs.api.Configuration;
|
||||
import net.woggioni.gbcs.api.Role;
|
||||
import net.woggioni.gbcs.base.Xml;
|
||||
import net.woggioni.gbcs.cache.FileSystemCacheConfiguration;
|
||||
import net.woggioni.gbcs.configuration.Serializer;
|
||||
import net.woggioni.gbcs.utils.CertificateUtils;
|
||||
import net.woggioni.gbcs.utils.CertificateUtils.X509Credentials;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStore.PasswordProtection;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
public class TlsServerTest extends AbstractServerTest {
|
||||
|
||||
private static final String CA_CERTIFICATE_ENTRY = "gbcs-ca";
|
||||
private static final String CLIENT_CERTIFICATE_ENTRY = "gbcs-client";
|
||||
private static final String SERVER_CERTIFICATE_ENTRY = "gbcs-server";
|
||||
private static final String PASSWORD = "password";
|
||||
|
||||
private Path cacheDir;
|
||||
private Path serverKeyStoreFile;
|
||||
private Path clientKeyStoreFile;
|
||||
private Path trustStoreFile;
|
||||
private KeyStore serverKeyStore;
|
||||
private KeyStore clientKeyStore;
|
||||
private KeyStore trustStore;
|
||||
private X509Credentials ca;
|
||||
|
||||
private final Configuration.Group readersGroup = new Configuration.Group("readers", Set.of(Role.Reader));
|
||||
private final Configuration.Group writersGroup = new Configuration.Group("writers", Set.of(Role.Writer));
|
||||
private final Random random = new Random(101325);
|
||||
private final Map.Entry<String, byte[]> keyValuePair;
|
||||
|
||||
private final List<Configuration.User> users = Arrays.asList(
|
||||
new Configuration.User("user1", null, Set.of(readersGroup)),
|
||||
new Configuration.User("user2", null, Set.of(writersGroup)),
|
||||
new Configuration.User("user3", null, Set.of(readersGroup, writersGroup))
|
||||
);
|
||||
|
||||
public TlsServerTest() {
|
||||
this.keyValuePair = newEntry(random);
|
||||
}
|
||||
|
||||
private void createKeyStoreAndTrustStore() throws Exception {
|
||||
ca = CertificateUtils.createCertificateAuthority(CA_CERTIFICATE_ENTRY, 30);
|
||||
var serverCert = CertificateUtils.createServerCertificate(ca, new X500Name("CN=" + SERVER_CERTIFICATE_ENTRY), 30);
|
||||
var clientCert = CertificateUtils.createClientCertificate(ca, new X500Name("CN=" + CLIENT_CERTIFICATE_ENTRY), 30);
|
||||
|
||||
serverKeyStore = KeyStore.getInstance("PKCS12");
|
||||
serverKeyStore.load(null, null);
|
||||
serverKeyStore.setEntry(
|
||||
CA_CERTIFICATE_ENTRY,
|
||||
new KeyStore.TrustedCertificateEntry(ca.certificate()),
|
||||
new PasswordProtection(null)
|
||||
);
|
||||
serverKeyStore.setEntry(
|
||||
SERVER_CERTIFICATE_ENTRY,
|
||||
new KeyStore.PrivateKeyEntry(
|
||||
serverCert.keyPair().getPrivate(),
|
||||
new java.security.cert.Certificate[]{serverCert.certificate(), ca.certificate()}
|
||||
),
|
||||
new PasswordProtection(PASSWORD.toCharArray())
|
||||
);
|
||||
|
||||
try (var out = Files.newOutputStream(this.serverKeyStoreFile)) {
|
||||
serverKeyStore.store(out, null);
|
||||
}
|
||||
|
||||
clientKeyStore = KeyStore.getInstance("PKCS12");
|
||||
clientKeyStore.load(null, null);
|
||||
clientKeyStore.setEntry(
|
||||
CA_CERTIFICATE_ENTRY,
|
||||
new KeyStore.TrustedCertificateEntry(ca.certificate()),
|
||||
new PasswordProtection(null)
|
||||
);
|
||||
clientKeyStore.setEntry(
|
||||
CLIENT_CERTIFICATE_ENTRY,
|
||||
new KeyStore.PrivateKeyEntry(
|
||||
clientCert.keyPair().getPrivate(),
|
||||
new java.security.cert.Certificate[]{clientCert.certificate(), ca.certificate()}
|
||||
),
|
||||
new PasswordProtection(PASSWORD.toCharArray())
|
||||
);
|
||||
|
||||
try (var out = Files.newOutputStream(this.clientKeyStoreFile)) {
|
||||
clientKeyStore.store(out, null);
|
||||
}
|
||||
|
||||
trustStore = KeyStore.getInstance("PKCS12");
|
||||
trustStore.load(null, null);
|
||||
trustStore.setEntry(
|
||||
CA_CERTIFICATE_ENTRY,
|
||||
new KeyStore.TrustedCertificateEntry(ca.certificate()),
|
||||
new PasswordProtection(null)
|
||||
);
|
||||
|
||||
try (var out = Files.newOutputStream(this.trustStoreFile)) {
|
||||
trustStore.store(out, null);
|
||||
}
|
||||
}
|
||||
|
||||
private KeyStore getClientKeyStore(X509Credentials ca, X500Name subject) throws Exception {
|
||||
var clientCert = CertificateUtils.createClientCertificate(ca, subject, 30);
|
||||
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||
keyStore.load(null, null);
|
||||
keyStore.setEntry(
|
||||
CA_CERTIFICATE_ENTRY,
|
||||
new KeyStore.TrustedCertificateEntry(ca.certificate()),
|
||||
new PasswordProtection(null)
|
||||
);
|
||||
keyStore.setEntry(
|
||||
CLIENT_CERTIFICATE_ENTRY,
|
||||
new KeyStore.PrivateKeyEntry(
|
||||
clientCert.keyPair().getPrivate(),
|
||||
new java.security.cert.Certificate[]{clientCert.certificate(), ca.certificate()}
|
||||
),
|
||||
new PasswordProtection(PASSWORD.toCharArray())
|
||||
);
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
private HttpClient getHttpClient(KeyStore clientKeyStore) throws Exception {
|
||||
KeyManagerFactory kmf = null;
|
||||
if (clientKeyStore != null) {
|
||||
kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
kmf.init(clientKeyStore, PASSWORD.toCharArray());
|
||||
}
|
||||
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
tmf.init(trustStore);
|
||||
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(
|
||||
kmf != null ? kmf.getKeyManagers() : null,
|
||||
tmf.getTrustManagers(),
|
||||
null
|
||||
);
|
||||
|
||||
return HttpClient.newBuilder().sslContext(sslContext).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
protected void setUp() {
|
||||
this.clientKeyStoreFile = testDir.resolve("client-keystore.p12");
|
||||
this.serverKeyStoreFile = testDir.resolve("server-keystore.p12");
|
||||
this.trustStoreFile = testDir.resolve("truststore.p12");
|
||||
this.cacheDir = testDir.resolve("cache");
|
||||
|
||||
createKeyStoreAndTrustStore();
|
||||
|
||||
Map<String, Configuration.User> usersMap = users.stream()
|
||||
.collect(Collectors.toMap(user -> user.getName(), user -> user));
|
||||
|
||||
Map<String, Configuration.Group> groupsMap = Stream.of(writersGroup, readersGroup)
|
||||
.collect(Collectors.toMap(group -> group.getName(), group -> group));
|
||||
|
||||
cfg = new Configuration(
|
||||
"127.0.0.1",
|
||||
new ServerSocket(0).getLocalPort() + 1,
|
||||
"gbcs",
|
||||
usersMap,
|
||||
groupsMap,
|
||||
new FileSystemCacheConfiguration(
|
||||
this.cacheDir,
|
||||
Duration.ofSeconds(3600 * 24),
|
||||
"MD5",
|
||||
true,
|
||||
Deflater.DEFAULT_COMPRESSION
|
||||
),
|
||||
new Configuration.ClientCertificateAuthentication(
|
||||
new Configuration.TlsCertificateExtractor("CN", "(.*)"),
|
||||
null
|
||||
),
|
||||
new Configuration.Tls(
|
||||
new Configuration.KeyStore(this.serverKeyStoreFile, null, SERVER_CERTIFICATE_ENTRY, PASSWORD),
|
||||
new Configuration.TrustStore(this.trustStoreFile, null, false),
|
||||
true
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
Xml.Companion.write(Serializer.INSTANCE.serialize(cfg), System.out);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() {
|
||||
// Empty implementation
|
||||
}
|
||||
|
||||
private HttpRequest.Builder newRequestBuilder(String key) {
|
||||
return HttpRequest.newBuilder()
|
||||
.uri(URI.create(String.format("https://%s:%d/%s", cfg.getHost(), cfg.getPort(), key)));
|
||||
}
|
||||
|
||||
private String buildAuthorizationHeader(Configuration.User user, String password) {
|
||||
String credentials = user.getName() + ":" + password;
|
||||
byte[] encodedCredentials = Base64.getEncoder().encode(credentials.getBytes(StandardCharsets.UTF_8));
|
||||
return "Basic " + new String(encodedCredentials, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private Map.Entry<String, byte[]> newEntry(Random random) {
|
||||
byte[] keyBytes = new byte[0x10];
|
||||
random.nextBytes(keyBytes);
|
||||
String key = Base64.getUrlEncoder().encodeToString(keyBytes);
|
||||
|
||||
byte[] value = new byte[0x1000];
|
||||
random.nextBytes(value);
|
||||
|
||||
return Map.entry(key, value);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void putWithNoClientCertificate() throws Exception {
|
||||
try (HttpClient client = getHttpClient(null)) {
|
||||
String key = keyValuePair.getKey();
|
||||
byte[] value = keyValuePair.getValue();
|
||||
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.PUT(HttpRequest.BodyPublishers.ofByteArray(value));
|
||||
|
||||
HttpResponse<String> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString());
|
||||
Assertions.assertEquals(HttpResponseStatus.UNAUTHORIZED.code(), response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void putAsAReaderUser() throws Exception {
|
||||
String key = keyValuePair.getKey();
|
||||
byte[] value = keyValuePair.getValue();
|
||||
|
||||
Configuration.User user = cfg.getUsers().values().stream()
|
||||
.filter(u -> u.getRoles().contains(Role.Reader) && !u.getRoles().contains(Role.Writer))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Reader user not found"));
|
||||
|
||||
try (HttpClient client = getHttpClient(getClientKeyStore(ca, new X500Name("CN=" + user.getName())))) {
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.PUT(HttpRequest.BodyPublishers.ofByteArray(value));
|
||||
|
||||
HttpResponse<String> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString());
|
||||
Assertions.assertEquals(HttpResponseStatus.FORBIDDEN.code(), response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void getAsAWriterUser() throws Exception {
|
||||
String key = keyValuePair.getKey();
|
||||
|
||||
Configuration.User user = cfg.getUsers().values().stream()
|
||||
.filter(u -> u.getRoles().contains(Role.Writer))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Writer user not found"));
|
||||
|
||||
try (HttpClient client = getHttpClient(getClientKeyStore(ca, new X500Name("CN=" + user.getName())))) {
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.header("Authorization", buildAuthorizationHeader(user, PASSWORD))
|
||||
.GET();
|
||||
|
||||
HttpResponse<String> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString());
|
||||
Assertions.assertEquals(HttpResponseStatus.FORBIDDEN.code(), response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void putAsAWriterUser() throws Exception {
|
||||
String key = keyValuePair.getKey();
|
||||
byte[] value = keyValuePair.getValue();
|
||||
|
||||
Configuration.User user = cfg.getUsers().values().stream()
|
||||
.filter(u -> u.getRoles().contains(Role.Writer))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Writer user not found"));
|
||||
|
||||
try (HttpClient client = getHttpClient(getClientKeyStore(ca, new X500Name("CN=" + user.getName())))) {
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.header("Authorization", buildAuthorizationHeader(user, PASSWORD))
|
||||
.PUT(HttpRequest.BodyPublishers.ofByteArray(value));
|
||||
|
||||
HttpResponse<String> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString());
|
||||
Assertions.assertEquals(HttpResponseStatus.CREATED.code(), response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void getAsAReaderUser() throws Exception {
|
||||
String key = keyValuePair.getKey();
|
||||
byte[] value = keyValuePair.getValue();
|
||||
|
||||
Configuration.User user = cfg.getUsers().values().stream()
|
||||
.filter(u -> u.getRoles().contains(Role.Reader))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Reader user not found"));
|
||||
|
||||
try (HttpClient client = getHttpClient(getClientKeyStore(ca, new X500Name("CN=" + user.getName())))) {
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.header("Authorization", buildAuthorizationHeader(user, PASSWORD))
|
||||
.GET();
|
||||
|
||||
HttpResponse<byte[]> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray());
|
||||
Assertions.assertEquals(HttpResponseStatus.OK.code(), response.statusCode());
|
||||
Assertions.assertArrayEquals(value, response.body());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(6)
|
||||
public void getMissingKeyAsAReaderUser() throws Exception {
|
||||
Map.Entry<String, byte[]> entry = newEntry(random);
|
||||
String key = entry.getKey();
|
||||
|
||||
Configuration.User user = cfg.getUsers().values().stream()
|
||||
.filter(u -> u.getRoles().contains(Role.Reader))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Reader user not found"));
|
||||
|
||||
try (HttpClient client = getHttpClient(getClientKeyStore(ca, new X500Name("CN=" + user.getName())))) {
|
||||
HttpRequest.Builder requestBuilder = newRequestBuilder(key)
|
||||
.header("Authorization", buildAuthorizationHeader(user, PASSWORD))
|
||||
.GET();
|
||||
|
||||
HttpResponse<byte[]> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray());
|
||||
Assertions.assertEquals(HttpResponseStatus.NOT_FOUND.code(), response.statusCode());
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
package net.woggioni.gbcs.test;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.naming.ldap.LdapName;
|
||||
import javax.naming.ldap.Rdn;
|
||||
import java.util.Objects;
|
||||
|
||||
public class X500NameTest {
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void test() {
|
||||
final var name =
|
||||
"C=SG, L=Bugis, CN=woggioni@f6aa5663ef26, emailAddress=oggioni.walter@gmail.com, street=1 Fraser Street\\, Duo Residences #23-05, postalCode=189350, GN=Walter, SN=Oggioni, pseudonym=woggioni";
|
||||
final var ldapName = new LdapName(name);
|
||||
final var value = ldapName.getRdns()
|
||||
.stream()
|
||||
.filter(it -> Objects.equals("CN", it.getType()))
|
||||
.findFirst()
|
||||
.map(Rdn::getValue)
|
||||
.orElseThrow(Assertions::fail);
|
||||
Assertions.assertEquals("woggioni@f6aa5663ef26", value);
|
||||
}
|
||||
}
|
||||
|
@@ -1,227 +0,0 @@
|
||||
package net.woggioni.gbcs.utils;
|
||||
|
||||
import org.bouncycastle.asn1.DERSequence;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x509.BasicConstraints;
|
||||
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
|
||||
import org.bouncycastle.asn1.x509.Extension;
|
||||
import org.bouncycastle.asn1.x509.GeneralName;
|
||||
import org.bouncycastle.asn1.x509.GeneralNames;
|
||||
import org.bouncycastle.asn1.x509.KeyPurposeId;
|
||||
import org.bouncycastle.asn1.x509.KeyUsage;
|
||||
import org.bouncycastle.asn1.x509.SubjectAltPublicKeyInfo;
|
||||
import org.bouncycastle.cert.X509v3CertificateBuilder;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
|
||||
import org.bouncycastle.operator.ContentSigner;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
|
||||
public class CertificateUtils {
|
||||
|
||||
public record X509Credentials(
|
||||
KeyPair keyPair,
|
||||
X509Certificate certificate
|
||||
){ }
|
||||
public static class CertificateAuthority {
|
||||
private final PrivateKey privateKey;
|
||||
private final X509Certificate certificate;
|
||||
|
||||
public CertificateAuthority(PrivateKey privateKey, X509Certificate certificate) {
|
||||
this.privateKey = privateKey;
|
||||
this.certificate = certificate;
|
||||
}
|
||||
|
||||
public PrivateKey getPrivateKey() { return privateKey; }
|
||||
public X509Certificate getCertificate() { return certificate; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Certificate Authority (CA)
|
||||
* @param commonName The CA's common name
|
||||
* @param validityDays How long the CA should be valid for
|
||||
* @return The generated CA containing both private key and certificate
|
||||
*/
|
||||
public static X509Credentials createCertificateAuthority(String commonName, int validityDays)
|
||||
throws Exception {
|
||||
// Generate key pair
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
keyPairGenerator.initialize(4096);
|
||||
KeyPair keyPair = keyPairGenerator.generateKeyPair();
|
||||
|
||||
// Prepare certificate data
|
||||
X500Name issuerName = new X500Name("CN=" + commonName);
|
||||
BigInteger serialNumber = new BigInteger(160, new SecureRandom());
|
||||
Instant now = Instant.now();
|
||||
Date startDate = Date.from(now);
|
||||
Date endDate = Date.from(now.plus(validityDays, ChronoUnit.DAYS));
|
||||
|
||||
// Create certificate builder
|
||||
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
|
||||
issuerName,
|
||||
serialNumber,
|
||||
startDate,
|
||||
endDate,
|
||||
issuerName,
|
||||
keyPair.getPublic()
|
||||
);
|
||||
|
||||
// Add CA extensions
|
||||
certBuilder.addExtension(
|
||||
Extension.basicConstraints,
|
||||
true,
|
||||
new BasicConstraints(true)
|
||||
);
|
||||
certBuilder.addExtension(
|
||||
Extension.keyUsage,
|
||||
true,
|
||||
new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign)
|
||||
);
|
||||
|
||||
// Sign the certificate
|
||||
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA")
|
||||
.build(keyPair.getPrivate());
|
||||
X509Certificate cert = new JcaX509CertificateConverter()
|
||||
.getCertificate(certBuilder.build(signer));
|
||||
|
||||
return new X509Credentials(keyPair, cert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a server certificate signed by the CA
|
||||
* @param ca The Certificate Authority to sign with
|
||||
* @param subjectName The server's common name
|
||||
* @param validityDays How long the certificate should be valid for
|
||||
* @return KeyPair containing the server's private key and certificate
|
||||
*/
|
||||
public static X509Credentials createServerCertificate(X509Credentials ca, X500Name subjectName, int validityDays)
|
||||
throws Exception {
|
||||
// Generate server key pair
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
keyPairGenerator.initialize(2048);
|
||||
KeyPair serverKeyPair = keyPairGenerator.generateKeyPair();
|
||||
|
||||
// Prepare certificate data
|
||||
X500Name issuerName = new X500Name(ca.certificate().getSubjectX500Principal().getName());
|
||||
BigInteger serialNumber = new BigInteger(160, new SecureRandom());
|
||||
Instant now = Instant.now();
|
||||
Date startDate = Date.from(now);
|
||||
Date endDate = Date.from(now.plus(validityDays, ChronoUnit.DAYS));
|
||||
|
||||
// Create certificate builder
|
||||
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
|
||||
issuerName,
|
||||
serialNumber,
|
||||
startDate,
|
||||
endDate,
|
||||
subjectName,
|
||||
serverKeyPair.getPublic()
|
||||
);
|
||||
|
||||
// Add server certificate extensions
|
||||
certBuilder.addExtension(
|
||||
Extension.basicConstraints,
|
||||
true,
|
||||
new BasicConstraints(false)
|
||||
);
|
||||
certBuilder.addExtension(
|
||||
Extension.keyUsage,
|
||||
true,
|
||||
new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment)
|
||||
);
|
||||
certBuilder.addExtension(
|
||||
Extension.extendedKeyUsage,
|
||||
true,
|
||||
new ExtendedKeyUsage(new KeyPurposeId[]{KeyPurposeId.id_kp_serverAuth})
|
||||
);
|
||||
GeneralNames subjectAltNames = GeneralNames.getInstance(
|
||||
new DERSequence(
|
||||
new GeneralName[] {
|
||||
new GeneralName(GeneralName.iPAddress, "127.0.0.1")
|
||||
}
|
||||
)
|
||||
);
|
||||
certBuilder.addExtension(
|
||||
Extension.subjectAlternativeName,
|
||||
true,
|
||||
subjectAltNames
|
||||
);
|
||||
|
||||
// Sign the certificate
|
||||
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA")
|
||||
.build(ca.keyPair().getPrivate());
|
||||
X509Certificate cert = new JcaX509CertificateConverter()
|
||||
.getCertificate(certBuilder.build(signer));
|
||||
|
||||
|
||||
return new X509Credentials(serverKeyPair, cert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a client certificate signed by the CA
|
||||
* @param ca The Certificate Authority to sign with
|
||||
* @param subjectName The client's common name
|
||||
* @param validityDays How long the certificate should be valid for
|
||||
* @return KeyPair containing the client's private key and certificate
|
||||
*/
|
||||
public static X509Credentials createClientCertificate(X509Credentials ca, X500Name subjectName, int validityDays)
|
||||
throws Exception {
|
||||
// Generate client key pair
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
keyPairGenerator.initialize(2048);
|
||||
KeyPair clientKeyPair = keyPairGenerator.generateKeyPair();
|
||||
|
||||
// Prepare certificate data
|
||||
X500Name issuerName = new X500Name(ca.certificate().getSubjectX500Principal().getName());
|
||||
BigInteger serialNumber = new BigInteger(160, new SecureRandom());
|
||||
Instant now = Instant.now();
|
||||
Date startDate = Date.from(now);
|
||||
Date endDate = Date.from(now.plus(validityDays, ChronoUnit.DAYS));
|
||||
|
||||
// Create certificate builder
|
||||
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
|
||||
issuerName,
|
||||
serialNumber,
|
||||
startDate,
|
||||
endDate,
|
||||
subjectName,
|
||||
clientKeyPair.getPublic()
|
||||
);
|
||||
|
||||
// Add client certificate extensions
|
||||
certBuilder.addExtension(
|
||||
Extension.basicConstraints,
|
||||
true,
|
||||
new BasicConstraints(false)
|
||||
);
|
||||
certBuilder.addExtension(
|
||||
Extension.keyUsage,
|
||||
true,
|
||||
new KeyUsage(KeyUsage.digitalSignature)
|
||||
);
|
||||
certBuilder.addExtension(
|
||||
Extension.extendedKeyUsage,
|
||||
true,
|
||||
new ExtendedKeyUsage(new KeyPurposeId[]{KeyPurposeId.id_kp_clientAuth})
|
||||
);
|
||||
|
||||
// Sign the certificate
|
||||
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA")
|
||||
.build(ca.keyPair().getPrivate());
|
||||
X509Certificate cert = new JcaX509CertificateConverter()
|
||||
.getCertificate(certBuilder.build(signer));
|
||||
|
||||
return new X509Credentials(clientKeyPair, cert);
|
||||
}
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<gbcs:server useVirtualThreads="false" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:gbcs="urn:net.woggioni.gbcs"
|
||||
xmlns:gbcs-memcached="urn:net.woggioni.gbcs-memcached"
|
||||
xs:schemaLocation="urn:net.woggioni.gbcs-memcached classpath:net/woggioni/gbcs/memcached/schema/gbcs-memcached.xsd urn:net.woggioni.gbcs classpath:net/woggioni/gbcs/schema/gbcs.xsd">
|
||||
<bind host="127.0.0.1" port="11443" />
|
||||
<cache xs:type="gbcs-memcached:memcachedCacheType" max-age="P7D" digest="MD5">
|
||||
<server host="127.0.0.1" port="11211"/>
|
||||
</cache>
|
||||
<authentication>
|
||||
<none/>
|
||||
</authentication>
|
||||
</gbcs:server>
|
@@ -3,12 +3,6 @@ import net.woggioni.gbcs.url.ClasspathUrlStreamHandlerFactoryProvider;
|
||||
import net.woggioni.gbcs.cache.FileSystemCacheProvider;
|
||||
|
||||
open module net.woggioni.gbcs {
|
||||
// exports net.woggioni.gbcs.cache to net.woggioni.gbcs.test;
|
||||
// exports net.woggioni.gbcs.configuration to net.woggioni.gbcs.test;
|
||||
// exports net.woggioni.gbcs.url to net.woggioni.gbcs.test;
|
||||
// exports net.woggioni.gbcs to net.woggioni.gbcs.test;
|
||||
// opens net.woggioni.gbcs.schema to net.woggioni.gbcs.test;
|
||||
|
||||
requires java.sql;
|
||||
requires java.xml;
|
||||
requires java.logging;
|
||||
@@ -25,12 +19,8 @@ open module net.woggioni.gbcs {
|
||||
requires net.woggioni.gbcs.base;
|
||||
requires net.woggioni.gbcs.api;
|
||||
|
||||
// exports net.woggioni.gbcs;
|
||||
// exports net.woggioni.gbcs.url;
|
||||
// opens net.woggioni.gbcs to net.woggioni.envelope;
|
||||
provides java.net.URLStreamHandlerFactory with ClasspathUrlStreamHandlerFactoryProvider;
|
||||
uses java.net.URLStreamHandlerFactory;
|
||||
// uses net.woggioni.gbcs.api.Cache;
|
||||
uses CacheProvider;
|
||||
|
||||
provides CacheProvider with FileSystemCacheProvider;
|
||||
|
@@ -61,14 +61,6 @@ abstract class AbstractNettyHttpAuthenticator(private val authorizer : Authorize
|
||||
return String(Base64.getEncoder().encode(concat(hash, actualSalt)))
|
||||
}
|
||||
|
||||
// fun decodePasswordHash(passwordHash : String) : Pair<String, String> {
|
||||
// return passwordHash.indexOf(':')
|
||||
// .takeIf { it > 0 }
|
||||
// ?.let { sep ->
|
||||
// passwordHash.substring(0, sep) to passwordHash.substring(sep)
|
||||
// } ?: throw IllegalArgumentException("Failed to decode password hash")
|
||||
// }
|
||||
|
||||
fun decodePasswordHash(passwordHash : String) : Pair<ByteArray, ByteArray> {
|
||||
val decoded = Base64.getDecoder().decode(passwordHash)
|
||||
val hash = ByteArray(KEY_LENGTH / 8)
|
||||
|
@@ -46,9 +46,8 @@ import net.woggioni.gbcs.api.Cache
|
||||
import net.woggioni.gbcs.api.Configuration
|
||||
import net.woggioni.gbcs.api.Role
|
||||
import net.woggioni.gbcs.api.exception.ContentTooLargeException
|
||||
import net.woggioni.gbcs.base.GBCS
|
||||
import net.woggioni.gbcs.base.Xml
|
||||
import net.woggioni.gbcs.base.GBCS.toUrl
|
||||
import net.woggioni.gbcs.base.Xml
|
||||
import net.woggioni.gbcs.base.contextLogger
|
||||
import net.woggioni.gbcs.base.debug
|
||||
import net.woggioni.gbcs.base.info
|
||||
@@ -76,7 +75,6 @@ import java.util.regex.Pattern
|
||||
import javax.naming.ldap.LdapName
|
||||
import javax.net.ssl.SSLEngine
|
||||
import javax.net.ssl.SSLPeerUnverifiedException
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
|
||||
class GradleBuildCacheServer(private val cfg: Configuration) {
|
||||
@@ -540,7 +538,6 @@ class GradleBuildCacheServer(private val cfg: Configuration) {
|
||||
}
|
||||
|
||||
fun loadConfiguration(args: Array<String>): Configuration {
|
||||
// registerUrlProtocolHandler()
|
||||
// Thread.currentThread().contextClassLoader = GradleBuildCacheServer::class.java.classLoader
|
||||
val app = Application.builder("gbcs")
|
||||
.configurationDirectoryEnvVar("GBCS_CONFIGURATION_DIR")
|
||||
|
@@ -1,309 +0,0 @@
|
||||
package net.woggioni.gbcs.configuration
|
||||
|
||||
import net.woggioni.gbcs.api.Role
|
||||
import java.nio.file.Path
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Duration
|
||||
|
||||
@ConsistentCopyVisibility
|
||||
data class Configuration private constructor(
|
||||
val host: String,
|
||||
val port: Int,
|
||||
val serverPath: String?,
|
||||
val users: Map<String, User>,
|
||||
val groups: Map<String, Group>,
|
||||
val cache: Cache,
|
||||
val authentication : Authentication?,
|
||||
val tls: Tls?,
|
||||
val useVirtualThread: Boolean
|
||||
) {
|
||||
|
||||
data class Group(val name: String, val roles: Set<Role>) {
|
||||
override fun hashCode(): Int {
|
||||
return name.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
data class User(val name: String, val password: String?, val groups: Set<Group>) {
|
||||
override fun hashCode(): Int {
|
||||
return name.hashCode()
|
||||
}
|
||||
|
||||
val roles : Set<Role>
|
||||
get() = groups.asSequence().flatMap { it.roles }.toSet()
|
||||
}
|
||||
|
||||
fun interface UserExtractor {
|
||||
fun extract(cert :X509Certificate) : User
|
||||
}
|
||||
|
||||
fun interface GroupExtractor {
|
||||
fun extract(cert :X509Certificate) : Group
|
||||
}
|
||||
|
||||
data class Tls(
|
||||
val keyStore: KeyStore?,
|
||||
val trustStore: TrustStore?,
|
||||
val verifyClients: Boolean,
|
||||
)
|
||||
|
||||
data class KeyStore(
|
||||
val file: Path,
|
||||
val password: String?,
|
||||
val keyAlias: String,
|
||||
val keyPassword: String?
|
||||
)
|
||||
|
||||
data class TrustStore(
|
||||
val file: Path,
|
||||
val password: String?,
|
||||
val checkCertificateStatus: Boolean
|
||||
)
|
||||
|
||||
|
||||
data class TlsCertificateExtractor(val rdnType : String, val pattern : String)
|
||||
|
||||
interface Authentication
|
||||
|
||||
class BasicAuthentication : Authentication
|
||||
|
||||
data class ClientCertificateAuthentication(
|
||||
val userExtractor: TlsCertificateExtractor?,
|
||||
val groupExtractor: TlsCertificateExtractor?) : Authentication
|
||||
|
||||
|
||||
interface Cache
|
||||
|
||||
data class FileSystemCache(val root: Path, val maxAge: Duration) : Cache
|
||||
|
||||
companion object {
|
||||
|
||||
fun of(
|
||||
host: String,
|
||||
port: Int,
|
||||
serverPath: String?,
|
||||
users: Map<String, User>,
|
||||
groups: Map<String, Group>,
|
||||
cache: Cache,
|
||||
authentication : Authentication?,
|
||||
tls: Tls?,
|
||||
useVirtualThread: Boolean
|
||||
) = Configuration(
|
||||
host,
|
||||
port,
|
||||
serverPath?.takeIf { it.isNotEmpty() && it != "/" },
|
||||
users,
|
||||
groups,
|
||||
cache,
|
||||
authentication,
|
||||
tls,
|
||||
useVirtualThread
|
||||
)
|
||||
|
||||
// fun parse(document: Document): Configuration {
|
||||
// val cacheSerializers = ServiceLoader.load(Configuration::class.java.module.layer, CacheSerializer::class.java)
|
||||
// .asSequence()
|
||||
// .map {
|
||||
// "${it.xmlType}:${it.xmlNamespace}" to it
|
||||
// }.toMap()
|
||||
// val root = document.documentElement
|
||||
// var cache: Cache? = null
|
||||
// var host = "127.0.0.1"
|
||||
// var port = 11080
|
||||
// var users = emptyMap<String, User>()
|
||||
// var groups = emptyMap<String, Group>()
|
||||
// var tls: Tls? = null
|
||||
// val serverPath = root.getAttribute("path")
|
||||
// val useVirtualThread = root.getAttribute("useVirtualThreads")
|
||||
// .takeIf(String::isNotEmpty)
|
||||
// ?.let(String::toBoolean) ?: false
|
||||
// var authentication : Authentication? = null
|
||||
// for (child in root.asIterable()) {
|
||||
// when (child.nodeName) {
|
||||
// "authorization" -> {
|
||||
// for (gchild in child.asIterable()) {
|
||||
// when (child.nodeName) {
|
||||
// "users" -> {
|
||||
// users = parseUsers(child)
|
||||
// }
|
||||
//
|
||||
// "groups" -> {
|
||||
// val pair = parseGroups(child, users)
|
||||
// users = pair.first
|
||||
// groups = pair.second
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// "bind" -> {
|
||||
// host = child.getAttribute("host")
|
||||
// port = Integer.parseInt(child.getAttribute("port"))
|
||||
// }
|
||||
//
|
||||
// "cache" -> {
|
||||
// val type = child.getAttribute("xs:type")
|
||||
// val serializer = cacheSerializers.get(type) ?: throw NotImplementedError()
|
||||
// cache = serializer.deserialize(child)
|
||||
//
|
||||
// when(child.getAttribute("xs:type")) {
|
||||
// "gbcs:fileSystemCacheType" -> {
|
||||
// val cacheFolder = child.getAttribute("path")
|
||||
// .takeIf(String::isNotEmpty)
|
||||
// ?.let(Paths::get)
|
||||
// ?: Paths.get(System.getProperty("user.home")).resolve(".gbcs")
|
||||
// val maxAge = child.getAttribute("max-age")
|
||||
// .takeIf(String::isNotEmpty)
|
||||
// ?.let(Duration::parse)
|
||||
// ?: Duration.ofDays(1)
|
||||
// cache = FileSystemCache(cacheFolder, maxAge)
|
||||
// }
|
||||
// }
|
||||
//// for (gchild in child.asIterable()) {
|
||||
//// when (gchild.nodeName) {
|
||||
//// "file-system-cache" -> {
|
||||
//// val cacheFolder = gchild.getAttribute("path")
|
||||
//// .takeIf(String::isNotEmpty)
|
||||
//// ?.let(Paths::get)
|
||||
//// ?: Paths.get(System.getProperty("user.home")).resolve(".gbcs")
|
||||
//// val maxAge = gchild.getAttribute("max-age")
|
||||
//// .takeIf(String::isNotEmpty)
|
||||
//// ?.let(Duration::parse)
|
||||
//// ?: Duration.ofDays(1)
|
||||
//// cache = FileSystemCache(cacheFolder, maxAge)
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
// }
|
||||
//
|
||||
// "authentication" -> {
|
||||
// for (gchild in child.asIterable()) {
|
||||
// when (gchild.nodeName) {
|
||||
// "basic" -> {
|
||||
// authentication = BasicAuthentication()
|
||||
// }
|
||||
//
|
||||
// "client-certificate" -> {
|
||||
// var tlsExtractorUser : TlsCertificateExtractor? = null
|
||||
// var tlsExtractorGroup : TlsCertificateExtractor? = null
|
||||
// for (gchild in child.asIterable()) {
|
||||
// when (gchild.nodeName) {
|
||||
// "group-extractor" -> {
|
||||
// val attrName = gchild.getAttribute("attribute-name")
|
||||
// val pattern = gchild.getAttribute("pattern")
|
||||
// tlsExtractorGroup = TlsCertificateExtractor(attrName, pattern)
|
||||
// }
|
||||
//
|
||||
// "user-extractor" -> {
|
||||
// val attrName = gchild.getAttribute("attribute-name")
|
||||
// val pattern = gchild.getAttribute("pattern")
|
||||
// tlsExtractorUser = TlsCertificateExtractor(attrName, pattern)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// authentication = ClientCertificateAuthentication(tlsExtractorUser, tlsExtractorGroup)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// "tls" -> {
|
||||
// val verifyClients = child.getAttribute("verify-clients")
|
||||
// .takeIf(String::isNotEmpty)
|
||||
// ?.let(String::toBoolean) ?: false
|
||||
// var keyStore: KeyStore? = null
|
||||
// var trustStore: TrustStore? = null
|
||||
// for (granChild in child.asIterable()) {
|
||||
// when (granChild.nodeName) {
|
||||
// "keystore" -> {
|
||||
// val keyStoreFile = Paths.get(granChild.getAttribute("file"))
|
||||
// val keyStorePassword = granChild.getAttribute("password")
|
||||
// .takeIf(String::isNotEmpty)
|
||||
// val keyAlias = granChild.getAttribute("key-alias")
|
||||
// val keyPassword = granChild.getAttribute("key-password")
|
||||
// .takeIf(String::isNotEmpty)
|
||||
// keyStore = KeyStore(
|
||||
// keyStoreFile,
|
||||
// keyStorePassword,
|
||||
// keyAlias,
|
||||
// keyPassword
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// "truststore" -> {
|
||||
// val trustStoreFile = Paths.get(granChild.getAttribute("file"))
|
||||
// val trustStorePassword = granChild.getAttribute("password")
|
||||
// .takeIf(String::isNotEmpty)
|
||||
// val checkCertificateStatus = granChild.getAttribute("check-certificate-status")
|
||||
// .takeIf(String::isNotEmpty)
|
||||
// ?.let(String::toBoolean)
|
||||
// ?: false
|
||||
// trustStore = TrustStore(
|
||||
// trustStoreFile,
|
||||
// trustStorePassword,
|
||||
// checkCertificateStatus
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// tls = Tls(keyStore, trustStore, verifyClients)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return of(host, port, serverPath, users, groups, cache!!, authentication, tls, useVirtualThread)
|
||||
// }
|
||||
//
|
||||
// private fun parseRoles(root: Element) = root.asIterable().asSequence().map {
|
||||
// when (it.nodeName) {
|
||||
// "reader" -> Role.Reader
|
||||
// "writer" -> Role.Writer
|
||||
// else -> throw UnsupportedOperationException("Illegal node '${it.nodeName}'")
|
||||
// }
|
||||
// }.toSet()
|
||||
//
|
||||
// private fun parseUserRefs(root: Element) = root.asIterable().asSequence().filter {
|
||||
// it.nodeName == "user"
|
||||
// }.map {
|
||||
// it.getAttribute("ref")
|
||||
// }.toSet()
|
||||
//
|
||||
// private fun parseUsers(root: Element): Map<String, User> {
|
||||
// return root.asIterable().asSequence().filter {
|
||||
// it.nodeName == "user"
|
||||
// }.map { el ->
|
||||
// val username = el.getAttribute("name")
|
||||
// val password = el.getAttribute("password").takeIf(String::isNotEmpty)
|
||||
// username to User(username, password, emptySet())
|
||||
// }.toMap()
|
||||
// }
|
||||
//
|
||||
// private fun parseGroups(root: Element, knownUsers : Map<String, User>): Pair<Map<String, User>, Map<String, Group>> {
|
||||
// val userGroups = mutableMapOf<String, MutableSet<String>>()
|
||||
// val groups = root.asIterable().asSequence().filter {
|
||||
// it.nodeName == "group"
|
||||
// }.map { el ->
|
||||
// val groupName = el.getAttribute("name")
|
||||
// var roles = emptySet<Role>()
|
||||
// for (child in el.asIterable()) {
|
||||
// when (child.nodeName) {
|
||||
// "users" -> {
|
||||
// parseUserRefs(child).mapNotNull(knownUsers::get).forEach { user ->
|
||||
// userGroups.computeIfAbsent(user.name) {
|
||||
// mutableSetOf()
|
||||
// }.add(groupName)
|
||||
// }
|
||||
// }
|
||||
// "roles" -> {
|
||||
// roles = parseRoles(child)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// groupName to Group(groupName, roles)
|
||||
// }.toMap()
|
||||
// val users = knownUsers.map { (name, user) ->
|
||||
// name to User(name, user.password, userGroups[name]?.mapNotNull { groups[it] }?.toSet() ?: emptySet())
|
||||
// }.toMap()
|
||||
// return users to groups
|
||||
// }
|
||||
}
|
||||
}
|
@@ -16,6 +16,7 @@ import net.woggioni.gbcs.base.Xml.Companion.asIterable
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.TypeInfo
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.nio.file.Paths
|
||||
|
||||
object Parser {
|
||||
@@ -57,45 +58,12 @@ object Parser {
|
||||
}
|
||||
|
||||
"cache" -> {
|
||||
// val type = child.getAttribute("xs:type").split(":")
|
||||
// val namespaceURI = child.lookupNamespaceURI(type[0])
|
||||
// val typeName = type[1]
|
||||
cache = (child as? TypeInfo)?.let { tf ->
|
||||
cache = (child as TypeInfo).let { tf ->
|
||||
val typeNamespace = tf.typeNamespace
|
||||
val typeName = tf.typeName
|
||||
CacheSerializers.index[typeNamespace to typeName]
|
||||
}?.deserialize(child) ?: throw NotImplementedError()
|
||||
|
||||
// cache = serializer.deserialize(child)
|
||||
|
||||
// when(child.getAttribute("xs:type")) {
|
||||
// "gbcs:fileSystemCacheType" -> {
|
||||
// val cacheFolder = child.getAttribute("path")
|
||||
// .takeIf(String::isNotEmpty)
|
||||
// ?.let(Paths::get)
|
||||
// ?: Paths.get(System.getProperty("user.home")).resolve(".gbcs")
|
||||
// val maxAge = child.getAttribute("max-age")
|
||||
// .takeIf(String::isNotEmpty)
|
||||
// ?.let(Duration::parse)
|
||||
// ?: Duration.ofDays(1)
|
||||
// cache = FileSystemCache(cacheFolder, maxAge)
|
||||
// }
|
||||
// }
|
||||
// for (gchild in child.asIterable()) {
|
||||
// when (gchild.localName) {
|
||||
// "file-system-cache" -> {
|
||||
// val cacheFolder = gchild.getAttribute("path")
|
||||
// .takeIf(String::isNotEmpty)
|
||||
// ?.let(Paths::get)
|
||||
// ?: Paths.get(System.getProperty("user.home")).resolve(".gbcs")
|
||||
// val maxAge = gchild.getAttribute("max-age")
|
||||
// .takeIf(String::isNotEmpty)
|
||||
// ?.let(Duration::parse)
|
||||
// ?: Duration.ofDays(1)
|
||||
// cache = FileSystemCache(cacheFolder, maxAge)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
?: throw IllegalArgumentException("Cache provider for namespace '$typeNamespace' not found")
|
||||
}.deserialize(child)
|
||||
}
|
||||
|
||||
"authentication" -> {
|
||||
|
@@ -4,7 +4,6 @@ import net.woggioni.gbcs.GradleBuildCacheServer
|
||||
import net.woggioni.gbcs.api.Configuration
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.ClassOrderer
|
||||
import org.junit.jupiter.api.MethodOrderer
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.TestMethodOrder
|
||||
@@ -14,7 +13,7 @@ import java.nio.file.Path
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
|
||||
abstract class AbstractServerTestKt {
|
||||
abstract class AbstractServerTest {
|
||||
|
||||
protected lateinit var cfg : Configuration
|
||||
|
||||
|
@@ -23,7 +23,7 @@ import java.util.zip.Deflater
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
class BasicAuthServerTestKt : AbstractServerTestKt() {
|
||||
class BasicAuthServerTest : AbstractServerTest() {
|
||||
|
||||
companion object {
|
||||
private const val PASSWORD = "password"
|
||||
|
@@ -9,17 +9,10 @@ import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.ValueSource
|
||||
import java.net.URL
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
class ConfigurationTestKt {
|
||||
|
||||
// companion object {
|
||||
// init {
|
||||
// URL.setURLStreamHandlerFactory(ClasspathUrlStreamHandlerFactoryProvider())
|
||||
// }
|
||||
// }
|
||||
class ConfigurationTest {
|
||||
|
||||
@ValueSource(
|
||||
strings = [
|
||||
|
@@ -20,7 +20,7 @@ import java.util.zip.Deflater
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
class NoAuthServerTestKt : AbstractServerTestKt() {
|
||||
class NoAuthServerTest : AbstractServerTest() {
|
||||
|
||||
private lateinit var cacheDir : Path
|
||||
|
||||
|
@@ -31,16 +31,13 @@ import javax.net.ssl.TrustManagerFactory
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
class TlsServerTestKt : AbstractServerTestKt() {
|
||||
class TlsServerTest : AbstractServerTest() {
|
||||
|
||||
companion object {
|
||||
private const val CA_CERTIFICATE_ENTRY = "gbcs-ca"
|
||||
private const val CLIENT_CERTIFICATE_ENTRY = "gbcs-client"
|
||||
private const val SERVER_CERTIFICATE_ENTRY = "gbcs-server"
|
||||
private const val PASSWORD = "password"
|
||||
|
||||
// private fun stripLeadingSlash(s : String) = Path.of("/").root.relativize(Path.of(s).normalize()).toString()
|
||||
|
||||
}
|
||||
|
||||
private lateinit var cacheDir: Path
|
||||
|
@@ -4,7 +4,7 @@ import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import javax.naming.ldap.LdapName
|
||||
|
||||
class X500NameTestKt {
|
||||
class X500NameTest {
|
||||
|
||||
@Test
|
||||
fun test() {
|
||||
|
Reference in New Issue
Block a user