Improved security
This commit is contained in:
parent
d2696df7bc
commit
dd9003190a
@ -24,7 +24,7 @@ function openServer(serverName){
|
|||||||
|
|
||||||
//Change server name and related info
|
//Change server name and related info
|
||||||
$("#serverTitle").text(serverName);
|
$("#serverTitle").text(serverName);
|
||||||
$("#consoleTextArea").text("Connecting...");
|
$("#consoleTextArea").text("");
|
||||||
$("#commandInput").prop("disabled", false);
|
$("#commandInput").prop("disabled", false);
|
||||||
$("#sendCommandButton").prop("disabled", false);
|
$("#sendCommandButton").prop("disabled", false);
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ class WebConsoleConnector {
|
|||||||
constructor(serverName, serverURI) {
|
constructor(serverName, serverURI) {
|
||||||
this.serverName = serverName;
|
this.serverName = serverName;
|
||||||
this.serverURI = serverURI;
|
this.serverURI = serverURI;
|
||||||
|
this.token;
|
||||||
this.subscribers = []; //List of functions called when a new message arrive
|
this.subscribers = []; //List of functions called when a new message arrive
|
||||||
this.messages = []; //All messages retrieved since connection start
|
this.messages = []; //All messages retrieved since connection start
|
||||||
this.commands = []; //EXEC Commands sent by user to this server
|
this.commands = []; //EXEC Commands sent by user to this server
|
||||||
@ -32,7 +33,7 @@ class WebConsoleConnector {
|
|||||||
* Internal function
|
* Internal function
|
||||||
*/
|
*/
|
||||||
onOpen(evt){
|
onOpen(evt){
|
||||||
//TODO Check que la version es correcta, y que es un WebSocket del plugin y no de otra cosa
|
//TODO Check version is correct, and this websocket server is a WebConsole WebSocket
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,6 +49,11 @@ class WebConsoleConnector {
|
|||||||
*/
|
*/
|
||||||
onMessage(evt){
|
onMessage(evt){
|
||||||
var obj = JSON.parse(evt.data);
|
var obj = JSON.parse(evt.data);
|
||||||
|
|
||||||
|
|
||||||
|
if(obj.status === 200) //If is a LoggedIn response, save our token
|
||||||
|
this.token = obj.token;
|
||||||
|
|
||||||
this.notify(obj); //Notify all subscribers
|
this.notify(obj); //Notify all subscribers
|
||||||
this.messages.push(obj);
|
this.messages.push(obj);
|
||||||
}
|
}
|
||||||
@ -63,7 +69,7 @@ class WebConsoleConnector {
|
|||||||
* Sends a WebSocket command to Server
|
* Sends a WebSocket command to Server
|
||||||
*/
|
*/
|
||||||
sendToServer(message){
|
sendToServer(message){
|
||||||
this.websocket.send(message);
|
this.websocket.send(JSON.stringify(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,14 +60,22 @@ class WebConsoleManager {
|
|||||||
* Send password to server
|
* Send password to server
|
||||||
*/
|
*/
|
||||||
sendPassword(pwd){
|
sendPassword(pwd){
|
||||||
this.activeConnection.sendToServer("LOGIN " + pwd);
|
this.activeConnection.sendToServer({
|
||||||
|
command: "LOGIN",
|
||||||
|
params: pwd
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send console command to server
|
* Send console command to server
|
||||||
*/
|
*/
|
||||||
sendConsoleCmd(cmd){
|
sendConsoleCmd(cmd){
|
||||||
this.activeConnection.sendToServer("EXEC " + cmd);
|
this.activeConnection.sendToServer({
|
||||||
|
command: "EXEC",
|
||||||
|
token: this.activeConnection.token,
|
||||||
|
params: cmd
|
||||||
|
});
|
||||||
|
|
||||||
this.activeConnection.commands.push(cmd);
|
this.activeConnection.commands.push(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,16 +83,30 @@ class WebConsoleManager {
|
|||||||
* Asks server for CPU, RAM and players info
|
* Asks server for CPU, RAM and players info
|
||||||
*/
|
*/
|
||||||
askForInfo(){
|
askForInfo(){
|
||||||
this.activeConnection.sendToServer("PLAYERS");
|
this.activeConnection.sendToServer({
|
||||||
this.activeConnection.sendToServer("CPUUSAGE");
|
command: "PLAYERS",
|
||||||
this.activeConnection.sendToServer("RAMUSAGE");
|
token: this.activeConnection.token,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.activeConnection.sendToServer({
|
||||||
|
command: "CPUUSAGE",
|
||||||
|
token: this.activeConnection.token,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.activeConnection.sendToServer({
|
||||||
|
command: "RAMUSAGE",
|
||||||
|
token: this.activeConnection.token,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks server for full latest.log
|
* Asks server for full latest.log
|
||||||
*/
|
*/
|
||||||
askForLogs(){
|
askForLogs(){
|
||||||
this.activeConnection.sendToServer("READLOGFILE");
|
this.activeConnection.sendToServer({
|
||||||
|
command: "READLOGFILE",
|
||||||
|
token: this.activeConnection.token,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -8,20 +8,26 @@ import es.mesacarlos.webconsole.util.Internationalization;
|
|||||||
public class ConnectedUser {
|
public class ConnectedUser {
|
||||||
private String username;
|
private String username;
|
||||||
private InetSocketAddress socketAddress;
|
private InetSocketAddress socketAddress;
|
||||||
|
private String token;
|
||||||
private UserType userType;
|
private UserType userType;
|
||||||
|
|
||||||
public ConnectedUser(InetSocketAddress socketAddress, String username, UserType userType) {
|
public ConnectedUser(InetSocketAddress socketAddress, String username, String token, UserType userType) {
|
||||||
this.socketAddress = socketAddress;
|
this.socketAddress = socketAddress;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
|
this.token = token;
|
||||||
this.userType = userType;
|
this.userType = userType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
public InetSocketAddress getSocketAddress() {
|
public InetSocketAddress getSocketAddress() {
|
||||||
return socketAddress;
|
return socketAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public String getToken() {
|
||||||
return username;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserType getUserType() {
|
public UserType getUserType() {
|
||||||
|
@ -46,11 +46,23 @@ public class LoginManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if user is logged in
|
* Check if user is logged in. It checks that both the socket adress and the user token corresponds to a logged in user.
|
||||||
* @param address User to check
|
* @param address User to check
|
||||||
* @return true if user is logged in, false otherwise
|
* @return true if user is logged in, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean isLoggedIn(InetSocketAddress address) {
|
public boolean isLoggedIn(InetSocketAddress address, String token) {
|
||||||
|
for(ConnectedUser user : loggedInUsers)
|
||||||
|
if(user.getSocketAddress().equals(address) && user.getToken().equals(token))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an user is logged in from a given socket address
|
||||||
|
* @param address User to check
|
||||||
|
* @return true if user is logged in, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean isSocketConnected(InetSocketAddress address) {
|
||||||
for(ConnectedUser user : loggedInUsers)
|
for(ConnectedUser user : loggedInUsers)
|
||||||
if(user.getSocketAddress().equals(address))
|
if(user.getSocketAddress().equals(address))
|
||||||
return true;
|
return true;
|
||||||
|
93
src/es/mesacarlos/webconsole/util/JsonUtils.java
Normal file
93
src/es/mesacarlos/webconsole/util/JsonUtils.java
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package es.mesacarlos.webconsole.util;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
|
public class JsonUtils {
|
||||||
|
/*{
|
||||||
|
"command": "LOGIN",
|
||||||
|
"token": "aosduhasiudgasuidgasdgaspid",
|
||||||
|
"params": ""
|
||||||
|
}*/
|
||||||
|
public final static String COMMAND_PROPERTY = "command";
|
||||||
|
public final static String TOKEN_PROPERTY = "token";
|
||||||
|
public final static String PARAMS_PROPERTY = "params";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that a given String is a valid JSON
|
||||||
|
* @param Json JSON to check
|
||||||
|
* @return true if it is a valid JSON, false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean isValidJson(String Json) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
gson.fromJson(Json, Object.class);
|
||||||
|
Object jsonObjType = gson.fromJson(Json, Object.class).getClass();
|
||||||
|
if(jsonObjType.equals(String.class)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (com.google.gson.JsonSyntaxException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that a given JSON contains some property
|
||||||
|
* @param JsonString JSON to check
|
||||||
|
* @param property property to check
|
||||||
|
* @return true if the JSON string contains that property, false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean containsProperty(String JsonString, String property) {
|
||||||
|
if(!isValidJson(JsonString))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
JsonParser parser = new JsonParser();
|
||||||
|
JsonObject obj = parser.parse(JsonString).getAsJsonObject();
|
||||||
|
JsonElement elem = obj.get(property);
|
||||||
|
if(elem == null)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that a given JSON contains some property, and its type is a String
|
||||||
|
* @param JsonString JSON to check
|
||||||
|
* @param property property to check
|
||||||
|
* @returntrue if the JSON string contains that property and it is a String, false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean containsStringProperty(String JsonString, String property) {
|
||||||
|
if(!isValidJson(JsonString))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
JsonParser parser = new JsonParser();
|
||||||
|
JsonObject obj = parser.parse(JsonString).getAsJsonObject();
|
||||||
|
JsonElement elem = obj.get(property);
|
||||||
|
if(elem == null)
|
||||||
|
return false;
|
||||||
|
try {
|
||||||
|
elem.getAsString();
|
||||||
|
return true;
|
||||||
|
}catch(Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a String property from a JSON
|
||||||
|
* @param JsonString JSON to check
|
||||||
|
* @param property property to extract from JSON string
|
||||||
|
* @return the value for that property. If the property is not set, an empty string will be returned
|
||||||
|
*/
|
||||||
|
public static String getStringProperty(String JsonString, String property) {
|
||||||
|
JsonParser parser = new JsonParser();
|
||||||
|
JsonObject obj = parser.parse(JsonString).getAsJsonObject();
|
||||||
|
JsonElement result = obj.get(property);
|
||||||
|
if(result != null)
|
||||||
|
return result.getAsString();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -13,6 +13,7 @@ import org.java_websocket.server.WebSocketServer;
|
|||||||
import es.mesacarlos.webconsole.auth.LoginManager;
|
import es.mesacarlos.webconsole.auth.LoginManager;
|
||||||
import es.mesacarlos.webconsole.util.DateTimeUtils;
|
import es.mesacarlos.webconsole.util.DateTimeUtils;
|
||||||
import es.mesacarlos.webconsole.util.Internationalization;
|
import es.mesacarlos.webconsole.util.Internationalization;
|
||||||
|
import es.mesacarlos.webconsole.util.JsonUtils;
|
||||||
import es.mesacarlos.webconsole.websocket.command.WSCommandFactory;
|
import es.mesacarlos.webconsole.websocket.command.WSCommandFactory;
|
||||||
import es.mesacarlos.webconsole.websocket.command.WSCommand;
|
import es.mesacarlos.webconsole.websocket.command.WSCommand;
|
||||||
import es.mesacarlos.webconsole.websocket.response.ConsoleOutput;
|
import es.mesacarlos.webconsole.websocket.response.ConsoleOutput;
|
||||||
@ -30,7 +31,7 @@ public class WSServer extends WebSocketServer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpen(WebSocket conn, ClientHandshake handshake) {
|
public void onOpen(WebSocket conn, ClientHandshake handshake) {
|
||||||
if (LoginManager.getInstance().isLoggedIn(conn.getRemoteSocketAddress())) {
|
if (LoginManager.getInstance().isSocketConnected(conn.getRemoteSocketAddress())) {
|
||||||
sendToClient(conn, new LoggedIn(Internationalization.getPhrase("connection-resumed-message")));
|
sendToClient(conn, new LoggedIn(Internationalization.getPhrase("connection-resumed-message")));
|
||||||
Bukkit.getLogger().info(Internationalization.getPhrase("connection-resumed-console", conn.getRemoteSocketAddress()));
|
Bukkit.getLogger().info(Internationalization.getPhrase("connection-resumed-console", conn.getRemoteSocketAddress()));
|
||||||
} else {
|
} else {
|
||||||
@ -41,11 +42,15 @@ public class WSServer extends WebSocketServer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(WebSocket conn, String message) {
|
public void onMessage(WebSocket conn, String message) {
|
||||||
|
if(!JsonUtils.containsStringProperty(message, "command") //Contains a command
|
||||||
|
|| ( !JsonUtils.containsStringProperty(message, "token") && !JsonUtils.getStringProperty(message, JsonUtils.COMMAND_PROPERTY).equals("LOGIN")) //Contains a token or it is a login command
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
// Get command and params
|
// Get command and params
|
||||||
String wsCommand = message.split(" ")[0];
|
String wsCommand = JsonUtils.getStringProperty(message, JsonUtils.COMMAND_PROPERTY);
|
||||||
String wsCommandParams = "";
|
String wsToken = JsonUtils.getStringProperty(message, JsonUtils.TOKEN_PROPERTY);
|
||||||
if (message.contains(" "))
|
String wsCommandParams = JsonUtils.getStringProperty(message, JsonUtils.PARAMS_PROPERTY);
|
||||||
wsCommandParams = message.split(" ", 2)[1];
|
|
||||||
|
|
||||||
// Run command
|
// Run command
|
||||||
WSCommand cmd = commands.get(wsCommand);
|
WSCommand cmd = commands.get(wsCommand);
|
||||||
@ -54,8 +59,8 @@ public class WSServer extends WebSocketServer {
|
|||||||
// Command does not exist
|
// Command does not exist
|
||||||
sendToClient(conn, new UnknownCommand(Internationalization.getPhrase("unknown-command-message"), message));
|
sendToClient(conn, new UnknownCommand(Internationalization.getPhrase("unknown-command-message"), message));
|
||||||
Bukkit.getLogger().info(Internationalization.getPhrase("unknown-command-console", message));
|
Bukkit.getLogger().info(Internationalization.getPhrase("unknown-command-console", message));
|
||||||
} else if (!LoginManager.getInstance().isLoggedIn(conn.getRemoteSocketAddress())
|
} else if (!wsCommand.equals("LOGIN")
|
||||||
&& !wsCommand.equals("LOGIN")) {
|
&& !LoginManager.getInstance().isLoggedIn(conn.getRemoteSocketAddress(), wsToken)) {
|
||||||
// User is not authorised. DO NOTHING, IMPORTANT!
|
// User is not authorised. DO NOTHING, IMPORTANT!
|
||||||
sendToClient(conn, new LoginRequired(Internationalization.getPhrase("forbidden-message")));
|
sendToClient(conn, new LoginRequired(Internationalization.getPhrase("forbidden-message")));
|
||||||
Bukkit.getLogger().warning(Internationalization.getPhrase("forbidden-console", conn.getRemoteSocketAddress(), message));
|
Bukkit.getLogger().warning(Internationalization.getPhrase("forbidden-console", conn.getRemoteSocketAddress(), message));
|
||||||
@ -86,7 +91,7 @@ public class WSServer extends WebSocketServer {
|
|||||||
public void onNewConsoleLinePrinted(String line) {
|
public void onNewConsoleLinePrinted(String line) {
|
||||||
Collection<WebSocket> connections = getConnections();
|
Collection<WebSocket> connections = getConnections();
|
||||||
for (WebSocket connection : connections) {
|
for (WebSocket connection : connections) {
|
||||||
if (LoginManager.getInstance().isLoggedIn(connection.getRemoteSocketAddress()))
|
if (LoginManager.getInstance().isSocketConnected(connection.getRemoteSocketAddress()))
|
||||||
sendToClient(connection, new ConsoleOutput(line, DateTimeUtils.getTimeAsString()));
|
sendToClient(connection, new ConsoleOutput(line, DateTimeUtils.getTimeAsString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package es.mesacarlos.webconsole.websocket.command;
|
package es.mesacarlos.webconsole.websocket.command;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.java_websocket.WebSocket;
|
import org.java_websocket.WebSocket;
|
||||||
|
|
||||||
@ -17,16 +19,16 @@ public class LogInCommand implements WSCommand {
|
|||||||
@Override
|
@Override
|
||||||
public void execute(WSServer wsServer, WebSocket conn, String password) {
|
public void execute(WSServer wsServer, WebSocket conn, String password) {
|
||||||
// If user is logged in, then return.
|
// If user is logged in, then return.
|
||||||
if (LoginManager.getInstance().isLoggedIn(conn.getRemoteSocketAddress()))
|
if (LoginManager.getInstance().isSocketConnected(conn.getRemoteSocketAddress()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//Check if user exists
|
//Check if user exists
|
||||||
for(UserData ud : ConfigManager.getInstance().getAllUsers()) {
|
for(UserData ud : ConfigManager.getInstance().getAllUsers()) {
|
||||||
if(ud.getPassword().equals(password)) {
|
if(ud.getPassword().equals(password)) {
|
||||||
ConnectedUser user = new ConnectedUser(conn.getRemoteSocketAddress(), ud.getUsername(), ud.getUserType());
|
ConnectedUser user = new ConnectedUser(conn.getRemoteSocketAddress(), ud.getUsername(), UUID.randomUUID().toString(), ud.getUserType());
|
||||||
LoginManager.getInstance().logIn(user);
|
LoginManager.getInstance().logIn(user);
|
||||||
|
|
||||||
wsServer.sendToClient(conn, new LoggedIn(Internationalization.getPhrase("login-sucessful-message"), "LOGIN ********", user.getUsername(), user.getUserType()));
|
wsServer.sendToClient(conn, new LoggedIn(Internationalization.getPhrase("login-sucessful-message"), "LOGIN ********", user.getUsername(), user.getUserType(), user.getToken()));
|
||||||
Bukkit.getLogger().info(Internationalization.getPhrase("login-sucessful-console", user.toString()));
|
Bukkit.getLogger().info(Internationalization.getPhrase("login-sucessful-console", user.toString()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -9,16 +9,18 @@ public class LoggedIn implements JSONOutput{
|
|||||||
private String respondsTo;
|
private String respondsTo;
|
||||||
private String username;
|
private String username;
|
||||||
private UserType as;
|
private UserType as;
|
||||||
|
private String token;
|
||||||
|
|
||||||
public LoggedIn(String message) {
|
public LoggedIn(String message) {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoggedIn(String message, String respondsTo, String username, UserType as) {
|
public LoggedIn(String message, String respondsTo, String username, UserType as, String token) {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.respondsTo = respondsTo;
|
this.respondsTo = respondsTo;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.as = as;
|
this.as = as;
|
||||||
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -53,6 +55,10 @@ public class LoggedIn implements JSONOutput{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toJSON() {
|
public String toJSON() {
|
||||||
JsonObject object = new JsonObject();
|
JsonObject object = new JsonObject();
|
||||||
@ -61,6 +67,7 @@ public class LoggedIn implements JSONOutput{
|
|||||||
object.addProperty("respondsTo", getRespondsTo());
|
object.addProperty("respondsTo", getRespondsTo());
|
||||||
object.addProperty("username", getUsername());
|
object.addProperty("username", getUsername());
|
||||||
object.addProperty("as", getAs());
|
object.addProperty("as", getAs());
|
||||||
|
object.addProperty("token", getToken());
|
||||||
object.addProperty("message", getMessage());
|
object.addProperty("message", getMessage());
|
||||||
return object.toString();
|
return object.toString();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user