From 4e68b4f8617b87f3cee25380b27d1274bb50c1a2 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 23 Jan 2025 15:40:11 +0100 Subject: [PATCH 1/5] formatting change only --- .../gwt/common/client/NavigatorUtil.java | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/NavigatorUtil.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/NavigatorUtil.java index ef82171a6d5..a7e8bd1407c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/NavigatorUtil.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/NavigatorUtil.java @@ -7,39 +7,44 @@ import com.sap.sse.gwt.client.Notification.NotificationType; public class NavigatorUtil { /** - * Note that this method will check whether the browser does support the copyToClipboard feature first - * and will display a warning notification if it does not, to prevent JS error messages in the console. - * Please use {@link clientHasNavigatorCopyToClipboardSupport} to check for browser support, - * if case specific handling is required. - * On a successful copy an info notification is displayed. - * @param String text: Text to share. + * Note that this method will check whether the browser does support the copyToClipboard feature first and will + * display a warning notification if it does not, to prevent JS error messages in the console. Please use + * {@link clientHasNavigatorCopyToClipboardSupport} to check for browser support, if case specific handling is + * required. On a successful copy an info notification is displayed. + * + * @param String + * text: Text to share. */ public static void copyToClipboard(String text) { - if(nativeClientHasNavigatorCopyToClipboardSupport()) { + if (nativeClientHasNavigatorCopyToClipboardSupport()) { nativeCopyToClipboard(text); Notification.notify(StringMessages.INSTANCE.sharingLinkCopied(), NotificationType.INFO); - }else { + } else { GWT.log("This browser does not support copying to clipboard"); - Notification.notify(StringMessages.INSTANCE.browserDoesNotSupportCopyToClipboard(), NotificationType.WARNING); + Notification.notify(StringMessages.INSTANCE.browserDoesNotSupportCopyToClipboard(), + NotificationType.WARNING); } } /** - * Note that this method will check whether the browser does support the sharing feature first - * and will display a warning notification if it does not, to prevent JS error messages in the console. - * Please use {@link clientHasNavigatorShareSupport} to check for browser support, if case specific handling is required. - * @param String url: URL to share. - * @param String text: Optional text to share. + * Note that this method will check whether the browser does support the sharing feature first and will display a + * warning notification if it does not, to prevent JS error messages in the console. Please use + * {@link clientHasNavigatorShareSupport} to check for browser support, if case specific handling is required. + * + * @param String + * url: URL to share. + * @param String + * text: Optional text to share. */ public static void shareUrl(String url, String text) { - if(nativeClientHasNavigatorShareSupport()) { - if(text != null) { + if (nativeClientHasNavigatorShareSupport()) { + if (text != null) { nativeShareUrlAndText(url, text); - }else { + } else { nativeShareUrl(url); } - }else { + } else { GWT.log("This browser does not support native sharing"); Notification.notify(StringMessages.INSTANCE.browserDoesNotSupportNativeSharing(), NotificationType.WARNING); } From 7de36861ce82765671fffc5df312a7f54c199e14 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 23 Jan 2025 16:14:21 +0100 Subject: [PATCH 2/5] formatting changes and use of final only --- .../gwt/home/shared/places/event/EventContext.java | 8 ++++---- .../resources/SharedProxy.html | 2 +- .../server/gateway/jaxrs/sharing/HomeSharingUtils.java | 9 ++++++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/event/EventContext.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/event/EventContext.java index 4c10ca45d17..203ee4119eb 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/event/EventContext.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/event/EventContext.java @@ -53,13 +53,13 @@ public class EventContext implements ShareablePlaceContext { @Override public String getContextAsPathParameters() { - if(eventId != null) { + if (eventId != null) { String path = "/events/" + eventId; - if(regattaId != null) { - path += "/regattas/" + URLEncoder.encode(regattaId); + if (regattaId != null) { + path += "/regattas/" + URLEncoder.encode(regattaId); } return path; - }else { + } else { return null; } } diff --git a/java/com.sap.sailing.server.gateway/resources/SharedProxy.html b/java/com.sap.sailing.server.gateway/resources/SharedProxy.html index be0ee8e32ad..c31fcfbd261 100644 --- a/java/com.sap.sailing.server.gateway/resources/SharedProxy.html +++ b/java/com.sap.sailing.server.gateway/resources/SharedProxy.html @@ -11,6 +11,6 @@ ${disable_redirect_end} -
In case the redirect fails, please click here.
+
In case the redirect fails, please click here.
diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/HomeSharingUtils.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/HomeSharingUtils.java index 1aa08a088d8..f329e78231f 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/HomeSharingUtils.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/HomeSharingUtils.java @@ -130,11 +130,14 @@ public class HomeSharingUtils { public static Map createReplacementMap(String title, String description, String imageUrl, String placeUrl, String userAgent) { final Map replacementMap = new HashMap(); - String disabledStart = ""; - String disabledEnd = ""; - if(userAgent != null && userAgent.contains("facebookexternalhit")) { + final String disabledStart; + final String disabledEnd; + if (userAgent != null && userAgent.contains("facebookexternalhit")) { disabledStart = HTML_COMMENT_START; disabledEnd = HTML_COMMENT_END; + } else { + disabledStart = ""; + disabledEnd = ""; } replacementMap.put(REPLACEMENT_KEY_TITLE, title); replacementMap.put(REPLACEMENT_KEY_DESCRIPTION, description); From 1630c43824aec6d8ed8212671cb5d98b804ca35c Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 23 Jan 2025 18:02:45 +0100 Subject: [PATCH 3/5] bug6088: interims commit, fixing [] handling in permissions first --- .../LeaderboardGroupConfigPanel.java | 4 ++-- .../LeaderboardGroupOverviewPanel.java | 12 +++++------ .../jaxrs/sharing/HomeSharingResource.java | 7 ++++--- .../com/sap/sse/gwt/client/URLEncoder.java | 2 +- .../AbstractCompositeAuthorizingRealm.java | 3 ++- .../security/impl/PermissionConverter.java | 8 +++----- .../src/com/sap/sse/test/URLEncoderTest.java | 20 +++++++++++++++++++ 7 files changed, 38 insertions(+), 18 deletions(-) create mode 100644 java/com.sap.sse.test/src/com/sap/sse/test/URLEncoderTest.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupConfigPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupConfigPanel.java index 58056ef778d..683bcbb78df 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupConfigPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupConfigPanel.java @@ -480,9 +480,9 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel @Override public SafeHtml getValue(LeaderboardGroupDTO group) { String debugParam = Window.Location.getParameter("gwt.codesvr"); - String link = URLEncoder.encode("/gwt/Spectator.html?leaderboardGroupId=" + group.getId()+"&showRaceDetails=true&" + String link = "/gwt/Spectator.html?leaderboardGroupId=" + group.getId()+"&showRaceDetails=true&" + RaceBoardPerspectiveOwnSettings.PARAM_CAN_REPLAY_DURING_LIVE_RACES + "=true" - + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + debugParam : "")); + + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + URLEncoder.encode(debugParam) : ""); return ANCHORTEMPLATE.cell(UriUtils.fromString(link), group.getName()); } }; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupOverviewPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupOverviewPanel.java index 7d09bcc6715..cc15f17951d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupOverviewPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupOverviewPanel.java @@ -362,10 +362,10 @@ public class LeaderboardGroupOverviewPanel extends FormPanel { public SafeHtml getValue(StrippedLeaderboardDTO leaderboard) { LeaderboardGroupDTO selectedGroup = groupsSelectionModel.getSelectedObject(); String debugParam = Window.Location.getParameter("gwt.codesvr"); - String link = URLEncoder.encode("/gwt/Leaderboard.html?name=" + leaderboard.getName() + String link = "/gwt/Leaderboard.html?name=" + URLEncoder.encode(leaderboard.getName()) + (showRaceDetails ? "&showRaceDetails=true" : "") + "&root=overview" + "&leaderboardGroupId=" + selectedGroup.getId().toString() - + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + debugParam : "")); + + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + URLEncoder.encode(debugParam) : ""); return ANCHORTEMPLATE.anchor(UriUtils.fromString(link), leaderboard.getName()); } }; @@ -455,11 +455,11 @@ public class LeaderboardGroupOverviewPanel extends FormPanel { StrippedLeaderboardDTO selectedLeaderboard = leaderboardsSelectionModel.getSelectedObject(); RegattaNameAndRaceName raceId = (RegattaNameAndRaceName) race.getRaceIdentifier(fleet); String debugParam = Window.Location.getParameter("gwt.codesvr"); - String link = URLEncoder.encode(RaceBoardPanel.RACEBOARD_PATH+"?leaderboardName=" - + selectedLeaderboard.getName() + "&raceName=" + raceId.getRaceName() + "®attaName=" - + raceId.getRegattaName() + "&leaderboardGroupId=" + selectedGroup.getId().toString() + String link = RaceBoardPanel.RACEBOARD_PATH+"?leaderboardName=" + + URLEncoder.encode(selectedLeaderboard.getName()) + "&raceName=" + URLEncoder.encode(raceId.getRaceName()) + "®attaName=" + + URLEncoder.encode(raceId.getRegattaName()) + "&leaderboardGroupId=" + selectedGroup.getId().toString() + "&root=overview" - + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + debugParam : "")); + + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + URLEncoder.encode(debugParam) : ""); name = ANCHORTEMPLATE.anchor(UriUtils.fromString(link), raceDisplayName); } else { name = new SafeHtmlBuilder().appendHtmlConstant(raceDisplayName).toSafeHtml(); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/HomeSharingResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/HomeSharingResource.java index e6ea4c40737..49482e076b5 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/HomeSharingResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/HomeSharingResource.java @@ -2,6 +2,7 @@ package com.sap.sailing.server.gateway.jaxrs.sharing; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.UUID; @@ -58,7 +59,7 @@ public class HomeSharingResource extends AbstractSailingServerResource { @Path("/events/{eventId}/regattas/{regattaId}") @Produces("text/html") public String getSharedRegatta(@PathParam("eventId") String eventId, @PathParam("regattaId") String regattaId, - @HeaderParam("user-agent") String userAgent) { + @HeaderParam("user-agent") String userAgent) throws UnsupportedEncodingException { RacingEventService eventService = getService(); SecurityService securityService = getSecurityService(); UUID uuid = UUID.fromString(eventId); @@ -80,13 +81,13 @@ public class HomeSharingResource extends AbstractSailingServerResource { if (leaderboardByName != null) { securityService.checkCurrentUserReadPermission(leaderboardByName); title = HomeSharingUtils.findTitle(leaderboardByName); - placeUrl = baseUrl.asRegattaPlaceLink(eventId, decodedRegattaId); + placeUrl = baseUrl.asRegattaPlaceLink(eventId, URLEncoder.encode(decodedRegattaId, "UTF-8")); } else { Regatta regattaByName = eventService.getRegattaByName(decodedRegattaId); if (regattaByName != null) { securityService.checkCurrentUserReadPermission(regattaByName); title = HomeSharingUtils.findTitle(leaderboardByName); - placeUrl = baseUrl.asRegattaPlaceLink(eventId, decodedRegattaId); + placeUrl = baseUrl.asRegattaPlaceLink(eventId, URLEncoder.encode(decodedRegattaId, "UTF-8")); } else { throw new IllegalArgumentException(); } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/URLEncoder.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/URLEncoder.java index 77219290e4c..e7cf6ea2055 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/URLEncoder.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/URLEncoder.java @@ -13,7 +13,7 @@ public class URLEncoder { private URLEncoder() { } - private final static char[] charsToReplace = { '\'', '(', ')', '"', '[', ']', '{', '}', '<', '>', '|', '+' }; + private final static char[] charsToReplace = { '\'', '(', ')', '"', '[', ']', '{', '}', '<', '>', '|', '+', ':', '/' }; public static String encode(String url) { String nearlyEncoded = URL.encode(url); diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/AbstractCompositeAuthorizingRealm.java b/java/com.sap.sse.security/src/com/sap/sse/security/AbstractCompositeAuthorizingRealm.java index 7118e04fd8b..2cf6bd96d6b 100755 --- a/java/com.sap.sse.security/src/com/sap/sse/security/AbstractCompositeAuthorizingRealm.java +++ b/java/com.sap.sse.security/src/com/sap/sse/security/AbstractCompositeAuthorizingRealm.java @@ -20,6 +20,7 @@ import org.osgi.framework.BundleContext; import org.osgi.util.tracker.ServiceTracker; import com.sap.sse.security.impl.Activator; +import com.sap.sse.security.impl.PermissionConverter; import com.sap.sse.security.interfaces.AccessControlStore; import com.sap.sse.security.interfaces.UserStore; import com.sap.sse.security.shared.AccessControlListAnnotation; @@ -169,7 +170,7 @@ public abstract class AbstractCompositeAuthorizingRealm extends AuthorizingRealm public boolean isPermitted(PrincipalCollection principals, Permission perm) { String username = (String) principals.getPrimaryPrincipal(); final User user = getUserStore().getUserByName(username); - final WildcardPermission wildcardPermission = new WildcardPermission(perm.toString().replaceAll("\\[|\\]", "")); + final WildcardPermission wildcardPermission = new PermissionConverter().getWildcardPermission(perm); List> parts = wildcardPermission.getParts(); final boolean result; if (parts.size() > 2 && !parts.get(2).isEmpty()) { diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/impl/PermissionConverter.java b/java/com.sap.sse.security/src/com/sap/sse/security/impl/PermissionConverter.java index f7da671ef64..6491cb7641e 100755 --- a/java/com.sap.sse.security/src/com/sap/sse/security/impl/PermissionConverter.java +++ b/java/com.sap.sse.security/src/com/sap/sse/security/impl/PermissionConverter.java @@ -27,13 +27,11 @@ public class PermissionConverter { private final PermissionToObjectIdConverter poc = new PermissionToObjectIdConverter(); public WildcardPermission getWildcardPermission(Permission permission) { - return new WildcardPermission(getAsString(permission)); + final WildcardPermission result; + result = new WildcardPermission(permission.toString()); + return result; } - private String getAsString(Permission permission) { - return permission.toString().replaceAll("\\[|\\]", ""); // FIXME for multi-subpart parts remove leading blanks after , - } - /** * Splits the permission along ":" occurrences */ diff --git a/java/com.sap.sse.test/src/com/sap/sse/test/URLEncoderTest.java b/java/com.sap.sse.test/src/com/sap/sse/test/URLEncoderTest.java new file mode 100644 index 00000000000..57d41e4c8dd --- /dev/null +++ b/java/com.sap.sse.test/src/com/sap/sse/test/URLEncoderTest.java @@ -0,0 +1,20 @@ +package com.sap.sse.test; + +import static org.junit.Assert.assertEquals; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +import org.junit.Test; + +public class URLEncoderTest { + @Test + public void testWithSlash() throws UnsupportedEncodingException { + assertEquals("a%2Fb", URLEncoder.encode("a/b", "UTF-8")); + } + + @Test + public void testWithColon() throws UnsupportedEncodingException { + assertEquals("a%3Ab", URLEncoder.encode("a:b", "UTF-8")); + } +} From 112ae6ef51f7730d69542a513fe497410fed9e7f Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 24 Jan 2025 10:45:46 +0100 Subject: [PATCH 4/5] bug6088: addded test case for permissions that have [] in their object names --- .../com/sap/sse/security/test/PermissionConverterTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/PermissionConverterTest.java b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/PermissionConverterTest.java index ae3ae1ad7e4..296ca1ef6f4 100755 --- a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/PermissionConverterTest.java +++ b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/PermissionConverterTest.java @@ -51,6 +51,12 @@ public class PermissionConverterTest { com.sap.sse.security.shared.WildcardPermission wp = new PermissionConverter().getWildcardPermission(new org.apache.shiro.authz.permission.WildcardPermission("LEADERBOARD:EDIT:KW2017 Laser Int.", /* case sensitive */ true)); assertEquals("LEADERBOARD:EDIT:KW2017 Laser Int.", wp.toString()); } + + @Test + public void testGetWildcardPermissionWithMultipleObjectsInThirdPart() { + com.sap.sse.security.shared.WildcardPermission wp = new PermissionConverter().getWildcardPermission(new org.apache.shiro.authz.permission.WildcardPermission("LEADERBOARD:EDIT:KW2017 Laser Int.,KW2024 [ILCA7]", /* case sensitive */ true)); + assertEquals("LEADERBOARD:EDIT:KW2017 Laser Int.,KW2024 [ILCA7]", wp.toString()); + } @Test public void testMixedCaseWildcardPermissionWithDedicatedRealm() { From 934688f96cd084ea3f6bac9a9c6b5f256a6514da Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 24 Jan 2025 18:34:01 +0100 Subject: [PATCH 5/5] bug6088: regatta name/ID is now base64-encoded in place tokens --- .../common/client/AbstractMapTokenizer.java | 54 +++++++++++++------ .../event/AbstractEventActivityProxy.java | 7 ++- .../places/event/AbstractEventPlace.java | 28 ++++++++-- .../shared/places/event/EventContext.java | 2 +- .../LeaderboardGroupConfigPanel.java | 2 +- .../LeaderboardGroupOverviewPanel.java | 10 ++-- .../jaxrs/sharing/HomeSharingResource.java | 5 +- .../sharing/TokenizedHomePlaceUrlBuilder.java | 12 ++++- .../com/sap/sse/gwt/client/URLEncoder.java | 12 ++--- 9 files changed, 91 insertions(+), 41 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/AbstractMapTokenizer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/AbstractMapTokenizer.java index 0decb6201bc..ee8b9ebfff7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/AbstractMapTokenizer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/AbstractMapTokenizer.java @@ -18,6 +18,26 @@ public abstract class AbstractMapTokenizer

implements PlaceToke @Override public P getPlace(String token) { + /* + * See bug 6088; the token delivered here as parameter has undergone encoding/decoding using URL.encodeURI; this + * leaves encoded characters with special meaning in the URL untouched, such as '/', '&' or '?', meaning that + * their encoding such as %2F for the '/' character will remain unchanged in the token and will not be replaced + * by '/'. Other characters, such as the '%' character, encoded as '%25', will be decoded. + * + * But we do need to be able to encode special characters in tokens, e.g., for regatta names which may contain + * any UTF character. The problem, though, is that what we receive here as the "token" parameter is already a + * "projection" that cannot be inverted to the original string. For example, when we see "%2F" as the "token" + * value, we cannot tell whether the original URL hash/fragment was "%252F" or just "%2F" as the latter would + * have been returned unchanged. + * + * Alternatively, we could look at Window.Location.getHash() and silently assume that that would be what was + * decoded and then passed to this method; interestingly, the call to here from SwitchingEntryPoint.onModuleLoad() + * passes an non-decoded hash/fragment taken straight from Window.Location.getHash(). + * + * Yet alternatively, we could find an encoding of values in tokens that is guaranteed to never produce the + * offending characters such as '%', '/' or the like. As an extreme case, we could use a Base64 encoding + * of all values and then run a Base64 decoding to obtain the original token again. + */ return getPlaceFromParameters(toParameterMap(token)); } @@ -32,7 +52,6 @@ public abstract class AbstractMapTokenizer

implements PlaceToke private String toToken(Map> parameters) { StringBuilder stringBuilder = new StringBuilder(); - for (Map.Entry> entry : parameters.entrySet()) { if (entry.getValue() == null || entry.getValue().isEmpty()) { continue; @@ -41,31 +60,34 @@ public abstract class AbstractMapTokenizer

implements PlaceToke if (stringBuilder.length() > 0) { stringBuilder.append("&"); } - stringBuilder.append(entry.getKey() + "=" + val); + stringBuilder.append(entry.getKey() + "=" + val); // see bug6088; values must contain only URL-compliant characters, so any encoding must be done by the caller } } - return stringBuilder.toString(); } private Map> toParameterMap(String placeParametersAsToken) { - List list = Arrays.asList(placeParametersAsToken.split("&")); - + final Map> result; + final List list = Arrays.asList(placeParametersAsToken.split("&")); if (list == null || list.size() < 1) { logger.warning("Token empty, no-op"); - Collections.emptyMap(); - } - - Map> result = new HashMap<>(); - for (String listItem : list) { - String[] nvPair = listItem.split("="); - if (nvPair == null || nvPair.length != 2) { - logger.warning("Invalid parameters"); - continue; + result = Collections.emptyMap(); + } else { + result = new HashMap<>(); + for (String listItem : list) { + final int indexOfEquals = listItem.indexOf('='); // don't use "split" because value may contain '=' characters + final String key; + final String value; + if (indexOfEquals < 0) { + key = listItem; + value = null; + } else { + key = listItem.substring(0, indexOfEquals); + value = listItem.substring(indexOfEquals+1); + } + Util.addToValueSet(result, key, value); // see bug6088; values are expected to travel safely through URLs; encoding is the caller's responsibility } - Util.addToValueSet(result, nvPair[0], nvPair[1]); } return result; } - } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/event/AbstractEventActivityProxy.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/event/AbstractEventActivityProxy.java index 7be9079e3ec..1da6a830671 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/event/AbstractEventActivityProxy.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/event/AbstractEventActivityProxy.java @@ -1,6 +1,7 @@ package com.sap.sailing.gwt.home.shared.places.event; import java.util.UUID; +import java.util.logging.Logger; import com.google.gwt.activity.shared.Activity; import com.sap.sailing.gwt.home.communication.event.GetEventViewAction; @@ -21,7 +22,8 @@ import com.sap.sse.gwt.client.mvp.AbstractActivityProxy; import com.sap.sse.gwt.client.mvp.ClientFactory; public abstract class AbstractEventActivityProxy extends AbstractActivityProxy implements ProvidesNavigationPath { - + private static final Logger logger = Logger.getLogger(AbstractEventActivityProxy.class.getName()); + private final C clientFactory; private AbstractEventPlace place; private NavigationPathDisplay navigationPathDisplay; @@ -55,7 +57,7 @@ public abstract class AbstractEventActivityProxy extends AbstractMapTokenizer { + /** + * See also {@code TokenizedHomePlaceUrlBuilder.EVENT_ID_PARAM} + */ private final static String PARAM_EVENTID = "eventId"; + + /** + * See also {@code TokenizedHomePlaceUrlBuilder.REGATTA_ID_PARAM}; expected to be a Base64 string that has been URL-encoded, + * so that the optional trailing '=' characters are properly encoded as %3D, for example. + */ private final static String PARAM_REGATTAID = "regattaId"; protected PLACE getPlaceFromParameters(Map> parameters) { + final String encodedRegattaId = extractSingleParameter(parameters, PARAM_REGATTAID); + String decodedRegattaId; + try { + decodedRegattaId = encodedRegattaId==null?null:new String(Base64Utils.fromBase64(encodedRegattaId)); + } catch (Throwable e) { + logger.warning("Error trying to decode regatta ID "+encodedRegattaId+"; trying to use URL decoding to obtain regatta name"); + decodedRegattaId = URL.decodeQueryString(encodedRegattaId); + } + // see bug 6088: regatta names may contain any UTF character and therefore need encoding return getRealPlace(new EventContext().withId(extractSingleParameter(parameters, PARAM_EVENTID)) - .withRegattaId(extractSingleParameter(parameters, PARAM_REGATTAID)), parameters); + .withRegattaId(decodedRegattaId), parameters); } private String extractSingleParameter(Map> parameters, String key) { @@ -63,9 +85,9 @@ public abstract class AbstractEventPlace extends Place implements HasLocationTit Map> parameters = new HashMap<>(); EventContext context = place.getCtx(); Util.addToValueSet(parameters, PARAM_EVENTID, context.getEventId()); - String regattaId = context.getRegattaId(); + String regattaId = context.getRegattaId(); // bug 6088: we assume that a regatta id/name can contain any UTF character and therefore needs encoding if (regattaId != null && !regattaId.isEmpty()) { - Util.addToValueSet(parameters, PARAM_REGATTAID, context.getRegattaId()); + Util.addToValueSet(parameters, PARAM_REGATTAID, Base64Utils.toBase64(context.getRegattaId().getBytes())); } return parameters; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/event/EventContext.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/event/EventContext.java index 203ee4119eb..e179004f2ac 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/event/EventContext.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/event/EventContext.java @@ -56,7 +56,7 @@ public class EventContext implements ShareablePlaceContext { if (eventId != null) { String path = "/events/" + eventId; if (regattaId != null) { - path += "/regattas/" + URLEncoder.encode(regattaId); + path += "/regattas/" + URLEncoder.encodeQueryString(regattaId); } return path; } else { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupConfigPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupConfigPanel.java index 683bcbb78df..61c24dc9bda 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupConfigPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupConfigPanel.java @@ -482,7 +482,7 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel String debugParam = Window.Location.getParameter("gwt.codesvr"); String link = "/gwt/Spectator.html?leaderboardGroupId=" + group.getId()+"&showRaceDetails=true&" + RaceBoardPerspectiveOwnSettings.PARAM_CAN_REPLAY_DURING_LIVE_RACES + "=true" - + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + URLEncoder.encode(debugParam) : ""); + + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + URLEncoder.encodeQueryString(debugParam) : ""); return ANCHORTEMPLATE.cell(UriUtils.fromString(link), group.getName()); } }; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupOverviewPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupOverviewPanel.java index cc15f17951d..0b38a043712 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupOverviewPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupOverviewPanel.java @@ -362,10 +362,10 @@ public class LeaderboardGroupOverviewPanel extends FormPanel { public SafeHtml getValue(StrippedLeaderboardDTO leaderboard) { LeaderboardGroupDTO selectedGroup = groupsSelectionModel.getSelectedObject(); String debugParam = Window.Location.getParameter("gwt.codesvr"); - String link = "/gwt/Leaderboard.html?name=" + URLEncoder.encode(leaderboard.getName()) + String link = "/gwt/Leaderboard.html?name=" + URLEncoder.encodeQueryString(leaderboard.getName()) + (showRaceDetails ? "&showRaceDetails=true" : "") + "&root=overview" + "&leaderboardGroupId=" + selectedGroup.getId().toString() - + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + URLEncoder.encode(debugParam) : ""); + + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + URLEncoder.encodeQueryString(debugParam) : ""); return ANCHORTEMPLATE.anchor(UriUtils.fromString(link), leaderboard.getName()); } }; @@ -456,10 +456,10 @@ public class LeaderboardGroupOverviewPanel extends FormPanel { RegattaNameAndRaceName raceId = (RegattaNameAndRaceName) race.getRaceIdentifier(fleet); String debugParam = Window.Location.getParameter("gwt.codesvr"); String link = RaceBoardPanel.RACEBOARD_PATH+"?leaderboardName=" - + URLEncoder.encode(selectedLeaderboard.getName()) + "&raceName=" + URLEncoder.encode(raceId.getRaceName()) + "®attaName=" - + URLEncoder.encode(raceId.getRegattaName()) + "&leaderboardGroupId=" + selectedGroup.getId().toString() + + URLEncoder.encodeQueryString(selectedLeaderboard.getName()) + "&raceName=" + URLEncoder.encodeQueryString(raceId.getRaceName()) + "®attaName=" + + URLEncoder.encodeQueryString(raceId.getRegattaName()) + "&leaderboardGroupId=" + selectedGroup.getId().toString() + "&root=overview" - + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + URLEncoder.encode(debugParam) : ""); + + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + URLEncoder.encodeQueryString(debugParam) : ""); name = ANCHORTEMPLATE.anchor(UriUtils.fromString(link), raceDisplayName); } else { name = new SafeHtmlBuilder().appendHtmlConstant(raceDisplayName).toSafeHtml(); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/HomeSharingResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/HomeSharingResource.java index 49482e076b5..c2686e1a71d 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/HomeSharingResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/HomeSharingResource.java @@ -2,7 +2,6 @@ package com.sap.sailing.server.gateway.jaxrs.sharing; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; -import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.UUID; @@ -81,13 +80,13 @@ public class HomeSharingResource extends AbstractSailingServerResource { if (leaderboardByName != null) { securityService.checkCurrentUserReadPermission(leaderboardByName); title = HomeSharingUtils.findTitle(leaderboardByName); - placeUrl = baseUrl.asRegattaPlaceLink(eventId, URLEncoder.encode(decodedRegattaId, "UTF-8")); + placeUrl = baseUrl.asRegattaPlaceLink(eventId, decodedRegattaId); } else { Regatta regattaByName = eventService.getRegattaByName(decodedRegattaId); if (regattaByName != null) { securityService.checkCurrentUserReadPermission(regattaByName); title = HomeSharingUtils.findTitle(leaderboardByName); - placeUrl = baseUrl.asRegattaPlaceLink(eventId, URLEncoder.encode(decodedRegattaId, "UTF-8")); + placeUrl = baseUrl.asRegattaPlaceLink(eventId, decodedRegattaId); } else { throw new IllegalArgumentException(); } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/TokenizedHomePlaceUrlBuilder.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/TokenizedHomePlaceUrlBuilder.java index 56e009ad4f0..0774f1e2d3f 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/TokenizedHomePlaceUrlBuilder.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/sharing/TokenizedHomePlaceUrlBuilder.java @@ -6,6 +6,8 @@ import java.net.URL; import javax.ws.rs.core.UriInfo; +import com.sap.sse.common.Base64Utils; + public class TokenizedHomePlaceUrlBuilder { private static final String EVENT_PATH = "/event"; private static final String SERIES_PATH = "/series"; @@ -14,7 +16,15 @@ public class TokenizedHomePlaceUrlBuilder { private static final String HOME_HTML = "/Home.html"; private static final String STARTOFPARAMS = "/:"; private static final String STARTOFTOKENPATH = "#"; + + /** + * See also {@code AbstractEventPlace.Tokenizer.PARAM_EVENTID} + */ private static final String EVENT_ID_PARAM = "eventId"; + + /** + * See also {@code AbstractEventPlace.Tokenizer.PARAM_REGATTAID} + */ private static final String REGATTA_ID_PARAM = "regattaId"; private static final String LEADERBOARD_GROUP_ID_PARAM = "leaderboardGroupId"; @@ -49,7 +59,7 @@ public class TokenizedHomePlaceUrlBuilder { String url = new String(baseUrl.toString()); url += STARTOFTOKENPATH + REGATTA_OVERVIEW_PATH; url += buildToken(eventId, EVENT_ID_PARAM); - url += buildToken(regattaId, REGATTA_ID_PARAM); + url += buildToken(Base64Utils.toBase64(regattaId.getBytes()), REGATTA_ID_PARAM); return url; } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/URLEncoder.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/URLEncoder.java index e7cf6ea2055..78ed9c419b8 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/URLEncoder.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/URLEncoder.java @@ -4,7 +4,7 @@ import com.google.gwt.http.client.URL; /** * A very basic URL encoder that also works in a GWT environment where java.net.URLEncoder is not available. - * It is based on {@link URL#encode(String)} and adds encoding for the backslash (\) character. + * It is based on {@link URL#encodeQueryString(String)}. * * @author Axel Uhl (D043530) * @@ -13,14 +13,8 @@ public class URLEncoder { private URLEncoder() { } - private final static char[] charsToReplace = { '\'', '(', ')', '"', '[', ']', '{', '}', '<', '>', '|', '+', ':', '/' }; - - public static String encode(String url) { - String nearlyEncoded = URL.encode(url); - for (char c : charsToReplace) { - nearlyEncoded = nearlyEncoded.replaceAll("\\" + Character.toString(c), "%" + Integer.toHexString(c)); - } - return nearlyEncoded; + public static String encodeQueryString(String url) { + return URL.encodeQueryString(url); } }