From d5a2c4a591864995bd31eef8e77d995cc8a04722 Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Thu, 9 Jan 2025 00:05:00 +0800 Subject: [PATCH] code simplification --- build.gradle | 91 ----- .../net/woggioni/gbcs/api/Configuration.java | 8 - .../gbcs/api/ConfigurationParser.java | 237 ------------ .../main/kotlin/net/woggioni/gbcs/base/Xml.kt | 37 -- src/integrationTest/java/module-info.java | 15 - .../gbcs/test/AbstractServerTest.java | 51 --- .../gbcs/test/BasicAuthServerTest.java | 229 ----------- .../woggioni/gbcs/test/ConfigurationTest.java | 48 --- .../woggioni/gbcs/test/NoAuthServerTest.java | 128 ------- .../net/woggioni/gbcs/test/TlsServerTest.java | 357 ------------------ .../net/woggioni/gbcs/test/X500NameTest.java | 28 -- .../woggioni/gbcs/utils/CertificateUtils.java | 227 ----------- .../net/woggioni/gbcs/test/gbcs-memcached.xml | 13 - src/main/java/module-info.java | 10 - .../kotlin/net/woggioni/gbcs/Authenticator.kt | 8 - .../woggioni/gbcs/GradleBuildCacheServer.kt | 5 +- .../gbcs/configuration/Configuration.kt | 309 --------------- .../net/woggioni/gbcs/configuration/Parser.kt | 40 +- .../woggioni/gbcs/test/AbstractServerTest.kt | 3 +- .../woggioni/gbcs/test/BasicAuthServerTest.kt | 2 +- .../woggioni/gbcs/test/ConfigurationTest.kt | 9 +- .../woggioni/gbcs/test/NoAuthServerTest.kt | 2 +- .../net/woggioni/gbcs/test/TlsServerTest.kt | 5 +- .../net/woggioni/gbcs/test/X500NameTest.kt | 2 +- 24 files changed, 11 insertions(+), 1853 deletions(-) delete mode 100644 gbcs-api/src/main/java/net/woggioni/gbcs/api/ConfigurationParser.java delete mode 100644 src/integrationTest/java/module-info.java delete mode 100644 src/integrationTest/java/net/woggioni/gbcs/test/AbstractServerTest.java delete mode 100644 src/integrationTest/java/net/woggioni/gbcs/test/BasicAuthServerTest.java delete mode 100644 src/integrationTest/java/net/woggioni/gbcs/test/ConfigurationTest.java delete mode 100644 src/integrationTest/java/net/woggioni/gbcs/test/NoAuthServerTest.java delete mode 100644 src/integrationTest/java/net/woggioni/gbcs/test/TlsServerTest.java delete mode 100644 src/integrationTest/java/net/woggioni/gbcs/test/X500NameTest.java delete mode 100644 src/integrationTest/java/net/woggioni/gbcs/utils/CertificateUtils.java delete mode 100644 src/integrationTest/resources/net/woggioni/gbcs/test/gbcs-memcached.xml delete mode 100644 src/main/kotlin/net/woggioni/gbcs/configuration/Configuration.kt diff --git a/build.gradle b/build.gradle index 4ea9799..c2b2651 100644 --- a/build.gradle +++ b/build.gradle @@ -103,50 +103,6 @@ tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile) { options.javaModuleMainClass = mainClassName } - -//tasks.named(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME, JavaCompile) { -// options.compilerArgs << '--patch-module' << 'net.woggioni.gbcs.test=' + project.sourceSets.test.output.asPath -// classpath = configurations.testCompileClasspath + files(tasks.jar.archiveFile) -// modularity.inferModulePath = true -// javaModuleDetector -//} - -//tasks.named(JavaPlugin.TEST_TASK_NAME, JavaForkOptions) { -// classpath = configurations.testRuntimeClasspath + project.files(tasks.jar.archiveFile) + project.sourceSets.test.output -// jvmArgumentProviders << new CommandLineArgumentProvider() { -// @CompileClasspath -// def kotlinClassesMain = kotlin.sourceSets.main.collect { it.kotlin.classesDirectory } -// -// @CompileClasspath -// def kotlinClassesTest = kotlin.sourceSets.main.collect { it.kotlin.classesDirectory } - -// @Override -// Iterable asArguments() { -// return [ -// "--patch-module", -// 'net.woggioni.gbcs=' + kotlinClassesMain.collect { it.get().asFile.absolutePath }, -// "--patch-module", -// 'net.woggioni.gbcs.test=' + project.sourceSets.test.output.asPath, -// ] -// } -// } -//} - -//configurations { -// integrationTestImplementation { -// attributes { -// attribute(LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.class, JAR)) -// } -// } -// -// integrationTestCompileClasspath { -// attributes { -// attribute(LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.class, JAR)) -// } -// } -//} -// - envelopeJar { mainModule = 'net.woggioni.gbcs' mainClass = mainClassName @@ -154,37 +110,6 @@ envelopeJar { extraClasspath = ["plugins"] } -// -//testing { -// suites { -// test { -// useJUnitJupiter(catalog.versions.junit.jupiter.get()) -// } -// -// integrationTest(JvmTestSuite) { -// dependencies { -// implementation project() -// implementation catalog.bcprov.jdk18on -// implementation catalog.bcpkix.jdk18on -// annotationProcessor catalog.lombok -// compileOnly catalog.lombok -// implementation project('gbcs-base') -// implementation project('gbcs-api') -// -// runtimeOnly project("gbcs-memcached") -// } -// -// targets { -// all { -// testTask.configure { -// shouldRunAfter(test) -// } -// } -// } -// } -// } -//} - dependencies { implementation catalog.jwo implementation catalog.slf4j.api @@ -235,19 +160,3 @@ publishing { } } - -//tasks.named('check') { -// dependsOn(testing.suites.integrationTest) -//} -// -//tasks.named("integrationTest", JavaForkOptions) { -// jvmArgumentProviders << new CommandLineArgumentProvider() { -// @Override -// Iterable asArguments() { -// return [ -// "--patch-module", -// 'net.woggioni.gbcs.test=' + project.sourceSets.integrationTest.output.asPath, -// ] -// } -// } -//} \ No newline at end of file diff --git a/gbcs-api/src/main/java/net/woggioni/gbcs/api/Configuration.java b/gbcs-api/src/main/java/net/woggioni/gbcs/api/Configuration.java index d895c72..dc78200 100644 --- a/gbcs-api/src/main/java/net/woggioni/gbcs/api/Configuration.java +++ b/gbcs-api/src/main/java/net/woggioni/gbcs/api/Configuration.java @@ -2,8 +2,6 @@ package net.woggioni.gbcs.api; import lombok.Value; -import org.w3c.dom.Document; -import org.w3c.dom.Element; import java.nio.file.Path; import java.security.cert.X509Certificate; @@ -106,12 +104,6 @@ public class Configuration { String getTypeName(); } -// @Value -// public static class FileSystemCache implements Cache { -// Path root; -// Duration maxAge; -// } - public static Configuration of( String host, int port, diff --git a/gbcs-api/src/main/java/net/woggioni/gbcs/api/ConfigurationParser.java b/gbcs-api/src/main/java/net/woggioni/gbcs/api/ConfigurationParser.java deleted file mode 100644 index 583e070..0000000 --- a/gbcs-api/src/main/java/net/woggioni/gbcs/api/ConfigurationParser.java +++ /dev/null @@ -1,237 +0,0 @@ -package net.woggioni.gbcs.api; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Duration; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; - -public class ConfigurationParser { - - public static Configuration parse(Document document) { - Element root = document.getDocumentElement(); - Configuration.Cache cache = null; - String host = "127.0.0.1"; - int port = 11080; - Map users = Collections.emptyMap(); - Map groups = Collections.emptyMap(); - Configuration.Tls tls = null; - String serverPath = root.getAttribute("path"); - boolean useVirtualThread = !root.getAttribute("useVirtualThreads").isEmpty() && - Boolean.parseBoolean(root.getAttribute("useVirtualThreads")); - Configuration.Authentication authentication = null; - - for (Node child : iterableOf(root)) { - switch (child.getNodeName()) { - case "authorization": - for (Node gchild : iterableOf((Element) child)) { - switch (gchild.getNodeName()) { - case "users": - users = parseUsers((Element) gchild); - break; - case "groups": - Map.Entry, Map> pair = parseGroups((Element) gchild, users); - users = pair.getKey(); - groups = pair.getValue(); - break; - } - } - break; - - case "bind": - Element bindEl = (Element) child; - host = bindEl.getAttribute("host"); - port = Integer.parseInt(bindEl.getAttribute("port")); - break; - - case "cache": - Element cacheEl = (Element) child; - cacheEl.getAttributeNode("xs:type").getSchemaTypeInfo(); - if ("gbcs:fileSystemCacheType".equals(cacheEl.getAttribute("xs:type"))) { - String cacheFolder = cacheEl.getAttribute("path"); - Path cachePath = !cacheFolder.isEmpty() - ? Paths.get(cacheFolder) - : Paths.get(System.getProperty("user.home")).resolve(".gbcs"); - - String maxAgeStr = cacheEl.getAttribute("max-age"); - Duration maxAge = !maxAgeStr.isEmpty() - ? Duration.parse(maxAgeStr) - : Duration.ofDays(1); - -// cache = new Configuration.FileSystemCache(cachePath, maxAge); - } - break; - - case "authentication": - for (Node gchild : iterableOf((Element) child)) { - switch (gchild.getNodeName()) { - case "basic": - authentication = new Configuration.BasicAuthentication(); - break; - case "client-certificate": - Configuration.TlsCertificateExtractor tlsExtractorUser = null; - Configuration.TlsCertificateExtractor tlsExtractorGroup = null; - - for (Node authChild : iterableOf((Element) gchild)) { - Element authEl = (Element) authChild; - switch (authChild.getNodeName()) { - case "group-extractor": - String groupAttrName = authEl.getAttribute("attribute-name"); - String groupPattern = authEl.getAttribute("pattern"); - tlsExtractorGroup = new Configuration.TlsCertificateExtractor(groupAttrName, groupPattern); - break; - case "user-extractor": - String userAttrName = authEl.getAttribute("attribute-name"); - String userPattern = authEl.getAttribute("pattern"); - tlsExtractorUser = new Configuration.TlsCertificateExtractor(userAttrName, userPattern); - break; - } - } - authentication = new Configuration.ClientCertificateAuthentication(tlsExtractorUser, tlsExtractorGroup); - break; - } - } - break; - - case "tls": - Element tlsEl = (Element) child; - boolean verifyClients = !tlsEl.getAttribute("verify-clients").isEmpty() && - Boolean.parseBoolean(tlsEl.getAttribute("verify-clients")); - Configuration.KeyStore keyStore = null; - Configuration.TrustStore trustStore = null; - - for (Node gchild : iterableOf(tlsEl)) { - Element tlsChild = (Element) gchild; - switch (gchild.getNodeName()) { - case "keystore": - Path keyStoreFile = Paths.get(tlsChild.getAttribute("file")); - String keyStorePassword = !tlsChild.getAttribute("password").isEmpty() - ? tlsChild.getAttribute("password") - : null; - String keyAlias = tlsChild.getAttribute("key-alias"); - String keyPassword = !tlsChild.getAttribute("key-password").isEmpty() - ? tlsChild.getAttribute("key-password") - : null; - keyStore = new Configuration.KeyStore(keyStoreFile, keyStorePassword, keyAlias, keyPassword); - break; - - case "truststore": - Path trustStoreFile = Paths.get(tlsChild.getAttribute("file")); - String trustStorePassword = !tlsChild.getAttribute("password").isEmpty() - ? tlsChild.getAttribute("password") - : null; - boolean checkCertificateStatus = !tlsChild.getAttribute("check-certificate-status").isEmpty() && - Boolean.parseBoolean(tlsChild.getAttribute("check-certificate-status")); - trustStore = new Configuration.TrustStore(trustStoreFile, trustStorePassword, checkCertificateStatus); - break; - } - } - tls = new Configuration.Tls(keyStore, trustStore, verifyClients); - break; - } - } - - return Configuration.of(host, port, serverPath, users, groups, cache, authentication, tls, useVirtualThread); - } - - private static Set parseRoles(Element root) { - return StreamSupport.stream(iterableOf(root).spliterator(), false) - .map(node -> switch (node.getNodeName()) { - case "reader" -> Role.Reader; - case "writer" -> Role.Writer; - default -> throw new UnsupportedOperationException("Illegal node '" + node.getNodeName() + "'"); - }) - .collect(Collectors.toSet()); - } - - private static Set parseUserRefs(Element root) { - return StreamSupport.stream(iterableOf(root).spliterator(), false) - .filter(node -> "user".equals(node.getNodeName())) - .map(node -> ((Element) node).getAttribute("ref")) - .collect(Collectors.toSet()); - } - - private static Map parseUsers(Element root) { - return StreamSupport.stream(iterableOf(root).spliterator(), false) - .filter(node -> "user".equals(node.getNodeName())) - .map(node -> { - Element el = (Element) node; - String username = el.getAttribute("name"); - String password = !el.getAttribute("password").isEmpty() ? el.getAttribute("password") : null; - return new AbstractMap.SimpleEntry<>(username, new Configuration.User(username, password, Collections.emptySet())); - }) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - - private static Map.Entry, Map> parseGroups(Element root, Map knownUsers) { - Map> userGroups = new HashMap<>(); - Map groups = StreamSupport.stream(iterableOf(root).spliterator(), false) - .filter(node -> "group".equals(node.getNodeName())) - .map(node -> { - Element el = (Element) node; - String groupName = el.getAttribute("name"); - Set roles = Collections.emptySet(); - - for (Node child : iterableOf(el)) { - switch (child.getNodeName()) { - case "users": - parseUserRefs((Element) child).stream() - .map(knownUsers::get) - .filter(Objects::nonNull) - .forEach(user -> - userGroups.computeIfAbsent(user.getName(), k -> new HashSet<>()) - .add(groupName)); - break; - case "roles": - roles = parseRoles((Element) child); - break; - } - } - return new AbstractMap.SimpleEntry<>(groupName, new Configuration.Group(groupName, roles)); - }) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - - Map users = knownUsers.entrySet().stream() - .map(entry -> { - String name = entry.getKey(); - Configuration.User user = entry.getValue(); - Set userGroupSet = userGroups.getOrDefault(name, Collections.emptySet()).stream() - .map(groups::get) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); - return new AbstractMap.SimpleEntry<>(name, new Configuration.User(name, user.getPassword(), userGroupSet)); - }) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - - return new AbstractMap.SimpleEntry<>(users, groups); - } - - private static Iterable iterableOf(Element element) { - return () -> new Iterator() { - private Node current = element.getFirstChild(); - - @Override - public boolean hasNext() { - while (current != null && !(current instanceof Element)) { - current = current.getNextSibling(); - } - return current != null; - } - - @Override - public Node next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - Node result = current; - current = current.getNextSibling(); - return result; - } - }; - } -} diff --git a/gbcs-base/src/main/kotlin/net/woggioni/gbcs/base/Xml.kt b/gbcs-base/src/main/kotlin/net/woggioni/gbcs/base/Xml.kt index 82e8e47..a2622f8 100644 --- a/gbcs-base/src/main/kotlin/net/woggioni/gbcs/base/Xml.kt +++ b/gbcs-base/src/main/kotlin/net/woggioni/gbcs/base/Xml.kt @@ -128,8 +128,6 @@ class Xml(val doc: Document, val element: Element) { fun getSchema(schema: URL): Schema { val sf = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI) sf.setFeature(FEATURE_SECURE_PROCESSING, false) -// disableProperty(sf, ACCESS_EXTERNAL_SCHEMA) -// disableProperty(sf, ACCESS_EXTERNAL_DTD) sf.errorHandler = ErrorHandler(schema) return sf.newSchema(schema) } @@ -137,15 +135,12 @@ class Xml(val doc: Document, val element: Element) { fun getSchema(inputStream: InputStream): Schema { val sf = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI) sf.setFeature(FEATURE_SECURE_PROCESSING, true) -// disableProperty(sf, ACCESS_EXTERNAL_SCHEMA) -// disableProperty(sf, ACCESS_EXTERNAL_DTD) return sf.newSchema(StreamSource(inputStream)) } fun newDocumentBuilderFactory(schemaResourceURL: URL?): DocumentBuilderFactory { val dbf = DocumentBuilderFactory.newInstance() dbf.setFeature(FEATURE_SECURE_PROCESSING, false) -// disableProperty(dbf, ACCESS_EXTERNAL_SCHEMA) dbf.setAttribute(ACCESS_EXTERNAL_SCHEMA, "all") disableProperty(dbf, ACCESS_EXTERNAL_DTD) dbf.isExpandEntityReferences = true @@ -175,32 +170,10 @@ class Xml(val doc: Document, val element: Element) { return sourceStream?.let(db::parse) ?: sourceURL.openStream().use(db::parse) } -// -// fun newDocumentBuilder(resource: URL): DocumentBuilder { -// val db = newDocumentBuilderFactory(null).newDocumentBuilder() -// db.setErrorHandler(XmlErrorHandler(resource)) -// return db -// } - -// fun parseXmlResource(resource: URL): Document { -// val db = newDocumentBuilder(resource, null) -// return resource.openStream().use(db::parse) -// } - fun write(doc: Document, output: OutputStream) { val transformerFactory = TransformerFactory.newInstance() val transformer = transformerFactory.newTransformer() transformer.setOutputProperty(OutputKeys.INDENT, "yes") -// val domImpl = doc.getImplementation() -// val docType = domImpl.createDocumentType( -// "plist", -// "-//Apple//DTD PLIST 1.0//EN", -// "http://www.apple.com/DTDs/PropertyList-1.0.dtd" -// ) -// transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, docType.getPublicId()) -// transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, docType.getSystemId()) -// val transformerFactory = TransformerFactory.newInstance() -// val transformer: Transformer = transformerFactory.newTransformer() transformer.setOutputProperty(OutputKeys.INDENT, "yes") transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4") transformer.setOutputProperty(OutputKeys.STANDALONE, "yes") @@ -230,7 +203,6 @@ class Xml(val doc: Document, val element: Element) { removeChild(firstChild ?: break) } } - } fun node( @@ -250,15 +222,6 @@ class Xml(val doc: Document, val element: Element) { } } - -// fun attrs(vararg attributes: Pair) { -// for (attr in attributes) element.setAttribute(attr.first, attr.second) -// } -// -// fun attrs(vararg attributes: Pair, String>) { -// for (attr in attributes) element.setAttributeNS(attr.first.first, attr.first.second, attr.second) -// } - fun attr(key: String, value: String, namespaceURI : String? = null) { element.setAttributeNS(namespaceURI, key, value) } diff --git a/src/integrationTest/java/module-info.java b/src/integrationTest/java/module-info.java deleted file mode 100644 index 19000f1..0000000 --- a/src/integrationTest/java/module-info.java +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/src/integrationTest/java/net/woggioni/gbcs/test/AbstractServerTest.java b/src/integrationTest/java/net/woggioni/gbcs/test/AbstractServerTest.java deleted file mode 100644 index de49b4c..0000000 --- a/src/integrationTest/java/net/woggioni/gbcs/test/AbstractServerTest.java +++ /dev/null @@ -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(); - } - } - } -} \ No newline at end of file diff --git a/src/integrationTest/java/net/woggioni/gbcs/test/BasicAuthServerTest.java b/src/integrationTest/java/net/woggioni/gbcs/test/BasicAuthServerTest.java deleted file mode 100644 index 2d6d6d4..0000000 --- a/src/integrationTest/java/net/woggioni/gbcs/test/BasicAuthServerTest.java +++ /dev/null @@ -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 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 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 usersMap = users.stream() - .collect(Collectors.toMap(user -> user.getName(), user -> user)); - - Map 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 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 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 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 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 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 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 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 response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray()); - Assertions.assertEquals(HttpResponseStatus.NOT_FOUND.code(), response.statusCode()); - } - } -} \ No newline at end of file diff --git a/src/integrationTest/java/net/woggioni/gbcs/test/ConfigurationTest.java b/src/integrationTest/java/net/woggioni/gbcs/test/ConfigurationTest.java deleted file mode 100644 index 2e7b91d..0000000 --- a/src/integrationTest/java/net/woggioni/gbcs/test/ConfigurationTest.java +++ /dev/null @@ -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); - } -} \ No newline at end of file diff --git a/src/integrationTest/java/net/woggioni/gbcs/test/NoAuthServerTest.java b/src/integrationTest/java/net/woggioni/gbcs/test/NoAuthServerTest.java deleted file mode 100644 index 962aff3..0000000 --- a/src/integrationTest/java/net/woggioni/gbcs/test/NoAuthServerTest.java +++ /dev/null @@ -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 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 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 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 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 entry = newEntry(random); - String key = entry.getKey(); - - HttpRequest.Builder requestBuilder = newRequestBuilder(key).GET(); - - HttpResponse response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray()); - Assertions.assertEquals(HttpResponseStatus.NOT_FOUND.code(), response.statusCode()); - } - } -} \ No newline at end of file diff --git a/src/integrationTest/java/net/woggioni/gbcs/test/TlsServerTest.java b/src/integrationTest/java/net/woggioni/gbcs/test/TlsServerTest.java deleted file mode 100644 index 4d46533..0000000 --- a/src/integrationTest/java/net/woggioni/gbcs/test/TlsServerTest.java +++ /dev/null @@ -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 keyValuePair; - - private final List 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 usersMap = users.stream() - .collect(Collectors.toMap(user -> user.getName(), user -> user)); - - Map 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 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 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 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 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 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 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 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 response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray()); - Assertions.assertEquals(HttpResponseStatus.NOT_FOUND.code(), response.statusCode()); - } - } -} \ No newline at end of file diff --git a/src/integrationTest/java/net/woggioni/gbcs/test/X500NameTest.java b/src/integrationTest/java/net/woggioni/gbcs/test/X500NameTest.java deleted file mode 100644 index e89351b..0000000 --- a/src/integrationTest/java/net/woggioni/gbcs/test/X500NameTest.java +++ /dev/null @@ -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); - } -} - diff --git a/src/integrationTest/java/net/woggioni/gbcs/utils/CertificateUtils.java b/src/integrationTest/java/net/woggioni/gbcs/utils/CertificateUtils.java deleted file mode 100644 index e89d315..0000000 --- a/src/integrationTest/java/net/woggioni/gbcs/utils/CertificateUtils.java +++ /dev/null @@ -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); - } -} \ No newline at end of file diff --git a/src/integrationTest/resources/net/woggioni/gbcs/test/gbcs-memcached.xml b/src/integrationTest/resources/net/woggioni/gbcs/test/gbcs-memcached.xml deleted file mode 100644 index 7abe02b..0000000 --- a/src/integrationTest/resources/net/woggioni/gbcs/test/gbcs-memcached.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index df35496..30f594d 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -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; diff --git a/src/main/kotlin/net/woggioni/gbcs/Authenticator.kt b/src/main/kotlin/net/woggioni/gbcs/Authenticator.kt index 013cbfb..1eca97b 100644 --- a/src/main/kotlin/net/woggioni/gbcs/Authenticator.kt +++ b/src/main/kotlin/net/woggioni/gbcs/Authenticator.kt @@ -61,14 +61,6 @@ abstract class AbstractNettyHttpAuthenticator(private val authorizer : Authorize return String(Base64.getEncoder().encode(concat(hash, actualSalt))) } -// fun decodePasswordHash(passwordHash : String) : Pair { -// 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 { val decoded = Base64.getDecoder().decode(passwordHash) val hash = ByteArray(KEY_LENGTH / 8) diff --git a/src/main/kotlin/net/woggioni/gbcs/GradleBuildCacheServer.kt b/src/main/kotlin/net/woggioni/gbcs/GradleBuildCacheServer.kt index dfaef5b..ce1b532 100644 --- a/src/main/kotlin/net/woggioni/gbcs/GradleBuildCacheServer.kt +++ b/src/main/kotlin/net/woggioni/gbcs/GradleBuildCacheServer.kt @@ -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): Configuration { -// registerUrlProtocolHandler() // Thread.currentThread().contextClassLoader = GradleBuildCacheServer::class.java.classLoader val app = Application.builder("gbcs") .configurationDirectoryEnvVar("GBCS_CONFIGURATION_DIR") diff --git a/src/main/kotlin/net/woggioni/gbcs/configuration/Configuration.kt b/src/main/kotlin/net/woggioni/gbcs/configuration/Configuration.kt deleted file mode 100644 index 2b2af28..0000000 --- a/src/main/kotlin/net/woggioni/gbcs/configuration/Configuration.kt +++ /dev/null @@ -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, - val groups: Map, - val cache: Cache, - val authentication : Authentication?, - val tls: Tls?, - val useVirtualThread: Boolean -) { - - data class Group(val name: String, val roles: Set) { - override fun hashCode(): Int { - return name.hashCode() - } - } - - data class User(val name: String, val password: String?, val groups: Set) { - override fun hashCode(): Int { - return name.hashCode() - } - - val roles : Set - 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, - groups: Map, - 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() -// var groups = emptyMap() -// 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 { -// 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): Pair, Map> { -// val userGroups = mutableMapOf>() -// val groups = root.asIterable().asSequence().filter { -// it.nodeName == "group" -// }.map { el -> -// val groupName = el.getAttribute("name") -// var roles = emptySet() -// 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 -// } - } -} diff --git a/src/main/kotlin/net/woggioni/gbcs/configuration/Parser.kt b/src/main/kotlin/net/woggioni/gbcs/configuration/Parser.kt index e8a3164..79591c0 100644 --- a/src/main/kotlin/net/woggioni/gbcs/configuration/Parser.kt +++ b/src/main/kotlin/net/woggioni/gbcs/configuration/Parser.kt @@ -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" -> { diff --git a/src/test/kotlin/net/woggioni/gbcs/test/AbstractServerTest.kt b/src/test/kotlin/net/woggioni/gbcs/test/AbstractServerTest.kt index 0173f72..a2fa282 100644 --- a/src/test/kotlin/net/woggioni/gbcs/test/AbstractServerTest.kt +++ b/src/test/kotlin/net/woggioni/gbcs/test/AbstractServerTest.kt @@ -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 diff --git a/src/test/kotlin/net/woggioni/gbcs/test/BasicAuthServerTest.kt b/src/test/kotlin/net/woggioni/gbcs/test/BasicAuthServerTest.kt index f55b678..bbea4b0 100644 --- a/src/test/kotlin/net/woggioni/gbcs/test/BasicAuthServerTest.kt +++ b/src/test/kotlin/net/woggioni/gbcs/test/BasicAuthServerTest.kt @@ -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" diff --git a/src/test/kotlin/net/woggioni/gbcs/test/ConfigurationTest.kt b/src/test/kotlin/net/woggioni/gbcs/test/ConfigurationTest.kt index 54bfccd..8dd6f76 100644 --- a/src/test/kotlin/net/woggioni/gbcs/test/ConfigurationTest.kt +++ b/src/test/kotlin/net/woggioni/gbcs/test/ConfigurationTest.kt @@ -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 = [ diff --git a/src/test/kotlin/net/woggioni/gbcs/test/NoAuthServerTest.kt b/src/test/kotlin/net/woggioni/gbcs/test/NoAuthServerTest.kt index a99262c..b02586a 100644 --- a/src/test/kotlin/net/woggioni/gbcs/test/NoAuthServerTest.kt +++ b/src/test/kotlin/net/woggioni/gbcs/test/NoAuthServerTest.kt @@ -20,7 +20,7 @@ import java.util.zip.Deflater import kotlin.random.Random -class NoAuthServerTestKt : AbstractServerTestKt() { +class NoAuthServerTest : AbstractServerTest() { private lateinit var cacheDir : Path diff --git a/src/test/kotlin/net/woggioni/gbcs/test/TlsServerTest.kt b/src/test/kotlin/net/woggioni/gbcs/test/TlsServerTest.kt index 5725776..d2de5b3 100644 --- a/src/test/kotlin/net/woggioni/gbcs/test/TlsServerTest.kt +++ b/src/test/kotlin/net/woggioni/gbcs/test/TlsServerTest.kt @@ -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 diff --git a/src/test/kotlin/net/woggioni/gbcs/test/X500NameTest.kt b/src/test/kotlin/net/woggioni/gbcs/test/X500NameTest.kt index bc2ec6e..fa847a7 100644 --- a/src/test/kotlin/net/woggioni/gbcs/test/X500NameTest.kt +++ b/src/test/kotlin/net/woggioni/gbcs/test/X500NameTest.kt @@ -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() {