JSON responses implemented and improvements

This commit is contained in:
Carlos 2019-08-11 15:22:09 +02:00
parent 5adbe63ede
commit 2f1385631d
10 changed files with 294 additions and 40 deletions

View File

@ -1,30 +1,51 @@
# WebConsole # WebConsole
WebConsole is a Spigot plugin for Minecraft 1.14 that enables you to view your server console and manage your server from anywhere. It creates a WebSocket server in the background used by the web interface to send commands, receive your console log and manage your server. WebConsole is a Spigot plugin for Minecraft 1.14 that enables you to view your server console and manage your server from anywhere. It creates a WebSocket server in the background used by the web interface to send commands, receive your console log and manage your server.
Dont worry about privacy: all data is stored in your browser offline and your PC will connect directly to your minecraft server. No intermediary web servers, just you and your server. Dont worry about privacy: all data is stored in your browser offline and your PC will connect directly to your minecraft server. No intermediary web servers, just you and your server.
## Plugin installation ## Plugin installation
1. Plugin download 1. Plugin download
2. Filling config.yml. Port and password configuration 2. Filling config.yml. Port and password configuration
## How it works ## How it works
1. How to install web interface / connect to github pages 1. How to install web interface / connect to github pages
2. How to add servers 2. How to add servers
## Technical information ## Technical information
### WebSocket commands ### WebSocket commands
The following tables represent how server communicates with the client(s), something like a language between them. The following tables represent how server communicates with the client(s), something like a language between them.
#### Websocket Server -> Client #### Websocket Server -> Client
| Code |Meaning |
|---------------------|--------------------------------------| Server communicate with all connected clients using JSON. The following table shows all possible JSON variables.
|200 *(message)* |Query was processed with no errors | | Variable |Meaning |
|403 *(message)* |You are not allowed to do that action | |---------------------|-----------------------------------------------------------------------------|
|LOG *(message)* |Message is a console output | | status |Status code (as integer), representing response type. See table below |
| statusDescription |Status description (as String) describing status code |
| respondsTo |`(Optional)` Original command sent by client which triggered this response|
| message |Response content |
*Status codes are listed below:
- **010**: Console output.
- **200**: Ok/Processed.
- **400**: Unknown command.
- **401**: Login required/Not logged in.
- **403**: Forbidden/Unauthorised (Probably not logged in).
#### Client -> Websocket Server #### Client -> Websocket Server
Clients can communicate with server using commands. The following table shows existing commands.
| Code |Meaning |Extra info | | Code |Meaning |Extra info |
|---------------------|-----------------------------------------|--------------| |---------------------|-----------------------------------------|--------------|
|LOGIN *(password)* |Login to start communication with server | | |LOGIN *(password)* |Login to start communication with server | |

View File

@ -8,20 +8,22 @@ import org.java_websocket.WebSocket;
import com.mesacarlos.webconsole.websockets.WSServer; import com.mesacarlos.webconsole.websockets.WSServer;
public class ExecuteCmdCommand implements WSCommand{ public class ExecuteCmdCommand implements WSCommand {
@Override @Override
public void execute(WSServer wsServer, WebSocket conn, String command) { public void execute(WSServer wsServer, WebSocket conn, String command) {
Bukkit.getLogger().info("[WebConsole] " + conn.getRemoteSocketAddress() + " executed '" + command + "'");
ConsoleCommandSender sender = Bukkit.getServer().getConsoleSender(); ConsoleCommandSender sender = Bukkit.getServer().getConsoleSender();
try { try {
@SuppressWarnings("unused") @SuppressWarnings("unused")
boolean success = Bukkit.getScheduler().callSyncMethod( wsServer.getMainClass(), () -> Bukkit.dispatchCommand( sender, command ) ).get(); boolean success = Bukkit.getScheduler()
.callSyncMethod(wsServer.getMainClass(), () -> Bukkit.dispatchCommand(sender, command)).get();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
} }

View File

@ -3,28 +3,31 @@ package com.mesacarlos.webconsole.command;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.java_websocket.WebSocket; import org.java_websocket.WebSocket;
import com.mesacarlos.webconsole.json.Forbidden;
import com.mesacarlos.webconsole.json.Processed;
import com.mesacarlos.webconsole.util.LoginManager; import com.mesacarlos.webconsole.util.LoginManager;
import com.mesacarlos.webconsole.websockets.WSServer; import com.mesacarlos.webconsole.websockets.WSServer;
public class LogInCommand implements WSCommand{ 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().getAddress().toString())) if (LoginManager.getInstance().isLoggedIn(conn.getRemoteSocketAddress().getAddress().toString()))
return; return;
//Get password from config files // Get password from config files
String receivedPassword = wsServer.getMainClass().getConfig().getString("password"); String receivedPassword = wsServer.getMainClass().getConfig().getString("password");
if(receivedPassword.equals(password)) { if (receivedPassword.equals(password)) {
//Password is correct, logging in // Password is correct, logging in
LoginManager.getInstance().logIn(conn.getRemoteSocketAddress().getAddress().toString()); LoginManager.getInstance().logIn(conn.getRemoteSocketAddress().getAddress().toString());
conn.send("200 Logged In"); wsServer.sendToClient(conn, new Processed("Logged In", "LOGIN ********"));
Bukkit.getLogger().info("[WebConsole] Successfully logged in from " + conn.getRemoteSocketAddress()); Bukkit.getLogger().info("[WebConsole] Successfully logged in from " + conn.getRemoteSocketAddress());
}else { } else {
conn.send("403 Forbidden"); wsServer.sendToClient(conn, new Forbidden("Forbidden","LOGIN ********"));
Bukkit.getLogger().info("[WebConsole] Password incorrect while login from " + conn.getRemoteSocketAddress()); Bukkit.getLogger()
.info("[WebConsole] Password incorrect while login from " + conn.getRemoteSocketAddress());
} }
} }

View File

@ -0,0 +1,36 @@
package com.mesacarlos.webconsole.json;
import com.google.gson.JsonObject;
public class ConsoleOutput implements JSONOutput{
private String message;
public ConsoleOutput(String message) {
this.message = message;
}
@Override
public int getStatusCode() {
return 10;
}
@Override
public String getMessage() {
return message;
}
@Override
public String getRespondsTo() {
return null;
}
@Override
public String toJSON() {
JsonObject object = new JsonObject();
object.addProperty("status", getStatusCode());
object.addProperty("statusDescription", "Console Output");
object.addProperty("message", getMessage());
return object.toString();
}
}

View File

@ -0,0 +1,39 @@
package com.mesacarlos.webconsole.json;
import com.google.gson.JsonObject;
public class Forbidden implements JSONOutput{
private String message;
private String respondsTo;
public Forbidden(String message, String respondsTo) {
this.message = message;
this.respondsTo = respondsTo;
}
@Override
public int getStatusCode() {
return 403;
}
@Override
public String getRespondsTo() {
return respondsTo;
}
@Override
public String getMessage() {
return message;
}
@Override
public String toJSON() {
JsonObject object = new JsonObject();
object.addProperty("status", getStatusCode());
object.addProperty("statusDescription", "Forbidden");
object.addProperty("respondsTo", getRespondsTo());
object.addProperty("message", getMessage());
return object.toString();
}
}

View File

@ -0,0 +1,28 @@
package com.mesacarlos.webconsole.json;
public interface JSONOutput {
/**
* Gets status code representing this message. See docs for code meanings.
* @return Status code representing this message
*/
int getStatusCode();
/**
* Returns the command sended by client who created this response.
* In case of a server-generated response (like ConsoleOutput), this will be null
* @return
*/
String getRespondsTo();
/**
* Explanatory message of this response
* @return Explanatory message of this response
*/
String getMessage();
/**
* Coverts this object into JSON, ready to send it over WS
* @return JSON Object Stringified
*/
String toJSON();
}

View File

@ -0,0 +1,36 @@
package com.mesacarlos.webconsole.json;
import com.google.gson.JsonObject;
public class LoginRequired implements JSONOutput{
private String message;
public LoginRequired(String message) {
this.message = message;
}
@Override
public int getStatusCode() {
return 401;
}
@Override
public String getMessage() {
return message;
}
@Override
public String getRespondsTo() {
return null;
}
@Override
public String toJSON() {
JsonObject object = new JsonObject();
object.addProperty("status", getStatusCode());
object.addProperty("statusDescription", "Login Required");
object.addProperty("message", getMessage());
return object.toString();
}
}

View File

@ -0,0 +1,39 @@
package com.mesacarlos.webconsole.json;
import com.google.gson.JsonObject;
public class Processed implements JSONOutput{
private String message;
private String respondsTo;
public Processed(String message, String respondsTo) {
this.message = message;
this.respondsTo = respondsTo;
}
@Override
public int getStatusCode() {
return 200;
}
@Override
public String getMessage() {
return message;
}
@Override
public String getRespondsTo() {
return respondsTo;
}
@Override
public String toJSON() {
JsonObject object = new JsonObject();
object.addProperty("status", getStatusCode());
object.addProperty("statusDescription", "Processed");
object.addProperty("respondsTo", getRespondsTo());
object.addProperty("message", getMessage());
return object.toString();
}
}

View File

@ -0,0 +1,38 @@
package com.mesacarlos.webconsole.json;
import com.google.gson.JsonObject;
public class UnknownWSCmd implements JSONOutput{
private String message;
private String respondsTo;
public UnknownWSCmd(String message, String respondsTo) {
this.message = message;
this.respondsTo = respondsTo;
}
@Override
public int getStatusCode() {
return 400;
}
@Override
public String getMessage() {
return message;
}
@Override
public String getRespondsTo() {
return respondsTo;
}
@Override
public String toJSON() {
JsonObject object = new JsonObject();
object.addProperty("status", getStatusCode());
object.addProperty("statusDescription", "Unknown Command");
object.addProperty("respondsTo", getRespondsTo());
object.addProperty("message", getMessage());
return object.toString();
}
}

View File

@ -12,11 +12,16 @@ import org.java_websocket.server.WebSocketServer;
import com.mesacarlos.webconsole.WebConsole; import com.mesacarlos.webconsole.WebConsole;
import com.mesacarlos.webconsole.command.CommandFactory; import com.mesacarlos.webconsole.command.CommandFactory;
import com.mesacarlos.webconsole.command.WSCommand; import com.mesacarlos.webconsole.command.WSCommand;
import com.mesacarlos.webconsole.json.ConsoleOutput;
import com.mesacarlos.webconsole.json.Forbidden;
import com.mesacarlos.webconsole.json.JSONOutput;
import com.mesacarlos.webconsole.json.LoginRequired;
import com.mesacarlos.webconsole.json.UnknownWSCmd;
import com.mesacarlos.webconsole.util.LoginManager; import com.mesacarlos.webconsole.util.LoginManager;
public class WSServer extends WebSocketServer { public class WSServer extends WebSocketServer {
private WebConsole plugin;
private HashMap<String, WSCommand> commands = CommandFactory.getCommandsHashMap(); private HashMap<String, WSCommand> commands = CommandFactory.getCommandsHashMap();
private WebConsole plugin;
public WSServer(WebConsole plugin, InetSocketAddress address) { public WSServer(WebConsole plugin, InetSocketAddress address) {
super(address); super(address);
@ -25,15 +30,12 @@ public class WSServer extends WebSocketServer {
@Override @Override
public void onOpen(WebSocket conn, ClientHandshake handshake) { public void onOpen(WebSocket conn, ClientHandshake handshake) {
conn.send("Connection started, waiting login"); sendToClient(conn, new LoginRequired("Connection started, waiting login"));
Bukkit.getLogger().info("[WebConsole] Connected and waiting login from " + conn.getRemoteSocketAddress()); Bukkit.getLogger().info("[WebConsole] Connected and waiting login from " + conn.getRemoteSocketAddress());
} }
@Override @Override
public void onMessage(WebSocket conn, String message) { public void onMessage(WebSocket conn, String message) {
// Log this action to console
Bukkit.getLogger().info("[WebConsole] Received signal from " + conn.getRemoteSocketAddress() + ": " + message);
// Get command and params // Get command and params
String wsCommand = message.split(" ")[0]; String wsCommand = message.split(" ")[0];
String wsCommandParams = ""; String wsCommandParams = "";
@ -44,14 +46,16 @@ public class WSServer extends WebSocketServer {
WSCommand cmd = commands.get(wsCommand); WSCommand cmd = commands.get(wsCommand);
if (cmd == null) { if (cmd == null) {
//Command does not exist
sendToClient(conn, new UnknownWSCmd("Unknown command", message));
Bukkit.getLogger().info( Bukkit.getLogger().info(
"[WebConsole] Signal was not processed since is not valid. Is your plugin/web interface up to date?"); "[WebConsole] Signal '" + message + "' was not processed since is not valid. Is your plugin/web interface up to date?");
} else if (!LoginManager.getInstance().isLoggedIn(conn.getRemoteSocketAddress().getAddress().toString()) } else if (!LoginManager.getInstance().isLoggedIn(conn.getRemoteSocketAddress().getAddress().toString())
&& !wsCommand.equals("LOGIN")) { && !wsCommand.equals("LOGIN")) {
// DO NOTHING. User is not authorised //User is not authorised. DO NOTHING, IMPORTANT!
conn.send("403 Forbidden"); sendToClient(conn, new Forbidden("Forbidden", message));
Bukkit.getLogger().warning("[WebConsole] " + conn.getRemoteSocketAddress() Bukkit.getLogger().warning("[WebConsole] " + conn.getRemoteSocketAddress()
+ " tried to run a command while not authenticated!"); + " tried to run '" + message + "' while not logged in!");
} else { } else {
cmd.execute(this, conn, wsCommandParams); cmd.execute(this, conn, wsCommandParams);
} }
@ -72,7 +76,15 @@ public class WSServer extends WebSocketServer {
@Override @Override
public void onStart() { public void onStart() {
Bukkit.getLogger().info("[WebConsole] WebSockets Server started successfully"); Bukkit.getLogger().info("[WebConsole] WebSocket Server started successfully");
}
/**
* Returns main class
* @return Main plugin class
*/
public WebConsole getMainClass() {
return plugin;
} }
/** /**
@ -82,17 +94,17 @@ public class WSServer extends WebSocketServer {
Collection<WebSocket> connections = getConnections(); Collection<WebSocket> connections = getConnections();
for (WebSocket connection : connections) { for (WebSocket connection : connections) {
if (LoginManager.getInstance().isLoggedIn(connection.getRemoteSocketAddress().getAddress().toString())) if (LoginManager.getInstance().isLoggedIn(connection.getRemoteSocketAddress().getAddress().toString()))
connection.send("LOG " + line); sendToClient(connection, new ConsoleOutput(line));
} }
} }
/** /**
* Returns main class * Sends this JSONOutput to client
* * @param conn Connection to client
* @return * @param content JSONOutput object
*/ */
public WebConsole getMainClass() { public void sendToClient(WebSocket conn, JSONOutput content) {
return plugin; conn.send(content.toJSON());
} }
} }