From ed555cce767631d7b29246b9794c1466ddacd6c9 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 29 Jul 2017 12:40:39 +0300 Subject: [PATCH] WebServer functionality restored. (Https instead of http) TODO: - Bump Http requests to Https - Http server if certificate load fails - No path automatic redirection based on permission level - Move to manual authentication instead of Authenticator - 403 Page - 401 Unauthorized Page - Tutorial for Keystore creation --- ...pitops_abstract_plugin_framework_2_0_1.xml | 9 +- .../plan/ui/webserver/Authenticator.java | 6 +- .../plan/ui/webserver/WebServer.java | 172 ++++++------------ .../plan/ui/webserver/response/Response.java | 38 +--- 4 files changed, 63 insertions(+), 162 deletions(-) diff --git a/Plan/.idea/libraries/Maven__com_djrapitops_abstract_plugin_framework_2_0_1.xml b/Plan/.idea/libraries/Maven__com_djrapitops_abstract_plugin_framework_2_0_1.xml index 826d210d1..554d77a3e 100644 --- a/Plan/.idea/libraries/Maven__com_djrapitops_abstract_plugin_framework_2_0_1.xml +++ b/Plan/.idea/libraries/Maven__com_djrapitops_abstract_plugin_framework_2_0_1.xml @@ -2,12 +2,9 @@ + - - - - - - + + \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/ui/webserver/Authenticator.java b/Plan/src/main/java/com/djrapitops/plan/ui/webserver/Authenticator.java index cf73b6219..5c797d3a7 100644 --- a/Plan/src/main/java/com/djrapitops/plan/ui/webserver/Authenticator.java +++ b/Plan/src/main/java/com/djrapitops/plan/ui/webserver/Authenticator.java @@ -31,14 +31,14 @@ public class Authenticator extends BasicAuthenticator { private boolean isAuthorized(String user, String passwordRaw, String target) throws PassEncryptUtil.CannotPerformOperationException, PassEncryptUtil.InvalidHashException, SQLException { SecurityTable securityTable = plugin.getDB().getSecurityTable(); if (!securityTable.userExists(user)) { - throw new IllegalArgumentException("User Doesn't exist"); + return false; } WebUser securityInfo = securityTable.getSecurityInfo(user); boolean correctPass = PassEncryptUtil.verifyPassword(passwordRaw, securityInfo.getSaltedPassHash()); if (!correctPass) { - throw new IllegalArgumentException("User and Password do not match"); - } + return false; + } int permLevel = securityInfo.getPermLevel(); // Lower number has higher clearance. int required = getRequiredPermLevel(target, securityInfo.getName()); return permLevel <= required; diff --git a/Plan/src/main/java/com/djrapitops/plan/ui/webserver/WebServer.java b/Plan/src/main/java/com/djrapitops/plan/ui/webserver/WebServer.java index db02cbfe5..a1e689f75 100644 --- a/Plan/src/main/java/com/djrapitops/plan/ui/webserver/WebServer.java +++ b/Plan/src/main/java/com/djrapitops/plan/ui/webserver/WebServer.java @@ -1,17 +1,13 @@ package main.java.com.djrapitops.plan.ui.webserver; -import com.djrapitops.plugin.utilities.Verify; import com.sun.net.httpserver.*; import main.java.com.djrapitops.plan.Log; import main.java.com.djrapitops.plan.Phrase; import main.java.com.djrapitops.plan.Plan; 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.webserver.response.*; import main.java.com.djrapitops.plan.utilities.HtmlUtils; -import main.java.com.djrapitops.plan.utilities.PassEncryptUtil; import main.java.com.djrapitops.plan.utilities.uuid.UUIDUtility; import javax.net.ssl.*; @@ -20,11 +16,10 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; +import java.net.URI; import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateException; -import java.sql.SQLException; -import java.util.Base64; import java.util.UUID; import java.util.concurrent.Executors; @@ -124,17 +119,29 @@ public class WebServer { } Log.debug("Create server context"); - server.createContext("/", serverResponse(null)); - HttpContext analysisPage = server.createContext("/server", serverResponse(null)); - HttpContext playersPage = server.createContext("/players", new PlayersPageResponse(null, plugin)); - HttpContext inspectPage = server.createContext("/player", new InspectPageResponse(null, dataReqHandler, UUID.randomUUID())); // TODO + HttpContext context = server.createContext("/", new HttpHandler() { + @Override + public void handle(HttpExchange xghng) throws IOException { + HttpsExchange exchange = (HttpsExchange) xghng; + try { + URI uri = exchange.getRequestURI(); + String target = uri.toString(); + Response response = getResponse(target); + String content = response.getContent(); + exchange.sendResponseHeaders(response.getCode(), content.length()); - if (startSuccessful) { - for (HttpContext c : new HttpContext[]{analysisPage, playersPage, inspectPage}) { - c.setAuthenticator(new Authenticator(plugin, c.getPath())); + OutputStream os = exchange.getResponseBody(); + os.write(content.getBytes()); + os.close(); + } catch (Exception e) { + Log.toLog(this.getClass().getName(), e); + throw e; + } } + }); + if (startSuccessful) { + context.setAuthenticator(new Authenticator(plugin, "/")); } - server.setExecutor(Executors.newSingleThreadExecutor()); server.start(); @@ -147,55 +154,37 @@ public class WebServer { } } - // Used for deciding the Response appropriate for the Request. - private Response getResponse(Request request, OutputStream output) { - try { - Verify.nullCheck(request); - Verify.nullCheck(output); +// if (!request.hasAuthorization()) { +// return new PromptAuthorizationResponse(output); +// } +// try { +// if (!isAuthorized(request)) { +// ForbiddenResponse response403 = new ForbiddenResponse(output); +// String content = "

403 Forbidden - Access Denied

" +// + "

Unauthorized User.
" +// + "Make sure your user has the correct access level.
" +// + "You can use /plan web check to check the permission level.

"; +// response403.setContent(content); +// return response403; +// } - if (isFaviconRequest(request)) { - return new RedirectResponse(output, "https://puu.sh/tK0KL/6aa2ba141b.ico"); - } - - if (!request.hasAuthorization()) { - return new PromptAuthorizationResponse(output); - } - try { - if (!isAuthorized(request)) { - ForbiddenResponse response403 = new ForbiddenResponse(output); - String content = "

403 Forbidden - Access Denied

" - + "

Unauthorized User.
" - + "Make sure your user has the correct access level.
" - + "You can use /plan web check to check the permission level.

"; - response403.setContent(content); - return response403; - } - } catch (IllegalArgumentException e) { - return new PromptAuthorizationResponse(output); - } - String req = request.getRequest(); - String target = request.getTarget(); - if (!req.equals("GET") || target.equals("/")) { - return responseNotFound(output); - } - String[] args = target.split("/"); - if (args.length < 2) { - return responseNotFound(output); - } - String page = args[1]; - switch (page) { - case "players": - return new PlayersPageResponse(output, plugin); - case "player": - return playerResponse(args, output); - case "server": - return serverResponse(output); - default: - return responseNotFound(output); - } - } catch (Exception e) { - Log.toLog(this.getClass().getName(), e); - return new InternalErrorResponse(output, e, request.getTarget()); + private Response getResponse(String target) { + String[] args = target.split("/"); + if (args.length < 2) { + return responseNotFound(null); + } + String page = args[1]; + switch (page) { + case "favicon.ico": + return new RedirectResponse(null, "https://puu.sh/tK0KL/6aa2ba141b.ico"); + case "players": + return new PlayersPageResponse(null, plugin); + case "player": + return playerResponse(args, null); + case "server": + return serverResponse(null); + default: + return responseNotFound(null); } } @@ -255,63 +244,4 @@ public class WebServer { public DataRequestHandler getDataReqHandler() { return dataReqHandler; } - - private boolean isAuthorized(Request request) throws PassEncryptUtil.CannotPerformOperationException, PassEncryptUtil.InvalidHashException, SQLException { - Base64.Decoder decoder = Base64.getDecoder(); - String auth = request.getAuthorization(); - 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]; - return isAuthorized(user, passwordRaw, request.getTarget()); - } - - private boolean isAuthorized(String user, String passwordRaw, String target) throws PassEncryptUtil.CannotPerformOperationException, PassEncryptUtil.InvalidHashException, SQLException { - - SecurityTable securityTable = plugin.getDB().getSecurityTable(); - if (!securityTable.userExists(user)) { - throw new IllegalArgumentException("User Doesn't exist"); - } - WebUser securityInfo = securityTable.getSecurityInfo(user); - - boolean correctPass = PassEncryptUtil.verifyPassword(passwordRaw, securityInfo.getSaltedPassHash()); - if (!correctPass) { - throw new IllegalArgumentException("User and Password do not match"); - } - 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; - } - - private boolean isFaviconRequest(Request request) { - String[] args = request.getTarget().split("/"); - if (args.length < 2 || args.length > 2) { - return false; - } - String page = args[1]; - return page.equals("favicon.ico"); - } } diff --git a/Plan/src/main/java/com/djrapitops/plan/ui/webserver/response/Response.java b/Plan/src/main/java/com/djrapitops/plan/ui/webserver/response/Response.java index be489e140..47b02e5bf 100644 --- a/Plan/src/main/java/com/djrapitops/plan/ui/webserver/response/Response.java +++ b/Plan/src/main/java/com/djrapitops/plan/ui/webserver/response/Response.java @@ -1,12 +1,5 @@ package main.java.com.djrapitops.plan.ui.webserver.response; -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpsExchange; -import main.java.com.djrapitops.plan.Log; -import main.java.com.djrapitops.plan.ui.webserver.Request; - import java.io.IOException; import java.io.OutputStream; @@ -14,7 +7,7 @@ import java.io.OutputStream; * @author Rsl1122 * @since 3.5.2 */ -public abstract class Response implements HttpHandler { +public abstract class Response { private final OutputStream output; @@ -51,6 +44,10 @@ public abstract class Response implements HttpHandler { + content; } + public String getContent() { + return content; + } + public void setHeader(String header) { this.header = header; } @@ -59,30 +56,7 @@ public abstract class Response implements HttpHandler { this.content = content; } - @Override - public void handle(HttpExchange xghng) throws IOException { - Log.debug("Recieved HTTP Exchange"); - Log.debug(xghng.toString()); - HttpsExchange exchange = (HttpsExchange) xghng; - try { - Headers headers = exchange.getRequestHeaders(); - Request req = new Request(exchange.getRequestBody()); - Log.debug(req.toString()); -// headers.set("Content-Type", "text/html"); - - exchange.sendResponseHeaders(getCode(), content.length()); - Log.debug("Content:"); - Log.debug(content); - OutputStream os = exchange.getResponseBody(); - os.write(content.getBytes()); - os.close(); - } catch (Exception e) { - Log.toLog(this.getClass().getName(), e); - throw e; - } - } - - private int getCode() { + public int getCode() { if (header == null) { return 500; } else {