From 466f37ca8f067916e434860a869c6155c79dd596 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sun, 4 Feb 2018 14:31:10 +0200 Subject: [PATCH] Fixed Forbidden pages due to wrong order for a boolean (#521 / 2) --- .../djrapitops/plan/data/ServerProfile.java | 16 -- .../plan/data/calculation/AnalysisData.java | 7 +- .../plan/system/webserver/RequestHandler.java | 1 + .../system/webserver/ResponseHandler.java | 14 +- .../webserver/auth/BasicAuthentication.java | 1 + .../webserver/pages/DebugPageHandler.java | 2 +- .../webserver/pages/RootPageHandler.java | 2 +- .../response/errors/ForbiddenResponse.java | 3 +- .../webserver/HTTPSWebServerAuthTest.java | 138 ++++++++++++++++++ 9 files changed, 155 insertions(+), 29 deletions(-) create mode 100644 Plan/src/test/java/com/djrapitops/plan/system/webserver/HTTPSWebServerAuthTest.java diff --git a/Plan/src/main/java/com/djrapitops/plan/data/ServerProfile.java b/Plan/src/main/java/com/djrapitops/plan/data/ServerProfile.java index f43429c1b..b0144671e 100644 --- a/Plan/src/main/java/com/djrapitops/plan/data/ServerProfile.java +++ b/Plan/src/main/java/com/djrapitops/plan/data/ServerProfile.java @@ -4,21 +4,17 @@ */ package com.djrapitops.plan.data; -import com.djrapitops.plan.Plan; -import com.djrapitops.plan.PlanBungee; import com.djrapitops.plan.data.container.GeoInfo; import com.djrapitops.plan.data.container.PlayerKill; import com.djrapitops.plan.data.container.Session; import com.djrapitops.plan.data.container.TPS; import com.djrapitops.plan.data.time.WorldTimes; -import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.settings.Settings; import com.djrapitops.plan.utilities.analysis.AnalysisUtils; import com.djrapitops.plan.utilities.analysis.MathUtils; import com.djrapitops.plan.utilities.comparators.PlayerProfileLastPlayedComparator; import com.djrapitops.plan.utilities.comparators.TPSComparator; import com.djrapitops.plan.utilities.html.tables.PlayersTableCreator; -import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.api.TimeAmount; import java.util.*; @@ -109,18 +105,6 @@ public class ServerProfile { return total; } - public static int getPlayersOnline() { - if (Check.isBungeeAvailable()) { - return PlanBungee.getInstance().getProxy().getOnlineCount(); - } else { - return Plan.getInstance().getServer().getOnlinePlayers().size(); - } - } - - public static int getPlayersMax() { - return ServerInfo.getServerProperties().getMaxPlayers(); - } - public static long serverDownTime(List tpsData) { long lastDate = -1; long downTime = 0; diff --git a/Plan/src/main/java/com/djrapitops/plan/data/calculation/AnalysisData.java b/Plan/src/main/java/com/djrapitops/plan/data/calculation/AnalysisData.java index 99e57a560..d5f2ed561 100644 --- a/Plan/src/main/java/com/djrapitops/plan/data/calculation/AnalysisData.java +++ b/Plan/src/main/java/com/djrapitops/plan/data/calculation/AnalysisData.java @@ -9,6 +9,8 @@ import com.djrapitops.plan.data.container.TPS; import com.djrapitops.plan.data.element.AnalysisContainer; import com.djrapitops.plan.data.plugin.PluginData; import com.djrapitops.plan.data.time.WorldTimes; +import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.system.info.server.ServerProperties; import com.djrapitops.plan.system.settings.Settings; import com.djrapitops.plan.system.settings.theme.Theme; import com.djrapitops.plan.system.settings.theme.ThemeVal; @@ -84,8 +86,9 @@ public class AnalysisData extends RawData { addValue("tpsMedium", Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber()); addValue("tpsHigh", Settings.THEME_GRAPH_TPS_THRESHOLD_HIGH.getNumber()); - addValue("playersMax", ServerProfile.getPlayersMax()); - addValue("playersOnline", ServerProfile.getPlayersOnline()); + ServerProperties serverProperties = ServerInfo.getServerProperties(); + addValue("playersMax", serverProperties.getMaxPlayers()); + addValue("playersOnline", serverProperties.getOnlinePlayers()); } public void analyze(ServerProfile profile) { diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java index 93ece4bc6..21fd069d1 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java @@ -47,6 +47,7 @@ public class RequestHandler implements HttpHandler { if (response instanceof PromptAuthorizationResponse) { responseHeaders.set("WWW-Authenticate", "Basic realm=\"/\";"); } + response.setResponseHeaders(responseHeaders); response.send(exchange); } catch (Exception e) { diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java index d9f4d5ffa..5f69b6597 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java @@ -117,7 +117,7 @@ public class ResponseHandler extends TreePageHandler { if (webServer.isUsingHTTPS()) { return DefaultResponses.BASIC_AUTH.get(); } else { - return forbiddenResponse(0, 0); + return forbiddenResponse(); } } } @@ -132,17 +132,15 @@ public class ResponseHandler extends TreePageHandler { return DefaultResponses.NOT_FOUND.get(); } else { if (authentication.isPresent() && pageHandler.isAuthorized(authentication.get(), target)) { - return forbiddenResponse(0, 0); + return pageHandler.getResponse(request, target); } - return pageHandler.getResponse(request, target); + return forbiddenResponse(); } } - public Response forbiddenResponse(int required, int permLevel) { - return ResponseCache.loadResponse(PageId.FORBIDDEN.of(required + "/" + permLevel), () -> + public Response forbiddenResponse() { + return ResponseCache.loadResponse(PageId.FORBIDDEN.id(), () -> new ForbiddenResponse("Unauthorized User.
" - + "Make sure your user has the correct access level.
" - + "This page requires permission level of " + required + ",
" - + "This user has permission level of " + permLevel)); + + "Make sure your user has the correct access level.")); } } \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/auth/BasicAuthentication.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/auth/BasicAuthentication.java index 3be7cc265..a40b58f0b 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/auth/BasicAuthentication.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/auth/BasicAuthentication.java @@ -31,6 +31,7 @@ public class BasicAuthentication implements Authentication { @Override public WebUser getWebUser() throws WebUserAuthException { String decoded = Base64Util.decode(authenticationString); + String[] userInfo = decoded.split(":"); if (userInfo.length != 2) { throw new WebUserAuthException(FailReason.USER_AND_PASS_NOT_SPECIFIED); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java index 6a4176c48..8f0abe9eb 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java @@ -28,6 +28,6 @@ public class DebugPageHandler extends PageHandler { @Override public boolean isAuthorized(Authentication auth, List target) throws WebUserAuthException { WebUser webUser = auth.getWebUser(); - return webUser.getPermLevel() == 0; + return webUser.getPermLevel() <= 0; } } \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java index b5f8ca26c..8c590f7ef 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java @@ -53,7 +53,7 @@ public class RootPageHandler extends PageHandler { case 2: return responseHandler.getPageHandler("player").getResponse(request, Collections.singletonList(webUser.getName())); default: - return responseHandler.forbiddenResponse(permLevel, 0); + return responseHandler.forbiddenResponse(); } } catch (WebUserAuthException e) { Log.toLog(this.getClass(), e); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ForbiddenResponse.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ForbiddenResponse.java index 1f0f25b63..7bd81d274 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ForbiddenResponse.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ForbiddenResponse.java @@ -13,7 +13,8 @@ public class ForbiddenResponse extends ErrorResponse { } public ForbiddenResponse(String msg) { - this(); + super.setHeader("HTTP/1.1 403 Forbidden"); + super.setTitle(Html.FONT_AWESOME_ICON.parse("hand-stop-o") + " 403 Forbidden - Access Denied"); super.setParagraph(msg); super.replacePlaceholders(); } diff --git a/Plan/src/test/java/com/djrapitops/plan/system/webserver/HTTPSWebServerAuthTest.java b/Plan/src/test/java/com/djrapitops/plan/system/webserver/HTTPSWebServerAuthTest.java new file mode 100644 index 000000000..43ea62a77 --- /dev/null +++ b/Plan/src/test/java/com/djrapitops/plan/system/webserver/HTTPSWebServerAuthTest.java @@ -0,0 +1,138 @@ +package com.djrapitops.plan.system.webserver; + +import com.djrapitops.plan.Plan; +import com.djrapitops.plan.api.exceptions.connection.*; +import com.djrapitops.plan.data.WebUser; +import com.djrapitops.plan.system.BukkitSystem; +import com.djrapitops.plan.system.settings.Settings; +import com.djrapitops.plan.utilities.Base64Util; +import com.djrapitops.plan.utilities.PassEncryptUtil; +import org.junit.*; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import utilities.Teardown; +import utilities.mocks.BukkitMockUtil; + +import javax.net.ssl.*; +import java.io.File; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +@RunWith(MockitoJUnitRunner.Silent.class) +public class HTTPSWebServerAuthTest { + + @ClassRule + public static TemporaryFolder temporaryFolder = new TemporaryFolder(); + private static BukkitSystem bukkitSystem; + + @BeforeClass + public static void setUpClass() throws Exception { + BukkitMockUtil mockUtil = BukkitMockUtil.setUp() + .withDataFolder(temporaryFolder.getRoot()) + .withLogging() + .withPluginDescription() + .withResourceFetchingFromJar() + .withServer(); + Plan planMock = mockUtil.getPlanMock(); + + URL resource = HTTPSWebServerAuthTest.class.getResource("/Cert.keystore"); + String keyStore = resource.getPath(); + String absolutePath = new File(keyStore).getAbsolutePath(); + + Settings.WEBSERVER_CERTIFICATE_PATH.setTemporaryValue(absolutePath); + Settings.WEBSERVER_CERTIFICATE_KEYPASS.setTemporaryValue("MnD3bU5HpmPXag0e"); + Settings.WEBSERVER_CERTIFICATE_STOREPASS.setTemporaryValue("wDwwf663NLTm73gL"); + Settings.WEBSERVER_CERTIFICATE_ALIAS.setTemporaryValue("DefaultPlanCert"); + + Settings.WEBSERVER_PORT.setTemporaryValue(9000); + + bukkitSystem = new BukkitSystem(planMock); + bukkitSystem.enable(); + + bukkitSystem.getDatabaseSystem().getActiveDatabase().save() + .webUser(new WebUser("test", PassEncryptUtil.createHash("testPass"), 0)); + } + + @Before + public void setUp() { + Teardown.resetSettingsTempValues(); + } + + @After + public void tearDown() { + if (bukkitSystem != null) { + bukkitSystem.disable(); + } + Teardown.resetSettingsTempValues(); + } + + private static final TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { + //No need to implement. + } + + public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { + //No need to implement. + } + } + }; + + private SSLSocketFactory getRelaxedSocketFactory() throws NoSuchAlgorithmException, KeyManagementException { + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + return sc.getSocketFactory(); + } + + /** + * Test case against "Perm level 0 required, got 0". + */ + @Test + public void testHTTPSAuthForPages() throws IOException, WebException, KeyManagementException, NoSuchAlgorithmException { + String address = "https://localhost:9000"; + URL url = new URL(address); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + if (address.startsWith("https")) { + HttpsURLConnection httpsConn = (HttpsURLConnection) connection; + + // Disables unsigned certificate & hostname check, because we're trusting the user given certificate. + // This allows https connections internally to local ports. + httpsConn.setHostnameVerifier((hostname, session) -> true); + httpsConn.setSSLSocketFactory(getRelaxedSocketFactory()); + } + connection.setConnectTimeout(10000); + connection.setInstanceFollowRedirects(false); + connection.setRequestMethod("GET"); + connection.setUseCaches(false); + + String user = Base64Util.encode("test:testPass"); + connection.setRequestProperty("Authorization", "Basic " + user); + + int responseCode = connection.getResponseCode(); + + switch (responseCode) { + case 200: + return; + case 400: + throw new BadRequestException("Bad Request: " + url.toString()); + case 403: + throw new ForbiddenException(url.toString() + " returned 403"); + case 404: + throw new NotFoundException(url.toString() + " returned a 404, ensure that your server is connected to an up to date Plan server."); + case 412: + throw new UnauthorizedServerException(url.toString() + " reported that it does not recognize this server. Make sure '/plan m setup' was successful."); + case 500: + throw new InternalErrorException(); + default: + throw new WebException(url.toString() + "| Wrong response code " + responseCode); + } + } +} \ No newline at end of file