From 113aa50d87d5c64208df004f6aeadf151c6e5c41 Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 27 Apr 2018 21:28:46 -0400 Subject: [PATCH] Websocket auth up and running --- .../main/java/net/simon987/server/Main.java | 44 ++++++++++++++++++- .../server/crypto/RandomStringGenerator.java | 8 ++-- .../java/net/simon987/server/user/User.java | 23 +++++++++- .../net/simon987/server/user/UserManager.java | 40 +++++++++++++++++ .../server/websocket/CodeRequestHandler.java | 2 +- .../server/websocket/CodeUploadHandler.java | 2 +- .../server/websocket/DebugCommandHandler.java | 2 +- .../server/websocket/FloppyHandler.java | 2 +- .../server/websocket/KeypressHandler.java | 2 +- .../simon987/server/websocket/OnlineUser.java | 20 --------- .../server/websocket/SocketServer.java | 40 +++++++++++++++-- .../websocket/UserInfoRequestHandler.java | 2 +- Server/src/main/resources/config.properties | 10 ++--- Server/src/main/resources/static/js/mar.js | 2 +- Server/src/main/typescript/GameClient.ts | 2 +- 15 files changed, 157 insertions(+), 44 deletions(-) diff --git a/Server/src/main/java/net/simon987/server/Main.java b/Server/src/main/java/net/simon987/server/Main.java index 9aadfa3..d8236cf 100644 --- a/Server/src/main/java/net/simon987/server/Main.java +++ b/Server/src/main/java/net/simon987/server/Main.java @@ -6,6 +6,7 @@ import net.simon987.server.web.AlertMessage; import net.simon987.server.web.AlertType; import net.simon987.server.websocket.SocketServer; import org.apache.velocity.app.VelocityEngine; +import org.json.simple.JSONObject; import spark.ModelAndView; import spark.Spark; import spark.template.velocity.VelocityTemplateEngine; @@ -153,11 +154,52 @@ public class Main { request.session().attribute("messages", messages); } - response.redirect("/account"); return null; }); + Spark.get("/server_info", (request, response) -> { + + //TODO put that in a constructor somewhere + String address; + if (GameServer.INSTANCE.getConfig().getInt("use_ssl") == 0) { + address = "ws://" + + GameServer.INSTANCE.getConfig().getString("mar_address") + ":" + + GameServer.INSTANCE.getConfig().getString("mar_port") + "/socket"; + } else { + address = "wss://" + + GameServer.INSTANCE.getConfig().getString("mar_address") + ":" + + GameServer.INSTANCE.getConfig().getString("mar_port") + "/socket"; + } + String serverName = GameServer.INSTANCE.getConfig().getString("server_name"); + int tickLength = GameServer.INSTANCE.getConfig().getInt("tick_length"); + + JSONObject json = new JSONObject(); + + String username = request.session().attribute("username"); + + if (username != null) { + String token = GameServer.INSTANCE.getUserManager().generateAndGetToken(username); + + json.put("token", token); + json.put("username", username); + + } else { + + json.put("token", "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + json.put("username", "guest"); + } + + json.put("address", address); + json.put("serverName", serverName); + json.put("tickLength", tickLength); + + response.header("Content-Type", "application/json"); + + return json.toJSONString(); + + }); + Spark.after((request, response) -> response.header("Content-Encoding", "gzip")); } diff --git a/Server/src/main/java/net/simon987/server/crypto/RandomStringGenerator.java b/Server/src/main/java/net/simon987/server/crypto/RandomStringGenerator.java index f474d5b..21fc1df 100644 --- a/Server/src/main/java/net/simon987/server/crypto/RandomStringGenerator.java +++ b/Server/src/main/java/net/simon987/server/crypto/RandomStringGenerator.java @@ -23,10 +23,10 @@ public class RandomStringGenerator { return new String(buf); } - public static final String UPPER_ALPHA_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - public static final String LOWER_ALPHA_CHARSET = UPPER_ALPHA_CHARSET.toLowerCase(Locale.ROOT); - public static final String NUMERIC_CHARSET = "0123456789"; - public static final String ALPHANUMERIC_CHARSET = UPPER_ALPHA_CHARSET + LOWER_ALPHA_CHARSET + NUMERIC_CHARSET; + static final String UPPER_ALPHA_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + static final String LOWER_ALPHA_CHARSET = UPPER_ALPHA_CHARSET.toLowerCase(Locale.ROOT); + static final String NUMERIC_CHARSET = "0123456789"; + static final String ALPHANUMERIC_CHARSET = UPPER_ALPHA_CHARSET + LOWER_ALPHA_CHARSET + NUMERIC_CHARSET; private final Random random; diff --git a/Server/src/main/java/net/simon987/server/user/User.java b/Server/src/main/java/net/simon987/server/user/User.java index 2470605..21345a8 100755 --- a/Server/src/main/java/net/simon987/server/user/User.java +++ b/Server/src/main/java/net/simon987/server/user/User.java @@ -19,12 +19,14 @@ public class User implements MongoSerialisable { private String userCode; private String password; + private String accessToken; private CPU cpu; private ControllableUnit controlledUnit; - private boolean guest; + private boolean guest = false; + private boolean moderator = false; public User() throws CancelledException { GameEvent event = new UserCreationEvent(this); @@ -33,7 +35,6 @@ public class User implements MongoSerialisable { throw new CancelledException(); } - } public User(ControllableUnit unit) { @@ -51,6 +52,7 @@ public class User implements MongoSerialisable { dbObject.put("controlledUnit", controlledUnit.getObjectId()); dbObject.put("cpu", cpu.mongoSerialise()); dbObject.put("password", password); + dbObject.put("moderator", moderator); return dbObject; @@ -62,6 +64,7 @@ public class User implements MongoSerialisable { user.username = (String) obj.get("username"); user.userCode = (String) obj.get("code"); user.password = (String) obj.get("password"); + user.moderator = (boolean) obj.get("moderator"); user.getControlledUnit().setParent(user); @@ -114,4 +117,20 @@ public class User implements MongoSerialisable { public void setPassword(String password) { this.password = password; } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public boolean isModerator() { + return moderator; + } + + public void setModerator(boolean moderator) { + this.moderator = moderator; + } } diff --git a/Server/src/main/java/net/simon987/server/user/UserManager.java b/Server/src/main/java/net/simon987/server/user/UserManager.java index 6b1888b..6e3e743 100644 --- a/Server/src/main/java/net/simon987/server/user/UserManager.java +++ b/Server/src/main/java/net/simon987/server/user/UserManager.java @@ -3,6 +3,8 @@ package net.simon987.server.user; import com.mongodb.*; import net.simon987.server.GameServer; import net.simon987.server.assembly.exception.CancelledException; +import net.simon987.server.crypto.RandomStringGenerator; +import net.simon987.server.logging.LogManager; import org.springframework.security.crypto.bcrypt.BCrypt; import java.util.ArrayList; @@ -91,4 +93,42 @@ public class UserManager { userCollection.save(user.mongoSerialise()); //Save new password immediately } + + /** + * Generate and save a access token for websocket auth + * + * @param username Username + * @return The generated token + */ + public String generateAndGetToken(String username) { + + User user = GameServer.INSTANCE.getGameUniverse().getUser(username); + + if (user == null) { + return null; + } + + RandomStringGenerator generator = new RandomStringGenerator(128); + String token = generator.nextString(); + + user.setAccessToken(token); + + LogManager.LOGGER.fine("(Web) Generated access token for " + username); + + return token; + } + + public User validateAuthToken(String token) { + + for (User user : GameServer.INSTANCE.getGameUniverse().getUsers()) { + + if (user.getAccessToken().equals(token)) { + user.setAccessToken(""); + + return user; + } + } + + return null; + } } diff --git a/Server/src/main/java/net/simon987/server/websocket/CodeRequestHandler.java b/Server/src/main/java/net/simon987/server/websocket/CodeRequestHandler.java index ed4eacf..4aa6cdc 100644 --- a/Server/src/main/java/net/simon987/server/websocket/CodeRequestHandler.java +++ b/Server/src/main/java/net/simon987/server/websocket/CodeRequestHandler.java @@ -13,7 +13,7 @@ public class CodeRequestHandler implements MessageHandler { LogManager.LOGGER.fine("(WS) Code request from " + user.getUser().getUsername()); - if (user.isGuest()) { + if (user.getUser().isGuest()) { JSONObject response = new JSONObject(); diff --git a/Server/src/main/java/net/simon987/server/websocket/CodeUploadHandler.java b/Server/src/main/java/net/simon987/server/websocket/CodeUploadHandler.java index 7aeaeb9..4558cd7 100644 --- a/Server/src/main/java/net/simon987/server/websocket/CodeUploadHandler.java +++ b/Server/src/main/java/net/simon987/server/websocket/CodeUploadHandler.java @@ -16,7 +16,7 @@ public class CodeUploadHandler implements MessageHandler { LogManager.LOGGER.fine("(WS) Code upload from " + user.getUser().getUsername()); - if (user.isGuest()) { + if (user.getUser().isGuest()) { //Ignore } else { diff --git a/Server/src/main/java/net/simon987/server/websocket/DebugCommandHandler.java b/Server/src/main/java/net/simon987/server/websocket/DebugCommandHandler.java index 0dd1ba6..c083c28 100644 --- a/Server/src/main/java/net/simon987/server/websocket/DebugCommandHandler.java +++ b/Server/src/main/java/net/simon987/server/websocket/DebugCommandHandler.java @@ -10,7 +10,7 @@ public class DebugCommandHandler implements MessageHandler { public void handle(OnlineUser user, JSONObject json) { - if (json.get("t").equals("debug") && user.isModerator()) { + if (json.get("t").equals("debug") && user.getUser().isModerator()) { DebugCommandEvent e = new DebugCommandEvent(json, user); GameServer.INSTANCE.getEventDispatcher().dispatch(e); //Ignore cancellation diff --git a/Server/src/main/java/net/simon987/server/websocket/FloppyHandler.java b/Server/src/main/java/net/simon987/server/websocket/FloppyHandler.java index 651d582..07d1970 100644 --- a/Server/src/main/java/net/simon987/server/websocket/FloppyHandler.java +++ b/Server/src/main/java/net/simon987/server/websocket/FloppyHandler.java @@ -15,7 +15,7 @@ public class FloppyHandler implements MessageHandler { LogManager.LOGGER.fine("(WS) Floppy download request from " + user.getUser().getUsername()); - if (user.isGuest()) { + if (user.getUser().isGuest()) { return; } diff --git a/Server/src/main/java/net/simon987/server/websocket/KeypressHandler.java b/Server/src/main/java/net/simon987/server/websocket/KeypressHandler.java index 390f6be..17e2cff 100644 --- a/Server/src/main/java/net/simon987/server/websocket/KeypressHandler.java +++ b/Server/src/main/java/net/simon987/server/websocket/KeypressHandler.java @@ -10,7 +10,7 @@ public class KeypressHandler implements MessageHandler { @Override public void handle(OnlineUser user, JSONObject json) { - if (!user.isGuest()) { + if (!user.getUser().isGuest()) { if (json.get("t").equals("k")) { //LogManager.LOGGER.fine("(WS) Received keypress"); diff --git a/Server/src/main/java/net/simon987/server/websocket/OnlineUser.java b/Server/src/main/java/net/simon987/server/websocket/OnlineUser.java index 0c2d762..de0cf96 100644 --- a/Server/src/main/java/net/simon987/server/websocket/OnlineUser.java +++ b/Server/src/main/java/net/simon987/server/websocket/OnlineUser.java @@ -10,10 +10,6 @@ public class OnlineUser { private Session webSocket; - private boolean guest; - - private boolean moderator; - /** * Associated game user (if authenticated) */ @@ -44,20 +40,4 @@ public class OnlineUser { this.authenticated = authenticated; } - public void setGuest(boolean guest) { - this.guest = guest; - user.setGuest(guest); - } - - public boolean isGuest() { - return guest; - } - - public boolean isModerator() { - return moderator; - } - - public void setModerator(boolean moderator) { - this.moderator = moderator; - } } diff --git a/Server/src/main/java/net/simon987/server/websocket/SocketServer.java b/Server/src/main/java/net/simon987/server/websocket/SocketServer.java index 01f4edb..4002276 100644 --- a/Server/src/main/java/net/simon987/server/websocket/SocketServer.java +++ b/Server/src/main/java/net/simon987/server/websocket/SocketServer.java @@ -1,7 +1,9 @@ package net.simon987.server.websocket; +import net.simon987.server.GameServer; import net.simon987.server.game.ControllableUnit; import net.simon987.server.logging.LogManager; +import net.simon987.server.user.User; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; @@ -57,14 +59,46 @@ public class SocketServer { } else { LogManager.LOGGER.info("(WS) Received message from unauthenticated user " + session.getRemoteAddress()); - //todo + if (message.length() == 128) { + + User user = GameServer.INSTANCE.getUserManager().validateAuthToken(message); + + if (user != null) { + + LogManager.LOGGER.info("(WS) User was successfully authenticated: " + user.getUsername()); + + onlineUser.setUser(user); + onlineUser.setAuthenticated(true); + + try { + session.getRemote().sendString("{\"t\":\"auth\", \"m\":\"ok\"}"); + } catch (IOException e) { + e.printStackTrace(); + } + + } else { + + User guestUser = GameServer.INSTANCE.getGameUniverse().getOrCreateUser(GameServer.INSTANCE.getGameUniverse().getGuestUsername(), false); + onlineUser.setUser(guestUser); + onlineUser.setAuthenticated(true); + onlineUser.getUser().setGuest(true); + + LogManager.LOGGER.info("(WS) Created guest user " + + onlineUser.getUser().getUsername() + session.getRemoteAddress()); + + try { + session.getRemote().sendString("{\"t\":\"auth\", \"m\":\"ok\"}"); + } catch (IOException e) { + e.printStackTrace(); + } + } + } } } else { LogManager.LOGGER.severe("(WS) FIXME: SocketServer:onMessage"); - } } @@ -81,7 +115,7 @@ public class SocketServer { if (user.getWebSocket().isOpen()) { - if (user.isGuest()) { + if (user.getUser().isGuest()) { json.remove("c"); try { diff --git a/Server/src/main/java/net/simon987/server/websocket/UserInfoRequestHandler.java b/Server/src/main/java/net/simon987/server/websocket/UserInfoRequestHandler.java index c6dba4a..540a930 100644 --- a/Server/src/main/java/net/simon987/server/websocket/UserInfoRequestHandler.java +++ b/Server/src/main/java/net/simon987/server/websocket/UserInfoRequestHandler.java @@ -18,7 +18,7 @@ public class UserInfoRequestHandler implements MessageHandler { LogManager.LOGGER.fine("(WS) User info request from " + user.getUser().getUsername()); JSONObject json = new JSONObject(); - if (user.isGuest()) { + if (user.getUser().isGuest()) { json.put("worldX", GameServer.INSTANCE.getConfig().getInt("new_user_worldX")); json.put("worldY", GameServer.INSTANCE.getConfig().getInt("new_user_worldY")); json.put("dimension", GameServer.INSTANCE.getConfig().getString("new_user_dimension")); diff --git a/Server/src/main/resources/config.properties b/Server/src/main/resources/config.properties index 2fdf094..a21237c 100644 --- a/Server/src/main/resources/config.properties +++ b/Server/src/main/resources/config.properties @@ -8,12 +8,11 @@ mysql_url=jdbc:mysql://localhost:3306/mar?useSSL=false save_interval=5 log_limit=2000000 log_count=10 -# Web server port -webSocket_port=8887 -webSocket_host=0.0.0.0 - -use_secure_webSocket=0 +use_ssl=0 cert_path=certificates +mar_port=4567 +mar_address=localhost +server_name=Official MAR server # ---------------------------------------------- # Length of a tick in ms @@ -86,7 +85,6 @@ wg_minCopperCount=0 # Maximum copper tile count for the WorldGenerator wg_maxCopperCount=2 - # ---------------------------------------------- # Maximum execution time of user code in ms user_timeout=100 diff --git a/Server/src/main/resources/static/js/mar.js b/Server/src/main/resources/static/js/mar.js index 6ced093..58c360b 100644 --- a/Server/src/main/resources/static/js/mar.js +++ b/Server/src/main/resources/static/js/mar.js @@ -695,7 +695,7 @@ var GameClient = (function () { console.log("[MAR] Getting server info... "); } var xhr = new XMLHttpRequest(); - xhr.open("GET", "./getServerInfo.php", true); + xhr.open("GET", "./server_info", true); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { if (DEBUG) { diff --git a/Server/src/main/typescript/GameClient.ts b/Server/src/main/typescript/GameClient.ts index 8b3663e..937406f 100644 --- a/Server/src/main/typescript/GameClient.ts +++ b/Server/src/main/typescript/GameClient.ts @@ -374,7 +374,7 @@ class GameClient { } let xhr = new XMLHttpRequest(); - xhr.open("GET", "./getServerInfo.php", true); + xhr.open("GET", "./server_info", true); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) {