Merge branch '3.6.0' into master

This commit is contained in:
Fuzzlemann 2017-07-29 21:44:53 +02:00 committed by GitHub
commit d1be610e96
23 changed files with 448 additions and 284 deletions

View File

@ -42,11 +42,14 @@ import main.java.com.djrapitops.plan.utilities.Benchmark;
import main.java.com.djrapitops.plan.utilities.Check; import main.java.com.djrapitops.plan.utilities.Check;
import main.java.com.djrapitops.plan.utilities.MiscUtils; import main.java.com.djrapitops.plan.utilities.MiscUtils;
import main.java.com.djrapitops.plan.utilities.metrics.BStats; import main.java.com.djrapitops.plan.utilities.metrics.BStats;
import org.bukkit.Bukkit; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.LoggerConfig;
import java.io.*; import java.io.*;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -186,7 +189,11 @@ public class Plan extends BukkitPlugin<Plan> {
Log.error("WebServer was not Initialized."); Log.error("WebServer was not Initialized.");
} }
// Prevent passwords showing up on console. // Prevent passwords showing up on console.
Bukkit.getLogger().setFilter(new RegisterCommandFilter()); LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
Collection<LoggerConfig> lc = ctx.getConfiguration().getLoggers().values();
for (LoggerConfig c : lc) {
c.addFilter(new RegisterCommandFilter());
}
} else if (!hasDataViewCapability) { } else if (!hasDataViewCapability) {
Log.infoColor(Phrase.ERROR_NO_DATA_VIEW.toString()); Log.infoColor(Phrase.ERROR_NO_DATA_VIEW.toString());
} }
@ -390,7 +397,7 @@ public class Plan extends BukkitPlugin<Plan> {
} }
try (InputStream inputStream = localeURL.openStream(); try (InputStream inputStream = localeURL.openStream();
OutputStream outputStream = new FileOutputStream(localeFile)) { OutputStream outputStream = new FileOutputStream(localeFile)) {
int read; int read;
byte[] bytes = new byte[1024]; byte[] bytes = new byte[1024];
@ -421,9 +428,8 @@ public class Plan extends BukkitPlugin<Plan> {
/** /**
* Stops initializing the locale * Stops initializing the locale
* *
* @implNote Removes clutter in the method
*
* @param usingLocale The locale that's used * @param usingLocale The locale that's used
* @implNote Removes clutter in the method
*/ */
private void stopInitLocale(String usingLocale) { private void stopInitLocale(String usingLocale) {
Benchmark.stop("Enable: Initializing locale"); Benchmark.stop("Enable: Initializing locale");

View File

@ -53,7 +53,7 @@ public enum Settings {
WEBSERVER_CERTIFICATE_KEYPASS("Settings.WebServer.Security.Certificate.KeyPass"), WEBSERVER_CERTIFICATE_KEYPASS("Settings.WebServer.Security.Certificate.KeyPass"),
WEBSERVER_CERTIFICATE_STOREPASS("Settings.WebServer.Security.Certificate.KeyPass"), WEBSERVER_CERTIFICATE_STOREPASS("Settings.WebServer.Security.Certificate.KeyPass"),
WEBSERVER_CERTIFICATE_ALIAS("Settings.WebServer.Security.Certificate.Alias"), WEBSERVER_CERTIFICATE_ALIAS("Settings.WebServer.Security.Certificate.Alias"),
LINK_PROTOCOL("Settings.WebServer.LinkProtocol"), LINK_PROTOCOL("Settings.WebServer.ExternalWebServerLinkProtocol"),
// //
SERVER_NAME("Customization.ServerName"), SERVER_NAME("Customization.ServerName"),
// //

View File

@ -68,23 +68,25 @@ public class AnalyzeCommand extends SubCommand {
} }
sender.sendMessage(Phrase.GRABBING_DATA_MESSAGE + ""); sender.sendMessage(Phrase.GRABBING_DATA_MESSAGE + "");
plugin.getRunnableFactory().createNew(new AbsRunnable("WebUser exist check task") { if (plugin.getUiServer().isAuthRequired()) {
@Override plugin.getRunnableFactory().createNew(new AbsRunnable("WebUser exist check task") {
public void run() { @Override
try { public void run() {
if (CommandUtils.isPlayer(sender)) { try {
boolean senderHasWebUser = plugin.getDB().getSecurityTable().userExists(sender.getName()); if (CommandUtils.isPlayer(sender)) {
if (!senderHasWebUser) { boolean senderHasWebUser = plugin.getDB().getSecurityTable().userExists(sender.getName());
sender.sendMessage(ChatColor.YELLOW + "[Plan] You might not have a web user, use /plan register <password>"); if (!senderHasWebUser) {
sender.sendMessage(ChatColor.YELLOW + "[Plan] You might not have a web user, use /plan register <password>");
}
} }
} catch (Exception e) {
Log.toLog(this.getClass().getName() + getName(), e);
} finally {
this.cancel();
} }
} catch (Exception e) {
Log.toLog(this.getClass().getName() + getName(), e);
} finally {
this.cancel();
} }
} }).runTaskAsynchronously();
}).runTaskAsynchronously(); }
updateCache(); updateCache();
runMessageSenderTask(sender); runMessageSenderTask(sender);
return true; return true;

View File

@ -90,7 +90,7 @@ public class InspectCommand extends SubCommand {
return; return;
} }
sender.sendMessage(Phrase.GRABBING_DATA_MESSAGE + ""); sender.sendMessage(Phrase.GRABBING_DATA_MESSAGE + "");
if (CommandUtils.isPlayer(sender)) { if (CommandUtils.isPlayer(sender) && plugin.getUiServer().isAuthRequired()) {
boolean senderHasWebUser = plugin.getDB().getSecurityTable().userExists(sender.getName()); boolean senderHasWebUser = plugin.getDB().getSecurityTable().userExists(sender.getName());
if (!senderHasWebUser) { if (!senderHasWebUser) {
sender.sendMessage(ChatColor.YELLOW + "[Plan] You might not have a web user, use /plan register <password>"); sender.sendMessage(ChatColor.YELLOW + "[Plan] You might not have a web user, use /plan register <password>");

View File

@ -1,7 +1,11 @@
package main.java.com.djrapitops.plan.command.commands; package main.java.com.djrapitops.plan.command.commands;
import java.util.logging.Filter; import org.apache.logging.log4j.Level;
import java.util.logging.LogRecord; 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;
/** /**
* Filters out WebUser registration command logs. * Filters out WebUser registration command logs.
@ -11,12 +15,126 @@ import java.util.logging.LogRecord;
*/ */
public class RegisterCommandFilter implements Filter { public class RegisterCommandFilter implements Filter {
@Override private boolean start = true;
public boolean isLoggable(LogRecord record) {
String message = record.getMessage(); private Result filter(String message) {
boolean block = message.contains("command: /plan register") boolean block = message.contains("command: /plan register")
|| message.contains("command: /plan web register") || message.contains("command: /plan web register")
|| message.contains("command: /plan webuser register"); || message.contains("command: /plan webuser register");
return !block; if (block) {
return Result.DENY;
}
return null;
}
@Override
public Result filter(LogEvent event) {
String message = event.getMessage().toString().toLowerCase();
return filter(message);
}
@Override
public Result filter(Logger arg0, Level arg1, Marker arg2, String arg3, Object... arg4) {
return filter(arg3);
}
@Override
public Result filter(Logger arg0, Level arg1, Marker arg2, Object arg3, Throwable arg4) {
return null;
}
@Override
public Result filter(Logger arg0, Level arg1, Marker arg2, Message arg3, Throwable arg4) {
return filter(arg3.toString());
}
@Override
public Result getOnMatch() {
return null;
}
@Override
public Result getOnMismatch() {
return null;
}
@Override
public Result filter(Logger logger, Level level, Marker marker, String s, Object o) {
return filter(s);
}
@Override
public Result filter(Logger logger, Level level, Marker marker, String s, Object o, Object o1) {
return filter(s);
}
@Override
public Result filter(Logger logger, Level level, Marker marker, String s, Object o, Object o1, Object o2) {
return filter(s);
}
@Override
public Result filter(Logger logger, Level level, Marker marker, String s, Object o, Object o1, Object o2, Object o3) {
return filter(s);
}
@Override
public Result filter(Logger logger, Level level, Marker marker, String s, Object o, Object o1, Object o2, Object o3, Object o4) {
return filter(s);
}
@Override
public Result filter(Logger logger, Level level, Marker marker, String s, Object o, Object o1, Object o2, Object o3, Object o4, Object o5) {
return filter(s);
}
@Override
public Result filter(Logger logger, Level level, Marker marker, String s, Object o, Object o1, Object o2, Object o3, Object o4, Object o5, Object o6) {
return filter(s);
}
@Override
public Result filter(Logger logger, Level level, Marker marker, String s, Object o, Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7) {
return filter(s);
}
@Override
public Result filter(Logger logger, Level level, Marker marker, String s, Object o, Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7, Object o8) {
return filter(s);
}
@Override
public Result filter(Logger logger, Level level, Marker marker, String s, Object o, Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7, Object o8, Object o9) {
return filter(s);
}
@Override
public State getState() {
return null;
}
@Override
public void initialize() {
}
@Override
public void start() {
start = true;
}
@Override
public void stop() {
start = false;
}
@Override
public boolean isStarted() {
return start;
}
@Override
public boolean isStopped() {
return !start;
} }
} }

View File

@ -805,7 +805,6 @@ public class UserData {
result = 31 * result + deaths; result = 31 * result + deaths;
result = 31 * result + name.hashCode(); result = 31 * result + name.hashCode();
result = 31 * result + (isOnline ? 1 : 0); result = 31 * result + (isOnline ? 1 : 0);
result = 31 * result + currentSession.hashCode();
return result; return result;
} }

View File

@ -42,7 +42,7 @@ import java.util.*;
public class DataCacheHandler extends SessionCache { public class DataCacheHandler extends SessionCache {
// Cache // Cache
private final HashMap<UUID, UserData> dataCache; private final Map<UUID, UserData> dataCache;
// Plan // Plan
private final Plan plugin; private final Plan plugin;

View File

@ -199,15 +199,17 @@ public class NicknamesTable extends Table {
lastNicks.put(id, nickname); lastNicks.put(id, nickname);
} }
} }
for (Map.Entry<Integer, String> entry : lastNicks.entrySet()) {
//TODO figure out what the heck that method does @Rsl1122 Integer id = entry.getKey();
for (Map.Entry<Integer, String> entrySet : lastNicks.entrySet()) { String lastNick = entry.getValue();
Integer id = entrySet.getKey();
String lastNick = entrySet.getValue();
List<String> list = nicks.get(id); List<String> list = nicks.get(id);
list.remove(lastNick); //NOTE: Remove here?
list.add(lastNick); //NOTE: And add here again? // Moves the last known nickname to the end of the List.
// This is due to the way nicknames are added to UserData,
// Nicknames are stored as a Set and last Nickname is a separate String.
list.remove(lastNick);
list.add(lastNick);
} }
return nicks; return nicks;

View File

@ -1,6 +1,5 @@
package main.java.com.djrapitops.plan.ui.html.graphs; package main.java.com.djrapitops.plan.ui.html.graphs;
import main.java.com.djrapitops.plan.Log;
import main.java.com.djrapitops.plan.data.SessionData; import main.java.com.djrapitops.plan.data.SessionData;
import main.java.com.djrapitops.plan.data.TPS; import main.java.com.djrapitops.plan.data.TPS;
import main.java.com.djrapitops.plan.utilities.MiscUtils; import main.java.com.djrapitops.plan.utilities.MiscUtils;
@ -41,8 +40,6 @@ public class PlayerActivityGraphCreator {
.map(session -> new Point[]{new Point(session.getSessionStart(), 1), new Point(session.getSessionEnd(), 0)}) .map(session -> new Point[]{new Point(session.getSessionStart(), 1), new Point(session.getSessionEnd(), 0)})
.flatMap(Arrays::stream) .flatMap(Arrays::stream)
.collect(Collectors.toList()); .collect(Collectors.toList());
Log.debug(points.stream().map(Point::getY).collect(Collectors.toList()).toString());
return ScatterGraphCreator.scatterGraph(points, true, false); return ScatterGraphCreator.scatterGraph(points, true, false);
} }

View File

@ -1,66 +0,0 @@
package main.java.com.djrapitops.plan.ui.webserver;
import com.sun.net.httpserver.BasicAuthenticator;
import main.java.com.djrapitops.plan.Log;
import main.java.com.djrapitops.plan.Plan;
import main.java.com.djrapitops.plan.data.WebUser;
import main.java.com.djrapitops.plan.database.tables.SecurityTable;
import main.java.com.djrapitops.plan.utilities.PassEncryptUtil;
import java.sql.SQLException;
public class Authenticator extends BasicAuthenticator {
private final Plan plugin;
public Authenticator(Plan plugin, String realm) {
super(realm);
this.plugin = plugin;
}
@Override
public boolean checkCredentials(String user, String pwd) {
try {
return isAuthorized(user, pwd, this.realm);
} catch (Exception e) {
Log.toLog(this.getClass().getName(), e);
return false;
}
}
private boolean isAuthorized(String user, String passwordRaw, String target) throws PassEncryptUtil.CannotPerformOperationException, PassEncryptUtil.InvalidHashException, SQLException {
SecurityTable securityTable = plugin.getDB().getSecurityTable();
if (!securityTable.userExists(user)) {
return false;
}
WebUser securityInfo = securityTable.getSecurityInfo(user);
boolean correctPass = PassEncryptUtil.verifyPassword(passwordRaw, securityInfo.getSaltedPassHash());
if (!correctPass) {
return false;
}
int permLevel = securityInfo.getPermLevel(); // Lower number has higher clearance.
int required = getRequiredPermLevel(target, securityInfo.getName());
return permLevel <= required;
}
private int getRequiredPermLevel(String target, String user) {
String[] t = target.split("/");
if (t.length < 3) {
return 0;
}
final String wantedUser = t[2].toLowerCase().trim();
final String theUser = user.trim().toLowerCase();
if (t[1].equals("players")) {
return 1;
}
if (t[1].equals("player")) {
if (wantedUser.equals(theUser)) {
return 2;
} else {
return 1;
}
}
return 0;
}
}

View File

@ -1,25 +1,30 @@
package main.java.com.djrapitops.plan.ui.webserver; package main.java.com.djrapitops.plan.ui.webserver;
import com.djrapitops.plugin.utilities.Verify;
import com.sun.net.httpserver.*; import com.sun.net.httpserver.*;
import main.java.com.djrapitops.plan.Log; import main.java.com.djrapitops.plan.Log;
import main.java.com.djrapitops.plan.Phrase; import main.java.com.djrapitops.plan.Phrase;
import main.java.com.djrapitops.plan.Plan; import main.java.com.djrapitops.plan.Plan;
import main.java.com.djrapitops.plan.Settings; import main.java.com.djrapitops.plan.Settings;
import main.java.com.djrapitops.plan.data.WebUser;
import main.java.com.djrapitops.plan.database.tables.SecurityTable;
import main.java.com.djrapitops.plan.ui.html.DataRequestHandler; import main.java.com.djrapitops.plan.ui.html.DataRequestHandler;
import main.java.com.djrapitops.plan.ui.webserver.response.*; import main.java.com.djrapitops.plan.ui.webserver.response.*;
import main.java.com.djrapitops.plan.utilities.HtmlUtils; import main.java.com.djrapitops.plan.utilities.HtmlUtils;
import main.java.com.djrapitops.plan.utilities.MiscUtils;
import main.java.com.djrapitops.plan.utilities.PassEncryptUtil;
import main.java.com.djrapitops.plan.utilities.uuid.UUIDUtility; import main.java.com.djrapitops.plan.utilities.uuid.UUIDUtility;
import org.bukkit.ChatColor;
import javax.net.ssl.*; import javax.net.ssl.*;
import java.io.FileInputStream; import java.io.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import java.security.*; import java.security.*;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.util.Base64;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
@ -35,7 +40,8 @@ public class WebServer {
private boolean enabled = false; private boolean enabled = false;
private HttpServer server; private HttpServer server;
private final int port; private final int port;
private boolean shutdown;
private boolean usingHttps;
/** /**
* Class Constructor. * Class Constructor.
@ -47,7 +53,6 @@ public class WebServer {
public WebServer(Plan plugin) { public WebServer(Plan plugin) {
this.plugin = plugin; this.plugin = plugin;
this.port = Settings.WEBSERVER_PORT.getNumber(); this.port = Settings.WEBSERVER_PORT.getNumber();
shutdown = false;
dataReqHandler = new DataRequestHandler(plugin); dataReqHandler = new DataRequestHandler(plugin);
} }
@ -62,83 +67,51 @@ public class WebServer {
Log.info(Phrase.WEBSERVER_INIT.toString()); Log.info(Phrase.WEBSERVER_INIT.toString());
try { try {
String keyStorePath = Settings.WEBSERVER_CERTIFICATE_PATH.toString(); usingHttps = startHttpsServer();
if (!keyStorePath.contains(":")) {
keyStorePath = plugin.getDataFolder() + keyStorePath;
}
char[] storepass = Settings.WEBSERVER_CERTIFICATE_STOREPASS.toString().toCharArray();
char[] keypass = Settings.WEBSERVER_CERTIFICATE_KEYPASS.toString().toCharArray();
String alias = Settings.WEBSERVER_CERTIFICATE_ALIAS.toString();
boolean startSuccessful = false; Log.debug(usingHttps ? "Https Start Successful." : "Https Start Failed.");
try (FileInputStream fIn = new FileInputStream(keyStorePath)) {
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(fIn, storepass); if (!usingHttps) {
Certificate cert = keystore.getCertificate(alias); server = HttpServer.create(new InetSocketAddress(port), 10);
Log.info("Found Certificate: " + cert.getType());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keystore, keypass);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustManagerFactory.init(keystore);
server = HttpsServer.create(new InetSocketAddress(port), 10);
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(keyManagerFactory.getKeyManagers(), null/*trustManagerFactory.getTrustManagers()*/, null);
((HttpsServer) server).setHttpsConfigurator(new HttpsConfigurator(sslContext) {
@Override
public void configure(HttpsParameters params) {
SSLEngine engine = sslContext.createSSLEngine();
params.setNeedClientAuth(false);
params.setCipherSuites(engine.getEnabledCipherSuites());
params.setProtocols(engine.getEnabledProtocols());
SSLParameters defaultSSLParameters = sslContext.getDefaultSSLParameters();
params.setSSLParameters(defaultSSLParameters);
}
});
startSuccessful = true;
} catch (KeyManagementException | NoSuchAlgorithmException e) {
Log.error("WebServer: SSL Context Initialization Failed.");
Log.toLog(this.getClass().getName(), e);
} catch (FileNotFoundException e) {
Log.error("!--------!---------!---------!");
Log.error("WebServer: SSL Certificate KeyStore File not Found: " + keyStorePath);
Log.error("!--------!---------!---------!");
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException e) {
Log.error("WebServer: SSL Certificate loading Failed.");
Log.toLog(this.getClass().getName(), e);
} }
Log.debug("Start Successful: " + startSuccessful); server.createContext("/", new HttpHandler() {
if (!startSuccessful) {
return; // TODO Http Server
}
Log.debug("Create server context");
HttpContext context = server.createContext("/", new HttpHandler() {
@Override @Override
public void handle(HttpExchange xghng) throws IOException { public void handle(HttpExchange exchange) throws IOException {
HttpsExchange exchange = (HttpsExchange) xghng; OutputStream os = null;
try { try {
URI uri = exchange.getRequestURI(); URI uri = exchange.getRequestURI();
String target = uri.toString(); String target = uri.toString();
Response response = getResponse(target);
WebUser user = null;
if (usingHttps) {
user = getUser(exchange.getRequestHeaders());
// Prompt authorization
if (user == null) {
Headers responseHeaders = exchange.getResponseHeaders();
responseHeaders.set("WWW-Authenticate", "Basic realm=\"/\";");
}
}
Response response = getResponse(target, user);
String content = response.getContent(); String content = response.getContent();
exchange.sendResponseHeaders(response.getCode(), content.length()); exchange.sendResponseHeaders(response.getCode(), content.length());
try (BufferedOutputStream out = new BufferedOutputStream(exchange.getResponseBody())) {
OutputStream os = exchange.getResponseBody(); try (ByteArrayInputStream bis = new ByteArrayInputStream(content.getBytes())) {
os.write(content.getBytes()); byte[] buffer = new byte[2048];
os.close(); int count;
while ((count = bis.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
}
}
} catch (Exception e) { } catch (Exception e) {
Log.toLog(this.getClass().getName(), e); Log.toLog(this.getClass().getName(), e);
throw e; throw e;
} finally {
MiscUtils.close(os);
exchange.close();
} }
} }
}); });
@ -150,6 +123,7 @@ public class WebServer {
server.setExecutor(new ThreadPoolExecutor(4, 8, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100))); server.setExecutor(new ThreadPoolExecutor(4, 8, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100)));
server.start(); server.start();
enabled = true; enabled = true;
Log.info(Phrase.WEBSERVER_RUNNING.parse(String.valueOf(server.getAddress().getPort()))); Log.info(Phrase.WEBSERVER_RUNNING.parse(String.valueOf(server.getAddress().getPort())));
@ -159,68 +133,192 @@ public class WebServer {
} }
} }
// if (!request.hasAuthorization()) { private WebUser getUser(Headers requestHeaders) {
// return new PromptAuthorizationResponse(output); try {
// } List<String> authorization = requestHeaders.get("Authorization");
// try { if (Verify.isEmpty(authorization)) {
// if (!isAuthorized(request)) { return null;
// ForbiddenResponse response403 = new ForbiddenResponse(output); }
// String content = "<h1>403 Forbidden - Access Denied</h1>" String auth = authorization.get(0);
// + "<p>Unauthorized User.<br>" if (auth.contains("Basic ")) {
// + "Make sure your user has the correct access level.<br>" auth = auth.split(" ")[1];
// + "You can use /plan web check <username> to check the permission level.</p>"; } else {
// response403.setContent(content); throw new IllegalArgumentException("Wrong format of Auth");
// return response403; }
// } Base64.Decoder decoder = Base64.getDecoder();
byte[] decoded = decoder.decode(auth);
String[] userInfo = new String(decoded).split(":");
if (userInfo.length != 2) {
throw new IllegalArgumentException("User and Password not specified");
}
String user = userInfo[0];
String passwordRaw = userInfo[1];
private Response getResponse(String target) { SecurityTable securityTable = plugin.getDB().getSecurityTable();
if (!securityTable.userExists(user)) {
throw new IllegalArgumentException("User Doesn't exist");
}
WebUser webUser = securityTable.getSecurityInfo(user);
boolean correctPass = PassEncryptUtil.verifyPassword(passwordRaw, webUser.getSaltedPassHash());
if (!correctPass) {
throw new IllegalArgumentException("User and Password do not match");
}
return webUser;
} catch (IllegalArgumentException e) {
Log.debug("WebServer: " + e.getMessage());
return null;
} catch (Exception e) {
Log.toLog(this.getClass().getName(), e);
return null;
}
}
private boolean startHttpsServer() throws IOException {
String keyStorePath = Settings.WEBSERVER_CERTIFICATE_PATH.toString();
if (!keyStorePath.contains(":")) {
keyStorePath = plugin.getDataFolder() + keyStorePath;
}
char[] storepass = Settings.WEBSERVER_CERTIFICATE_STOREPASS.toString().toCharArray();
char[] keypass = Settings.WEBSERVER_CERTIFICATE_KEYPASS.toString().toCharArray();
String alias = Settings.WEBSERVER_CERTIFICATE_ALIAS.toString();
boolean startSuccessful = false;
try (FileInputStream fIn = new FileInputStream(keyStorePath)) {
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(fIn, storepass);
Certificate cert = keystore.getCertificate(alias);
Log.info("Found Certificate: " + cert.getType());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keystore, keypass);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustManagerFactory.init(keystore);
server = HttpsServer.create(new InetSocketAddress(port), 10);
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(keyManagerFactory.getKeyManagers(), null/*trustManagerFactory.getTrustManagers()*/, null);
((HttpsServer) server).setHttpsConfigurator(new HttpsConfigurator(sslContext) {
@Override
public void configure(HttpsParameters params) {
SSLEngine engine = sslContext.createSSLEngine();
params.setNeedClientAuth(false);
params.setCipherSuites(engine.getEnabledCipherSuites());
params.setProtocols(engine.getEnabledProtocols());
SSLParameters defaultSSLParameters = sslContext.getDefaultSSLParameters();
params.setSSLParameters(defaultSSLParameters);
}
});
startSuccessful = true;
} catch (KeyManagementException | NoSuchAlgorithmException e) {
Log.error("WebServer: SSL Context Initialization Failed.");
Log.toLog(this.getClass().getName(), e);
} catch (FileNotFoundException e) {
Log.infoColor(ChatColor.YELLOW + "WebServer: SSL Certificate KeyStore File not Found: " + keyStorePath);
Log.info("No Certificate -> Using Http server for Visualization.");
Log.infoColor(ChatColor.YELLOW + "User Authorization Disabled! (Not possible over http)");
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException e) {
Log.error("WebServer: SSL Certificate loading Failed.");
Log.toLog(this.getClass().getName(), e);
}
return startSuccessful;
}
private Response getResponse(String target, WebUser user) {
if ("/favicon.ico".equals(target)) {
return new RedirectResponse("https://puu.sh/tK0KL/6aa2ba141b.ico");
}
if (usingHttps) {
if (user == null) {
return new PromptAuthorizationResponse();
}
int permLevel = user.getPermLevel(); // Lower number has higher clearance.
int required = getRequiredPermLevel(target, user.getName());
if (permLevel > required) {
return forbiddenResponse(permLevel, required);
}
}
String[] args = target.split("/"); String[] args = target.split("/");
if (args.length < 2) { if (args.length < 2) {
return responseNotFound(null); return rootPageResponse(user);
} }
String page = args[1]; String page = args[1];
switch (page) { switch (page) {
case "favicon.ico":
return new RedirectResponse(null, "https://puu.sh/tK0KL/6aa2ba141b.ico");
case "players": case "players":
return new PlayersPageResponse(null, plugin); return new PlayersPageResponse(plugin);
case "player": case "player":
return playerResponse(args, null); return playerResponse(args);
case "server": case "server":
return serverResponse(null); return serverResponse();
default: default:
return responseNotFound(null); return notFoundResponse();
} }
} }
private Response serverResponse(OutputStream output) { private ForbiddenResponse forbiddenResponse(int permLevel, int required) {
ForbiddenResponse response403 = new ForbiddenResponse();
String content = "<h1>403 Forbidden - Access Denied</h1>"
+ "<p>Unauthorized User.<br>"
+ "Make sure your user has the correct access level.<br>"
+ "This page requires permission level of " + String.valueOf(required) + ",<br>"
+ "This user has permission level of " + String.valueOf(permLevel) + "</p>";
response403.setContent(content);
return response403;
}
private Response rootPageResponse(WebUser user) {
if (user == null) {
return notFoundResponse();
}
switch (user.getPermLevel()) {
case 0:
return serverResponse();
case 1:
return new PlayersPageResponse(plugin);
case 2:
return playerResponse(new String[]{"", "", user.getName()});
default:
return forbiddenResponse(user.getPermLevel(), 0);
}
}
private Response serverResponse() {
if (!dataReqHandler.checkIfAnalysisIsCached()) { if (!dataReqHandler.checkIfAnalysisIsCached()) {
return new NotFoundResponse(output, "Analysis data was not cached."); return new NotFoundResponse("Analysis Data was not cached.<br>Use /plan analyze to cache the Data.");
} }
return new AnalysisPageResponse(output, dataReqHandler); return new AnalysisPageResponse(dataReqHandler);
} }
private Response playerResponse(String[] args, OutputStream output) { private Response playerResponse(String[] args) {
if (args.length < 3) { if (args.length < 3) {
return new NotFoundResponse(output); return new NotFoundResponse();
} }
String playerName = args[2].trim(); String playerName = args[2].trim();
UUID uuid = UUIDUtility.getUUIDOf(playerName); UUID uuid = UUIDUtility.getUUIDOf(playerName);
if (uuid == null) { if (uuid == null) {
return new NotFoundResponse(output, "Player has no UUID"); return new NotFoundResponse("Player has no UUID");
} }
if (!dataReqHandler.checkIfCached(uuid)) { if (!dataReqHandler.checkIfCached(uuid)) {
return new NotFoundResponse(output, "Player's data was not cached."); return new NotFoundResponse("Player's data was not cached.<br>Use /plan inspect " + playerName + " to cache the Data.");
} }
return new InspectPageResponse(output, dataReqHandler, uuid); return new InspectPageResponse(dataReqHandler, uuid);
} }
private Response responseNotFound(OutputStream output) { private Response notFoundResponse() {
NotFoundResponse response404 = new NotFoundResponse(output); NotFoundResponse response404 = new NotFoundResponse();
String content = "<h1>404 Not Found</h1>" String content = "<h1>404 Not Found</h1>"
+ "<p>Make sure you're accessing a link given by a command, Examples:</p>" + "<p>Make sure you're accessing a link given by a command, Examples:</p>"
+ "<p>" + HtmlUtils.getInspectUrl("<player>") + " or<br>" + "<p>" + getProtocol() + HtmlUtils.getInspectUrl("<player>") + " or<br>"
+ HtmlUtils.getServerAnalysisUrl() + "</p>"; + getProtocol() + HtmlUtils.getServerAnalysisUrl() + "</p>";
response404.setContent(content); response404.setContent(content);
return response404; return response404;
} }
@ -237,16 +335,58 @@ public class WebServer {
*/ */
public void stop() { public void stop() {
Log.info(Phrase.WEBSERVER_CLOSE.toString()); Log.info(Phrase.WEBSERVER_CLOSE.toString());
shutdown = true;
if (server != null) { if (server != null) {
server.stop(0); server.stop(0);
} }
} }
/** /**
* Used to get the handler for Html content requests.
*
* @return DataRequestHandler used by the WebServer. * @return DataRequestHandler used by the WebServer.
*/ */
public DataRequestHandler getDataReqHandler() { public DataRequestHandler getDataReqHandler() {
return dataReqHandler; return dataReqHandler;
} }
private int getRequiredPermLevel(String target, String user) {
String[] t = target.split("/");
if (t.length < 2) {
return 100;
}
if (t.length > 3) {
return 0;
}
String page = t[1];
switch (page) {
case "players":
return 1;
case "player":
// /player/ - 404 for perm lvl 1
if (t.length < 3) {
return 1;
}
final String wantedUser = t[2].toLowerCase().trim();
final String theUser = user.trim().toLowerCase();
if (wantedUser.equals(theUser)) {
return 2;
} else {
return 1;
}
default:
return 0;
}
}
public String getProtocol() {
return usingHttps ? "https" : "http";
}
public boolean usingHttps() {
return usingHttps;
}
public boolean isAuthRequired() {
return usingHttps;
}
} }

View File

@ -2,16 +2,13 @@ package main.java.com.djrapitops.plan.ui.webserver.response;
import main.java.com.djrapitops.plan.ui.html.DataRequestHandler; import main.java.com.djrapitops.plan.ui.html.DataRequestHandler;
import java.io.OutputStream;
/** /**
* @author Rsl1122 * @author Rsl1122
* @since 3.5.2 * @since 3.5.2
*/ */
public class AnalysisPageResponse extends Response { public class AnalysisPageResponse extends Response {
public AnalysisPageResponse(OutputStream output, DataRequestHandler h) { public AnalysisPageResponse(DataRequestHandler h) {
super(output);
super.setHeader("HTTP/1.1 200 OK"); super.setHeader("HTTP/1.1 200 OK");
super.setContent(h.getAnalysisHtml()); super.setContent(h.getAnalysisHtml());
} }

View File

@ -1,15 +1,12 @@
package main.java.com.djrapitops.plan.ui.webserver.response; package main.java.com.djrapitops.plan.ui.webserver.response;
import java.io.OutputStream;
/** /**
* @author Rsl1122 * @author Rsl1122
* @since 3.5.2 * @since 3.5.2
*/ */
public class ForbiddenResponse extends Response { public class ForbiddenResponse extends Response {
public ForbiddenResponse(OutputStream output) { public ForbiddenResponse() {
super(output);
super.setHeader("HTTP/1.1 403 Forbidden"); super.setHeader("HTTP/1.1 403 Forbidden");
} }
} }

View File

@ -2,7 +2,6 @@ package main.java.com.djrapitops.plan.ui.webserver.response;
import main.java.com.djrapitops.plan.ui.html.DataRequestHandler; import main.java.com.djrapitops.plan.ui.html.DataRequestHandler;
import java.io.OutputStream;
import java.util.UUID; import java.util.UUID;
/** /**
@ -11,8 +10,7 @@ import java.util.UUID;
*/ */
public class InspectPageResponse extends Response { public class InspectPageResponse extends Response {
public InspectPageResponse(OutputStream output, DataRequestHandler h, UUID uuid) { public InspectPageResponse(DataRequestHandler h, UUID uuid) {
super(output);
super.setHeader("HTTP/1.1 200 OK"); super.setHeader("HTTP/1.1 200 OK");
super.setContent(h.getInspectHtml(uuid)); super.setContent(h.getInspectHtml(uuid));
} }

View File

@ -2,16 +2,13 @@ package main.java.com.djrapitops.plan.ui.webserver.response;
import main.java.com.djrapitops.plan.ui.html.Html; import main.java.com.djrapitops.plan.ui.html.Html;
import java.io.OutputStream;
/** /**
* @author Rsl1122 * @author Rsl1122
* @since 3.5.2 * @since 3.5.2
*/ */
public class InternalErrorResponse extends Response { public class InternalErrorResponse extends Response {
public InternalErrorResponse(OutputStream output, Throwable e, String cause) { public InternalErrorResponse(Throwable e, String cause) {
super(output);
super.setHeader("HTTP/1.1 500 Internal Error"); super.setHeader("HTTP/1.1 500 Internal Error");
StringBuilder content = new StringBuilder(); StringBuilder content = new StringBuilder();
content.append("<h1>500 Internal Error occurred</h1>"); content.append("<h1>500 Internal Error occurred</h1>");

View File

@ -4,7 +4,6 @@ import main.java.com.djrapitops.plan.Log;
import main.java.com.djrapitops.plan.utilities.HtmlUtils; import main.java.com.djrapitops.plan.utilities.HtmlUtils;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.OutputStream;
/** /**
* @author Rsl1122 * @author Rsl1122
@ -12,8 +11,7 @@ import java.io.OutputStream;
*/ */
public class JavaScriptResponse extends Response { public class JavaScriptResponse extends Response {
public JavaScriptResponse(OutputStream output, String resource) { public JavaScriptResponse(String resource) {
super(output);
super.setHeader("HTTP/1.1 200 OK"); super.setHeader("HTTP/1.1 200 OK");
try { try {
super.setContent(HtmlUtils.getStringFromResource(resource)); super.setContent(HtmlUtils.getStringFromResource(resource));

View File

@ -1,21 +1,17 @@
package main.java.com.djrapitops.plan.ui.webserver.response; package main.java.com.djrapitops.plan.ui.webserver.response;
import java.io.OutputStream;
/** /**
* @author Rsl1122 * @author Rsl1122
* @since 3.5.2 * @since 3.5.2
*/ */
public class NotFoundResponse extends Response { public class NotFoundResponse extends Response {
public NotFoundResponse(OutputStream output) { public NotFoundResponse() {
super(output);
super.setHeader("HTTP/1.1 404 Not Found"); super.setHeader("HTTP/1.1 404 Not Found");
super.setContent("<h1>404 Not Found</h1><p>Page does not exist.</p>"); super.setContent("<h1>404 Not Found</h1><p>Page does not exist.</p>");
} }
public NotFoundResponse(OutputStream output, String msg) { public NotFoundResponse(String msg) {
super(output);
super.setHeader("HTTP/1.1 404 Not Found"); super.setHeader("HTTP/1.1 404 Not Found");
super.setContent("<h1>404 Not Found</h1><p>" + msg + "</p>"); super.setContent("<h1>404 Not Found</h1><p>" + msg + "</p>");
} }

View File

@ -6,7 +6,6 @@ import main.java.com.djrapitops.plan.ui.html.Html;
import main.java.com.djrapitops.plan.utilities.HtmlUtils; import main.java.com.djrapitops.plan.utilities.HtmlUtils;
import main.java.com.djrapitops.plan.utilities.comparators.UserDataNameComparator; import main.java.com.djrapitops.plan.utilities.comparators.UserDataNameComparator;
import java.io.OutputStream;
import java.util.List; import java.util.List;
/** /**
@ -15,14 +14,13 @@ import java.util.List;
*/ */
public class PlayersPageResponse extends Response { public class PlayersPageResponse extends Response {
public PlayersPageResponse(OutputStream output, Plan plugin) { public PlayersPageResponse(Plan plugin) {
super(output);
super.setHeader("HTTP/1.1 200 OK"); super.setHeader("HTTP/1.1 200 OK");
super.setContent(buildContent(plugin.getInspectCache().getCachedUserData())); super.setContent(buildContent(plugin.getInspectCache().getCachedUserData()));
} }
public static String buildContent(List<UserData> cached) { public static String buildContent(List<UserData> cached) {
StringBuilder html = new StringBuilder("<h1>Cached Players</h1><p>"); StringBuilder html = new StringBuilder("<!DOCTYPE html><html><body><h1>Cached Players</h1><p>");
int size = cached.size(); int size = cached.size();
html.append(size) html.append(size)
@ -39,7 +37,7 @@ public class PlayersPageResponse extends Response {
} }
i++; i++;
} }
html.append("</tr></table>"); html.append("</tr></table></body></html>");
return html.toString(); return html.toString();
} }
} }

View File

@ -1,17 +1,14 @@
package main.java.com.djrapitops.plan.ui.webserver.response; package main.java.com.djrapitops.plan.ui.webserver.response;
import java.io.OutputStream;
/** /**
* @author Rsl1122 * @author Rsl1122
* @since 3.5.2 * @since 3.5.2
*/ */
public class PromptAuthorizationResponse extends Response { public class PromptAuthorizationResponse extends Response {
public PromptAuthorizationResponse(OutputStream output) { public PromptAuthorizationResponse() {
super(output);
super.setHeader("HTTP/1.1 401 Access Denied\r\n" super.setHeader("HTTP/1.1 401 Access Denied\r\n"
+ "WWW-Authenticate: Basic realm=\"Analysis\";"); + "WWW-Authenticate: Basic realm=\"/\";");
super.setContent("<h1>401 Unauthorized</h1><p>Authentication Failed.<br>" super.setContent("<h1>401 Unauthorized</h1><p>Authentication Failed.<br>"
+ "- Ensure you have registered a user with <b>/plan register</b><br>" + "- Ensure you have registered a user with <b>/plan register</b><br>"
+ "- Check that the username and password are correct<br>" + "- Check that the username and password are correct<br>"

View File

@ -1,15 +1,12 @@
package main.java.com.djrapitops.plan.ui.webserver.response; package main.java.com.djrapitops.plan.ui.webserver.response;
import java.io.OutputStream;
/** /**
* @author Rsl1122 * @author Rsl1122
* @since 3.5.2 * @since 3.5.2
*/ */
public class RedirectResponse extends Response { public class RedirectResponse extends Response {
public RedirectResponse(OutputStream output, String direct) { public RedirectResponse(String direct) {
super(output);
super.setHeader("HTTP/1.1 302 Found"); super.setHeader("HTTP/1.1 302 Found");
super.setContent("Location: " + direct); super.setContent("Location: " + direct);
} }

View File

@ -1,39 +1,18 @@
package main.java.com.djrapitops.plan.ui.webserver.response; package main.java.com.djrapitops.plan.ui.webserver.response;
import java.io.IOException;
import java.io.OutputStream;
/** /**
* @author Rsl1122 * @author Rsl1122
* @since 3.5.2 * @since 3.5.2
*/ */
public abstract class Response { public abstract class Response {
private final OutputStream output;
private String header; private String header;
private String content; private String content;
/** /**
* Class Constructor. * Class Constructor.
*
* @param output Website OutputStream to write the response to.
*/ */
public Response(OutputStream output) { public Response() {
this.output = output;
}
/**
* Writes the HTML to the OutputStream according to the requested page.
*
* @throws IOException
*/
public void sendStaticResource() throws IOException {
String response = getResponse();
// Log.debug("Response: " + response); // Responses should not be logged, html content large.
output.write(response.getBytes());
output.flush();
} }
public String getResponse() { public String getResponse() {

View File

@ -3,6 +3,7 @@ package main.java.com.djrapitops.plan.utilities;
import main.java.com.djrapitops.plan.Plan; import main.java.com.djrapitops.plan.Plan;
import main.java.com.djrapitops.plan.Settings; import main.java.com.djrapitops.plan.Settings;
import main.java.com.djrapitops.plan.ui.html.Html; import main.java.com.djrapitops.plan.ui.html.Html;
import main.java.com.djrapitops.plan.ui.webserver.WebServer;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@ -73,28 +74,44 @@ public class HtmlUtils {
* @return * @return
*/ */
public static String getServerAnalysisUrlWithProtocol() { public static String getServerAnalysisUrlWithProtocol() {
return Settings.LINK_PROTOCOL.toString() + ":" + getServerAnalysisUrl(); return getProtocol() + ":" + getServerAnalysisUrl();
} }
/** /**
* @return * @return
*/ */
public static String getServerAnalysisUrl() { public static String getServerAnalysisUrl() {
int port = Settings.WEBSERVER_PORT.getNumber(); String ip = getIP();
String ip = Plan.getInstance().getVariable().getIp() + ":" + port;
boolean useAlternativeIP = Settings.SHOW_ALTERNATIVE_IP.isTrue();
if (useAlternativeIP) {
ip = Settings.ALTERNATIVE_IP.toString().replace("%port%", String.valueOf(port));
}
return "//" + ip + "/server"; return "//" + ip + "/server";
} }
/**
* Used to get the WebServer's IP with Port.
*
* @return For example 127.0.0.1:8804
*/
public static String getIP() {
int port = Settings.WEBSERVER_PORT.getNumber();
String ip;
if (Settings.SHOW_ALTERNATIVE_IP.isTrue()) {
ip = Settings.ALTERNATIVE_IP.toString().replace("%port%", String.valueOf(port));
} else {
ip = Plan.getInstance().getVariable().getIp() + ":" + port;
}
return ip;
}
private static String getProtocol() {
WebServer uiServer = Plan.getInstance().getUiServer();
return uiServer.isEnabled() ? uiServer.getProtocol() : Settings.LINK_PROTOCOL.toString();
}
/** /**
* @param playerName * @param playerName
* @return * @return
*/ */
public static String getInspectUrlWithProtocol(String playerName) { public static String getInspectUrlWithProtocol(String playerName) {
return Settings.LINK_PROTOCOL.toString() + ":" + getInspectUrl(playerName); return getProtocol() + ":" + getInspectUrl(playerName);
} }
/** /**
@ -102,12 +119,7 @@ public class HtmlUtils {
* @return * @return
*/ */
public static String getInspectUrl(String playerName) { public static String getInspectUrl(String playerName) {
int port = Settings.WEBSERVER_PORT.getNumber(); String ip = getIP();
String ip = Plan.getInstance().getVariable().getIp() + ":" + port;
boolean useAlternativeIP = Settings.SHOW_ALTERNATIVE_IP.isTrue();
if (useAlternativeIP) {
ip = Settings.ALTERNATIVE_IP.toString().replace("%port%", String.valueOf(port));
}
return "//" + ip + "/player/" + playerName; return "//" + ip + "/player/" + playerName;
} }

View File

@ -34,7 +34,7 @@ Settings:
InternalIP: 0.0.0.0 InternalIP: 0.0.0.0
ShowAlternativeServerIP: false ShowAlternativeServerIP: false
AlternativeIP: your.ip.here:%port% AlternativeIP: your.ip.here:%port%
LinkProtocol: http ExternalWebServerLinkProtocol: http
Security: Security:
DisplayIPsAndUUIDs: true DisplayIPsAndUUIDs: true
Certificate: Certificate: