From 812f70f5e1653853399fdf7fdb29bb2cfa31e7bc Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Wed, 28 Jun 2017 22:33:38 +0200 Subject: [PATCH] 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; + } +}