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
This commit is contained in:
Rsl1122 2017-07-29 12:40:39 +03:00
parent a2f7587b8d
commit ed555cce76
4 changed files with 63 additions and 162 deletions

View File

@ -2,12 +2,9 @@
<library name="Maven: com.djrapitops:abstract-plugin-framework:2.0.1"> <library name="Maven: com.djrapitops:abstract-plugin-framework:2.0.1">
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/djrapitops/abstract-plugin-framework/2.0.1/abstract-plugin-framework-2.0.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/djrapitops/abstract-plugin-framework/2.0.1/abstract-plugin-framework-2.0.1.jar!/" />
<root url="jar://$PROJECT_DIR$/../../Abstract-Plugin-Framework/AbstractPluginFramework/target/AbstractPluginFramework.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC />
<root url="jar://$MAVEN_REPOSITORY$/com/djrapitops/abstract-plugin-framework/2.0.1/abstract-plugin-framework-2.0.1-javadoc.jar!/" /> <SOURCES />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/djrapitops/abstract-plugin-framework/2.0.1/abstract-plugin-framework-2.0.1-sources.jar!/" />
</SOURCES>
</library> </library>
</component> </component>

View File

@ -31,14 +31,14 @@ public class Authenticator extends BasicAuthenticator {
private boolean isAuthorized(String user, String passwordRaw, String target) throws PassEncryptUtil.CannotPerformOperationException, PassEncryptUtil.InvalidHashException, SQLException { private boolean isAuthorized(String user, String passwordRaw, String target) throws PassEncryptUtil.CannotPerformOperationException, PassEncryptUtil.InvalidHashException, SQLException {
SecurityTable securityTable = plugin.getDB().getSecurityTable(); SecurityTable securityTable = plugin.getDB().getSecurityTable();
if (!securityTable.userExists(user)) { if (!securityTable.userExists(user)) {
throw new IllegalArgumentException("User Doesn't exist"); return false;
} }
WebUser securityInfo = securityTable.getSecurityInfo(user); WebUser securityInfo = securityTable.getSecurityInfo(user);
boolean correctPass = PassEncryptUtil.verifyPassword(passwordRaw, securityInfo.getSaltedPassHash()); boolean correctPass = PassEncryptUtil.verifyPassword(passwordRaw, securityInfo.getSaltedPassHash());
if (!correctPass) { if (!correctPass) {
throw new IllegalArgumentException("User and Password do not match"); return false;
} }
int permLevel = securityInfo.getPermLevel(); // Lower number has higher clearance. int permLevel = securityInfo.getPermLevel(); // Lower number has higher clearance.
int required = getRequiredPermLevel(target, securityInfo.getName()); int required = getRequiredPermLevel(target, securityInfo.getName());
return permLevel <= required; return permLevel <= required;

View File

@ -1,17 +1,13 @@
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.PassEncryptUtil;
import main.java.com.djrapitops.plan.utilities.uuid.UUIDUtility; import main.java.com.djrapitops.plan.utilities.uuid.UUIDUtility;
import javax.net.ssl.*; import javax.net.ssl.*;
@ -20,11 +16,10 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
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.sql.SQLException;
import java.util.Base64;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -124,17 +119,29 @@ public class WebServer {
} }
Log.debug("Create server context"); Log.debug("Create server context");
server.createContext("/", serverResponse(null)); HttpContext context = server.createContext("/", new HttpHandler() {
HttpContext analysisPage = server.createContext("/server", serverResponse(null)); @Override
HttpContext playersPage = server.createContext("/players", new PlayersPageResponse(null, plugin)); public void handle(HttpExchange xghng) throws IOException {
HttpContext inspectPage = server.createContext("/player", new InspectPageResponse(null, dataReqHandler, UUID.randomUUID())); // TODO 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) { OutputStream os = exchange.getResponseBody();
for (HttpContext c : new HttpContext[]{analysisPage, playersPage, inspectPage}) { os.write(content.getBytes());
c.setAuthenticator(new Authenticator(plugin, c.getPath())); 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.setExecutor(Executors.newSingleThreadExecutor());
server.start(); server.start();
@ -147,55 +154,37 @@ public class WebServer {
} }
} }
// Used for deciding the Response appropriate for the Request. // if (!request.hasAuthorization()) {
private Response getResponse(Request request, OutputStream output) { // return new PromptAuthorizationResponse(output);
try { // }
Verify.nullCheck(request); // try {
Verify.nullCheck(output); // if (!isAuthorized(request)) {
// ForbiddenResponse response403 = new ForbiddenResponse(output);
// String content = "<h1>403 Forbidden - Access Denied</h1>"
// + "<p>Unauthorized User.<br>"
// + "Make sure your user has the correct access level.<br>"
// + "You can use /plan web check <username> to check the permission level.</p>";
// response403.setContent(content);
// return response403;
// }
if (isFaviconRequest(request)) { private Response getResponse(String target) {
return new RedirectResponse(output, "https://puu.sh/tK0KL/6aa2ba141b.ico"); String[] args = target.split("/");
} if (args.length < 2) {
return responseNotFound(null);
if (!request.hasAuthorization()) { }
return new PromptAuthorizationResponse(output); String page = args[1];
} switch (page) {
try { case "favicon.ico":
if (!isAuthorized(request)) { return new RedirectResponse(null, "https://puu.sh/tK0KL/6aa2ba141b.ico");
ForbiddenResponse response403 = new ForbiddenResponse(output); case "players":
String content = "<h1>403 Forbidden - Access Denied</h1>" return new PlayersPageResponse(null, plugin);
+ "<p>Unauthorized User.<br>" case "player":
+ "Make sure your user has the correct access level.<br>" return playerResponse(args, null);
+ "You can use /plan web check <username> to check the permission level.</p>"; case "server":
response403.setContent(content); return serverResponse(null);
return response403; default:
} return responseNotFound(null);
} 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());
} }
} }
@ -255,63 +244,4 @@ public class WebServer {
public DataRequestHandler getDataReqHandler() { public DataRequestHandler getDataReqHandler() {
return dataReqHandler; 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");
}
} }

View File

@ -1,12 +1,5 @@
package main.java.com.djrapitops.plan.ui.webserver.response; 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.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@ -14,7 +7,7 @@ import java.io.OutputStream;
* @author Rsl1122 * @author Rsl1122
* @since 3.5.2 * @since 3.5.2
*/ */
public abstract class Response implements HttpHandler { public abstract class Response {
private final OutputStream output; private final OutputStream output;
@ -51,6 +44,10 @@ public abstract class Response implements HttpHandler {
+ content; + content;
} }
public String getContent() {
return content;
}
public void setHeader(String header) { public void setHeader(String header) {
this.header = header; this.header = header;
} }
@ -59,30 +56,7 @@ public abstract class Response implements HttpHandler {
this.content = content; this.content = content;
} }
@Override public int getCode() {
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() {
if (header == null) { if (header == null) {
return 500; return 500;
} else { } else {