From 4c4301d2d9f7f06665b2b69023b0e0483184329c Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sun, 18 Jun 2017 21:35:42 +0200 Subject: [PATCH 1/8] Create .travis.yml --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9bcf999 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: java +jdk: + - oraclejdk8 From 897372f0982cf028262bc84d01f24ad82defb876 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Tue, 27 Jun 2017 23:06:01 +0200 Subject: [PATCH 2/8] Probably should use TFTP actually --- .../website/ButtonWebsiteModule.java | 3 ++ .../website/page/AutoUpdatePage.java | 35 +++++++++++++++++++ src/buttondevteam/website/page/Page.java | 2 +- 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/buttondevteam/website/page/AutoUpdatePage.java diff --git a/src/buttondevteam/website/ButtonWebsiteModule.java b/src/buttondevteam/website/ButtonWebsiteModule.java index a219aab..6d3ce4f 100644 --- a/src/buttondevteam/website/ButtonWebsiteModule.java +++ b/src/buttondevteam/website/ButtonWebsiteModule.java @@ -111,6 +111,9 @@ public class ButtonWebsiteModule extends JavaPlugin { @Override public void onEnable() { addPage(new IndexPage()); + addPage(new AutoUpdatePage()); + addPage(new LoginPage()); + addPage(new ProfilePage()); TBMCCoreAPI.RegisterUserClass(WebUser.class); Bukkit.getScheduler().runTaskAsynchronously(this, () -> { this.getLogger().info("Starting webserver..."); diff --git a/src/buttondevteam/website/page/AutoUpdatePage.java b/src/buttondevteam/website/page/AutoUpdatePage.java new file mode 100644 index 0000000..d064d05 --- /dev/null +++ b/src/buttondevteam/website/page/AutoUpdatePage.java @@ -0,0 +1,35 @@ +package buttondevteam.website.page; + +import java.io.IOException; +import java.util.stream.Collectors; + +import com.sun.net.httpserver.HttpExchange; + +import buttondevteam.website.io.IOHelper; +import buttondevteam.website.io.Response; + +public class AutoUpdatePage extends Page { + + @Override + public String GetName() { + return "autoupdate"; + } + + @Override + public Response handlePage(HttpExchange exchange) { + if (exchange.getRequestHeaders().containsKey("Expect") + && exchange.getRequestHeaders().get("Expect").contains("100-continue")) + try { + exchange.sendResponseHeaders(100, -1); + exchange.getResponseBody().close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + return new Response(200, + "Headers:\n" + exchange.getRequestHeaders().entrySet().stream() + .map(e -> e.getKey() + ": " + e.getValue().stream().collect(Collectors.joining(" "))) + .collect(Collectors.joining("\n")) + "\nPOST: " + IOHelper.GetPOST(exchange), + exchange); + } + +} diff --git a/src/buttondevteam/website/page/Page.java b/src/buttondevteam/website/page/Page.java index 4128e8f..0df2d80 100644 --- a/src/buttondevteam/website/page/Page.java +++ b/src/buttondevteam/website/page/Page.java @@ -22,7 +22,7 @@ public abstract class Page implements HttpHandler { if (exchange.getRequestURI().getPath().equals("/" + GetName())) IOHelper.SendResponse(handlePage(exchange)); else { - IOHelper.SendResponse(404, "404 Not found", exchange); + IOHelper.SendResponse(404, "404 Not found: " + exchange.getRequestURI().getPath(), exchange); } } catch (Exception e) { TBMCCoreAPI.SendException("Internal Server Error in ButtonWebsiteModule!", e); From 609f309cd1b6009eab714a7fa98f67b51cafd6a6 Mon Sep 17 00:00:00 2001 From: BuildTools Date: Wed, 28 Jun 2017 18:52:18 +0200 Subject: [PATCH 3/8] Removed autoupdate, addded deploy --- .travis.yml | 20 +++++++++++ deploy.sh | 11 ++++++ .../website/ButtonWebsiteModule.java | 1 - .../website/page/AutoUpdatePage.java | 35 ------------------- 4 files changed, 31 insertions(+), 36 deletions(-) create mode 100644 deploy.sh delete mode 100644 src/buttondevteam/website/page/AutoUpdatePage.java diff --git a/.travis.yml b/.travis.yml index 9bcf999..57bc7e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,23 @@ +cache: + directories: + - $HOME/.m2/repository/org/bukkit/craftbukkit +before_install: | # Wget BuildTools and run if cached folder not found + if [ ! -d "$HOME/.m2/repository/org/bukkit/craftbukkit/1.12-R0.1-SNAPSHOT" ]; then + wget -O BuildTools.jar https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar + java -jar BuildTools.jar --rev 1.12 + fi + language: java jdk: - oraclejdk8 +deploy: + # deploy develop to the staging environment + - provider: script + script: deploy.sh staging + on: + branch: dev + # deploy master to production + - provider: script + script: deploy.sh production + on: + branch: master diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..636c48d --- /dev/null +++ b/deploy.sh @@ -0,0 +1,11 @@ +#!/bin/sh +FILENAME=$(find target/ ! -name '*original*' -name '*.jar') +echo Found file: $FILENAME + +if [ $1 = 'production' ]; then +echo Production mode +echo $UPLOAD_KEY > upload_key +chmod 400 upload_key +yes | scp -B -i upload_key -o StrictHostKeyChecking=no $FILENAME travis@server.figytuna.com:/minecraft/main/plugins +fi + diff --git a/src/buttondevteam/website/ButtonWebsiteModule.java b/src/buttondevteam/website/ButtonWebsiteModule.java index 6d3ce4f..2a3f9ff 100644 --- a/src/buttondevteam/website/ButtonWebsiteModule.java +++ b/src/buttondevteam/website/ButtonWebsiteModule.java @@ -111,7 +111,6 @@ public class ButtonWebsiteModule extends JavaPlugin { @Override public void onEnable() { addPage(new IndexPage()); - addPage(new AutoUpdatePage()); addPage(new LoginPage()); addPage(new ProfilePage()); TBMCCoreAPI.RegisterUserClass(WebUser.class); diff --git a/src/buttondevteam/website/page/AutoUpdatePage.java b/src/buttondevteam/website/page/AutoUpdatePage.java deleted file mode 100644 index d064d05..0000000 --- a/src/buttondevteam/website/page/AutoUpdatePage.java +++ /dev/null @@ -1,35 +0,0 @@ -package buttondevteam.website.page; - -import java.io.IOException; -import java.util.stream.Collectors; - -import com.sun.net.httpserver.HttpExchange; - -import buttondevteam.website.io.IOHelper; -import buttondevteam.website.io.Response; - -public class AutoUpdatePage extends Page { - - @Override - public String GetName() { - return "autoupdate"; - } - - @Override - public Response handlePage(HttpExchange exchange) { - if (exchange.getRequestHeaders().containsKey("Expect") - && exchange.getRequestHeaders().get("Expect").contains("100-continue")) - try { - exchange.sendResponseHeaders(100, -1); - exchange.getResponseBody().close(); - } catch (IOException e1) { - e1.printStackTrace(); - } - return new Response(200, - "Headers:\n" + exchange.getRequestHeaders().entrySet().stream() - .map(e -> e.getKey() + ": " + e.getValue().stream().collect(Collectors.joining(" "))) - .collect(Collectors.joining("\n")) + "\nPOST: " + IOHelper.GetPOST(exchange), - exchange); - } - -} From 5be1f9f73b676cc3a38d23116171a742d8a746d3 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Wed, 28 Jun 2017 18:55:14 +0200 Subject: [PATCH 4/8] Ofc forgot to remove this --- .travis.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 57bc7e3..23eba2f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,3 @@ -cache: - directories: - - $HOME/.m2/repository/org/bukkit/craftbukkit -before_install: | # Wget BuildTools and run if cached folder not found - if [ ! -d "$HOME/.m2/repository/org/bukkit/craftbukkit/1.12-R0.1-SNAPSHOT" ]; then - wget -O BuildTools.jar https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar - java -jar BuildTools.jar --rev 1.12 - fi - language: java jdk: - oraclejdk8 From 91b64f9541f5f6d015791e6dfca4f2090a4bd6ad Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Wed, 28 Jun 2017 18:58:10 +0200 Subject: [PATCH 5/8] [Insert curse here] I've done this with ChunkArchive... --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 23eba2f..6f1ca80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,13 @@ jdk: deploy: # deploy develop to the staging environment - provider: script - script: deploy.sh staging + script: chmod +x deploy.sh && sh deploy.sh staging on: branch: dev + skip_cleanup: true # deploy master to production - provider: script - script: deploy.sh production + script: chmod +x deploy.sh && sh deploy.sh production on: branch: master + skip_cleanup: true From 812f70f5e1653853399fdf7fdb29bb2cfa31e7bc Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Wed, 28 Jun 2017 22:33:38 +0200 Subject: [PATCH 6/8] Added build notifications, fixes --- .gitignore | 2 + .travis.yml | 2 + src/buttondevteam/website/io/IOHelper.java | 26 +++++- .../website/page/BuildNotificationsPage.java | 86 +++++++++++++++++++ 4 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 src/buttondevteam/website/page/BuildNotificationsPage.java diff --git a/.gitignore b/.gitignore index 7d0ab85..d6ecfc9 100644 --- a/.gitignore +++ b/.gitignore @@ -218,3 +218,5 @@ TheButtonAutoFlair/out/artifacts/Autoflair/Autoflair.jar *.name .idea/compiler.xml *.xml + +upload_key diff --git a/.travis.yml b/.travis.yml index 6f1ca80..c822f5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,3 +14,5 @@ deploy: on: branch: master skip_cleanup: true +notifications: + webhooks: https://server.figytuna.com:8080/build_notifications diff --git a/src/buttondevteam/website/io/IOHelper.java b/src/buttondevteam/website/io/IOHelper.java index faae178..ba0ba46 100644 --- a/src/buttondevteam/website/io/IOHelper.java +++ b/src/buttondevteam/website/io/IOHelper.java @@ -76,13 +76,15 @@ public class IOHelper { public static void LoginUser(HttpExchange exchange, WebUser user) { Bukkit.getLogger().fine("Logging in user: " + user); user.sessionID().set(UUID.randomUUID()); + user.save(); new Cookies(2).add(new Cookie("user_id", user.getUUID() + "")) - .add(new Cookie("session_id", user.sessionID().get().toString())).SendHeaders(exchange); + .add(new Cookie("session_id", user.sessionID().getOrDefault(null).toString())).SendHeaders(exchange); Bukkit.getLogger().fine("Logged in user."); } public static void LogoutUser(HttpExchange exchange, WebUser user) { user.sessionID().set(new UUID(0, 0)); + user.save(); SendLogoutHeaders(exchange); } @@ -125,7 +127,7 @@ public class IOHelper { } /** - * Get logged in user. It may also send logout headers if the cookies are invalid, or login headers to keep the user logged in. + * Get logged in user. It may also send logout headers if the cookies are invalid, or login headers to keep the user logged in. Make sure to save the user data. * * @param exchange * @return The logged in user or null if not logged in. @@ -137,7 +139,7 @@ public class IOHelper { return null; WebUser user = ChromaGamerBase.getUser(cookies.get("user_id").getValue(), WebUser.class); if (user != null && cookies.get("session_id") != null - && cookies.get("session_id").getValue().equals(user.sessionID().get())) { + && cookies.get("session_id").getValue().equals(user.sessionID().getOrDefault(null))) { if (cookies.getExpireTimeParsed().minusYears(1).isBefore(ZonedDateTime.now(ZoneId.of("GMT")))) LoginUser(exchange, user); return user; @@ -174,4 +176,22 @@ public class IOHelper { } return result; } + + public static HashMap GetPOSTKeyValues(HttpExchange exchange) { + try { + String[] content = GetPOST(exchange).split("\\&"); + HashMap vars = new HashMap<>(); + for (String var : content) { + String[] spl = var.split("\\="); + if (spl.length == 1) + vars.put(spl[0], ""); + else + vars.put(spl[0], spl[1]); + } + return vars; + } catch (Exception e) { + e.printStackTrace(); + return new HashMap<>(); + } + } } diff --git a/src/buttondevteam/website/page/BuildNotificationsPage.java b/src/buttondevteam/website/page/BuildNotificationsPage.java new file mode 100644 index 0000000..ddd5864 --- /dev/null +++ b/src/buttondevteam/website/page/BuildNotificationsPage.java @@ -0,0 +1,86 @@ +package buttondevteam.website.page; + +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.function.Supplier; + +import com.google.gson.*; +import com.sun.net.httpserver.HttpExchange; + +import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.website.io.IOHelper; +import buttondevteam.website.io.Response; + +public class BuildNotificationsPage extends Page { + + @Override + public String GetName() { + return "build_notifications"; + } + + private static final String signature = ((Supplier) () -> { + try { + return fromString(TBMCCoreAPI.DownloadString("https://api.travis-ci.org/config"), + "config.notifications.webhook.public_key").getAsString(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }).get(); + + @Override + public Response handlePage(HttpExchange exchange) { + HashMap post = IOHelper.GetPOSTKeyValues(exchange); + try { + final List signatures = exchange.getRequestHeaders().get("Signature"); + if (signatures.size() > 0 && post.containsKey("payload") + && verifySignature(Base64.getDecoder().decode(post.get("payload")), + Base64.getDecoder().decode(signatures.get(0)), signature)) { + // TODO: Send event + return new Response(200, "All right", exchange); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return new Response(400, "Verification failed", exchange); + } + + // Method for signature verification that initializes with the Public Key, + // updates the data to be verified and then verifies them using the signature + private boolean verifySignature(byte[] data, byte[] signature, String keystr) throws Exception { + Signature sig = Signature.getInstance("SHA1withRSA"); + sig.initVerify(getPublic(keystr)); + sig.update(data); + + return sig.verify(signature); + } + + // Method to retrieve the Public Key from a file + public PublicKey getPublic(String keystr) throws Exception { + byte[] keyBytes = Base64.getDecoder().decode(keystr); + X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePublic(spec); + } + + public static JsonElement fromString(String json, String path) throws JsonSyntaxException { + JsonObject obj = new GsonBuilder().create().fromJson(json, JsonObject.class); + String[] seg = path.split("\\."); + for (String element : seg) { + if (obj != null) { + JsonElement ele = obj.get(element); + if (!ele.isJsonObject()) + return ele; + else + obj = ele.getAsJsonObject(); + } else { + return null; + } + } + return obj; + } +} From 2aa02a34cbc8e621c457109f0f0b3114a7bd69e6 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Fri, 30 Jun 2017 18:21:03 +0200 Subject: [PATCH 7/8] Applied changes --- src/buttondevteam/website/WebUser.java | 2 +- src/buttondevteam/website/io/IOHelper.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/buttondevteam/website/WebUser.java b/src/buttondevteam/website/WebUser.java index 859e5ab..93feaee 100644 --- a/src/buttondevteam/website/WebUser.java +++ b/src/buttondevteam/website/WebUser.java @@ -17,6 +17,6 @@ public class WebUser extends ChromaGamerBase { } public PlayerData sessionID() { - return data(); + return data(null); } } diff --git a/src/buttondevteam/website/io/IOHelper.java b/src/buttondevteam/website/io/IOHelper.java index ba0ba46..bc9d5c9 100644 --- a/src/buttondevteam/website/io/IOHelper.java +++ b/src/buttondevteam/website/io/IOHelper.java @@ -78,7 +78,7 @@ public class IOHelper { user.sessionID().set(UUID.randomUUID()); user.save(); new Cookies(2).add(new Cookie("user_id", user.getUUID() + "")) - .add(new Cookie("session_id", user.sessionID().getOrDefault(null).toString())).SendHeaders(exchange); + .add(new Cookie("session_id", user.sessionID().get().toString())).SendHeaders(exchange); Bukkit.getLogger().fine("Logged in user."); } @@ -139,7 +139,7 @@ public class IOHelper { return null; WebUser user = ChromaGamerBase.getUser(cookies.get("user_id").getValue(), WebUser.class); if (user != null && cookies.get("session_id") != null - && cookies.get("session_id").getValue().equals(user.sessionID().getOrDefault(null))) { + && cookies.get("session_id").getValue().equals(user.sessionID().get())) { if (cookies.getExpireTimeParsed().minusYears(1).isBefore(ZonedDateTime.now(ZoneId.of("GMT")))) LoginUser(exchange, user); return user; From f0aba479e3beb681b6531599fc2fa12e67784efb Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Fri, 30 Jun 2017 23:58:36 +0200 Subject: [PATCH 8/8] This should work - Fixed build notifications Although I can't actually test it, just roughly --- .../website/ButtonWebsiteModule.java | 1 + src/buttondevteam/website/io/IOHelper.java | 2 +- .../website/page/BuildNotificationsPage.java | 26 +++++++++++++------ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/buttondevteam/website/ButtonWebsiteModule.java b/src/buttondevteam/website/ButtonWebsiteModule.java index 2a3f9ff..3db487d 100644 --- a/src/buttondevteam/website/ButtonWebsiteModule.java +++ b/src/buttondevteam/website/ButtonWebsiteModule.java @@ -113,6 +113,7 @@ public class ButtonWebsiteModule extends JavaPlugin { addPage(new IndexPage()); addPage(new LoginPage()); addPage(new ProfilePage()); + addPage(new BuildNotificationsPage()); TBMCCoreAPI.RegisterUserClass(WebUser.class); Bukkit.getScheduler().runTaskAsynchronously(this, () -> { this.getLogger().info("Starting webserver..."); diff --git a/src/buttondevteam/website/io/IOHelper.java b/src/buttondevteam/website/io/IOHelper.java index bc9d5c9..15a7432 100644 --- a/src/buttondevteam/website/io/IOHelper.java +++ b/src/buttondevteam/website/io/IOHelper.java @@ -186,7 +186,7 @@ public class IOHelper { if (spl.length == 1) vars.put(spl[0], ""); else - vars.put(spl[0], spl[1]); + vars.put(spl[0], URLDecoder.decode(spl[1], "utf-8")); } return vars; } catch (Exception e) { diff --git a/src/buttondevteam/website/page/BuildNotificationsPage.java b/src/buttondevteam/website/page/BuildNotificationsPage.java index ddd5864..fff65a1 100644 --- a/src/buttondevteam/website/page/BuildNotificationsPage.java +++ b/src/buttondevteam/website/page/BuildNotificationsPage.java @@ -1,5 +1,6 @@ package buttondevteam.website.page; +import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.PublicKey; import java.security.Signature; @@ -9,9 +10,12 @@ import java.util.HashMap; import java.util.List; import java.util.function.Supplier; +import org.bukkit.Bukkit; + import com.google.gson.*; import com.sun.net.httpserver.HttpExchange; +import buttondevteam.lib.PluginUpdater; import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.website.io.IOHelper; import buttondevteam.website.io.Response; @@ -23,10 +27,13 @@ public class BuildNotificationsPage extends Page { return "build_notifications"; } - private static final String signature = ((Supplier) () -> { + private static final Gson gson = new Gson(); + + private static final String publickey = ((Supplier) () -> { try { return fromString(TBMCCoreAPI.DownloadString("https://api.travis-ci.org/config"), - "config.notifications.webhook.public_key").getAsString(); + "config.notifications.webhook.public_key").getAsString().replace("-----BEGIN PUBLIC KEY-----", "") + .replaceAll("\n", "").replace("-----END PUBLIC KEY-----", ""); } catch (Exception e) { throw new RuntimeException(e); } @@ -37,14 +44,17 @@ public class BuildNotificationsPage extends Page { HashMap post = IOHelper.GetPOSTKeyValues(exchange); try { final List signatures = exchange.getRequestHeaders().get("Signature"); - if (signatures.size() > 0 && post.containsKey("payload") - && verifySignature(Base64.getDecoder().decode(post.get("payload")), - Base64.getDecoder().decode(signatures.get(0)), signature)) { - // TODO: Send event + final String payload = post.get("payload"); + if (signatures != null && signatures.size() > 0 && post.containsKey("payload") + && verifySignature(payload.getBytes(StandardCharsets.UTF_8), + Base64.getDecoder().decode(signatures.get(0)), publickey)) { + Bukkit.getPluginManager() + .callEvent(new PluginUpdater.UpdatedEvent(gson.fromJson(payload, JsonObject.class))); return new Response(200, "All right", exchange); } } catch (Exception e) { - throw new RuntimeException(e); + return new Response(400, + "Invalid data, error: " + e + " If you're messing with this, stop messing with this.", exchange); // Blame the user } return new Response(400, "Verification failed", exchange); } @@ -68,7 +78,7 @@ public class BuildNotificationsPage extends Page { } public static JsonElement fromString(String json, String path) throws JsonSyntaxException { - JsonObject obj = new GsonBuilder().create().fromJson(json, JsonObject.class); + JsonObject obj = gson.fromJson(json, JsonObject.class); String[] seg = path.split("\\."); for (String element : seg) { if (obj != null) {