Introduce authentication framework with AuthConfig, AuthGate, and Authenticator classes, alongside comprehensive tests for rules, modes, and schemes.
CI - Test, Publish and Release / run-tests (push) Successful in 18s
CI - Test, Publish and Release / create-release (push) Successful in 20s
CI - Test, Publish and Release / check-and-publish (push) Successful in 18s

This commit is contained in:
CodingPhoenixx
2026-05-29 13:22:31 +02:00
parent d9b639a539
commit bcf5572aeb
39 changed files with 2629 additions and 326 deletions
@@ -0,0 +1,124 @@
package dev.coph.nextusweb.server.tls;
import io.netty.buffer.ByteBufAllocator;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import javax.net.ssl.SSLException;
import java.io.File;
import java.io.InputStream;
import java.util.Objects;
/**
* Configuration that enables TLS (HTTPS / WSS) on the server. Holds a ready-built Netty
* {@link SslContext} and produces the per-connection {@link SslHandler} the pipeline installs as
* its first handler.
*
* <p>Enabling TLS is meant to be a one-liner — point the factory at a PEM certificate chain and
* private key and pass the result to {@code HttpServer.withTls(...)}:</p>
* <pre>{@code
* HttpServer.builder(443, router)
* .withTls(TlsConfig.fromPem(new File("fullchain.pem"), new File("privkey.pem")))
* .start();
* }</pre>
*
* <p>For full control (custom cipher suites, client-auth / mutual TLS, a non-default provider)
* build a Netty {@link SslContext} yourself and wrap it with {@link #fromSslContext(SslContext)}.
* The {@code SslContext} is built once and reused for every connection, so it is cheap per
* request.</p>
*/
public final class TlsConfig {
/** The pre-built, shareable server SSL context. */
private final SslContext sslContext;
private TlsConfig(SslContext sslContext) {
this.sslContext = Objects.requireNonNull(sslContext, "sslContext");
}
/**
* Builds a TLS configuration from a PEM-encoded certificate chain and an unencrypted
* PKCS#8 private key.
*
* @param certificateChain the PEM file containing the certificate (chain)
* @param privateKey the PEM file containing the PKCS#8 private key
* @return a TLS configuration
* @throws IllegalStateException if the certificate/key cannot be loaded
*/
public static TlsConfig fromPem(File certificateChain, File privateKey) {
return fromPem(certificateChain, privateKey, null);
}
/**
* Builds a TLS configuration from a PEM-encoded certificate chain and a (optionally
* password-protected) PKCS#8 private key.
*
* @param certificateChain the PEM file containing the certificate (chain)
* @param privateKey the PEM file containing the PKCS#8 private key
* @param keyPassword the password protecting {@code privateKey}, or {@code null} if none
* @return a TLS configuration
* @throws IllegalStateException if the certificate/key cannot be loaded
*/
public static TlsConfig fromPem(File certificateChain, File privateKey, String keyPassword) {
Objects.requireNonNull(certificateChain, "certificateChain");
Objects.requireNonNull(privateKey, "privateKey");
try {
return new TlsConfig(SslContextBuilder.forServer(certificateChain, privateKey, keyPassword).build());
} catch (SSLException | RuntimeException e) {
// Netty surfaces missing/invalid PEM material as IllegalArgumentException; normalise
// every initialisation failure to a single, predictable exception type.
throw new IllegalStateException("Failed to initialise TLS from PEM files", e);
}
}
/**
* Builds a TLS configuration from PEM-encoded streams, for certificates/keys loaded from the
* classpath or another non-file source. The caller retains ownership of the streams.
*
* @param certificateChain a stream of the PEM certificate (chain)
* @param privateKey a stream of the PEM PKCS#8 private key
* @param keyPassword the password protecting {@code privateKey}, or {@code null} if none
* @return a TLS configuration
* @throws IllegalStateException if the certificate/key cannot be loaded
*/
public static TlsConfig fromPem(InputStream certificateChain, InputStream privateKey, String keyPassword) {
Objects.requireNonNull(certificateChain, "certificateChain");
Objects.requireNonNull(privateKey, "privateKey");
try {
return new TlsConfig(SslContextBuilder.forServer(certificateChain, privateKey, keyPassword).build());
} catch (SSLException | RuntimeException e) {
throw new IllegalStateException("Failed to initialise TLS from PEM streams", e);
}
}
/**
* Wraps a fully configured Netty {@link SslContext}, for advanced setups such as custom cipher
* suites or mutual TLS.
*
* @param sslContext a server-mode SSL context
* @return a TLS configuration backed by the given context
*/
public static TlsConfig fromSslContext(SslContext sslContext) {
return new TlsConfig(sslContext);
}
/**
* Creates a new per-connection {@link SslHandler} from the shared context.
*
* @param alloc the channel's buffer allocator
* @return a fresh TLS handler for one connection
*/
public SslHandler newHandler(ByteBufAllocator alloc) {
return sslContext.newHandler(alloc);
}
/**
* Returns the underlying Netty SSL context.
*
* @return the shared server SSL context
*/
public SslContext sslContext() {
return sslContext;
}
}