From 45d6bfb8404ff0222e60e6471a3f5dc9046f103b Mon Sep 17 00:00:00 2001 From: Risto Lahtela <24460436+Rsl1122@users.noreply.github.com> Date: Sat, 28 Mar 2020 13:00:34 +0200 Subject: [PATCH] Made resource resolution more flexible - API 5.1-R0.2: Added ResolverService#getResolvers method - /players/ now redirects to /players - All error pages now have proper css Affects issues: - Fixed #1378 --- Plan/api/build.gradle | 2 +- .../plan/capability/Capability.java | 4 ++ .../plan/delivery/web/ResolverService.java | 13 ++++++ .../plan/delivery/web/ResolverSvc.java | 12 ++++++ .../delivery/webserver/ResponseResolver.java | 40 ++++++++++--------- .../resolver/PlayerPageResolver.java | 27 +++++++++---- .../resolver/PlayersPageResolver.java | 2 + .../resolver/StaticResourceResolver.java | 12 +++++- .../settings/locale/lang/ErrorPageLang.java | 2 +- .../assets/plan/locale/locale_EN.txt | 2 +- 10 files changed, 86 insertions(+), 30 deletions(-) diff --git a/Plan/api/build.gradle b/Plan/api/build.gradle index 46ad4aa29..bd0df50bd 100644 --- a/Plan/api/build.gradle +++ b/Plan/api/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly "com.google.code.gson:gson:$gsonVersion" } -ext.apiVersion = '5.1-R0.1' +ext.apiVersion = '5.1-R0.2' bintray { user = System.getenv('BINTRAY_USER') diff --git a/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java b/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java index 39ec75cd2..b1ac9bf8c 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java @@ -67,6 +67,10 @@ enum Capability { * {@link com.djrapitops.plan.delivery.web.ResolverService} */ PAGE_EXTENSION_RESOLVERS, + /** + * {@link com.djrapitops.plan.delivery.web.ResolverService#getResolvers(String)} + */ + PAGE_EXTENSION_RESOLVERS_LIST, /** * {@link com.djrapitops.plan.delivery.web.ResourceService} */ diff --git a/Plan/api/src/main/java/com/djrapitops/plan/delivery/web/ResolverService.java b/Plan/api/src/main/java/com/djrapitops/plan/delivery/web/ResolverService.java index 68d57985c..096c95df3 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/delivery/web/ResolverService.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/delivery/web/ResolverService.java @@ -18,6 +18,7 @@ package com.djrapitops.plan.delivery.web; import com.djrapitops.plan.delivery.web.resolver.Resolver; +import java.util.List; import java.util.Optional; import java.util.regex.Pattern; @@ -74,6 +75,18 @@ public interface ResolverService { */ Optional getResolver(String target); + /** + * Obtain all Resolvers that match the target. + *

+ * If first returns Optional.empty next one should be used. + *

+ * Requires Capability PAGE_EXTENSION_RESOLVERS_LIST. + * + * @param target "/example/target" + * @return List of Resolvers if registered or empty list. + */ + List getResolvers(String target); + class Holder { static ResolverService service; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/web/ResolverSvc.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/web/ResolverSvc.java index f16aec7f8..6c40f3d5c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/web/ResolverSvc.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/web/ResolverSvc.java @@ -68,6 +68,18 @@ public class ResolverSvc implements ResolverService { return Optional.empty(); } + @Override + public List getResolvers(String target) { + List resolvers = new ArrayList<>(); + for (Container container : basicResolvers) { + if (container.matcher.test(target)) resolvers.add(container.resolver); + } + for (Container container : regexResolvers) { + if (container.matcher.test(target)) resolvers.add(container.resolver); + } + return resolvers; + } + public Optional getPluginInChargeOf(String target) { for (Container container : basicResolvers) { if (container.matcher.test(target)) return Optional.of(container.plugin); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseResolver.java index 7031b594e..600ba49ff 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseResolver.java @@ -35,6 +35,7 @@ import dagger.Lazy; import javax.inject.Inject; import javax.inject.Singleton; +import java.util.List; import java.util.Optional; import java.util.regex.Pattern; @@ -100,7 +101,7 @@ public class ResponseResolver { resolverService.registerResolver(plugin, "/network", serverPageResolver); resolverService.registerResolver(plugin, "/server", serverPageResolver); resolverService.registerResolverForMatches(plugin, Pattern.compile("^/$"), rootPageResolver); - resolverService.registerResolverForMatches(plugin, Pattern.compile("^/(vendor|css|js|img)/.*"), staticResourceResolver); + resolverService.registerResolverForMatches(plugin, Pattern.compile("^.*/(vendor|css|js|img)/.*"), staticResourceResolver); resolverService.registerResolver(plugin, "/v1", rootJSONResolver.getResolver()); } @@ -136,30 +137,33 @@ public class ResponseResolver { Optional authentication = internalRequest.getAuth(); - Optional foundResolver = resolverService.getResolver(internalRequest.getPath().asString()); - if (!foundResolver.isPresent()) return responseFactory.pageNotFound404(); + List foundResolvers = resolverService.getResolvers(internalRequest.getPath().asString()); + if (foundResolvers.isEmpty()) return responseFactory.pageNotFound404(); - Resolver resolver = foundResolver.get(); + for (Resolver resolver : foundResolvers) { + Request request = internalRequest.toAPIRequest(); + if (resolver.requiresAuth(request)) { + // Get required auth + boolean isAuthRequired = webServer.get().isAuthRequired(); + if (isAuthRequired && !authentication.isPresent()) { + if (webServer.get().isUsingHTTPS()) { + return responseFactory.basicAuth(); + } else { + return responseFactory.forbidden403(); + } + } - Request request = internalRequest.toAPIRequest(); - if (resolver.requiresAuth(request)) { - // Get required auth - boolean isAuthRequired = webServer.get().isAuthRequired(); - if (isAuthRequired && !authentication.isPresent()) { - if (webServer.get().isUsingHTTPS()) { - return responseFactory.basicAuth(); + if (!isAuthRequired || resolver.canAccess(request)) { + Optional resolved = resolver.resolve(request); + if (resolved.isPresent()) return resolved.get(); } else { return responseFactory.forbidden403(); } - } - - if (!isAuthRequired || resolver.canAccess(request)) { - return resolver.resolve(request).orElseGet(responseFactory::pageNotFound404); } else { - return responseFactory.forbidden403(); + Optional resolved = resolver.resolve(request); + if (resolved.isPresent()) return resolved.get(); } - } else { - return resolver.resolve(request).orElseGet(responseFactory::pageNotFound404); } + return responseFactory.pageNotFound404(); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/PlayerPageResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/PlayerPageResolver.java index 570dd7ed6..949a7cc49 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/PlayerPageResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/PlayerPageResolver.java @@ -16,6 +16,7 @@ */ package com.djrapitops.plan.delivery.webserver.resolver; +import com.djrapitops.plan.delivery.rendering.html.Html; import com.djrapitops.plan.delivery.web.resolver.Resolver; import com.djrapitops.plan.delivery.web.resolver.Response; import com.djrapitops.plan.delivery.web.resolver.request.Request; @@ -23,6 +24,7 @@ import com.djrapitops.plan.delivery.web.resolver.request.URIPath; import com.djrapitops.plan.delivery.web.resolver.request.WebUser; import com.djrapitops.plan.delivery.webserver.ResponseFactory; import com.djrapitops.plan.identification.UUIDUtility; +import org.apache.commons.lang3.StringUtils; import javax.inject.Inject; import javax.inject.Singleton; @@ -60,17 +62,26 @@ public class PlayerPageResolver implements Resolver { @Override public Optional resolve(Request request) { URIPath path = request.getPath(); - Optional part = path.getPart(1); - if (!part.isPresent()) return Optional.empty(); + if (StringUtils.containsAny(path.asString(), "/vendor/", "/js/", "/css/", "/img/")) { + return Optional.empty(); + } + return path.getPart(1) + .map(playerName -> getResponse(request.getPath(), playerName)); + } - String playerName = part.get(); + private Response getResponse(URIPath path, String playerName) { UUID playerUUID = uuidUtility.getUUIDOf(playerName); - if (playerUUID == null) return Optional.of(responseFactory.uuidNotFound404()); + if (playerUUID == null) return responseFactory.uuidNotFound404(); boolean raw = path.getPart(2).map("raw"::equalsIgnoreCase).orElse(false); - return Optional.of( - raw ? responseFactory.rawPlayerPageResponse(playerUUID) - : responseFactory.playerPageResponse(playerUUID) - ); + if (raw) { + return responseFactory.rawPlayerPageResponse(playerUUID); + } + + if (path.getPart(2).isPresent()) { + // Redirect /player/Name/ to /player/Name + return responseFactory.redirectResponse("../" + Html.encodeToURL(playerName)); + } + return responseFactory.playerPageResponse(playerUUID); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/PlayersPageResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/PlayersPageResolver.java index 07309136b..d9e10b9c3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/PlayersPageResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/PlayersPageResolver.java @@ -49,6 +49,8 @@ public class PlayersPageResolver implements Resolver { @Override public Optional resolve(Request request) { + // Redirect /players/ to /players + if (request.getPath().getPart(1).isPresent()) return Optional.of(responseFactory.redirectResponse("/players")); return Optional.of(responseFactory.playersPageResponse()); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/StaticResourceResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/StaticResourceResolver.java index 3b2786575..6c0d1867f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/StaticResourceResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/StaticResourceResolver.java @@ -19,6 +19,7 @@ package com.djrapitops.plan.delivery.webserver.resolver; import com.djrapitops.plan.delivery.web.resolver.NoAuthResolver; import com.djrapitops.plan.delivery.web.resolver.Response; import com.djrapitops.plan.delivery.web.resolver.request.Request; +import com.djrapitops.plan.delivery.web.resolver.request.URIPath; import com.djrapitops.plan.delivery.webserver.ResponseFactory; import org.apache.commons.lang3.StringUtils; @@ -47,7 +48,7 @@ public class StaticResourceResolver implements NoAuthResolver { } private Response getResponse(Request request) { - String resource = request.getPath().asString().substring(1); + String resource = getPath(request).asString().substring(1); if (resource.endsWith(".css")) { return responseFactory.cssResponse(resource); } @@ -62,4 +63,13 @@ public class StaticResourceResolver implements NoAuthResolver { } return null; } + + private URIPath getPath(Request request) { + URIPath path = request.getPath(); + // Remove everything before /vendor /css /js or /img + while (!path.getPart(0).map(part -> part.matches("(vendor|css|js|img)")).orElse(true)) { + path = path.omitFirst(); + } + return path; + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/ErrorPageLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/ErrorPageLang.java index 4c0b3e180..8091a1c36 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/ErrorPageLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/ErrorPageLang.java @@ -24,7 +24,7 @@ package com.djrapitops.plan.settings.locale.lang; public enum ErrorPageLang implements Lang { UUID_404("Player UUID was not found in the database."), NO_SERVERS_404("No Servers online to perform the request."), - NOT_PLAYED_404("Player has not played on this server."), + NOT_PLAYED_404("Plan has not seen this player."), UNKNOWN_PAGE_404("Make sure you're accessing a link given by a command, Examples:

/player/PlayerName
/server/ServerName

"), UNAUTHORIZED_401("Unauthorized"), AUTHENTICATION_FAILED_401("Authentication Failed."), diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_EN.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_EN.txt index 0d9d56df7..53d2e10eb 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_EN.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_EN.txt @@ -295,7 +295,7 @@ HTML ERRORS - AUTHENTICATION_FAILED_401 || Authentication Failed. HTML ERRORS - FORBIDDEN_403 || Forbidden HTML ERRORS - NO_SERVERS_404 || No Servers online to perform the request. HTML ERRORS - NOT_FOUND_404 || Not Found -HTML ERRORS - NOT_PLAYED_404 || Player has not played on this server. +HTML ERRORS - NOT_PLAYED_404 || Plan has not seen this player. HTML ERRORS - PAGE_NOT_FOUND_404 || Page does not exist. HTML ERRORS - UNAUTHORIZED_401 || Unauthorized HTML ERRORS - UNKNOWN_PAGE_404 || Make sure you're accessing a link given by a command, Examples:

/player/PlayerName
/server/ServerName