@@ -111,17 +111,10 @@
3.20.0-GA
provided
-
-
- commons-io
- commons-io
- 1.3.2
- provided
-
com.github.TBMCPlugins.ChromaCore
Chroma-Core
- ${branch}-SNAPSHOT
+ v1.0.0
provided
diff --git a/src/buttondevteam/website/AcmeClient.java b/src/buttondevteam/website/AcmeClient.java
deleted file mode 100644
index 13a0e09..0000000
--- a/src/buttondevteam/website/AcmeClient.java
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * acme4j - Java ACME client
- *
- * Copyright (C) 2015 Richard "Shred" Körber
- * http://acme4j.shredzone.org
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- */ //Modified
-package buttondevteam.website;
-
-import buttondevteam.lib.TBMCCoreAPI;
-import buttondevteam.website.page.AcmeChallengePage;
-import org.shredzone.acme4j.*;
-import org.shredzone.acme4j.challenge.Challenge;
-import org.shredzone.acme4j.challenge.Http01Challenge;
-import org.shredzone.acme4j.exception.AcmeException;
-import org.shredzone.acme4j.util.CSRBuilder;
-import org.shredzone.acme4j.util.KeyPairUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.*;
-import java.net.URI;
-import java.security.KeyPair;
-import java.util.Arrays;
-import java.util.Collection;
-
-/**
- * A simple client test tool.
- *
- * Pass the names of the domains as parameters.
- */
-public class AcmeClient {
- // File name of the User Key Pair
- public static final File USER_KEY_FILE = new File("user.key");
-
- // File name of the Domain Key Pair
- public static final File DOMAIN_KEY_FILE = new File("domain.key");
-
- // File name of the CSR
- public static final File DOMAIN_CSR_FILE = new File("domain.csr");
-
- // File name of the signed certificate
- public static final File DOMAIN_CHAIN_FILE = new File("domain-chain.crt");
-
- // RSA key size of generated key pairs
- private static final int KEY_SIZE = 2048;
-
- private static final Logger LOG = LoggerFactory.getLogger(AcmeClient.class);
-
- /**
- * Generates a certificate for the given domains. Also takes care for the registration process.
- *
- * @param domains
- * Domains to get a common certificate for
- */
- public void fetchCertificate(Collection domains) throws IOException, AcmeException {
- // Load the user key file. If there is no key file, create a new one.
- // Keep this key pair in a safe place! In a production environment, you will not be
- // able to access your account again if you should lose the key pair.
- KeyPair userKeyPair = loadOrCreateKeyPair(USER_KEY_FILE);
-
- // Create a session for Let's Encrypt.
- // Use "acme://letsencrypt.org" for production server
- Session session = new Session("acme://letsencrypt.org" + (TBMCCoreAPI.IsTestServer() ? "/staging" : ""));
-
- // Get the Registration to the account.
- // If there is no account yet, create a new one.
- Account acc = findOrRegisterAccount(session, userKeyPair);
-
- Order order = acc.newOrder().domains(domains).create();
-
- // Separately authorize every requested domain.
- for (Authorization auth : order.getAuthorizations()) {
- authorize(auth);
- }
-
- // Load or create a key pair for the domains. This should not be the userKeyPair!
- KeyPair domainKeyPair = loadOrCreateKeyPair(DOMAIN_KEY_FILE);
-
- // Generate a CSR for all of the domains, and sign it with the domain key pair.
- CSRBuilder csrb = new CSRBuilder();
- csrb.addDomains(domains);
- csrb.sign(domainKeyPair);
-
- // Write the CSR to a file, for later use.
- try (Writer out = new FileWriter(DOMAIN_CSR_FILE)) {
- csrb.write(out);
- }
-
- LOG.info("Ordering certificate...");
- // Order the certificate
- order.execute(csrb.getEncoded());
-
- // Wait for the order to complete
- try {
- int attempts = 10;
- while (order.getStatus() != Status.VALID && attempts-- > 0) {
- // Did the order fail?
- if (order.getStatus() == Status.INVALID) {
- throw new AcmeException("Order failed... Giving up.");
- }
-
- // Wait for a few seconds
- Thread.sleep(3000L);
-
- // Then update the status
- order.update();
- if (order.getStatus() != Status.VALID)
- LOG.info("Not yet...");
- }
- } catch (InterruptedException ex) {
- LOG.error("interrupted", ex);
- Thread.currentThread().interrupt();
- }
-
- // Get the certificate
- Certificate certificate = order.getCertificate();
-
- if (certificate == null)
- throw new AcmeException("Certificate is null. Wot.");
-
- LOG.info("Success! The certificate for domains " + domains + " has been generated!");
- LOG.info("Certificate URL: " + certificate.getLocation());
-
- // Write a combined file containing the certificate and chain.
- try (FileWriter fw = new FileWriter(DOMAIN_CHAIN_FILE)) {
- certificate.writeCertificate(fw);
- }
-
- // That's all! Configure your web server to use the DOMAIN_KEY_FILE and
- // DOMAIN_CHAIN_FILE for the requested domans.
- }
-
- /**
- * Loads a key pair from specified file. If the file does not exist, a new key pair is generated and saved.
- *
- * @return {@link KeyPair}.
- */
- private KeyPair loadOrCreateKeyPair(File file) throws IOException {
- if (file.exists()) {
- try (FileReader fr = new FileReader(file)) {
- return KeyPairUtils.readKeyPair(fr);
- }
- } else {
- KeyPair domainKeyPair = KeyPairUtils.createKeyPair(KEY_SIZE);
- try (FileWriter fw = new FileWriter(file)) {
- KeyPairUtils.writeKeyPair(domainKeyPair, fw);
- }
- return domainKeyPair;
- }
- }
-
- /**
- * Finds your {@link Account} at the ACME server. It will be found by your user's public key. If your key is not known to the server yet, a new registration will be created.
- *
- * @param session
- * {@link Session} to bind with
- * @param kp The user keypair
- * @return {@link Account} connected to your account
- */
- private Account findOrRegisterAccount(Session session, KeyPair kp) throws AcmeException, IOException {
- Account acc;
-
- URI loc = ButtonWebsiteModule.getRegistration();
- if (loc != null) {
- LOG.info("Loading account from file");
- return new Login(loc.toURL(), kp, session).getAccount();
- }
-
- // Try to create a new Registration.
- AccountBuilder ab = new AccountBuilder().useKeyPair(kp);
-
- // This is a new account. Let the user accept the Terms of Service.
- // We won't be able to authorize domains until the ToS is accepted.
- URI agreement = session.getMetadata().getTermsOfService();
- acceptAgreement(ab, agreement);
- acc = ab.create(session);
- LOG.info("Registered a new user, URI: " + acc.getLocation());
- ButtonWebsiteModule.storeRegistration(acc.getLocation());
-
- return acc;
- }
-
- /**
- * Authorize a domain. It will be associated with your account, so you will be able to retrieve a signed certificate for the domain later.
- *
- * You need separate authorizations for subdomains (e.g. "www" subdomain). Wildcard certificates are not currently supported.
- *
- * @param auth
- * {@link Authorization} for the domain
- */
- private void authorize(Authorization auth) throws AcmeException {
- LOG.info("Authorization for domain " + auth.getDomain());
-
- // The authorization is already valid. No need to process a challenge.
- if (auth.getStatus() == Status.VALID) {
- return;
- }
-
- Challenge challenge = httpChallenge(auth);
-
- if (challenge == null) {
- throw new AcmeException("No challenge found");
- }
-
- // If the challenge is already verified, there's no need to execute it again.
- if (challenge.getStatus() == Status.VALID) {
- return;
- }
-
- // Now trigger the challenge.
- challenge.trigger();
-
- // Poll for the challenge to complete.
- try {
- int attempts = 10;
- while (challenge.getStatus() != Status.VALID && attempts-- > 0) {
- // Did the authorization fail?
- if (challenge.getStatus() == Status.INVALID) {
- throw new AcmeException("Challenge failed... Giving up.");
- }
-
- // Wait for a few seconds
- Thread.sleep(3000L);
-
- // Then update the status
- challenge.update();
- }
- } catch (InterruptedException ex) {
- LOG.error("interrupted", ex);
- Thread.currentThread().interrupt();
- }
-
- // All reattempts are used up and there is still no valid authorization?
- if (challenge.getStatus() != Status.VALID) {
- throw new AcmeException("Failed to pass the challenge for domain " + auth.getDomain() + ", ... Giving up.");
- }
- }
-
- /**
- * Prepares a HTTP challenge.
- *
- * The verification of this challenge expects a file with a certain content to be reachable at a given path under the domain to be tested.
- *
- * This example outputs instructions that need to be executed manually. In a production environment, you would rather generate this file automatically, or maybe use a servlet that returns
- * {@link Http01Challenge#getAuthorization()}.
- *
- * @param auth
- * {@link Authorization} to find the challenge in
- * @return {@link Challenge} to verify
- */
- public Challenge httpChallenge(Authorization auth) throws AcmeException {
- // Find a single http-01 challenge
- Http01Challenge challenge = auth.findChallenge(Http01Challenge.TYPE);
- if (challenge == null) {
- throw new AcmeException("Found no " + Http01Challenge.TYPE + " challenge, don't know what to do...");
- }
- LOG.info("Storing the challenge data.");
- LOG.info("It should be reachable at: http://" + auth.getDomain() + "/.well-known/acme-challenge/" + challenge.getToken());
- ButtonWebsiteModule.addHttpPage(new AcmeChallengePage(challenge.getToken(), challenge.getAuthorization()));
- ButtonWebsiteModule.startHttp();
- try {
- Thread.sleep(1000); // Just to make sure
- } catch (InterruptedException e) {
- }
- return challenge;
- }
-
- /**
- * Presents the user a link to the Terms of Service, and asks for confirmation. If the user denies confirmation, an exception is thrown.
- *
- * @param ab
- * {@link AccountBuilder} for the user
- * @param agreement
- * {@link URI} of the Terms of Service
- */
- public void acceptAgreement(AccountBuilder ab, URI agreement) throws AcmeException, IOException {
- LOG.info("Terms of Service: " + agreement);
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- System.out.println("Do you accept the terms? (y/n)");
- if (br.readLine().equalsIgnoreCase("y\n")) {
- throw new AcmeException("User did not accept Terms of Service");
- }
-
- // Motify the Registration and accept the agreement
- ab.agreeToTermsOfService();
- LOG.info("Updated user's ToS");
- }
-
- /**
- * Invokes this example.
- *
- * @param args
- * Domains to get a certificate for
- */
- public static void main(String... args) {
- if (args.length == 0) {
- LOG.error("Error while doing ACME!", new Exception("No domains given"));
- return;
- }
-
- LOG.info("Starting up...");
-
- Collection domains = Arrays.asList(args);
- try {
- AcmeClient ct = new AcmeClient();
- ct.fetchCertificate(domains);
- } catch (Exception ex) {
- LOG.error("Failed to get a certificate for domains " + domains, ex);
- }
- }
-}
diff --git a/src/buttondevteam/website/ButtonWebsiteModule.java b/src/buttondevteam/website/ButtonWebsiteModule.java
index df04447..380b56b 100644
--- a/src/buttondevteam/website/ButtonWebsiteModule.java
+++ b/src/buttondevteam/website/ButtonWebsiteModule.java
@@ -5,31 +5,12 @@ import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.website.io.IOHelper;
import buttondevteam.website.page.*;
import com.sun.net.httpserver.HttpServer;
-import com.sun.net.httpserver.HttpsConfigurator;
-import com.sun.net.httpserver.HttpsParameters;
-import com.sun.net.httpserver.HttpsServer;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openssl.PEMKeyPair;
-import org.bouncycastle.openssl.PEMParser;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bukkit.Bukkit;
-import javax.net.ssl.*;
import java.io.*;
import java.net.*;
-import java.security.KeyPair;
-import java.security.KeyStore;
-import java.security.PrivateKey;
-import java.security.Security;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateFactory;
-import java.util.Calendar;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
public class ButtonWebsiteModule extends ButtonPlugin {
- private static HttpsServer server;
/**
* For ACME validation and user redirection
*/
@@ -38,75 +19,8 @@ public class ButtonWebsiteModule extends ButtonPlugin {
public ButtonWebsiteModule() {
try {
- int ps = getConfig().getInt("https-port", 443);
int p = getConfig().getInt("http-port", 80);
- server = HttpsServer.create(new InetSocketAddress((InetAddress) null, ps), 10);
httpserver = HttpServer.create(new InetSocketAddress((InetAddress) null, p), 10);
- SSLContext sslContext = SSLContext.getInstance("TLS");
-
- // initialise the keystore
- char[] password = "password".toCharArray();
- KeyStore ks = KeyStore.getInstance("JKS");
- File certfile = AcmeClient.DOMAIN_CHAIN_FILE; /* your cert path */
- File keystoreFile = new File("keystore.keystore");
-
- ks.load(keystoreFile.exists() ? new FileInputStream(keystoreFile) : null, password);
-
- String alias = "chroma";
-
- //////
-
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- InputStream certstream = fullStream(certfile);
- Certificate[] certs = cf.generateCertificates(certstream).toArray(new Certificate[0]);
-
- BufferedReader br = new BufferedReader(new FileReader(AcmeClient.DOMAIN_KEY_FILE));
-
- Security.addProvider(new BouncyCastleProvider());
-
- PEMParser pp = new PEMParser(br);
- PEMKeyPair pemKeyPair = (PEMKeyPair) pp.readObject();
- KeyPair kp = new JcaPEMKeyConverter().getKeyPair(pemKeyPair);
- pp.close();
- PrivateKey pk = kp.getPrivate();
-
- // Add the certificate
- ks.setKeyEntry(alias, pk, password, certs); // TODO: Only set if updated
-
- // Save the new keystore contents
- FileOutputStream out = new FileOutputStream(keystoreFile);
- ks.store(out, password);
- out.close();
-
- // setup the key manager factory
- KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
- kmf.init(ks, password);
-
- // setup the trust manager factory
- TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
- tmf.init(ks);
-
- // setup the HTTPS context and parameters
- sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
- server.setHttpsConfigurator(new HttpsConfigurator(sslContext) {
- public void configure(HttpsParameters params) {
- try {
- // initialise the SSL context
- SSLContext c = SSLContext.getDefault();
- SSLEngine engine = c.createSSLEngine();
- params.setNeedClientAuth(false);
- params.setCipherSuites(engine.getEnabledCipherSuites());
- params.setProtocols(engine.getEnabledProtocols());
-
- // get the default parameters
- SSLParameters defaultSSLParameters = c.getDefaultSSLParameters();
- params.setSSLParameters(defaultSSLParameters);
-
- } catch (Exception ex) {
- System.out.println("Failed to create HTTPS port");
- }
- }
- });
enabled = true;
} catch (Exception e) {
TBMCCoreAPI.SendException("An error occurred while starting the webserver!", e, this);
@@ -125,19 +39,12 @@ public class ButtonWebsiteModule extends ButtonPlugin {
addPage(new ProfilePage());
addPage(new BuildNotificationsPage());
addPage(new BridgePage());
+ addHttpPage(new BridgePage());
TBMCCoreAPI.RegisterUserClass(WebUser.class, WebUser::new);
getCommand2MC().registerCommand(new LoginCommand());
Bukkit.getScheduler().runTaskAsynchronously(this, () -> {
this.getLogger().info("Starting webserver...");
- server.setExecutor(
- new ThreadPoolExecutor(4, 8, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100)));
- httpserver.createContext("/", exchange -> IOHelper.SendResponse(IOHelper.Redirect("https://server.figytuna.com/", exchange)));
- final Calendar calendar = Calendar.getInstance();
- if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY && !TBMCCoreAPI.IsTestServer()) { // Only update every week
- Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
- AcmeClient.main("server.figytuna.com"); // Task is running async so we don't need an extra thread
- }
- ((Runnable) server::start).run(); // Totally normal way of calling a method
+ httpserver.createContext("/", exchange -> IOHelper.SendResponse(IOHelper.Redirect("https://www.youtube.com/watch?v=dQw4w9WgXcQ", exchange)));
if (!httpstarted)
httpserver.start();
this.getLogger().info("Webserver started");
@@ -162,9 +69,7 @@ public class ButtonWebsiteModule extends ButtonPlugin {
* Adds a new page/endpoint to the website. This method needs to be called before the server finishes loading (onEnable).
*/
public static void addPage(Page page) {
- if (!enabled)
- return;
- server.createContext("/" + page.GetName(), page);
+ // No HTTPS support for now but these pages should be secured (and updated)
}
/**
diff --git a/src/buttondevteam/website/io/IOHelper.java b/src/buttondevteam/website/io/IOHelper.java
index 5dfd80a..d70a184 100644
--- a/src/buttondevteam/website/io/IOHelper.java
+++ b/src/buttondevteam/website/io/IOHelper.java
@@ -6,13 +6,9 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.sun.net.httpserver.HttpExchange;
-import org.apache.commons.io.IOUtils;
import org.bukkit.Bukkit;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
+import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
@@ -57,7 +53,10 @@ public class IOHelper {
try {
if (exchange.getRequestBody().available() == 0)
return "";
- return IOUtils.toString(exchange.getRequestBody(), "UTF-8");
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ exchange.getRequestBody().transferTo(baos);
+ return baos.toString(StandardCharsets.UTF_8);
+ }
} catch (Exception e) {
e.printStackTrace();
return "";
diff --git a/src/buttondevteam/website/page/Page.java b/src/buttondevteam/website/page/Page.java
index 77255ec..67d88a1 100644
--- a/src/buttondevteam/website/page/Page.java
+++ b/src/buttondevteam/website/page/Page.java
@@ -6,8 +6,8 @@ import buttondevteam.website.io.IOHelper;
import buttondevteam.website.io.Response;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
-import org.apache.commons.io.output.ByteArrayOutputStream;
+import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
/**