diff --git a/dotCMS/hotfix_tracking.md b/dotCMS/hotfix_tracking.md index 6fbc4e053252..0c046b80f7ab 100644 --- a/dotCMS/hotfix_tracking.md +++ b/dotCMS/hotfix_tracking.md @@ -36,4 +36,5 @@ This maintenance release includes the following code fixes: 30. https://github.com/dotCMS/core/issues/26706 : Change Default PUBSUB Provider #26706 31. https://github.com/dotCMS/core/issues/26825 : Change order of config precidence #26825 32. https://github.com/dotCMS/core/issues/26439 : Relationship fields not respecting the order identifiers are sent via the workflow API #26439 -33. https://github.com/dotCMS/core/issues/26693 : Edit Permissions Individually stuck when editing folder with legacy ID #26693 \ No newline at end of file +33. https://github.com/dotCMS/core/issues/26693 : Edit Permissions Individually stuck when editing folder with legacy ID #26693 +34. https://github.com/dotCMS/core/issues/26640 : Cyrillic URLs encoding issue when configured by Vanity URL Redirect #26640 \ No newline at end of file diff --git a/dotCMS/src/integration-test/java/com/dotcms/filters/VanityUrlFilterTest.java b/dotCMS/src/integration-test/java/com/dotcms/filters/VanityUrlFilterTest.java index 52b9103f3b94..684f59c3b229 100644 --- a/dotCMS/src/integration-test/java/com/dotcms/filters/VanityUrlFilterTest.java +++ b/dotCMS/src/integration-test/java/com/dotcms/filters/VanityUrlFilterTest.java @@ -227,18 +227,19 @@ public void test_that_vanity_url_filter_handles_redirects() throws Exception { String title = "VanityURL" + System.currentTimeMillis(); - String site = defaultHost.getIdentifier(); - String uri = "/testing_301" + System.currentTimeMillis(); - String forwardTo = "https://dotcms.com"; + String baseURI = "/testing_301" + System.currentTimeMillis(); + String vanityURIPattern = baseURI + "/(.*)"; + String forwardBaseURI = "https://dotcms.com/redirected_301"; + String forwardTo = forwardBaseURI + "/$1"; int action = 301; int order = 1; - Contentlet contentlet1 = filtersUtil.createVanityUrl(title, defaultHost, uri, + Contentlet contentlet1 = filtersUtil.createVanityUrl(title, defaultHost, vanityURIPattern, forwardTo, action, order, defaultLanguage.getId()); filtersUtil.publishVanityUrl(contentlet1); - - final HttpServletRequest request = new MockHttpRequestIntegrationTest(defaultHost.getHostname(), uri).request(); + final String testURI = baseURI + "/test redirect 301"; + final HttpServletRequest request = new MockHttpRequestIntegrationTest(defaultHost.getHostname(), testURI).request(); final HttpServletResponse response = new MockHttpStatusResponse(new MockHeaderResponse(new MockHttpResponse().response()).response()).response(); @@ -246,9 +247,9 @@ public void test_that_vanity_url_filter_handles_redirects() throws Exception { filter.doFilter(request, response, null); - Collection list= response.getHeaderNames(); - assert(response.getHeader("Location").equals(forwardTo)); - assert(response.getStatus()==301); + final String expectedLocation = forwardBaseURI + "/test%20redirect%20301"; + Assert.assertEquals(expectedLocation, response.getHeader("Location")); + Assert.assertEquals(301,response.getStatus()); Assert.assertNotNull(response.getHeader("X-DOT-VanityUrl")); } diff --git a/dotCMS/src/main/java/com/dotcms/vanityurl/business/VanityUrlAPIImpl.java b/dotCMS/src/main/java/com/dotcms/vanityurl/business/VanityUrlAPIImpl.java index c207f0260785..d3ec510cd0f3 100644 --- a/dotCMS/src/main/java/com/dotcms/vanityurl/business/VanityUrlAPIImpl.java +++ b/dotCMS/src/main/java/com/dotcms/vanityurl/business/VanityUrlAPIImpl.java @@ -1,5 +1,6 @@ package com.dotcms.vanityurl.business; +import java.net.URISyntaxException; import java.text.MessageFormat; import java.util.List; import java.util.Map; @@ -8,6 +9,8 @@ import java.util.Set; import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; + +import com.dotmarketing.util.URLUtils; import org.apache.http.HttpStatus; import com.dotcms.api.web.HttpServletRequestThreadLocal; import com.dotcms.business.CloseDBIfOpened; @@ -42,6 +45,7 @@ import com.liferay.util.StringPool; import io.vavr.control.Try; +import org.apache.http.client.utils.URIBuilder; /** * Implementation class for the {@link VanityUrlAPI}. @@ -375,7 +379,7 @@ public boolean handleVanityURLRedirects(final VanityUrlRequestWrapper request, f final String newUrl = uri + (queryString != null ? StringPool.QUESTION + queryString : StringPool.BLANK); if (responseCode == 301 || responseCode == 302) { response.setStatus(responseCode); - response.setHeader("Location", newUrl); + response.setHeader("Location", encodeRedirectURL(newUrl)); return true; } @@ -388,6 +392,61 @@ public boolean handleVanityURLRedirects(final VanityUrlRequestWrapper request, f return false; } + /** + * Encodes the redirect URL with the parameters from the request. + * + * @param uri The URI to redirect to. + * @return The encoded redirect URL. + */ + private String encodeRedirectURL(final String uri) { + try { + boolean hasProtocol = true; + String redirectURI = uri; + if (uri.startsWith("//")) { + hasProtocol = false; + redirectURI = "none:" + uri; + } + final URLUtils.ParsedURL urlToEncode = URLUtils.parseURL(redirectURI); + if (urlToEncode == null) { + throw new DotRuntimeException("Could not parse redirect URL: " + uri); + } + final URIBuilder uriBuilder = new URIBuilder(); + if (UtilMethods.isSet(urlToEncode.getProtocol()) && hasProtocol) { + uriBuilder.setScheme(urlToEncode.getProtocol()); + } + if (UtilMethods.isSet(urlToEncode.getHost())) { + final String hostWithUserInfo = urlToEncode.getHost(); + String host = hostWithUserInfo; + String userInfo = ""; + if (hostWithUserInfo.contains("@")) { + userInfo = hostWithUserInfo.substring(0, hostWithUserInfo.indexOf("@")); + host = hostWithUserInfo.substring(hostWithUserInfo.indexOf("@") + 1); + } + if (UtilMethods.isSet(userInfo)) { + uriBuilder.setUserInfo(userInfo); + } + uriBuilder.setHost(host); + } + if (urlToEncode.getPort() > 0) { + uriBuilder.setPort(urlToEncode.getPort()); + } + if (UtilMethods.isSet(urlToEncode.getURI())) { + uriBuilder.setPath(urlToEncode.getURI()); + } + final Map paramMap = urlToEncode.getParameters(); + if (paramMap != null) { + for (final Map.Entry entry : paramMap.entrySet()) { + for (final String value : entry.getValue()) { + uriBuilder.addParameter(entry.getKey(), value); + } + } + } + return uriBuilder.build().toASCIIString(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + @Override @CloseDBIfOpened public List findByForward(final Host host, final Language language, final String forward,int action) { diff --git a/dotCMS/src/test/java/com/dotmarketing/portlets/rules/actionlet/SendRedirectActionletTest.java b/dotCMS/src/test/java/com/dotmarketing/portlets/rules/actionlet/SendRedirectActionletTest.java index 1db19df85131..ffc50dc1ec99 100644 --- a/dotCMS/src/test/java/com/dotmarketing/portlets/rules/actionlet/SendRedirectActionletTest.java +++ b/dotCMS/src/test/java/com/dotmarketing/portlets/rules/actionlet/SendRedirectActionletTest.java @@ -72,7 +72,7 @@ public void testExecuteAction() throws Exception { HttpServletRequest request = mock(HttpServletRequest.class); when(request.getAttribute(CMS_FILTER_URI_OVERRIDE)).thenReturn("/index"); - String url = "//foo"; + String url = "/foo"; ParameterModel paramURL = new ParameterModel(URL_KEY, url); ParameterModel paramRedirectMethod = new ParameterModel(INPUT_REDIRECT_METHOD, REDIRECT_METHOD.MOVED_PERM.name()); Map params = new HashMap<>();