From 5adbe63edeeb1be71a3a7d6ecd41c274a4bc61c2 Mon Sep 17 00:00:00 2001
From: Carlos <28845529+mesacarlos@users.noreply.github.com>
Date: Sun, 11 Aug 2019 02:46:36 +0200
Subject: [PATCH] First commit
---
.gitignore | 83 +++++++++--
README.md | 31 ++++
plugin.yml | 7 +
pom.xml | 83 +++++++++++
src/com/mesacarlos/webconsole/WebConsole.java | 69 +++++++++
.../webconsole/command/CommandFactory.java | 13 ++
.../webconsole/command/ExecuteCmdCommand.java | 27 ++++
.../webconsole/command/LogInCommand.java | 31 ++++
.../webconsole/command/WSCommand.java | 9 ++
.../webconsole/util/DateTimeUtils.java | 10 ++
.../mesacarlos/webconsole/util/LogFilter.java | 138 ++++++++++++++++++
.../webconsole/util/LoginManager.java | 29 ++++
.../webconsole/websockets/WSServer.java | 98 +++++++++++++
13 files changed, 616 insertions(+), 12 deletions(-)
create mode 100644 README.md
create mode 100644 plugin.yml
create mode 100644 pom.xml
create mode 100644 src/com/mesacarlos/webconsole/WebConsole.java
create mode 100644 src/com/mesacarlos/webconsole/command/CommandFactory.java
create mode 100644 src/com/mesacarlos/webconsole/command/ExecuteCmdCommand.java
create mode 100644 src/com/mesacarlos/webconsole/command/LogInCommand.java
create mode 100644 src/com/mesacarlos/webconsole/command/WSCommand.java
create mode 100644 src/com/mesacarlos/webconsole/util/DateTimeUtils.java
create mode 100644 src/com/mesacarlos/webconsole/util/LogFilter.java
create mode 100644 src/com/mesacarlos/webconsole/util/LoginManager.java
create mode 100644 src/com/mesacarlos/webconsole/websockets/WSServer.java
diff --git a/.gitignore b/.gitignore
index 5f2dbe1..b7bdbd2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,71 @@
-target/
-pom.xml.tag
-pom.xml.releaseBackup
-pom.xml.versionsBackup
-pom.xml.next
-release.properties
-dependency-reduced-pom.xml
-buildNumber.properties
-.mvn/timing.properties
-
-# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
-!/.mvn/wrapper/maven-wrapper.jar
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.classpath
+.project
+.mvn/timing.properties
+
+# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
+!/.mvn/wrapper/maven-wrapper.jar
+/bin/
+
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# PyDev specific (Python IDE for Eclipse)
+*.pydevproject
+
+# CDT-specific (C/C++ Development Tooling)
+.cproject
+
+# CDT- autotools
+.autotools
+
+# Java annotation processor (APT)
+.factorypath
+
+# PDT-specific (PHP Development Tools)
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# Tern plugin
+.tern-project
+
+# TeXlipse plugin
+.texlipse
+
+# STS (Spring Tool Suite)
+.springBeans
+
+# Code Recommenders
+.recommenders/
+
+# Annotation Processing
+.apt_generated/
+
+# Scala IDE specific (Scala & Java development for Eclipse)
+.cache-main
+.scala_dependencies
+.worksheet
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d605250
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+# 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.
+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
+1. Plugin download
+2. Filling config.yml. Port and password configuration
+
+## How it works
+1. How to install web interface / connect to github pages
+2. How to add servers
+
+## Technical information
+
+### WebSocket commands
+The following tables represent how server communicates with the client(s), something like a language between them.
+
+#### Websocket Server -> Client
+| Code |Meaning |
+|---------------------|--------------------------------------|
+|200 *(message)* |Query was processed with no errors |
+|403 *(message)* |You are not allowed to do that action |
+|LOG *(message)* |Message is a console output |
+
+
+#### Client -> Websocket Server
+| Code |Meaning |Extra info |
+|---------------------|-----------------------------------------|--------------|
+|LOGIN *(password)* |Login to start communication with server | |
+|EXEC *(command)* |Run desired command in Minecraft Server |Login required|
diff --git a/plugin.yml b/plugin.yml
new file mode 100644
index 0000000..809278f
--- /dev/null
+++ b/plugin.yml
@@ -0,0 +1,7 @@
+name: WebConsole
+main: com.mesacarlos.webconsole.WebConsole
+version: 1.0
+description: WebSockets-based web console
+api-version: 1.14
+author: Carlos Mesa
+commands:
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..1f8a065
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,83 @@
+
+ 4.0.0
+ WebConsole
+ WebConsole
+ 1.0
+
+ src
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+
+
+
+ maven-assembly-plugin
+
+
+ package
+
+ single
+
+
+
+
+
+ jar-with-dependencies
+
+
+
+
+
+
+ .
+
+ **/*.yml
+
+
+
+
+
+
+ spigot-repo
+ https://hub.spigotmc.org/nexus/content/repositories/snapshots/
+
+
+
+
+ bungeecord-repo
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+
+
+
+ org.spigotmc
+ spigot-api
+ 1.14.2-R0.1-SNAPSHOT
+ provided
+
+
+
+ org.java-websocket
+ Java-WebSocket
+ 1.4.0
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.12.1
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.12.1
+
+
+
\ No newline at end of file
diff --git a/src/com/mesacarlos/webconsole/WebConsole.java b/src/com/mesacarlos/webconsole/WebConsole.java
new file mode 100644
index 0000000..95a7d98
--- /dev/null
+++ b/src/com/mesacarlos/webconsole/WebConsole.java
@@ -0,0 +1,69 @@
+package com.mesacarlos.webconsole;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.Filter;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import com.mesacarlos.webconsole.util.LogFilter;
+import com.mesacarlos.webconsole.websockets.WSServer;
+
+public class WebConsole extends JavaPlugin {
+ FileConfiguration config = this.getConfig();
+
+ // Websocket server and thread
+ private WSServer server;
+ private Thread wsThread;
+
+ @Override
+ public void onEnable() {
+ createConfig();
+ startWS();
+
+ Filter f = new LogFilter(getWSServer());
+ ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addFilter(f);
+ }
+
+ @Override
+ public void onDisable() {
+ try {
+ server.stop();
+ wsThread = null;
+ } catch (IOException | InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Creates configuration file
+ */
+ private void createConfig() {
+ config.addDefault("host", "localhost");
+ config.addDefault("port", 8080);
+ config.addDefault("password", 1234);
+ config.options().copyDefaults(true);
+ saveConfig();
+ }
+
+ /**
+ * Start WebSockets server
+ */
+ private void startWS() {
+ //Start WebSockets server
+ server = new WSServer(this, new InetSocketAddress(config.getString("host"), config.getInt("port")));
+ wsThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ server.run();
+ }
+ });
+ wsThread.start();
+ }
+
+ public WSServer getWSServer() {
+ return (WSServer)server;
+ }
+}
\ No newline at end of file
diff --git a/src/com/mesacarlos/webconsole/command/CommandFactory.java b/src/com/mesacarlos/webconsole/command/CommandFactory.java
new file mode 100644
index 0000000..bfa259c
--- /dev/null
+++ b/src/com/mesacarlos/webconsole/command/CommandFactory.java
@@ -0,0 +1,13 @@
+package com.mesacarlos.webconsole.command;
+
+import java.util.HashMap;
+
+public class CommandFactory {
+
+ public static HashMap getCommandsHashMap() {
+ HashMap commands = new HashMap();
+ commands.put("LOGIN", new LogInCommand());
+ commands.put("EXEC", new ExecuteCmdCommand());
+ return commands;
+ }
+}
\ No newline at end of file
diff --git a/src/com/mesacarlos/webconsole/command/ExecuteCmdCommand.java b/src/com/mesacarlos/webconsole/command/ExecuteCmdCommand.java
new file mode 100644
index 0000000..6f3fa99
--- /dev/null
+++ b/src/com/mesacarlos/webconsole/command/ExecuteCmdCommand.java
@@ -0,0 +1,27 @@
+package com.mesacarlos.webconsole.command;
+
+import java.util.concurrent.ExecutionException;
+
+import org.bukkit.Bukkit;
+import org.bukkit.command.ConsoleCommandSender;
+import org.java_websocket.WebSocket;
+
+import com.mesacarlos.webconsole.websockets.WSServer;
+
+public class ExecuteCmdCommand implements WSCommand{
+
+ @Override
+ public void execute(WSServer wsServer, WebSocket conn, String command) {
+ ConsoleCommandSender sender = Bukkit.getServer().getConsoleSender();
+
+ try {
+ @SuppressWarnings("unused")
+ boolean success = Bukkit.getScheduler().callSyncMethod( wsServer.getMainClass(), () -> Bukkit.dispatchCommand( sender, command ) ).get();
+ } catch (InterruptedException | ExecutionException e) {
+ e.printStackTrace();
+ }
+
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/com/mesacarlos/webconsole/command/LogInCommand.java b/src/com/mesacarlos/webconsole/command/LogInCommand.java
new file mode 100644
index 0000000..fb98c52
--- /dev/null
+++ b/src/com/mesacarlos/webconsole/command/LogInCommand.java
@@ -0,0 +1,31 @@
+package com.mesacarlos.webconsole.command;
+
+import org.bukkit.Bukkit;
+import org.java_websocket.WebSocket;
+
+import com.mesacarlos.webconsole.util.LoginManager;
+import com.mesacarlos.webconsole.websockets.WSServer;
+
+public class LogInCommand implements WSCommand{
+
+ @Override
+ public void execute(WSServer wsServer, WebSocket conn, String password) {
+ //If user is logged in, then return.
+ if(LoginManager.getInstance().isLoggedIn(conn.getRemoteSocketAddress().getAddress().toString()))
+ return;
+
+ //Get password from config files
+ String receivedPassword = wsServer.getMainClass().getConfig().getString("password");
+
+ if(receivedPassword.equals(password)) {
+ //Password is correct, logging in
+ LoginManager.getInstance().logIn(conn.getRemoteSocketAddress().getAddress().toString());
+ conn.send("200 Logged In");
+ Bukkit.getLogger().info("[WebConsole] Successfully logged in from " + conn.getRemoteSocketAddress());
+ }else {
+ conn.send("403 Forbidden");
+ Bukkit.getLogger().info("[WebConsole] Password incorrect while login from " + conn.getRemoteSocketAddress());
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/com/mesacarlos/webconsole/command/WSCommand.java b/src/com/mesacarlos/webconsole/command/WSCommand.java
new file mode 100644
index 0000000..4f4103d
--- /dev/null
+++ b/src/com/mesacarlos/webconsole/command/WSCommand.java
@@ -0,0 +1,9 @@
+package com.mesacarlos.webconsole.command;
+
+import org.java_websocket.WebSocket;
+
+import com.mesacarlos.webconsole.websockets.WSServer;
+
+public interface WSCommand {
+ void execute(WSServer wsServer, WebSocket conn, String params);
+}
\ No newline at end of file
diff --git a/src/com/mesacarlos/webconsole/util/DateTimeUtils.java b/src/com/mesacarlos/webconsole/util/DateTimeUtils.java
new file mode 100644
index 0000000..d5de833
--- /dev/null
+++ b/src/com/mesacarlos/webconsole/util/DateTimeUtils.java
@@ -0,0 +1,10 @@
+package com.mesacarlos.webconsole.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class DateTimeUtils {
+ public static String getDateAsString() {
+ return new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").format(new Date());
+ }
+}
\ No newline at end of file
diff --git a/src/com/mesacarlos/webconsole/util/LogFilter.java b/src/com/mesacarlos/webconsole/util/LogFilter.java
new file mode 100644
index 0000000..09e81db
--- /dev/null
+++ b/src/com/mesacarlos/webconsole/util/LogFilter.java
@@ -0,0 +1,138 @@
+package com.mesacarlos.webconsole.util;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.message.Message;
+
+import com.mesacarlos.webconsole.websockets.WSServer;
+
+public class LogFilter implements Filter{
+ private WSServer wsServer;
+
+ public LogFilter(WSServer wsServer) {
+ this.wsServer = wsServer;
+ }
+
+ @Override
+ public State getState() {
+ return null;
+ }
+
+ @Override
+ public void initialize() {
+
+ }
+
+ @Override
+ public void start() {
+
+ }
+
+ @Override
+ public void stop() {
+
+ }
+
+ @Override
+ public boolean isStarted() {
+ return false;
+ }
+
+ @Override
+ public boolean isStopped() {
+ return false;
+ }
+
+ @Override
+ public Result getOnMismatch() {
+ return null;
+ }
+
+ @Override
+ public Result getOnMatch() {
+ return null;
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, String msg, Object... params) {
+ return null;
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, String message, Object p0) {
+ return null;
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1) {
+ return null;
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2) {
+ return null;
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2,
+ Object p3) {
+ return null;
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2,
+ Object p3, Object p4) {
+ return null;
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2,
+ Object p3, Object p4, Object p5) {
+ return null;
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2,
+ Object p3, Object p4, Object p5, Object p6) {
+ return null;
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2,
+ Object p3, Object p4, Object p5, Object p6, Object p7) {
+ return null;
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2,
+ Object p3, Object p4, Object p5, Object p6, Object p7, Object p8) {
+ return null;
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2,
+ Object p3, Object p4, Object p5, Object p6, Object p7, Object p8, Object p9) {
+ return null;
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
+ return null;
+ }
+
+ @Override
+ public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
+ return null;
+ }
+
+ @Override
+ public Result filter(LogEvent event) {
+ String message = event.getMessage().getFormattedMessage().replaceAll("\u001b"," ");
+ wsServer.onNewConsoleLinePrinted(message);
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/src/com/mesacarlos/webconsole/util/LoginManager.java b/src/com/mesacarlos/webconsole/util/LoginManager.java
new file mode 100644
index 0000000..7b7b49f
--- /dev/null
+++ b/src/com/mesacarlos/webconsole/util/LoginManager.java
@@ -0,0 +1,29 @@
+package com.mesacarlos.webconsole.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LoginManager {
+ private List loggedInUsers = new ArrayList();
+ private static LoginManager instance;
+
+ private LoginManager() {}
+
+ public static LoginManager getInstance() {
+ if(instance == null)
+ instance = new LoginManager();
+ return instance;
+ }
+
+ public void logIn(String address) {
+ loggedInUsers.add(address);
+ }
+
+ public void logOut(String address) {
+ loggedInUsers.remove(address);
+ }
+
+ public boolean isLoggedIn(String address) {
+ return loggedInUsers.contains(address);
+ }
+}
\ No newline at end of file
diff --git a/src/com/mesacarlos/webconsole/websockets/WSServer.java b/src/com/mesacarlos/webconsole/websockets/WSServer.java
new file mode 100644
index 0000000..56ad7d2
--- /dev/null
+++ b/src/com/mesacarlos/webconsole/websockets/WSServer.java
@@ -0,0 +1,98 @@
+package com.mesacarlos.webconsole.websockets;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.HashMap;
+
+import org.bukkit.Bukkit;
+import org.java_websocket.WebSocket;
+import org.java_websocket.handshake.ClientHandshake;
+import org.java_websocket.server.WebSocketServer;
+
+import com.mesacarlos.webconsole.WebConsole;
+import com.mesacarlos.webconsole.command.CommandFactory;
+import com.mesacarlos.webconsole.command.WSCommand;
+import com.mesacarlos.webconsole.util.LoginManager;
+
+public class WSServer extends WebSocketServer {
+ private WebConsole plugin;
+ private HashMap commands = CommandFactory.getCommandsHashMap();
+
+ public WSServer(WebConsole plugin, InetSocketAddress address) {
+ super(address);
+ this.plugin = plugin;
+ }
+
+ @Override
+ public void onOpen(WebSocket conn, ClientHandshake handshake) {
+ conn.send("Connection started, waiting login");
+ Bukkit.getLogger().info("[WebConsole] Connected and waiting login from " + conn.getRemoteSocketAddress());
+ }
+
+ @Override
+ 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
+ String wsCommand = message.split(" ")[0];
+ String wsCommandParams = "";
+ if (message.contains(" "))
+ wsCommandParams = message.split(" ", 2)[1];
+
+ // Run command
+ WSCommand cmd = commands.get(wsCommand);
+
+ if (cmd == null) {
+ Bukkit.getLogger().info(
+ "[WebConsole] Signal was not processed since is not valid. Is your plugin/web interface up to date?");
+ } else if (!LoginManager.getInstance().isLoggedIn(conn.getRemoteSocketAddress().getAddress().toString())
+ && !wsCommand.equals("LOGIN")) {
+ // DO NOTHING. User is not authorised
+ conn.send("403 Forbidden");
+ Bukkit.getLogger().warning("[WebConsole] " + conn.getRemoteSocketAddress()
+ + " tried to run a command while not authenticated!");
+ } else {
+ cmd.execute(this, conn, wsCommandParams);
+ }
+ }
+
+ @Override
+ public void onClose(WebSocket conn, int code, String reason, boolean remote) {
+ LoginManager.getInstance().logOut(conn.getRemoteSocketAddress().getAddress().toString());
+ Bukkit.getLogger()
+ .info("[WebConsole] Closed WS connection " + conn.getRemoteSocketAddress() + ". Reason: " + reason);
+ }
+
+ @Override
+ public void onError(WebSocket conn, Exception ex) {
+ Bukkit.getLogger()
+ .warning("[WebConsole] Error occured on connection " + conn.getRemoteSocketAddress() + ":" + ex);
+ }
+
+ @Override
+ public void onStart() {
+ Bukkit.getLogger().info("[WebConsole] WebSockets Server started successfully");
+ }
+
+ /**
+ * Sends the message to all connected AND logged-in users
+ */
+ public void onNewConsoleLinePrinted(String line) {
+ Collection connections = getConnections();
+ for (WebSocket connection : connections) {
+ if (LoginManager.getInstance().isLoggedIn(connection.getRemoteSocketAddress().getAddress().toString()))
+ connection.send("LOG " + line);
+ }
+ }
+
+ /**
+ * Returns main class
+ *
+ * @return
+ */
+ public WebConsole getMainClass() {
+ return plugin;
+ }
+
+}
\ No newline at end of file