First commit

This commit is contained in:
Carlos 2019-08-11 02:46:36 +02:00
parent 107658bf9b
commit 5adbe63ede
13 changed files with 616 additions and 12 deletions

83
.gitignore vendored
View File

@ -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

31
README.md Normal file
View File

@ -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|

7
plugin.yml Normal file
View File

@ -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:

83
pom.xml Normal file
View File

@ -0,0 +1,83 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>WebConsole</groupId>
<artifactId>WebConsole</artifactId>
<version>1.0</version>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>.</directory>
<includes>
<include>**/*.yml</include>
</includes>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<!--Bungeecord Repo -->
<!--Only include if using the Spigot API dependency -->
<repository>
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<dependencies>
<!--Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.14.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- WebSockets Server Dependency -->
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.4.0</version>
</dependency>
<!-- Logger -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
</dependencies>
</project>

View File

@ -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;
}
}

View File

@ -0,0 +1,13 @@
package com.mesacarlos.webconsole.command;
import java.util.HashMap;
public class CommandFactory {
public static HashMap<String, WSCommand> getCommandsHashMap() {
HashMap<String, WSCommand> commands = new HashMap<String, WSCommand>();
commands.put("LOGIN", new LogInCommand());
commands.put("EXEC", new ExecuteCmdCommand());
return commands;
}
}

View File

@ -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();
}
}
}

View File

@ -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());
}
}
}

View File

@ -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);
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,29 @@
package com.mesacarlos.webconsole.util;
import java.util.ArrayList;
import java.util.List;
public class LoginManager {
private List<String> loggedInUsers = new ArrayList<String>();
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);
}
}

View File

@ -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<String, WSCommand> 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<WebSocket> 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;
}
}