Finished basic responses for ResponseHandler

This commit is contained in:
Rsl1122 2018-01-14 21:31:31 +02:00
parent 65b45ce92c
commit 15c6e17173
24 changed files with 281 additions and 444 deletions

View File

@ -3,6 +3,7 @@ package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.Plan;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.api.exceptions.database.FatalDBException;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.Msg;
@ -95,9 +96,13 @@ public class ManageClearCommand extends SubCommand {
new Session(now, player.getWorld().getName(), player.getGameMode().name()))
);
sender.sendMessage(Locale.get(Msg.MANAGE_INFO_CLEAR_SUCCESS).toString());
} catch (FatalDBException e) {
sender.sendMessage(Locale.get(Msg.MANAGE_INFO_FAIL).toString()
+ " Error was fatal, so all information may not have been removed.");
Log.toLog(this.getClass(), e);
} catch (DBException e) {
sender.sendMessage(Locale.get(Msg.MANAGE_INFO_FAIL).toString());
Log.toLog(this.getClass().getSimpleName() + "/" + this.getTaskName(), e);
Log.toLog(this.getClass(), e);
} finally {
this.cancel();
}

View File

@ -5,6 +5,7 @@ import com.djrapitops.plan.data.PlayerProfile;
import com.djrapitops.plan.data.ServerProfile;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@ -19,4 +20,6 @@ public interface FetchOperations {
Set<UUID> getSavedUUIDs(UUID server) throws DBException;
Map<UUID, String> getServerNames() throws DBException;
Optional<UUID> getServerUUID(String serverName) throws DBException;
}

View File

@ -9,6 +9,10 @@ import com.djrapitops.plan.system.database.databases.operation.BackupOperations;
import com.djrapitops.plan.system.database.databases.operation.CheckOperations;
import com.djrapitops.plan.system.database.databases.operation.FetchOperations;
import com.djrapitops.plan.system.database.databases.operation.RemoveOperations;
import com.djrapitops.plan.system.database.databases.sql.operation.SQLBackupOps;
import com.djrapitops.plan.system.database.databases.sql.operation.SQLCheckOps;
import com.djrapitops.plan.system.database.databases.sql.operation.SQLFetchOps;
import com.djrapitops.plan.system.database.databases.sql.operation.SQLRemoveOps;
import com.djrapitops.plan.system.database.databases.sql.tables.*;
import com.djrapitops.plan.system.database.databases.sql.tables.move.Version8TransferTable;
import com.djrapitops.plan.utilities.MiscUtils;

View File

@ -1,7 +1,8 @@
package com.djrapitops.plan.system.database.databases.sql;
package com.djrapitops.plan.system.database.databases.sql.operation;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.database.databases.operation.BackupOperations;
import com.djrapitops.plan.system.database.databases.sql.SQLDB;
public class SQLBackupOps implements BackupOperations {

View File

@ -1,7 +1,9 @@
package com.djrapitops.plan.system.database.databases.sql;
package com.djrapitops.plan.system.database.databases.sql.operation;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.system.database.databases.operation.CheckOperations;
import com.djrapitops.plan.system.database.databases.sql.ErrorUtil;
import com.djrapitops.plan.system.database.databases.sql.SQLDB;
import java.sql.SQLException;
import java.util.UUID;

View File

@ -1,4 +1,4 @@
package com.djrapitops.plan.system.database.databases.sql;
package com.djrapitops.plan.system.database.databases.sql.operation;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.data.PlayerProfile;
@ -6,6 +6,8 @@ import com.djrapitops.plan.data.ServerProfile;
import com.djrapitops.plan.data.container.Session;
import com.djrapitops.plan.data.container.UserInfo;
import com.djrapitops.plan.system.database.databases.operation.FetchOperations;
import com.djrapitops.plan.system.database.databases.sql.ErrorUtil;
import com.djrapitops.plan.system.database.databases.sql.SQLDB;
import java.sql.SQLException;
import java.util.*;
@ -97,4 +99,13 @@ public class SQLFetchOps extends SQLOps implements FetchOperations {
throw ErrorUtil.getExceptionFor(e);
}
}
@Override
public Optional<UUID> getServerUUID(String serverName) throws DBException {
try {
return serverTable.getServerUUID(serverName);
} catch (SQLException e) {
throw ErrorUtil.getExceptionFor(e);
}
}
}

View File

@ -1,5 +1,6 @@
package com.djrapitops.plan.system.database.databases.sql;
package com.djrapitops.plan.system.database.databases.sql.operation;
import com.djrapitops.plan.system.database.databases.sql.SQLDB;
import com.djrapitops.plan.system.database.databases.sql.tables.*;
public class SQLOps {

View File

@ -1,8 +1,10 @@
package com.djrapitops.plan.system.database.databases.sql;
package com.djrapitops.plan.system.database.databases.sql.operation;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.FatalDBException;
import com.djrapitops.plan.system.database.databases.operation.RemoveOperations;
import com.djrapitops.plan.system.database.databases.sql.ErrorUtil;
import com.djrapitops.plan.system.database.databases.sql.SQLDB;
import com.djrapitops.plan.system.database.databases.sql.tables.Table;
import com.djrapitops.plan.system.database.databases.sql.tables.UserIDTable;
import com.djrapitops.plugin.api.Benchmark;
@ -59,7 +61,7 @@ public class SQLRemoveOps extends SQLOps implements RemoveOperations {
table.removeAllData();
}
} catch (SQLException e) {
throw ErrorUtil.getExceptionFor(e);
throw ErrorUtil.getFatalExceptionFor(e);
}
}
@ -68,7 +70,7 @@ public class SQLRemoveOps extends SQLOps implements RemoveOperations {
try {
securityTable.removeUser(userName);
} catch (SQLException e) {
throw ErrorUtil.getExceptionFor(e);
throw ErrorUtil.getFatalExceptionFor(e);
}
}
}

View File

@ -1,54 +0,0 @@
/*
* Licence is provided in the jar as license.yml also here:
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml
*/
package com.djrapitops.plan.system.webserver;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.webapi.WebAPIManager;
import com.djrapitops.plugin.api.Benchmark;
import com.djrapitops.plugin.api.utility.log.Log;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
/**
* HttpHandler for webserver request management.
*
* @author Rsl1122
*/
public class APIRequestHandler implements HttpHandler {
private final APIResponseHandler responseHandler;
APIRequestHandler(WebAPIManager webAPI) {
responseHandler = new APIResponseHandler(webAPI);
}
@Override
public void handle(HttpExchange exchange) {
Headers responseHeaders = exchange.getResponseHeaders();
Request request = new Request(exchange);
String requestString = request.toString();
Benchmark.start("", requestString);
int responseCode = -1;
try {
Response response = responseHandler.getAPIResponse(request);
responseCode = response.getCode();
response.setResponseHeaders(responseHeaders);
response.send(exchange);
} catch (Exception e) {
if (Settings.DEV_MODE.isTrue()) {
Log.toLog(this.getClass().getName(), e);
}
} finally {
exchange.close();
if (Settings.DEV_MODE.isTrue()) {
Log.debug(requestString + " Response code: " + responseCode + " took " + Benchmark.stop("", requestString) + " ms");
}
}
}
}

View File

@ -1,167 +0,0 @@
/*
* Licence is provided in the jar as license.yml also here:
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml
*/
package com.djrapitops.plan.system.webserver;
import com.djrapitops.plan.system.webserver.pagecache.PageId;
import com.djrapitops.plan.system.webserver.pagecache.ResponseCache;
import com.djrapitops.plan.system.webserver.response.CSSResponse;
import com.djrapitops.plan.system.webserver.response.JavaScriptResponse;
import com.djrapitops.plan.system.webserver.response.RedirectResponse;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.api.BadRequestResponse;
import com.djrapitops.plan.system.webserver.response.errors.ForbiddenResponse;
import com.djrapitops.plan.system.webserver.response.errors.NotFoundResponse;
import com.djrapitops.plan.system.webserver.response.pages.DebugPageResponse;
import com.djrapitops.plan.system.webserver.webapi.WebAPI;
import com.djrapitops.plan.system.webserver.webapi.WebAPIManager;
import com.djrapitops.plan.utilities.html.Html;
import com.djrapitops.plugin.api.utility.log.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* Handles choosing of the correct API response to an API request.
*
* @author Rsl1122
*/
public class APIResponseHandler {
private final WebAPIManager webAPI;
public APIResponseHandler(WebAPIManager webAPI) {
this.webAPI = webAPI;
}
Response getAPIResponse(Request request) throws IOException {
String target = request.getTarget();
String[] args = target.split("/");
if ("/favicon.ico".equalsIgnoreCase(target)) {
return ResponseCache.loadResponse(PageId.FAVICON_REDIRECT.id(), () -> new RedirectResponse("https://puu.sh/tK0KL/6aa2ba141b.ico"));
}
if ("/debug".equalsIgnoreCase(target)) {
return new DebugPageResponse();
}
if (target.endsWith(".css")) {
return ResponseCache.loadResponse(PageId.CSS.of(target), () -> new CSSResponse(target));
}
if (target.endsWith(".js")) {
return ResponseCache.loadResponse(PageId.JS.of(target), () -> new JavaScriptResponse(target));
}
if (args.length < 2 || !"api".equals(args[1])) {
String address = PlanPlugin.getInstance().getInfoManager().getWebServerAddress() + target;
String link = Html.LINK.parse(address, address);
return ResponseCache.loadResponse(PageId.ERROR.of("Non-API Request"), () -> new NotFoundResponse("WebServer is in WebAPI-only mode, " +
"connect to the Bungee server instead: " + link));
}
if (args.length < 3) {
String error = "API Method not specified";
return ResponseCache.loadResponse(PageId.ERROR.of(error), () -> new BadRequestResponse(error));
}
String method = args[2];
String requestBody;
try (InputStream inputStream = request.getRequestBody()) {
requestBody = readPOSTRequest(inputStream);
}
if (requestBody == null) {
String error = "Error at reading the POST request." +
"Note that the Encoding must be ISO-8859-1.";
return ResponseCache.loadResponse(PageId.ERROR.of(error), () -> new BadRequestResponse(error));
}
Map<String, String> variables = WebAPI.readVariables(requestBody);
String sender = variables.get("sender");
Log.debug("Received WebAPI Request" + target + " from " + sender);
boolean isPing = "pingwebapi".equalsIgnoreCase(method);
boolean isSetupRequest = "requestsetupwebapi".equalsIgnoreCase(method);
boolean isPostOriginalSettings = "postoriginalbukkitsettingswebapi".equalsIgnoreCase(method);
boolean skipAuthCheck = isPing || isSetupRequest || isPostOriginalSettings;
// TODO refactor to more methods
if (!skipAuthCheck) {
String accessKey = variables.get("accessKey");
if (accessKey == null) {
if (!checkKey(sender)) {
String error = "Server Key not given or invalid";
Log.debug("Request had invalid Server key: " + sender);
return ResponseCache.loadResponse(PageId.ERROR.of(error), () -> {
ForbiddenResponse forbidden = new ForbiddenResponse();
forbidden.setContent(error);
return forbidden;
});
}
} else {
if (!webAPI.isAuthorized(accessKey)) {
String error = "Access Key invalid";
Log.debug("Request had invalid Access key: " + accessKey);
return ResponseCache.loadResponse(PageId.ERROR.of(error), () -> {
ForbiddenResponse forbidden = new ForbiddenResponse();
forbidden.setContent(error);
return forbidden;
});
}
}
}
WebAPI api = webAPI.getAPI(method);
if (api == null) {
String error = "API Method not found";
Log.debug(error);
return ResponseCache.loadResponse(PageId.ERROR.of(error), () -> new BadRequestResponse(error));
}
Response response = api.processRequest(PlanPlugin.getInstance(), variables);
Log.debug("Response: " + response.getResponse().split("\r\n")[0]);
return response;
}
private String readPOSTRequest(InputStream in) throws IOException {
byte[] bytes;
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
for (int n = in.read(buf); n > 0; n = in.read(buf)) {
out.write(buf, 0, n);
}
bytes = out.toByteArray();
try {
return new String(bytes, StandardCharsets.ISO_8859_1);
} catch (Exception e) {
return null;
}
}
private boolean checkKey(String sender) {
if (sender == null) {
return false;
}
try {
List<UUID> uuids = PlanPlugin.getInstance().getDB().getServerTable().getServerUUIDs();
UUID keyUUID = UUID.fromString(sender);
return uuids.contains(keyUUID);
} catch (SQLException | IllegalArgumentException e) {
return false;
}
}
}

View File

@ -75,4 +75,7 @@ public class RequestHandler implements HttpHandler {
return null;
}
public ResponseHandler getResponseHandler() {
return responseHandler;
}
}

View File

@ -4,9 +4,7 @@
*/
package com.djrapitops.plan.system.webserver;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.exceptions.WebUserAuthException;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.pagecache.PageId;
import com.djrapitops.plan.system.webserver.pagecache.ResponseCache;
@ -14,17 +12,11 @@ import com.djrapitops.plan.system.webserver.pages.*;
import com.djrapitops.plan.system.webserver.response.*;
import com.djrapitops.plan.system.webserver.response.errors.ForbiddenResponse;
import com.djrapitops.plan.system.webserver.response.errors.InternalErrorResponse;
import com.djrapitops.plan.system.webserver.response.errors.NotFoundResponse;
import com.djrapitops.plan.system.webserver.response.pages.AnalysisPageResponse;
import com.djrapitops.plan.system.webserver.response.pages.DebugPageResponse;
import com.djrapitops.plan.system.webserver.response.pages.PlayersPageResponse;
import com.djrapitops.plan.systems.webserver.response.*;
import com.djrapitops.plugin.api.utility.log.Log;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
/**
* Handles choosing of the correct response to a request.
@ -34,7 +26,6 @@ import java.util.UUID;
public class ResponseHandler extends TreePageHandler {
private final boolean authRequired;
private final boolean usingHttps;
public ResponseHandler(WebServer webServer) {
@ -43,7 +34,7 @@ public class ResponseHandler extends TreePageHandler {
}
public void registerDefaultPages() {
registerPage("favicon.ico", new RedirectResponse("https://puu.sh/tK0KL/6aa2ba141b.ico"));
registerPage("favicon.ico", new RedirectResponse("https://puu.sh/tK0KL/6aa2ba141b.ico"), 5);
registerPage("debug", new DebugPageHandler());
registerPage("players", new PlayersPageHandler());
registerPage("player", new PlayerPageHandler());
@ -51,10 +42,35 @@ public class ResponseHandler extends TreePageHandler {
ServerPageHandler serverPageHandler = new ServerPageHandler();
registerPage("network", serverPageHandler);
registerPage("server", serverPageHandler);
if (authRequired) {
registerPage("", new RootPageHandler(this));
}
}
public void registerWebAPIPages() {
// TODO WebAPIPageHandler
// private void registerWebAPIs() {
// webAPI.registerNewAPI(
// new AnalysisReadyWebAPI(),
// new AnalyzeWebAPI(),
// new ConfigurationWebAPI(),
// new InspectWebAPI(),
// new IsOnlineWebAPI(),
// new RequestInspectPluginsTabBukkitWebAPI(),
// new PingWebAPI()
// );
//
// webAPI.registerNewAPI(
// new IsCachedWebAPI(),
// new PostHtmlWebAPI(),
// new PostInspectPluginsTabWebAPI(),
// new PostNetworkPageContentWebAPI(),
// new PostOriginalBukkitSettingsWebAPI(),
// new RequestPluginsTabWebAPI(),
// new RequestSetupWebAPI()
// );
// }
//Log.infoColor("§aWebServer Running in WebAPI-only Mode");
}
public Response getResponse(Request request) {
@ -65,12 +81,12 @@ public class ResponseHandler extends TreePageHandler {
Optional<Authentication> authentication = Optional.empty();
if (authRequired) {
authentication = request.getAuth();
if (!authentication.isPresent() && usingHttps) {
return DefaultResponses.BASIC_AUTH.get();
}
if (authentication.isPresent() && !authentication.get().isAuthorized(null)) {
return forbiddenResponse(0, 0);
if (!authentication.isPresent()) {
if (usingHttps) {
return DefaultResponses.BASIC_AUTH.get();
} else {
return forbiddenResponse(0, 0);
}
}
}
@ -84,7 +100,7 @@ public class ResponseHandler extends TreePageHandler {
}
return DefaultResponses.NOT_FOUND.get();
} else {
if (authentication.isPresent() && authentication.get().isAuthorized(pageHandler.getPermission())) {
if (authentication.isPresent() && pageHandler.isAuthorized(authentication.get(), target)) {
return forbiddenResponse(0, 0);
}
return pageHandler.getResponse(request, target);
@ -93,142 +109,15 @@ public class ResponseHandler extends TreePageHandler {
return PromptAuthorizationResponse.getBasicAuthResponse(e);
} catch (Exception e) {
Log.toLog(this.getClass().getName(), e);
return new InternalErrorResponse(e, request.getTarget());
}
try {
if ("/favicon.ico".equals(targetString)) {
return ResponseCache.loadResponse(PageId.FAVICON_REDIRECT.id(), () -> new RedirectResponse("https://puu.sh/tK0KL/6aa2ba141b.ico"));
}
if (request.isAPIRequest()) {
return getAPIResponse(request);
}
UUID serverUUID = PlanPlugin.getInstance().getServerUuid();
if (usingHttps) {
if (!request.hasAuth()) {
throw new WebUserAuthException("No Authorization");
}
WebUser user = getUser(request.getAuth());
int required = getRequiredPermLevel(targetString, user.getName());
int permLevel = user.getPermLevel();
if (!isAuthorized(required, permLevel)) {
return forbiddenResponse(required, permLevel);
}
if (args.length < 2) {
return rootPageResponse(user, serverUUID);
}
} else if (args.length < 2) {
return notFoundResponse();
}
String page = args[1];
switch (page) {
case "debug":
return new DebugPageResponse();
case "players":
return ResponseCache.loadResponse(PageId.PLAYERS.id(), PlayersPageResponse::new);
case "player":
return playerResponse(args);
case "network":
case "server":
if (args.length > 2) {
try {
Optional<UUID> serverUUIDOptional = PlanPlugin.getInstance().getDB().getServerTable().getServerUUID(args[2].replace("%20", " "));
if (serverUUIDOptional.isPresent()) {
serverUUID = serverUUIDOptional.get();
}
} catch (IllegalArgumentException ignore) {
/*ignored*/
}
}
return serverResponse(serverUUID);
default:
return notFoundResponse();
}
} catch (WebUserAuthException e) {
return ResponseCache.loadResponse(PageId.AUTH_PROMPT.id(), PromptAuthorizationResponse::getBasicAuthResponse);
} catch (Exception e) {
Log.toLog(this.getClass().getName(), e);
return new InternalErrorResponse(e, request.getTarget());
return new InternalErrorResponse(request.getTarget(), e);
}
}
private Response forbiddenResponse(int required, int permLevel) {
public Response forbiddenResponse(int required, int permLevel) {
return ResponseCache.loadResponse(PageId.FORBIDDEN.of(required + "/" + permLevel), () ->
new ForbiddenResponse("Unauthorized User.<br>"
+ "Make sure your user has the correct access level.<br>"
+ "This page requires permission level of " + required + ",<br>"
+ "This user has permission level of " + permLevel));
}
private boolean isAuthorized(int requiredPermLevel, int permLevel) {
return permLevel <= requiredPermLevel;
}
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();
return wantedUser.equals(theUser) ? 2 : 1;
default:
return 0;
}
}
private Response rootPageResponse(WebUser user, UUID serverUUID) {
if (user == null) {
return notFoundResponse();
}
switch (user.getPermLevel()) {
case 0:
return serverResponse(serverUUID);
case 1:
return ResponseCache.loadResponse(PageId.PLAYERS.id(), PlayersPageResponse::new);
case 2:
return playerResponse(new String[]{"", "", user.getName()});
default:
return forbiddenResponse(user.getPermLevel(), 0);
}
}
private Response serverResponse(UUID serverUUID) {
return ResponseCache.loadResponse(PageId.SERVER.of(serverUUID), () -> new AnalysisPageResponse(plugin.getInfoManager()));
}
private Response notFoundResponse() {
String error = "404 Not Found";
return ResponseCache.loadResponse(PageId.NOT_FOUND.of("Wrong Page"), () -> {
String url = plugin.getInfoManager().getWebServerAddress();
return new NotFoundResponse("Make sure you're accessing a link given by a command, Examples:</p>"
+ "<p>" + url + "/player/Playername<br>" +
url + "/server</p>");
}
);
}
}

View File

@ -40,15 +40,19 @@ public class WebServer implements SubSystem {
private boolean usingHttps = false;
private RequestHandler requestHandler;
private ResponseHandler responseHandler;
@Override
public void enable() {
this.port = Settings.WEBSERVER_PORT.getNumber();
PlanPlugin plugin = PlanPlugin.getInstance();
StaticHolder.saveInstance(APIRequestHandler.class, plugin.getClass());
StaticHolder.saveInstance(RequestHandler.class, plugin.getClass());
StaticHolder.saveInstance(ResponseHandler.class, plugin.getClass());
StaticHolder.saveInstance(APIResponseHandler.class, plugin.getClass());
requestHandler = new RequestHandler(this);
responseHandler = requestHandler.getResponseHandler();
initServer();
}
@ -58,30 +62,6 @@ public class WebServer implements SubSystem {
stop();
}
// TODO WebAPIPageHandler
// private void registerWebAPIs() {
// webAPI.registerNewAPI(
// new AnalysisReadyWebAPI(),
// new AnalyzeWebAPI(),
// new ConfigurationWebAPI(),
// new InspectWebAPI(),
// new IsOnlineWebAPI(),
// new RequestInspectPluginsTabBukkitWebAPI(),
// new PingWebAPI()
// );
//
// webAPI.registerNewAPI(
// new IsCachedWebAPI(),
// new PostHtmlWebAPI(),
// new PostInspectPluginsTabWebAPI(),
// new PostNetworkPageContentWebAPI(),
// new PostOriginalBukkitSettingsWebAPI(),
// new RequestPluginsTabWebAPI(),
// new RequestSetupWebAPI()
// );
// }
//Log.infoColor("§aWebServer Running in WebAPI-only Mode");
/**
* Starts up the WebServer in a new Thread Pool.
*/
@ -107,7 +87,7 @@ public class WebServer implements SubSystem {
server = HttpServer.create(new InetSocketAddress(Settings.WEBSERVER_IP.toString(), port), 10);
}
server.createContext("/", new RequestHandler(this));
server.createContext("/", requestHandler);
server.setExecutor(new ThreadPoolExecutor(4, 8, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100)));
server.start();
@ -213,4 +193,12 @@ public class WebServer implements SubSystem {
public String getAccessAddress() {
return isEnabled() ? getProtocol() + "://" + HtmlUtils.getIP() : Settings.EXTERNAL_WEBSERVER_LINK.toString();
}
public RequestHandler getRequestHandler() {
return requestHandler;
}
public ResponseHandler getResponseHandler() {
return responseHandler;
}
}

View File

@ -28,11 +28,14 @@ public class WebServerSystem implements SubSystem {
@Override
public void enable() throws EnableException {
webServer = new WebServer(PlanPlugin.getInstance());
webServer = new WebServer();
webServer.initServer();
if (Check.isBungeeAvailable() && !webServer.isEnabled()) {
throw new EnableException("WebServer did not initialize!");
}
ResponseHandler responseHandler = webServer.getResponseHandler();
responseHandler.registerWebAPIPages();
responseHandler.registerDefaultPages();
}
@Override

View File

@ -5,6 +5,7 @@
package com.djrapitops.plan.system.webserver.auth;
import com.djrapitops.plan.api.exceptions.WebUserAuthException;
import com.djrapitops.plan.data.WebUser;
/**
* //TODO Class Javadoc Comment
@ -13,6 +14,6 @@ import com.djrapitops.plan.api.exceptions.WebUserAuthException;
*/
public interface Authentication {
boolean isAuthorized(String permission) throws WebUserAuthException;
WebUser getWebUser() throws WebUserAuthException;
}

View File

@ -4,7 +4,10 @@
*/
package com.djrapitops.plan.system.webserver.pages;
import com.djrapitops.plan.api.exceptions.WebUserAuthException;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.system.webserver.Request;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.pages.DebugPageResponse;
@ -21,4 +24,10 @@ public class DebugPageHandler extends PageHandler {
public Response getResponse(Request request, List<String> target) {
return new DebugPageResponse();
}
@Override
public boolean isAuthorized(Authentication auth, List<String> target) throws WebUserAuthException {
WebUser webUser = auth.getWebUser();
return webUser.getPermLevel() == 0;
}
}

View File

@ -4,7 +4,9 @@
*/
package com.djrapitops.plan.system.webserver.pages;
import com.djrapitops.plan.api.exceptions.WebUserAuthException;
import com.djrapitops.plan.system.webserver.Request;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.response.Response;
import java.util.List;
@ -16,11 +18,17 @@ import java.util.List;
*/
public abstract class PageHandler {
protected String permission = "*";
/**
* Get the Response of a PageHandler.
*
* @param request Request in case it is useful for choosing page.
* @param target Rest of the target coordinates after this page has been solved.
* @return Response appropriate to the PageHandler.
*/
public abstract Response getResponse(Request request, List<String> target);
public String getPermission() {
return permission;
public boolean isAuthorized(Authentication auth, List<String> target) throws WebUserAuthException {
return true;
}
}

View File

@ -6,8 +6,12 @@ package com.djrapitops.plan.system.webserver.pages;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.exceptions.ParseException;
import com.djrapitops.plan.api.exceptions.WebUserAuthException;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.webserver.Request;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.pagecache.PageId;
import com.djrapitops.plan.system.webserver.pagecache.ResponseCache;
import com.djrapitops.plan.system.webserver.response.Response;
@ -15,6 +19,7 @@ import com.djrapitops.plan.system.webserver.response.errors.InternalErrorRespons
import com.djrapitops.plan.system.webserver.response.errors.NotFoundResponse;
import com.djrapitops.plan.system.webserver.response.pages.InspectPageResponse;
import com.djrapitops.plan.utilities.uuid.UUIDUtility;
import com.djrapitops.plugin.api.utility.log.Log;
import java.util.List;
import java.util.UUID;
@ -26,10 +31,6 @@ import java.util.UUID;
*/
public class PlayerPageHandler extends PageHandler {
public PlayerPageHandler() {
permission = "special_player";
}
@Override
public Response getResponse(Request request, List<String> target) {
if (target.isEmpty()) {
@ -43,21 +44,25 @@ public class PlayerPageHandler extends PageHandler {
return notFound("Player has no UUID");
}
if (Database.getActive().check().isPlayerRegistered(uuid)) {
PlanPlugin.getInstance().getInfoManager().cachePlayer(uuid);
Response response = ResponseCache.loadResponse(PageId.PLAYER.of(uuid));
// TODO Create a new method that places NotFoundResponse to ResponseCache instead.
if (response == null || response.getContent().contains("No Bukkit Servers were online to process this request")) {
ResponseCache.cacheResponse(PageId.PLAYER.of(uuid), () -> {
try {
return new InspectPageResponse(PlanPlugin.getInstance().getInfoManager(), uuid);
} catch (ParseException e) {
return new InternalErrorResponse(e, this.getClass().getName());
}
});
response = ResponseCache.loadResponse(PageId.PLAYER.of(uuid));
try {
if (Database.getActive().check().isPlayerRegistered(uuid)) {
PlanPlugin.getInstance().getInfoManager().cachePlayer(uuid);
Response response = ResponseCache.loadResponse(PageId.PLAYER.of(uuid));
// TODO Create a new method that places NotFoundResponse to ResponseCache instead.
if (response == null || response.getContent().contains("No Bukkit Servers were online to process this request")) {
ResponseCache.cacheResponse(PageId.PLAYER.of(uuid), () -> {
try {
return new InspectPageResponse(PlanPlugin.getInstance().getInfoManager(), uuid);
} catch (ParseException e) {
return new InternalErrorResponse(e, this.getClass().getName());
}
});
response = ResponseCache.loadResponse(PageId.PLAYER.of(uuid));
}
return response;
}
return response;
} catch (DBException e) {
Log.toLog(this.getClass().getName(), e);
}
return notFound("Player has not played on this server.");
}
@ -65,4 +70,10 @@ public class PlayerPageHandler extends PageHandler {
private Response notFound(String error) {
return ResponseCache.loadResponse(PageId.NOT_FOUND.of(error), () -> new NotFoundResponse(error));
}
@Override
public boolean isAuthorized(Authentication auth, List<String> target) throws WebUserAuthException {
WebUser webUser = auth.getWebUser();
return webUser.getPermLevel() <= 1 || webUser.getName().equalsIgnoreCase(target.get(target.size() - 1));
}
}

View File

@ -4,7 +4,9 @@
*/
package com.djrapitops.plan.system.webserver.pages;
import com.djrapitops.plan.api.exceptions.WebUserAuthException;
import com.djrapitops.plan.system.webserver.Request;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.pagecache.PageId;
import com.djrapitops.plan.system.webserver.pagecache.ResponseCache;
import com.djrapitops.plan.system.webserver.response.Response;
@ -19,12 +21,13 @@ import java.util.List;
*/
public class PlayersPageHandler extends PageHandler {
public PlayersPageHandler() {
permission = "players";
}
@Override
public Response getResponse(Request request, List<String> target) {
return ResponseCache.loadResponse(PageId.PLAYERS.id(), PlayersPageResponse::new);
}
@Override
public boolean isAuthorized(Authentication auth, List<String> target) throws WebUserAuthException {
return auth.getWebUser().getPermLevel() <= 1;
}
}

View File

@ -0,0 +1,64 @@
/*
* Licence is provided in the jar as license.yml also here:
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml
*/
package com.djrapitops.plan.system.webserver.pages;
import com.djrapitops.plan.api.exceptions.WebUserAuthException;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.system.webserver.Request;
import com.djrapitops.plan.system.webserver.ResponseHandler;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.errors.InternalErrorResponse;
import com.djrapitops.plugin.api.utility.log.Log;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
/**
* //TODO Class Javadoc Comment
*
* @author Rsl1122
*/
public class RootPageHandler extends PageHandler {
private final ResponseHandler responseHandler;
public RootPageHandler(ResponseHandler responseHandler) {
this.responseHandler = responseHandler;
}
@Override
public Response getResponse(Request request, List<String> target) {
Optional<Authentication> auth = request.getAuth();
if (!auth.isPresent()) {
return DefaultResponses.BASIC_AUTH.get();
}
try {
WebUser webUser = auth.get().getWebUser();
int permLevel = webUser.getPermLevel();
switch (permLevel) {
case 0:
return responseHandler.getPageHandler("server").getResponse(request, Collections.emptyList());
case 1:
return responseHandler.getPageHandler("players").getResponse(request, Collections.emptyList());
case 2:
return responseHandler.getPageHandler("player").getResponse(request, Collections.singletonList(webUser.getName()));
default:
return responseHandler.forbiddenResponse(permLevel, 0);
}
} catch (WebUserAuthException e) {
Log.toLog(this.getClass(), e);
return new InternalErrorResponse("/", e);
}
}
@Override
public boolean isAuthorized(Authentication auth, List<String> target) {
return true;
}
}

View File

@ -4,24 +4,53 @@
*/
package com.djrapitops.plan.system.webserver.pages;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.exceptions.WebUserAuthException;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.webserver.Request;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.pagecache.PageId;
import com.djrapitops.plan.system.webserver.pagecache.ResponseCache;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.pages.AnalysisPageResponse;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
/**
* //TODO Class Javadoc Comment
* PageHandler for /server and /network pages.
*
* @author Rsl1122
*/
public class ServerPageHandler extends PageHandler {
public ServerPageHandler() {
permission = "server";
@Override
public Response getResponse(Request request, List<String> target) {
UUID serverUUID = getServerUUID(target);
return ResponseCache.loadResponse(PageId.SERVER.of(serverUUID),
() -> new AnalysisPageResponse(PlanPlugin.getInstance().getInfoManager())
);
}
private UUID getServerUUID(List<String> target) {
UUID serverUUID = PlanPlugin.getInstance().getServerUuid();
if (!target.isEmpty()) {
try {
String serverName = target.get(0).replace("%20", " ");
Optional<UUID> serverUUIDOptional = Database.getActive().fetch().getServerUUID(serverName);
if (serverUUIDOptional.isPresent()) {
serverUUID = serverUUIDOptional.get();
}
} catch (IllegalArgumentException ignore) {
/*ignored*/
}
}
return serverUUID;
}
@Override
public Response getResponse(Request request, List<String> target) {
return null;
public boolean isAuthorized(Authentication auth, List<String> target) throws WebUserAuthException {
return auth.getWebUser().getPermLevel() <= 0;
}
}

View File

@ -4,7 +4,9 @@
*/
package com.djrapitops.plan.system.webserver.pages;
import com.djrapitops.plan.api.exceptions.WebUserAuthException;
import com.djrapitops.plan.system.webserver.Request;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.response.Response;
import java.util.HashMap;
@ -28,12 +30,17 @@ public abstract class TreePageHandler extends PageHandler {
pages.put(targetPage, handler);
}
public void registerPage(String targetPage, Response response) {
public void registerPage(String targetPage, Response response, int requiredPerm) {
pages.put(targetPage, new PageHandler() {
@Override
public Response getResponse(Request request, List<String> target) {
return response;
}
@Override
public boolean isAuthorized(Authentication auth, List<String> target) throws WebUserAuthException {
return auth.getWebUser().getPermLevel() <= requiredPerm;
}
});
}
@ -46,8 +53,15 @@ public abstract class TreePageHandler extends PageHandler {
}
public PageHandler getPageHandler(List<String> target) {
if (target.isEmpty()) {
return pages.get("");
}
String targetPage = target.get(0);
target.remove(0);
return pages.get(targetPage);
}
public PageHandler getPageHandler(String targetPage) {
return pages.get(targetPage);
}
}

View File

@ -12,6 +12,11 @@ import com.djrapitops.plan.utilities.html.Html;
*/
public class PromptAuthorizationResponse extends ErrorResponse {
private static final String TIPS = "<br>- Ensure you have registered a user with <b>/plan register</b><br>"
+ "- Check that the username and password are correct<br>"
+ "- Username and password are case-sensitive<br>"
+ "<br>If you have forgotten your password, ask a staff member to delete your old user and re-register.";
private PromptAuthorizationResponse() {
super.setTitle(Html.FONT_AWESOME_ICON.parse("lock") + " 401 Unauthorized");
}
@ -20,11 +25,8 @@ public class PromptAuthorizationResponse extends ErrorResponse {
PromptAuthorizationResponse response = new PromptAuthorizationResponse();
response.setHeader("HTTP/1.1 401 Access Denied\r\n"
+ "WWW-Authenticate: Basic realm=\"/\";");
response.setParagraph("Authentication Failed.<br>"
+ "- Ensure you have registered a user with <b>/plan register</b><br>"
+ "- Check that the username and password are correct<br>"
+ "- Username and password are case-sensitive<br>"
+ "<br>If you have forgotten your password, ask a staff member to delete your old user and re-register.");
response.setParagraph("Authentication Failed." + TIPS);
response.replacePlaceholders();
return response;
}
@ -47,7 +49,7 @@ public class PromptAuthorizationResponse extends ErrorResponse {
reason += errorBuilder.toString();
}
response.setParagraph("Authentication Failed.<br>Reason: " + reason);
response.setParagraph("Authentication Failed.<br>Reason: " + reason + TIPS);
response.replacePlaceholders();
return response;
}

View File

@ -8,7 +8,7 @@ import com.djrapitops.plan.utilities.html.Html;
*/
public class InternalErrorResponse extends ErrorResponse {
public InternalErrorResponse(Throwable e, String cause) {
public InternalErrorResponse(String cause, Throwable e) {
super.setHeader("HTTP/1.1 500 Internal Error");
super.setTitle(Html.FONT_AWESOME_ICON.parse("bug") + " 500 Internal Error occurred");
@ -33,6 +33,11 @@ public class InternalErrorResponse extends ErrorResponse {
super.replacePlaceholders();
}
@Deprecated
public InternalErrorResponse(Throwable e, String cause) {
this(cause, e);
}
private void appendCause(Throwable cause, StringBuilder paragraph) {
paragraph.append("<br>Caused by: ").append(cause);
for (StackTraceElement element : cause.getStackTrace()) {