Added MC login, needs testing

#1
This commit is contained in:
Norbi Peti 2018-03-24 23:37:27 +01:00
parent 96b200c379
commit 2b2a5fac50
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
6 changed files with 106 additions and 41 deletions

View file

@ -4,3 +4,6 @@ version: 4.0
author: NorbiPeti author: NorbiPeti
depend: depend:
- ButtonCore - ButtonCore
commands:
login:
aliases: [web, weblogin, website]

View file

@ -1,5 +1,18 @@
package buttondevteam.website; 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.io.*;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
@ -9,33 +22,13 @@ import java.security.KeyPair;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.Security; import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.util.Calendar; import java.util.Calendar;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; 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 class ButtonWebsiteModule extends JavaPlugin {
public static final int PORT = 443; public static final int PORT = 443;
private static HttpsServer server; private static HttpsServer server;
@ -127,6 +120,7 @@ public class ButtonWebsiteModule extends JavaPlugin {
addPage(new BuildNotificationsPage()); addPage(new BuildNotificationsPage());
addPage(new BridgePage()); addPage(new BridgePage());
TBMCCoreAPI.RegisterUserClass(WebUser.class); TBMCCoreAPI.RegisterUserClass(WebUser.class);
TBMCChatAPI.AddCommand(this, LoginCommand.class);
Bukkit.getScheduler().runTaskAsynchronously(this, () -> { Bukkit.getScheduler().runTaskAsynchronously(this, () -> {
this.getLogger().info("Starting webserver..."); this.getLogger().info("Starting webserver...");
server.setExecutor( server.setExecutor(

View file

@ -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." //
};
}
}

View file

@ -1,5 +1,7 @@
package buttondevteam.website.io; package buttondevteam.website.io;
import com.sun.net.httpserver.HttpExchange;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.Period; import java.time.Period;
import java.time.ZoneId; import java.time.ZoneId;
@ -7,8 +9,6 @@ import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.HashMap; import java.util.HashMap;
import com.sun.net.httpserver.HttpExchange;
public class Cookies extends HashMap<String, Cookie> { public class Cookies extends HashMap<String, Cookie> {
private static final long serialVersionUID = -328053564170765287L; private static final long serialVersionUID = -328053564170765287L;
@ -30,7 +30,7 @@ public class Cookies extends HashMap<String, Cookie> {
this.expiretime = ZonedDateTime.now(ZoneId.of("GMT")).format(DateTimeFormatter.RFC_1123_DATE_TIME); 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<String, Cookie> item : entrySet()) for (Entry<String, Cookie> item : entrySet())
exchange.getResponseHeaders().add("Set-Cookie", exchange.getResponseHeaders().add("Set-Cookie",
item.getKey() + "=" + item.getValue().getValue() + "; expires=" + expiretime); item.getKey() + "=" + item.getValue().getValue() + "; expires=" + expiretime);

View file

@ -1,5 +1,14 @@
package buttondevteam.website.io; 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.BufferedOutputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
@ -12,16 +21,6 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.UUID; 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 class IOHelper {
public static void SendResponse(Response resp) throws IOException { public static void SendResponse(Response resp) throws IOException {
@ -90,7 +89,7 @@ public class IOHelper {
user.sessionID().set(UUID.randomUUID()); user.sessionID().set(UUID.randomUUID());
user.save(); user.save();
new Cookies(2).add(new Cookie("user_id", user.getUUID() + "")) 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."); Bukkit.getLogger().fine("Logged in user.");
} }
@ -103,7 +102,7 @@ public class IOHelper {
private static void SendLogoutHeaders(HttpExchange exchange) { private static void SendLogoutHeaders(HttpExchange exchange) {
String expiretime = "Sat, 19 Mar 2016 23:33:00 GMT"; 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")) 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 { public static Response Redirect(String url, HttpExchange exchange) throws IOException {

View file

@ -1,11 +1,15 @@
package buttondevteam.website.page; package buttondevteam.website.page;
import java.util.Map; import buttondevteam.lib.player.TBMCPlayer;
import buttondevteam.website.WebUser;
import com.sun.net.httpserver.HttpExchange;
import buttondevteam.website.io.IOHelper; import buttondevteam.website.io.IOHelper;
import buttondevteam.website.io.Response; 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 { public class LoginPage extends Page {
@ -23,7 +27,47 @@ public class LoginPage extends Page {
/*if (type.equalsIgnoreCase("getstate")) /*if (type.equalsIgnoreCase("getstate"))
return new Response(200, "TODO", exchange); // TO!DO: Store and return a random state and check on other types 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");*/ 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); return new Response(200, "", exchange);
} }
/**
* Value: Folder-ID
*/
private static final HashBiMap<UUID, String> 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;
}
} }