diff --git a/flow-tests/vaadin-spring-tests/test-spring-security-flow/src/main/java/com/vaadin/flow/spring/flowsecurity/views/MainView.java b/flow-tests/vaadin-spring-tests/test-spring-security-flow/src/main/java/com/vaadin/flow/spring/flowsecurity/views/MainView.java index 72713cd33bb..45dbf6be8cc 100644 --- a/flow-tests/vaadin-spring-tests/test-spring-security-flow/src/main/java/com/vaadin/flow/spring/flowsecurity/views/MainView.java +++ b/flow-tests/vaadin-spring-tests/test-spring-security-flow/src/main/java/com/vaadin/flow/spring/flowsecurity/views/MainView.java @@ -1,9 +1,14 @@ package com.vaadin.flow.spring.flowsecurity.views; import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.springframework.security.concurrent.DelegatingSecurityContextExecutor; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.ComponentUtil; +import com.vaadin.flow.component.UI; import com.vaadin.flow.component.applayout.AppLayout; import com.vaadin.flow.component.applayout.DrawerToggle; import com.vaadin.flow.component.avatar.Avatar; @@ -97,6 +102,18 @@ private Component createDrawerContent(Tabs menu) { }); layout.add(logout); + Button logoutFromServer = new Button("Logout from server"); + logoutFromServer.setId("logout-server"); + logoutFromServer.addClickListener(e -> { + UI ui = UI.getCurrent(); + Runnable action = ui.accessLater(() -> securityUtils.logout(), + null); + CompletableFuture.runAsync(action, + new DelegatingSecurityContextExecutor(CompletableFuture + .delayedExecutor(1, TimeUnit.SECONDS))); + }); + layout.add(logoutFromServer); + Anchor logoutWithUrl = new Anchor("doLogout", "Logout with URL"); logoutWithUrl.getElement().setAttribute("router-ignore", true); logoutWithUrl.setId("logout-anchor"); diff --git a/flow-tests/vaadin-spring-tests/test-spring-security-flow/src/test/java/com/vaadin/flow/spring/flowsecurity/AppViewIT.java b/flow-tests/vaadin-spring-tests/test-spring-security-flow/src/test/java/com/vaadin/flow/spring/flowsecurity/AppViewIT.java index 7e1e2be3dc5..e0704b26b3b 100644 --- a/flow-tests/vaadin-spring-tests/test-spring-security-flow/src/test/java/com/vaadin/flow/spring/flowsecurity/AppViewIT.java +++ b/flow-tests/vaadin-spring-tests/test-spring-security-flow/src/test/java/com/vaadin/flow/spring/flowsecurity/AppViewIT.java @@ -308,6 +308,16 @@ public void logout_via_doLogoutURL_redirects_to_logout() { assertLogoutViewShown(); } + @Test + public void logout_server_initiated_redirects_to_logout() { + open(LOGIN_PATH); + loginAdmin(); + navigateTo("admin"); + assertAdminPageShown(ADMIN_FULLNAME); + getMainView().$(ButtonElement.class).id("logout-server").click(); + assertRootPageShown(); + } + @Test public void client_menu_routes_correct_for_anonymous() { navigateToClientMenuList(); diff --git a/flow-tests/vaadin-spring-tests/test-spring-security-flow/src/test/java/com/vaadin/flow/spring/flowsecurity/UIAccessContextIT.java b/flow-tests/vaadin-spring-tests/test-spring-security-flow/src/test/java/com/vaadin/flow/spring/flowsecurity/UIAccessContextIT.java index 94ec1226421..d285f7e0422 100644 --- a/flow-tests/vaadin-spring-tests/test-spring-security-flow/src/test/java/com/vaadin/flow/spring/flowsecurity/UIAccessContextIT.java +++ b/flow-tests/vaadin-spring-tests/test-spring-security-flow/src/test/java/com/vaadin/flow/spring/flowsecurity/UIAccessContextIT.java @@ -15,16 +15,16 @@ */ package com.vaadin.flow.spring.flowsecurity; +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.WebDriver; + import com.vaadin.flow.component.button.testbench.ButtonElement; import com.vaadin.flow.component.login.testbench.LoginFormElement; import com.vaadin.flow.component.login.testbench.LoginOverlayElement; import com.vaadin.testbench.HasElementQuery; import com.vaadin.testbench.TestBenchElement; -import org.junit.Assert; -import org.junit.Test; -import org.openqa.selenium.WebDriver; - public class UIAccessContextIT extends AbstractIT { @Test @@ -37,14 +37,15 @@ public void securityContextSetForUIAccess() throws Exception { super.setup(); open("private"); loginUser(); - TestBenchElement balance = $("span").id("balanceText"); + TestBenchElement balance = waitUntil( + d -> $("span").id("balanceText")); Assert.assertEquals(expectedUserBalance, balance.getText()); open("private", adminBrowser); HasElementQuery adminContext = () -> adminBrowser; loginAdmin(adminContext); - TestBenchElement adminBalance = adminContext.$("span") - .id("balanceText"); + TestBenchElement adminBalance = waitUntil( + d -> adminContext.$("span").id("balanceText")); Assert.assertEquals(expectedAdminBalance, adminBalance.getText()); ButtonElement sendRefresh = $(ButtonElement.class) @@ -70,6 +71,10 @@ private void loginAdmin(HasElementQuery adminContext) { form.getUsernameField().setValue("emma"); form.getPasswordField().setValue("emma"); form.submit(); + waitUntilNot(driver -> ((WebDriver) adminContext.getContext()) + .getCurrentUrl().contains("my/login/page")); + waitUntilNot( + driver -> adminContext.$(LoginOverlayElement.class).exists()); } } diff --git a/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/AuthenticationContext.java b/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/AuthenticationContext.java index 1c907d95a15..3a9da0fa765 100644 --- a/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/AuthenticationContext.java +++ b/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/AuthenticationContext.java @@ -45,6 +45,7 @@ import org.springframework.util.Assert; import com.vaadin.flow.component.UI; +import com.vaadin.flow.server.VaadinRequest; import com.vaadin.flow.server.VaadinServletRequest; import com.vaadin.flow.server.VaadinServletResponse; import com.vaadin.flow.shared.ui.Transport; @@ -131,8 +132,10 @@ public boolean isAuthenticated() { */ public void logout() { final UI ui = UI.getCurrent(); - if (ui.getPushConfiguration().getTransport() == Transport.WEBSOCKET - && ui.getInternals().getPushConnection().isConnected()) { + boolean pushWebsocketConnected = ui.getPushConfiguration() + .getTransport() == Transport.WEBSOCKET + && ui.getInternals().getPushConnection().isConnected(); + if (pushWebsocketConnected) { // WEBSOCKET transport mode would not log out properly after session // invalidation. Switching to WEBSOCKET_XHR for a single request // to do the logout. @@ -151,6 +154,11 @@ public void logout() { ui.getPushConfiguration().setTransport(Transport.WEBSOCKET); doLogout(ui); }); + } else if (VaadinRequest.getCurrent() == null) { + // Logout started from a background thread, force client to send + // a request + ui.getPage().executeJs("return true").then(ignored -> doLogout(ui), + error -> doLogout(ui)); } else { doLogout(ui); } @@ -496,7 +504,7 @@ public void logout(HttpServletRequest request, private boolean isContinueToNextHandler(HttpServletRequest request, LogoutHandler handler) { return handler instanceof SecurityContextLogoutHandler - && (request.getSession() == null + && (request.getSession(false) == null || !request.isRequestedSessionIdValid()); } }