From 8fa8fdc9ff3f4fb72fd4e8bd4ebb1253e9c85a20 Mon Sep 17 00:00:00 2001 From: Haroun EL ALAMI Date: Fri, 31 May 2024 17:50:23 +0200 Subject: [PATCH] feat(engine): add support for url redirection param (#3013) covers [BPM-85](https://bonitasoft.atlassian.net/browse/BPM-85) --- .../common/server/filter/RedirectFilter.java | 55 +++++++++++ .../login/filter/AuthenticationFilter.java | 6 +- .../src/main/webapp/WEB-INF/web.xml | 11 +++ .../server/filter/RedirectFilterTest.java | 93 +++++++++++++++++++ .../filter/AuthenticationFilterTest.java | 8 ++ 5 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/RedirectFilter.java create mode 100644 bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/RedirectFilterTest.java diff --git a/bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/RedirectFilter.java b/bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/RedirectFilter.java new file mode 100644 index 00000000000..deb94a29e73 --- /dev/null +++ b/bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/RedirectFilter.java @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2024 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.console.common.server.filter; + +import java.io.IOException; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.bonitasoft.console.common.server.auth.AuthenticationManager; + +/** + * Filter that redirects to a URL specified as a parameter in the request. + * The URL must be relative to the current domain. + * + * @author Haroun EL ALAMI + */ +@Slf4j +public class RedirectFilter implements Filter { + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + + final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; + final HttpServletResponse httpResponse = (HttpServletResponse) servletResponse; + + String redirectUrl = httpRequest.getParameter(AuthenticationManager.REDIRECT_URL); + if (StringUtils.isNoneBlank(redirectUrl)) { + // avoid redirecting to a different domain + if (!redirectUrl.contains("//")) { + httpResponse.sendRedirect(redirectUrl); + return; + } + // If the redirectUrl is not valid + log.warn("Invalid redirect URL: {}", redirectUrl); + } + + filterChain.doFilter(servletRequest, servletResponse); + } +} diff --git a/bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/AuthenticationFilter.java b/bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/AuthenticationFilter.java index 4d620e4eab9..aa711499790 100644 --- a/bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/AuthenticationFilter.java +++ b/bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/AuthenticationFilter.java @@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import org.apache.commons.lang3.StringUtils; import org.bonitasoft.console.common.server.auth.AuthenticationManager; import org.bonitasoft.console.common.server.auth.AuthenticationManagerFactory; import org.bonitasoft.console.common.server.auth.AuthenticationManagerNotFoundException; @@ -259,7 +260,10 @@ protected AuthenticationManager getAuthenticationManager() throws ServletExcepti } protected RedirectUrl makeRedirectUrl(final HttpServletRequestAccessor httpRequest) { - final RedirectUrlBuilder builder = new RedirectUrlBuilder(httpRequest.getRequestedUri()); + // if the request has a redirection URL, we use it, otherwise we use the requested URI + boolean hasRedirectionURL = StringUtils.isNotBlank(httpRequest.getRedirectUrl()); + final RedirectUrlBuilder builder = new RedirectUrlBuilder( + hasRedirectionURL ? httpRequest.getRedirectUrl() : httpRequest.getRequestedUri()); builder.appendParameters(httpRequest.getParameterMap()); return builder.build(); } diff --git a/bpm/bonita-web-server/src/main/webapp/WEB-INF/web.xml b/bpm/bonita-web-server/src/main/webapp/WEB-INF/web.xml index 4b72250ea7a..0f8b1c3c88f 100644 --- a/bpm/bonita-web-server/src/main/webapp/WEB-INF/web.xml +++ b/bpm/bonita-web-server/src/main/webapp/WEB-INF/web.xml @@ -126,6 +126,11 @@ true + + + RedirectFilter + org.bonitasoft.console.common.server.filter.RedirectFilter + SanitizerFilter @@ -238,6 +243,12 @@ REQUEST FORWARD + + RedirectFilter + /apps/sendRedirect + REQUEST + FORWARD + RestAPIAuthorizationFilter /API/* diff --git a/bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/RedirectFilterTest.java b/bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/RedirectFilterTest.java new file mode 100644 index 00000000000..b4fc57620cd --- /dev/null +++ b/bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/RedirectFilterTest.java @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2024 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.console.common.server.filter; + +import static org.mockito.Mockito.*; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class RedirectFilterTest { + + @Mock + private HttpServletRequest httpRequest; + + @Mock + private HttpServletResponse httpResponse; + + @Mock + private FilterChain filterChain; + + private RedirectFilter redirectFilter; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + redirectFilter = new RedirectFilter(); + } + + @Test + public void testDoFilterWithInternalRedirect() throws IOException, ServletException { + when(httpRequest.getParameter("redirectUrl")).thenReturn("/redirectLink"); + when(httpRequest.getRequestURL()).thenReturn(new StringBuffer("http://bonita:8080/currentPath")); + when(httpRequest.getRequestURI()).thenReturn("/currentPath"); + + redirectFilter.doFilter(httpRequest, httpResponse, filterChain); + + verify(httpResponse).sendRedirect("/redirectLink"); + verify(filterChain, never()).doFilter(httpRequest, httpResponse); + } + + @Test + public void testDoFilterWithExternalRedirect() throws IOException, ServletException { + // using http:// as a prefix to simulate an external redirect + when(httpRequest.getParameter("redirectUrl")).thenReturn("http://external:9090/redirectLink"); + when(httpRequest.getRequestURL()).thenReturn(new StringBuffer("http://bonita:8080/currentPath")); + when(httpRequest.getRequestURI()).thenReturn("/currentPath"); + + redirectFilter.doFilter(httpRequest, httpResponse, filterChain); + + verify(httpResponse, never()).sendRedirect(anyString()); + verify(filterChain).doFilter(httpRequest, httpResponse); + + // using // as a prefix to simulate an external redirect + when(httpRequest.getParameter("redirectUrl")).thenReturn("//external:9090/redirectLink"); + when(httpRequest.getRequestURL()).thenReturn(new StringBuffer("http://bonita:8080/currentPath")); + when(httpRequest.getRequestURI()).thenReturn("/currentPath"); + + redirectFilter.doFilter(httpRequest, httpResponse, filterChain); + + verify(httpResponse, never()).sendRedirect(anyString()); + verify(filterChain, times(2)).doFilter(httpRequest, httpResponse); + } + + @Test + public void testDoFilterWithoutRedirect() throws IOException, ServletException { + when(httpRequest.getParameter("redirectUrl")).thenReturn(null); + + redirectFilter.doFilter(httpRequest, httpResponse, filterChain); + + verify(httpResponse, never()).sendRedirect(anyString()); + verify(filterChain).doFilter(httpRequest, httpResponse); + } +} diff --git a/bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/filter/AuthenticationFilterTest.java b/bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/filter/AuthenticationFilterTest.java index e3a96523538..bfe35503d92 100644 --- a/bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/filter/AuthenticationFilterTest.java +++ b/bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/login/filter/AuthenticationFilterTest.java @@ -371,6 +371,14 @@ public void testMakeRedirectUrl() { assertThat(redirectUrl.getUrl()).isEqualToIgnoringCase("/apps/appDirectoryBonita"); } + @Test + public void testMakeRedirectUrlWithRedirectUrlIncluded() { + when(httpRequest.getRequestURI()).thenReturn("/apps/appDirectoryBonita"); + when(request.getRedirectUrl()).thenReturn("/redirectLink"); + final RedirectUrl redirectUrl = authenticationFilter.makeRedirectUrl(request); + assertThat(redirectUrl.getUrl()).isEqualToIgnoringCase("/redirectLink"); + } + @Test public void testCompileNullPattern() { assertThat(authenticationFilter.compilePattern(null)).isNull();