diff --git a/plugin.yml b/plugin.yml index a290ca9..ecef1ab 100644 --- a/plugin.yml +++ b/plugin.yml @@ -3,4 +3,7 @@ main: buttondevteam.website.ButtonWebsiteModule version: 4.0 author: NorbiPeti depend: -- ButtonCore \ No newline at end of file +- ButtonCore +commands: + login: + aliases: [web, weblogin, website] \ No newline at end of file diff --git a/src/buttondevteam/website/ButtonWebsiteModule.java b/src/buttondevteam/website/ButtonWebsiteModule.java index 0f1e1c4..c1f022a 100644 --- a/src/buttondevteam/website/ButtonWebsiteModule.java +++ b/src/buttondevteam/website/ButtonWebsiteModule.java @@ -1,5 +1,18 @@ package buttondevteam.website; +import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.lib.chat.TBMCChatAPI; +import buttondevteam.website.io.IOHelper; +import buttondevteam.website.page.*; +import com.sun.net.httpserver.*; +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 org.bukkit.plugin.java.JavaPlugin; + +import javax.net.ssl.*; import java.io.*; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -9,33 +22,13 @@ 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; -import javax.net.ssl.*; -import java.security.cert.Certificate; - -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 org.bukkit.plugin.java.JavaPlugin; - -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -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 buttondevteam.lib.TBMCCoreAPI; -import buttondevteam.website.io.IOHelper; -import buttondevteam.website.page.*; - public class ButtonWebsiteModule extends JavaPlugin { public static final int PORT = 443; private static HttpsServer server; @@ -127,6 +120,7 @@ public class ButtonWebsiteModule extends JavaPlugin { addPage(new BuildNotificationsPage()); addPage(new BridgePage()); TBMCCoreAPI.RegisterUserClass(WebUser.class); + TBMCChatAPI.AddCommand(this, LoginCommand.class); Bukkit.getScheduler().runTaskAsynchronously(this, () -> { this.getLogger().info("Starting webserver..."); server.setExecutor( diff --git a/src/buttondevteam/website/LoginCommand.java b/src/buttondevteam/website/LoginCommand.java new file mode 100644 index 0000000..6fe2f27 --- /dev/null +++ b/src/buttondevteam/website/LoginCommand.java @@ -0,0 +1,25 @@ +package buttondevteam.website; + +import buttondevteam.lib.chat.PlayerCommandBase; +import buttondevteam.website.page.LoginPage; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class LoginCommand extends PlayerCommandBase { + @Override //TODO: Ask about linking already existing accounts, to prevent linking someone else's + public boolean OnCommand(Player player, String s, String[] strings) { + String state = LoginPage.generateState("minecraft", player.getUniqueId().toString()).toString(); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + player.getName() + " [\"\",{\"text\":\"Please \",\"color\":\"aqua\"},{\"text\":\"Click Here\",\"color\":\"aqua\",\"bold\":true,\"underlined\":true,\"clickEvent\":{\"action\":\"open_url\",\"value\":\"https://server.figytuna.com/login?type=minecraft&state=" + state + "\"}},{\"text\":\" to log in to our site using your Minecraft account.\",\"color\":\"aqua\",\"bold\":false,\"underlined\":false}]"); + return true; + } + + @Override + public String[] GetHelpText(String s) { + return new String[]{// + "ยง6---- Login ----", // + "This command allows you to log in to our website using your Minecraft account.", // + "If you are already logged in to the site, you can connect your MC account to it.", // + "This is good for getting Minecraft rewards if you're a patreon for example." // + }; + } +} diff --git a/src/buttondevteam/website/io/Cookies.java b/src/buttondevteam/website/io/Cookies.java index b9451dd..089cc67 100644 --- a/src/buttondevteam/website/io/Cookies.java +++ b/src/buttondevteam/website/io/Cookies.java @@ -1,5 +1,7 @@ package buttondevteam.website.io; +import com.sun.net.httpserver.HttpExchange; + import java.time.LocalDateTime; import java.time.Period; import java.time.ZoneId; @@ -7,8 +9,6 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; -import com.sun.net.httpserver.HttpExchange; - public class Cookies extends HashMap { private static final long serialVersionUID = -328053564170765287L; @@ -30,7 +30,7 @@ public class Cookies extends HashMap { this.expiretime = ZonedDateTime.now(ZoneId.of("GMT")).format(DateTimeFormatter.RFC_1123_DATE_TIME); } - public void SendHeaders(HttpExchange exchange) { + public void AddHeaders(HttpExchange exchange) { for (Entry item : entrySet()) exchange.getResponseHeaders().add("Set-Cookie", item.getKey() + "=" + item.getValue().getValue() + "; expires=" + expiretime); diff --git a/src/buttondevteam/website/io/IOHelper.java b/src/buttondevteam/website/io/IOHelper.java index 611711d..0b49bad 100644 --- a/src/buttondevteam/website/io/IOHelper.java +++ b/src/buttondevteam/website/io/IOHelper.java @@ -1,5 +1,14 @@ package buttondevteam.website.io; +import buttondevteam.lib.player.ChromaGamerBase; +import buttondevteam.website.WebUser; +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; @@ -12,16 +21,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.UUID; -import org.apache.commons.io.IOUtils; -import org.bukkit.Bukkit; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.sun.net.httpserver.HttpExchange; - -import buttondevteam.lib.player.ChromaGamerBase; -import buttondevteam.website.WebUser; public class IOHelper { public static void SendResponse(Response resp) throws IOException { @@ -90,7 +89,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().get().toString())).SendHeaders(exchange); + .add(new Cookie("session_id", user.sessionID().get().toString())).AddHeaders(exchange); Bukkit.getLogger().fine("Logged in user."); } @@ -103,7 +102,7 @@ public class IOHelper { private static void SendLogoutHeaders(HttpExchange exchange) { String expiretime = "Sat, 19 Mar 2016 23:33:00 GMT"; new Cookies(expiretime).add(new Cookie("user_id", "del")).add(new Cookie("session_id", "del")) - .SendHeaders(exchange); + .AddHeaders(exchange); } public static Response Redirect(String url, HttpExchange exchange) throws IOException { diff --git a/src/buttondevteam/website/page/LoginPage.java b/src/buttondevteam/website/page/LoginPage.java index 3214d65..871c33a 100644 --- a/src/buttondevteam/website/page/LoginPage.java +++ b/src/buttondevteam/website/page/LoginPage.java @@ -1,11 +1,15 @@ package buttondevteam.website.page; -import java.util.Map; - -import com.sun.net.httpserver.HttpExchange; - +import buttondevteam.lib.player.TBMCPlayer; +import buttondevteam.website.WebUser; import buttondevteam.website.io.IOHelper; import buttondevteam.website.io.Response; +import com.google.common.collect.HashBiMap; +import com.sun.net.httpserver.HttpExchange; + +import java.io.IOException; +import java.util.Map; +import java.util.UUID; public class LoginPage extends Page { @@ -23,7 +27,47 @@ public class LoginPage extends Page { /*if (type.equalsIgnoreCase("getstate")) return new Response(200, "TODO", exchange); // TO!DO: Store and return a random state and check on other types String state = q.get("state"), code = q.get("code");*/ + Response nope = new Response(401, "401 Nope", exchange); + if (type.equalsIgnoreCase("minecraft")) { + //In case of Minecraft, we don't need the full OAuth2 flow, we only need to ensure the state matches + if (q.containsKey("state")) { + UUID state = UUID.fromString(q.get("state")); + if (!states.containsKey(state)) + return nope; + String[] folder_id = states.get(state).split("-"); + if (!folder_id[0].equalsIgnoreCase(type)) //TODO: Use for other OAuth stuff as well + return nope; + TBMCPlayer cp = TBMCPlayer.getPlayer(UUID.fromString(folder_id[1]), TBMCPlayer.class); + WebUser wu = cp.getAs(WebUser.class); + if (wu == null) //getAs return Optional? + cp.connectWith(wu = WebUser.getUser(UUID.randomUUID().toString(), WebUser.class)); //Create new user with random UUID + IOHelper.LoginUser(exchange, wu); + states.remove(state); + try { + return IOHelper.Redirect("https://tbmcplugins.github.io/", exchange); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } return new Response(200, "", exchange); } + /** + * Value: Folder-ID + */ + private static final HashBiMap states = HashBiMap.create(); + + /** + * Generates a temporary state data that can be used to authenticate a user. + * + * @param type The service type. Only used to separate in temporary storage. + * @param id The user id in the service. Only used to separate in temporary storage. + * @return A unique state that can be used to authenticate a user. + */ + public static UUID generateState(String type, String id) { + UUID state = UUID.randomUUID(); + states.forcePut(state, type + "-" + id); //Replace existing for an user + return state; + } }