parent
96b200c379
commit
2b2a5fac50
6 changed files with 106 additions and 41 deletions
|
@ -4,3 +4,6 @@ version: 4.0
|
||||||
author: NorbiPeti
|
author: NorbiPeti
|
||||||
depend:
|
depend:
|
||||||
- ButtonCore
|
- ButtonCore
|
||||||
|
commands:
|
||||||
|
login:
|
||||||
|
aliases: [web, weblogin, website]
|
|
@ -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(
|
||||||
|
|
25
src/buttondevteam/website/LoginCommand.java
Normal file
25
src/buttondevteam/website/LoginCommand.java
Normal 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." //
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue