From 5b3c6e8c8bc4a8f1e377c8f3320c11d49e976c54 Mon Sep 17 00:00:00 2001 From: Anton Platonov Date: Tue, 26 Oct 2021 19:59:37 +0300 Subject: [PATCH 01/10] fix: configure 401 unauthorized response for endpoints Fixes #924 --- .../VaadinWebSecurityConfigurerAdapter.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/VaadinWebSecurityConfigurerAdapter.java b/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/VaadinWebSecurityConfigurerAdapter.java index 7cb964ee1..b0438e476 100644 --- a/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/VaadinWebSecurityConfigurerAdapter.java +++ b/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/VaadinWebSecurityConfigurerAdapter.java @@ -21,6 +21,7 @@ import java.util.stream.Stream; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @@ -28,8 +29,11 @@ import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.jose.jws.MacAlgorithm; +import org.springframework.security.web.authentication.HttpStatusEntryPoint; +import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.savedrequest.RequestCache; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.AnyRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -93,6 +97,11 @@ protected void configure(HttpSecurity http) throws Exception { SecurityContextHolder.setStrategyName( VaadinAwareSecurityContextHolderStrategy.class.getName()); + http.exceptionHandling() + .defaultAuthenticationEntryPointFor( + new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), + requestUtil::isEndpointRequest); + // Vaadin has its own CSRF protection. // Spring CSRF is not compatible with Vaadin internal requests http.csrf().ignoringRequestMatchers( @@ -193,6 +202,9 @@ protected void setLoginView(HttpSecurity http, String fusionLoginViewPath, formLogin.successHandler( getVaadinSavedRequestAwareAuthenticationSuccessHandler(http)); http.logout().logoutSuccessUrl(logoutUrl); + http.exceptionHandling().defaultAuthenticationEntryPointFor( + new LoginUrlAuthenticationEntryPoint(fusionLoginViewPath), + AnyRequestMatcher.INSTANCE); viewAccessChecker.setLoginView(fusionLoginViewPath); } @@ -248,6 +260,9 @@ protected void setLoginView(HttpSecurity http, getVaadinSavedRequestAwareAuthenticationSuccessHandler(http)); http.csrf().ignoringAntMatchers(loginPath); http.logout().logoutSuccessUrl(logoutUrl); + http.exceptionHandling().defaultAuthenticationEntryPointFor( + new LoginUrlAuthenticationEntryPoint(loginPath), + AnyRequestMatcher.INSTANCE); viewAccessChecker.setLoginView(flowLoginView); } From 7cd24767464dd09a8de3afe424047ef5436c0540 Mon Sep 17 00:00:00 2001 From: ZheSun88 Date: Wed, 27 Oct 2021 11:03:10 +0300 Subject: [PATCH 02/10] fix formatting --- .../security/VaadinWebSecurityConfigurerAdapter.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/VaadinWebSecurityConfigurerAdapter.java b/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/VaadinWebSecurityConfigurerAdapter.java index b0438e476..5816dcb26 100644 --- a/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/VaadinWebSecurityConfigurerAdapter.java +++ b/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/VaadinWebSecurityConfigurerAdapter.java @@ -97,10 +97,9 @@ protected void configure(HttpSecurity http) throws Exception { SecurityContextHolder.setStrategyName( VaadinAwareSecurityContextHolderStrategy.class.getName()); - http.exceptionHandling() - .defaultAuthenticationEntryPointFor( - new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), - requestUtil::isEndpointRequest); + http.exceptionHandling().defaultAuthenticationEntryPointFor( + new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), + requestUtil::isEndpointRequest); // Vaadin has its own CSRF protection. // Spring CSRF is not compatible with Vaadin internal requests From 55554f7e603deeaeb1740b7c615a397fc0426592 Mon Sep 17 00:00:00 2001 From: Anton Platonov Date: Wed, 27 Oct 2021 21:39:25 +0300 Subject: [PATCH 03/10] Add CSRF access denied handler for endpoints and tests --- .../spring/fusionsecurityjwt/SecurityIT.java | 68 +++++----------- .../frontend/connect-client.ts | 18 +++++ .../spring/fusionsecurity/SecurityIT.java | 78 ++++++++++++++++++- .../VaadinWebSecurityConfigurerAdapter.java | 43 ++++++++++ 4 files changed, 158 insertions(+), 49 deletions(-) create mode 100644 vaadin-spring-tests/test-spring-security-fusion/frontend/connect-client.ts diff --git a/vaadin-spring-tests/test-spring-security-fusion-jwt/src/test/java/com/vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java b/vaadin-spring-tests/test-spring-security-fusion-jwt/src/test/java/com/vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java index 11e58a22d..125d19512 100644 --- a/vaadin-spring-tests/test-spring-security-fusion-jwt/src/test/java/com/vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java +++ b/vaadin-spring-tests/test-spring-security-fusion-jwt/src/test/java/com/vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java @@ -1,17 +1,10 @@ package com.vaadin.flow.spring.fusionsecurityjwt; import java.util.Base64; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; import org.junit.Assert; import org.junit.Test; -import org.openqa.selenium.By; import org.openqa.selenium.Cookie; -import org.openqa.selenium.JavascriptExecutor; - -import com.vaadin.testbench.TestBenchElement; import elemental.json.Json; import elemental.json.JsonObject; @@ -104,6 +97,26 @@ public void stateless_for_anonymous_after_logout() { assertPublicEndpointWorks(); } + @Override + public void reload_when_anonymous_session_expires() { + // Skip: the server session is not relevant in the stateless mode + } + + @Override + public void reload_when_user_session_expires() { + // Skip: the server session is not relevant in the stateless mode + } + + @Test + public void reload_when_user_jwt_expires() { + openLogin(); + loginUser(); + getDriver().manage().deleteCookieNamed("jwt.headerAndPayload"); + getDriver().manage().deleteCookieNamed("jwt.signature"); + navigateTo("private", false); + assertLoginViewShown(); + } + private void openLogin() { getDriver().get(getRootURL() + "/login"); } @@ -113,7 +126,7 @@ private Cookie getSpringCsrfCookie() { } private Cookie getJwtCookie() { - return getDriver().manage().getCookieNamed("jwt" + ".headerAndPayload"); + return getDriver().manage().getCookieNamed("jwt.headerAndPayload"); } private void checkJwtUsername(String expectedUsername) { @@ -126,43 +139,4 @@ private void checkJwtUsername(String expectedUsername) { Assert.assertEquals(expectedUsername, payloadJson.getString("sub")); } - private void simulateNewServer() { - TestBenchElement mainView = waitUntil(driver -> $("main-view").get(0)); - callAsyncMethod(mainView, "invalidateSessionIfPresent"); - } - - private void assertPublicEndpointWorks() { - TestBenchElement publicView = waitUntil( - driver -> $("public-view").get(0)); - TestBenchElement timeText = publicView.findElement(By.id("time")); - String timeBefore = timeText.getText(); - Assert.assertNotNull(timeBefore); - callAsyncMethod(publicView, "updateTime"); - String timeAfter = timeText.getText(); - Assert.assertNotNull(timeAfter); - Assert.assertNotEquals(timeAfter, timeBefore); - } - - private String formatArgumentRef(int index) { - return String.format("arguments[%d]", index); - } - - private Object callAsyncMethod(TestBenchElement element, String methodName, - Object... args) { - String objectRef = formatArgumentRef(0); - String argRefs = IntStream.range(1, args.length + 1) - .mapToObj(this::formatArgumentRef) - .collect(Collectors.joining(",")); - String callbackRef = formatArgumentRef(args.length + 1); - String script = String.format("%s.%s(%s).then(%s)", objectRef, - methodName, argRefs, callbackRef); - Object[] scriptArgs = Stream.concat(Stream.of(element), Stream.of(args)) - .toArray(); - return getJavascriptExecutor().executeAsyncScript(script, scriptArgs); - } - - private JavascriptExecutor getJavascriptExecutor() { - return (JavascriptExecutor) getDriver(); - } - } diff --git a/vaadin-spring-tests/test-spring-security-fusion/frontend/connect-client.ts b/vaadin-spring-tests/test-spring-security-fusion/frontend/connect-client.ts new file mode 100644 index 000000000..e256a0574 --- /dev/null +++ b/vaadin-spring-tests/test-spring-security-fusion/frontend/connect-client.ts @@ -0,0 +1,18 @@ +import { + ConnectClient, + InvalidSessionMiddleware, +} from '@vaadin/fusion-frontend'; + +const client = new ConnectClient({ + prefix: 'connect', + middlewares: [ + new InvalidSessionMiddleware(async () => { + location.reload(); + return { + error: true + } + }) + ], +}); + +export default client; diff --git a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java index 1b7e30a2d..f1a0d93c1 100644 --- a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java +++ b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java @@ -3,10 +3,14 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; import org.junit.After; import org.junit.Assert; import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; import com.vaadin.flow.component.button.testbench.ButtonElement; import com.vaadin.flow.component.login.testbench.LoginFormElement; @@ -37,8 +41,13 @@ public void tearDown() { private void checkForBrowserErrors() { checkLogsForErrors(msg -> { return msg.contains( - "admin-only/secret.txt - Failed to load resource: the " + "/admin-only/secret.txt - Failed to load resource: the " + "server responded with a status of 403") + || msg.contains("/connect/") && msg.contains("Failed to " + + "load resource: the server responded with " + + "a status of 401") + || msg.contains("expected \"200 OK\" response, but got 401") + || msg.contains(" Event") || msg.contains("webpack-internal://"); }); } @@ -233,6 +242,22 @@ public void public_app_resources_available_for_all() { shouldBeTextFile.contains("Public document for all users")); } + @Test + public void reload_when_anonymous_session_expires() { + open(""); + simulateNewServer(); + assertPublicEndpointReloadsPage(); + } + + @Test + public void reload_when_user_session_expires() { + open("login"); + loginUser(); + simulateNewServer(); + navigateTo("private", false); + assertLoginViewShown(); + } + protected void navigateTo(String path) { navigateTo(path, true); } @@ -248,7 +273,7 @@ private TestBenchElement getMainView() { return waitUntil(driver -> $("*").id("main-view")); } - private void assertLoginViewShown() { + protected void assertLoginViewShown() { assertPathShown("login"); waitUntil(driver -> $(LoginOverlayElement.class).exists()); } @@ -329,4 +354,53 @@ protected List getMenuItems() { }).collect(Collectors.toList()); } + protected void simulateNewServer() { + TestBenchElement mainView = waitUntil(driver -> $("main-view").get(0)); + callAsyncMethod(mainView, "invalidateSessionIfPresent"); + } + + protected void assertPublicEndpointReloadsPage() { + TestBenchElement publicView = waitUntil( + driver -> $("public-view").get(0)); + String timeBefore = publicView.findElement(By.id("time")).getText(); + Assert.assertNotNull(timeBefore); + publicView.callFunction("updateTime"); + publicView = waitUntil(driver -> $("public-view").get(0)); + String timeAfter = publicView.findElement(By.id("time")).getText(); + Assert.assertNotNull(timeAfter); + Assert.assertNotEquals(timeAfter, timeBefore); + } + + protected void assertPublicEndpointWorks() { + TestBenchElement publicView = waitUntil( + driver -> $("public-view").get(0)); + String timeBefore = publicView.findElement(By.id("time")).getText(); + Assert.assertNotNull(timeBefore); + callAsyncMethod(publicView, "updateTime"); + String timeAfter = publicView.findElement(By.id("time")).getText(); + Assert.assertNotNull(timeAfter); + Assert.assertNotEquals(timeAfter, timeBefore); + } + + private String formatArgumentRef(int index) { + return String.format("arguments[%d]", index); + } + + private JavascriptExecutor getJavascriptExecutor() { + return (JavascriptExecutor) getDriver(); + } + + private Object callAsyncMethod(TestBenchElement element, String methodName, + Object... args) { + String objectRef = formatArgumentRef(0); + String argRefs = IntStream.range(1, args.length + 1) + .mapToObj(this::formatArgumentRef) + .collect(Collectors.joining(",")); + String callbackRef = formatArgumentRef(args.length + 1); + String script = String.format("%s.%s(%s).then(%s)", objectRef, + methodName, argRefs, callbackRef); + Object[] scriptArgs = Stream.concat(Stream.of(element), Stream.of(args)) + .toArray(); + return getJavascriptExecutor().executeAsyncScript(script, scriptArgs); + } } diff --git a/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/VaadinWebSecurityConfigurerAdapter.java b/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/VaadinWebSecurityConfigurerAdapter.java index b0438e476..7d6d0d658 100644 --- a/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/VaadinWebSecurityConfigurerAdapter.java +++ b/vaadin-spring/src/main/java/com/vaadin/flow/spring/security/VaadinWebSecurityConfigurerAdapter.java @@ -16,12 +16,18 @@ package com.vaadin.flow.spring.security; import javax.crypto.SecretKey; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.LinkedHashMap; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @@ -29,8 +35,13 @@ import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.jose.jws.MacAlgorithm; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.security.web.access.AccessDeniedHandlerImpl; +import org.springframework.security.web.access.DelegatingAccessDeniedHandler; +import org.springframework.security.web.access.RequestMatcherDelegatingAccessDeniedHandler; import org.springframework.security.web.authentication.HttpStatusEntryPoint; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; +import org.springframework.security.web.csrf.CsrfException; import org.springframework.security.web.savedrequest.RequestCache; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AnyRequestMatcher; @@ -97,7 +108,11 @@ protected void configure(HttpSecurity http) throws Exception { SecurityContextHolder.setStrategyName( VaadinAwareSecurityContextHolderStrategy.class.getName()); + // Respond with 401 Unauthorized HTTP status code for unauthorized + // requests for protected Fusion endpoints, so that the response could + // be handled on the client side using e.g. `InvalidSessionMiddleware`. http.exceptionHandling() + .accessDeniedHandler(createAccessDeniedHandler()) .defaultAuthenticationEntryPointFor( new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), requestUtil::isEndpointRequest); @@ -319,4 +334,32 @@ private VaadinSavedRequestAwareAuthenticationSuccessHandler getVaadinSavedReques } return vaadinSavedRequestAwareAuthenticationSuccessHandler; } + + private AccessDeniedHandler createAccessDeniedHandler() { + final AccessDeniedHandler defaultHandler = new AccessDeniedHandlerImpl(); + + final AccessDeniedHandler http401UnauthorizedHandler = new Http401UnauthorizedAccessDeniedHandler(); + + final LinkedHashMap, AccessDeniedHandler> exceptionHandlers = new LinkedHashMap<>(); + exceptionHandlers.put(CsrfException.class, http401UnauthorizedHandler); + + final LinkedHashMap matcherHandlers = new LinkedHashMap<>(); + matcherHandlers.put(requestUtil::isEndpointRequest, + new DelegatingAccessDeniedHandler(exceptionHandlers, + new AccessDeniedHandlerImpl())); + + return new RequestMatcherDelegatingAccessDeniedHandler(matcherHandlers, + defaultHandler); + } + + private static class Http401UnauthorizedAccessDeniedHandler + implements AccessDeniedHandler { + @Override + public void handle(HttpServletRequest request, + HttpServletResponse response, + AccessDeniedException accessDeniedException) + throws IOException, ServletException { + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + } + } } From afbacda4294a79a3008144ecde9e5605e8b70101 Mon Sep 17 00:00:00 2001 From: Anton Platonov Date: Wed, 27 Oct 2021 21:42:46 +0300 Subject: [PATCH 04/10] =?UTF-8?q?Don=E2=80=99t=20ignore=20obscure=20browse?= =?UTF-8?q?r=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java | 1 - 1 file changed, 1 deletion(-) diff --git a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java index f1a0d93c1..aba20af37 100644 --- a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java +++ b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java @@ -47,7 +47,6 @@ private void checkForBrowserErrors() { + "load resource: the server responded with " + "a status of 401") || msg.contains("expected \"200 OK\" response, but got 401") - || msg.contains(" Event") || msg.contains("webpack-internal://"); }); } From 450d70b6f27ae21d347a8871fe6bd1cf60deaae3 Mon Sep 17 00:00:00 2001 From: Anton Platonov Date: Thu, 28 Oct 2021 10:23:11 +0300 Subject: [PATCH 05/10] Fix SpringClassesSerializableTest --- .../com/vaadin/flow/spring/SpringClassesSerializableTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vaadin-spring/src/test/java/com/vaadin/flow/spring/SpringClassesSerializableTest.java b/vaadin-spring/src/test/java/com/vaadin/flow/spring/SpringClassesSerializableTest.java index 499ee013d..80465b15f 100644 --- a/vaadin-spring/src/test/java/com/vaadin/flow/spring/SpringClassesSerializableTest.java +++ b/vaadin-spring/src/test/java/com/vaadin/flow/spring/SpringClassesSerializableTest.java @@ -93,6 +93,8 @@ protected Stream getExcludedPatterns() { "com\\.vaadin\\.flow\\.spring\\.security\\.SerializedJwtSplitCookieRepository", "com\\.vaadin\\.flow\\.spring\\.security\\.VaadinAwareSecurityContextHolderStrategy", "com\\.vaadin\\.flow\\.spring\\.security\\.VaadinWebSecurityConfigurerAdapter", + "com\\.vaadin\\.flow\\.spring\\.security\\.VaadinWebSecurityConfigurerAdapter", + "com\\.vaadin\\.flow\\.spring\\.security\\.VaadinWebSecurityConfigurerAdapter\\$Http401UnauthorizedAccessDeniedHandler", "com\\.vaadin\\.flow\\.spring\\.security\\.VaadinDefaultRequestCache", "com\\.vaadin\\.flow\\.spring\\.security\\.VaadinSavedRequestAwareAuthenticationSuccessHandler", "com\\.vaadin\\.flow\\.spring\\.security\\.VaadinSavedRequestAwareAuthenticationSuccessHandler\\$RedirectStrategy", From 0731c190d50aa314007cf4b7c48237f4c96b522f Mon Sep 17 00:00:00 2001 From: Anton Platonov Date: Thu, 28 Oct 2021 10:51:41 +0300 Subject: [PATCH 06/10] Fix stale PublicView element reference in test --- .../spring/fusionsecurity/SecurityIT.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java index aba20af37..2eb43b094 100644 --- a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java +++ b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java @@ -353,30 +353,29 @@ protected List getMenuItems() { }).collect(Collectors.toList()); } + private TestBenchElement getPublicView() { + return waitUntil(driver -> $("public-view").get(0)); + } + protected void simulateNewServer() { TestBenchElement mainView = waitUntil(driver -> $("main-view").get(0)); callAsyncMethod(mainView, "invalidateSessionIfPresent"); } protected void assertPublicEndpointReloadsPage() { - TestBenchElement publicView = waitUntil( - driver -> $("public-view").get(0)); - String timeBefore = publicView.findElement(By.id("time")).getText(); + String timeBefore = getPublicView().findElement(By.id("time")).getText(); Assert.assertNotNull(timeBefore); - publicView.callFunction("updateTime"); - publicView = waitUntil(driver -> $("public-view").get(0)); - String timeAfter = publicView.findElement(By.id("time")).getText(); + getPublicView().callFunction("updateTime"); + String timeAfter = getPublicView().findElement(By.id("time")).getText(); Assert.assertNotNull(timeAfter); Assert.assertNotEquals(timeAfter, timeBefore); } protected void assertPublicEndpointWorks() { - TestBenchElement publicView = waitUntil( - driver -> $("public-view").get(0)); - String timeBefore = publicView.findElement(By.id("time")).getText(); + String timeBefore = getPublicView().findElement(By.id("time")).getText(); Assert.assertNotNull(timeBefore); - callAsyncMethod(publicView, "updateTime"); - String timeAfter = publicView.findElement(By.id("time")).getText(); + callAsyncMethod(getPublicView(), "updateTime"); + String timeAfter = getPublicView().findElement(By.id("time")).getText(); Assert.assertNotNull(timeAfter); Assert.assertNotEquals(timeAfter, timeBefore); } From 898c3fe970977fc4523b501e22cf29b6b542d5c0 Mon Sep 17 00:00:00 2001 From: Anton Platonov Date: Thu, 28 Oct 2021 11:06:48 +0300 Subject: [PATCH 07/10] Fix formatting, hope one day there will be a pre-commit hook --- .../com/vaadin/flow/spring/fusionsecurity/SecurityIT.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java index 2eb43b094..cb34818db 100644 --- a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java +++ b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java @@ -363,7 +363,8 @@ protected void simulateNewServer() { } protected void assertPublicEndpointReloadsPage() { - String timeBefore = getPublicView().findElement(By.id("time")).getText(); + String timeBefore = getPublicView().findElement(By.id("time")) + .getText(); Assert.assertNotNull(timeBefore); getPublicView().callFunction("updateTime"); String timeAfter = getPublicView().findElement(By.id("time")).getText(); @@ -372,7 +373,8 @@ protected void assertPublicEndpointReloadsPage() { } protected void assertPublicEndpointWorks() { - String timeBefore = getPublicView().findElement(By.id("time")).getText(); + String timeBefore = getPublicView().findElement(By.id("time")) + .getText(); Assert.assertNotNull(timeBefore); callAsyncMethod(getPublicView(), "updateTime"); String timeAfter = getPublicView().findElement(By.id("time")).getText(); From a211a0b070ca3508a33e42365ece4fe032d78226 Mon Sep 17 00:00:00 2001 From: Anton Platonov Date: Thu, 28 Oct 2021 13:10:19 +0300 Subject: [PATCH 08/10] Handle StaleElementReferenceException --- .../vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java | 1 + .../vaadin/flow/spring/fusionsecurity/SecurityIT.java | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/vaadin-spring-tests/test-spring-security-fusion-jwt/src/test/java/com/vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java b/vaadin-spring-tests/test-spring-security-fusion-jwt/src/test/java/com/vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java index 125d19512..3ade3916d 100644 --- a/vaadin-spring-tests/test-spring-security-fusion-jwt/src/test/java/com/vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java +++ b/vaadin-spring-tests/test-spring-security-fusion-jwt/src/test/java/com/vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java @@ -114,6 +114,7 @@ public void reload_when_user_jwt_expires() { getDriver().manage().deleteCookieNamed("jwt.headerAndPayload"); getDriver().manage().deleteCookieNamed("jwt.signature"); navigateTo("private", false); + waitForClientRouter(); assertLoginViewShown(); } diff --git a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java index cb34818db..f57350e2a 100644 --- a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java +++ b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java @@ -11,6 +11,7 @@ import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.StaleElementReferenceException; import com.vaadin.flow.component.button.testbench.ButtonElement; import com.vaadin.flow.component.login.testbench.LoginFormElement; @@ -69,6 +70,7 @@ private void clickLogout() { protected void open(String path) { getDriver().get(getRootURL() + "/" + path); + waitForClientRouter(); } @Test @@ -366,7 +368,12 @@ protected void assertPublicEndpointReloadsPage() { String timeBefore = getPublicView().findElement(By.id("time")) .getText(); Assert.assertNotNull(timeBefore); - getPublicView().callFunction("updateTime"); + try { + getPublicView().callFunction("updateTime"); + } catch (StaleElementReferenceException e) { + // Page reload causes the exception, ignore + } + waitForClientRouter(); String timeAfter = getPublicView().findElement(By.id("time")).getText(); Assert.assertNotNull(timeAfter); Assert.assertNotEquals(timeAfter, timeBefore); From cba383ddcf98d09065f04581569671fc0545f879 Mon Sep 17 00:00:00 2001 From: Anton Platonov Date: Thu, 28 Oct 2021 15:33:54 +0300 Subject: [PATCH 09/10] Fix open in SecurityIT --- .../java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java | 1 - 1 file changed, 1 deletion(-) diff --git a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java index f57350e2a..3aa2ceb12 100644 --- a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java +++ b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java @@ -70,7 +70,6 @@ private void clickLogout() { protected void open(String path) { getDriver().get(getRootURL() + "/" + path); - waitForClientRouter(); } @Test From 50f82132d16b85d0dbeaf930472e64edfaec6806 Mon Sep 17 00:00:00 2001 From: Anton Platonov Date: Thu, 28 Oct 2021 15:54:57 +0300 Subject: [PATCH 10/10] Remove waitForClientRouter --- .../com/vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java | 1 - .../java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java | 1 - 2 files changed, 2 deletions(-) diff --git a/vaadin-spring-tests/test-spring-security-fusion-jwt/src/test/java/com/vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java b/vaadin-spring-tests/test-spring-security-fusion-jwt/src/test/java/com/vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java index 3ade3916d..125d19512 100644 --- a/vaadin-spring-tests/test-spring-security-fusion-jwt/src/test/java/com/vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java +++ b/vaadin-spring-tests/test-spring-security-fusion-jwt/src/test/java/com/vaadin/flow/spring/fusionsecurityjwt/SecurityIT.java @@ -114,7 +114,6 @@ public void reload_when_user_jwt_expires() { getDriver().manage().deleteCookieNamed("jwt.headerAndPayload"); getDriver().manage().deleteCookieNamed("jwt.signature"); navigateTo("private", false); - waitForClientRouter(); assertLoginViewShown(); } diff --git a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java index 3aa2ceb12..785a68501 100644 --- a/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java +++ b/vaadin-spring-tests/test-spring-security-fusion/src/test/java/com/vaadin/flow/spring/fusionsecurity/SecurityIT.java @@ -372,7 +372,6 @@ protected void assertPublicEndpointReloadsPage() { } catch (StaleElementReferenceException e) { // Page reload causes the exception, ignore } - waitForClientRouter(); String timeAfter = getPublicView().findElement(By.id("time")).getText(); Assert.assertNotNull(timeAfter); Assert.assertNotEquals(timeAfter, timeBefore);