From c9fce6285e5d85de8106437489da31f94bea588b Mon Sep 17 00:00:00 2001 From: Marco Collovati Date: Wed, 11 Dec 2024 11:58:55 +0100 Subject: [PATCH] fix: fix redirect URL for PUSH with websocket transport (#20666) When PUSH is enabled with websocket transport, the redirect URL to be used after a successfull login is not correctly computed because it is based on the PUSH servlet mapping. This change detects the situation and computes the correct URL. Fixes #20575 --- .../server/auth/NavigationAccessControl.java | 10 +- flow-tests/vaadin-spring-tests/pom.xml | 1 + .../pom.xml | 113 ++++++++++++++++++ .../flowsecuritywebsocket/Application.java | 14 +++ .../PushWebsocketConfigurer.java | 52 ++++++++ .../src/main/resources/application.properties | 4 + .../flowsecuritywebsocket/AppViewIT.java | 20 ++++ scripts/computeMatrix.js | 1 + 8 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/pom.xml create mode 100644 flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/main/java/com/vaadin/flow/spring/flowsecuritywebsocket/Application.java create mode 100644 flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/main/java/com/vaadin/flow/spring/flowsecuritywebsocket/PushWebsocketConfigurer.java create mode 100644 flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/main/resources/application.properties create mode 100644 flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/test/java/com/vaadin/flow/spring/flowsecuritywebsocket/AppViewIT.java diff --git a/flow-server/src/main/java/com/vaadin/flow/server/auth/NavigationAccessControl.java b/flow-server/src/main/java/com/vaadin/flow/server/auth/NavigationAccessControl.java index a33042392f8..46937421001 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/auth/NavigationAccessControl.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/auth/NavigationAccessControl.java @@ -34,6 +34,8 @@ import com.vaadin.flow.router.NotFoundException; import com.vaadin.flow.router.RouteParameters; import com.vaadin.flow.router.internal.PathUtil; +import com.vaadin.flow.server.Constants; +import com.vaadin.flow.server.HandlerHelper; import com.vaadin.flow.server.VaadinRequest; import com.vaadin.flow.server.VaadinService; import com.vaadin.flow.server.VaadinServletRequest; @@ -369,7 +371,13 @@ protected Predicate getRolesChecker(VaadinRequest request) { */ protected String getRequestURL(VaadinRequest vaadinRequest) { if (vaadinRequest instanceof VaadinServletRequest httpRequest) { - return httpRequest.getRequestURL().toString(); + String url = httpRequest.getRequestURL().toString(); + if (HandlerHelper.isRequestType(vaadinRequest, + HandlerHelper.RequestType.PUSH) + && url.endsWith(Constants.PUSH_MAPPING)) { + url = url.substring(0, url.indexOf(Constants.PUSH_MAPPING)); + } + return url; } return ""; } diff --git a/flow-tests/vaadin-spring-tests/pom.xml b/flow-tests/vaadin-spring-tests/pom.xml index ccb694ba3a7..913ca91c6ef 100644 --- a/flow-tests/vaadin-spring-tests/pom.xml +++ b/flow-tests/vaadin-spring-tests/pom.xml @@ -324,6 +324,7 @@ test-mvc-without-endpoints test-spring-security-flow + test-spring-security-flow-websocket test-spring-security-webicons test-spring-security-webicons-urlmapping test-spring-security-flow-contextpath diff --git a/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/pom.xml b/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/pom.xml new file mode 100644 index 00000000000..193d3f896ea --- /dev/null +++ b/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + com.vaadin + vaadin-spring-tests + 24.7-SNAPSHOT + + test-spring-security-flow-websocket + Integration tests for Vaadin Spring Security and Flow With Websocket PUSH + jar + + true + + + + + com.vaadin + vaadin-spring + ${project.version} + + + com.vaadin + vaadin-dev-server + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + + + org.springframework.boot + spring-boot-starter-security + + + + com.vaadin + test-spring-security-flow + ${project.version} + + + com.vaadin + test-spring-security-flow + ${project.version} + test-jar + tests + test + + + + + spring-boot:run + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + + + + com.vaadin + flow-maven-plugin + ${project.version} + + true + + + + + prepare-frontend + build-frontend + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=18888 + + + + + + pre-integration-test + + start + + + + post-integration-test + + stop + + + + + + + + diff --git a/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/main/java/com/vaadin/flow/spring/flowsecuritywebsocket/Application.java b/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/main/java/com/vaadin/flow/spring/flowsecuritywebsocket/Application.java new file mode 100644 index 00000000000..76111e15eef --- /dev/null +++ b/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/main/java/com/vaadin/flow/spring/flowsecuritywebsocket/Application.java @@ -0,0 +1,14 @@ +package com.vaadin.flow.spring.flowsecuritywebsocket; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application + extends com.vaadin.flow.spring.flowsecurity.Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/main/java/com/vaadin/flow/spring/flowsecuritywebsocket/PushWebsocketConfigurer.java b/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/main/java/com/vaadin/flow/spring/flowsecuritywebsocket/PushWebsocketConfigurer.java new file mode 100644 index 00000000000..20f6420c3e3 --- /dev/null +++ b/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/main/java/com/vaadin/flow/spring/flowsecuritywebsocket/PushWebsocketConfigurer.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2024 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.flow.spring.flowsecuritywebsocket; + +import org.springframework.stereotype.Component; + +import com.vaadin.flow.router.BeforeEnterEvent; +import com.vaadin.flow.router.BeforeEnterListener; +import com.vaadin.flow.router.ListenerPriority; +import com.vaadin.flow.server.ServiceInitEvent; +import com.vaadin.flow.server.VaadinServiceInitListener; +import com.vaadin.flow.shared.ui.Transport; + +@Component +public class PushWebsocketConfigurer implements VaadinServiceInitListener { + + private final PushTransportSetter pushTransportSetter = new PushTransportSetter(); + + @Override + public void serviceInit(ServiceInitEvent event) { + + event.getSource().addUIInitListener(uiInitEvent -> { + // Transport cannot be set directly in UI listener because + // BootstrapHandler overrides it with @Push annotation value. + uiInitEvent.getUI().addBeforeEnterListener(pushTransportSetter); + }); + } + + @ListenerPriority(10) + private static class PushTransportSetter implements BeforeEnterListener { + + @Override + public void beforeEnter(BeforeEnterEvent event) { + event.getUI().getPushConfiguration() + .setTransport(Transport.WEBSOCKET); + } + } +} diff --git a/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/main/resources/application.properties b/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/main/resources/application.properties new file mode 100644 index 00000000000..00f3e202f50 --- /dev/null +++ b/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/main/resources/application.properties @@ -0,0 +1,4 @@ +server.port=8888 +logging.level.org.springframework.security=TRACE +logging.level.org.atmosphere=DEBUG +server.servlet.session.persistent=false diff --git a/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/test/java/com/vaadin/flow/spring/flowsecuritywebsocket/AppViewIT.java b/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/test/java/com/vaadin/flow/spring/flowsecuritywebsocket/AppViewIT.java new file mode 100644 index 00000000000..48832d4aa83 --- /dev/null +++ b/flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket/src/test/java/com/vaadin/flow/spring/flowsecuritywebsocket/AppViewIT.java @@ -0,0 +1,20 @@ +package com.vaadin.flow.spring.flowsecuritywebsocket; + +import org.junit.Ignore; +import org.junit.Test; + +public class AppViewIT extends com.vaadin.flow.spring.flowsecurity.AppViewIT { + + @Test + @Ignore(""" + With WEBSOCKET transport the WS connection is closed when session + is invalidated, but Flow client attempts a reconnection and + re-enables heartbeat. The heartbeat ping resolves in a 403 HTTP + status code because of session expiration, causing the client-side + session expiration handler to redirect to the timeout page instead + of the logout view, because the logout process is still ongoing. + """) + public void logout_via_doLogin_redirects_to_logout() { + super.logout_via_doLogin_redirects_to_logout(); + } +} diff --git a/scripts/computeMatrix.js b/scripts/computeMatrix.js index 30ee4561c76..4defe8f237e 100755 --- a/scripts/computeMatrix.js +++ b/scripts/computeMatrix.js @@ -97,6 +97,7 @@ const moduleWeights = { 'flow-tests/vaadin-spring-tests/test-spring-security-flow': { pos: 5, weight: 3 }, 'flow-tests/vaadin-spring-tests/test-spring-security-webicons': { pos: 5, weight: 3 }, 'flow-tests/vaadin-spring-tests/test-spring-security-webicons-urlmapping': { pos: 5, weight: 3 }, + 'flow-tests/vaadin-spring-tests/test-spring-security-flow-websocket': { pos: 5, weight: 3 }, 'flow-tests/vaadin-spring-tests/test-spring-security-flow-contextpath': { pos: 5, weight: 3 }, 'flow-tests/vaadin-spring-tests/test-spring-security-flow-methodsecurity': { pos: 5, weight: 3 }, 'flow-tests/vaadin-spring-tests/test-spring-security-flow-urlmapping': { pos: 5, weight: 3 },