-
Notifications
You must be signed in to change notification settings - Fork 168
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Support view access checking in background threads with Spring (#…
…13675) (#13754) Requires the spring context to be properly set up for the background thread. Based on #13674 Co-authored-by: Artur <[email protected]>
- Loading branch information
1 parent
5ac8695
commit 903bfa1
Showing
9 changed files
with
284 additions
and
18 deletions.
There are no files selected for viewing
2 changes: 2 additions & 0 deletions
2
...-spring-security-flow/src/main/java/com/vaadin/flow/spring/flowsecurity/Configurator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,13 @@ | ||
package com.vaadin.flow.spring.flowsecurity; | ||
|
||
import com.vaadin.flow.component.page.AppShellConfigurator; | ||
import com.vaadin.flow.component.page.Push; | ||
import com.vaadin.flow.server.PWA; | ||
import com.vaadin.flow.theme.Theme; | ||
|
||
@PWA(name = "Spring Security Helper Test Project", shortName = "SSH Test") | ||
@Theme("spring-security-test-app") | ||
@Push | ||
public class Configurator implements AppShellConfigurator { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
fusion-endpoint/src/main/java/dev/hilla/AuthenticationUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package dev.hilla; | ||
|
||
import java.util.function.Function; | ||
|
||
import org.springframework.security.authentication.AnonymousAuthenticationToken; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
|
||
/** | ||
* Helpers for authentication related tasks. | ||
*/ | ||
public class AuthenticationUtil { | ||
|
||
/** | ||
* Gets the authenticated user from the Spring SecurityContextHolder. | ||
* | ||
* @return the authenticated user or {@code null} | ||
*/ | ||
public static Authentication getSecurityHolderAuthentication() { | ||
Authentication authentication = SecurityContextHolder.getContext() | ||
.getAuthentication(); | ||
if (authentication instanceof AnonymousAuthenticationToken) { | ||
return null; | ||
} | ||
|
||
return authentication; | ||
|
||
} | ||
|
||
/** | ||
* Gets a function for checking if the authenticated user from the Spring | ||
* SecurityContextHolder is in a given role. | ||
* | ||
* @return a function for checking if the given user has the given role | ||
*/ | ||
public static Function<String, Boolean> getSecurityHolderRoleChecker() { | ||
Authentication authentication = getSecurityHolderAuthentication(); | ||
if (authentication == null) { | ||
return role -> false; | ||
} | ||
return role -> authentication.getAuthorities().stream() | ||
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority() | ||
.equals("ROLE_" + role)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
vaadin-spring/src/main/java/com/vaadin/flow/spring/AuthenticationUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package com.vaadin.flow.spring; | ||
|
||
import java.util.function.Function; | ||
|
||
import org.springframework.security.authentication.AnonymousAuthenticationToken; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
|
||
/** | ||
* Helpers for authentication related tasks. | ||
*/ | ||
public class AuthenticationUtil { | ||
|
||
/** | ||
* Gets the authenticated user from the Spring SecurityContextHolder. | ||
* | ||
* @return the authenticated user or {@code null} | ||
*/ | ||
public static Authentication getSecurityHolderAuthentication() { | ||
Authentication authentication = SecurityContextHolder.getContext() | ||
.getAuthentication(); | ||
if (authentication instanceof AnonymousAuthenticationToken) { | ||
return null; | ||
} | ||
|
||
return authentication; | ||
|
||
} | ||
|
||
/** | ||
* Gets a function for checking if the authenticated user from the Spring | ||
* SecurityContextHolder is in a given role. | ||
* | ||
* @return a function for checking if the given user has the given role | ||
*/ | ||
public static Function<String, Boolean> getSecurityHolderRoleChecker() { | ||
Authentication authentication = getSecurityHolderAuthentication(); | ||
if (authentication == null) { | ||
return role -> false; | ||
} | ||
return role -> authentication.getAuthorities().stream() | ||
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority() | ||
.equals("ROLE_" + role)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringViewAccessChecker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package com.vaadin.flow.spring; | ||
|
||
import java.security.Principal; | ||
import java.util.function.Function; | ||
|
||
import com.vaadin.flow.server.VaadinRequest; | ||
import com.vaadin.flow.server.auth.AccessAnnotationChecker; | ||
import com.vaadin.flow.server.auth.ViewAccessChecker; | ||
|
||
/** | ||
* A Spring specific view access checker that falls back to Spring mechanisms | ||
* when the generic mechanisms do not work. | ||
*/ | ||
public class SpringViewAccessChecker extends ViewAccessChecker { | ||
|
||
/** | ||
* Creates an instance with the given annotation checker. | ||
* | ||
* The created instance is disabled by default. | ||
* | ||
* @param accessAnnotationChecker | ||
* the annotation checker to use | ||
*/ | ||
public SpringViewAccessChecker( | ||
AccessAnnotationChecker accessAnnotationChecker) { | ||
super(accessAnnotationChecker); | ||
} | ||
|
||
@Override | ||
protected Principal getPrincipal(VaadinRequest request) { | ||
if (request == null) { | ||
return AuthenticationUtil.getSecurityHolderAuthentication(); | ||
} | ||
return super.getPrincipal(request); | ||
} | ||
|
||
@Override | ||
protected Function<String, Boolean> getRolesChecker(VaadinRequest request) { | ||
if (request == null) { | ||
return AuthenticationUtil.getSecurityHolderRoleChecker(); | ||
} | ||
return super.getRolesChecker(request); | ||
} | ||
|
||
} |
92 changes: 92 additions & 0 deletions
92
vaadin-spring/src/test/java/com/vaadin/flow/spring/SpringViewAccessCheckerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package com.vaadin.flow.spring; | ||
|
||
import java.security.Principal; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
import java.util.function.Function; | ||
|
||
import com.vaadin.flow.component.Component; | ||
import com.vaadin.flow.router.BeforeEnterEvent; | ||
import com.vaadin.flow.server.auth.AccessAnnotationChecker; | ||
import com.vaadin.flow.server.auth.ViewAccessChecker; | ||
|
||
import org.junit.Assert; | ||
import org.junit.jupiter.api.Test; | ||
import org.mockito.Mockito; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.boot.test.mock.mockito.MockBean; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
import org.springframework.security.core.context.SecurityContext; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
|
||
@SpringBootTest(classes = { SpringViewAccessChecker.class }) | ||
class SpringViewAccessCheckerTest { | ||
|
||
@MockBean | ||
private AccessAnnotationChecker annotationChecker; | ||
@MockBean | ||
private Authentication authentication; | ||
|
||
@Autowired | ||
private ViewAccessChecker checker; | ||
|
||
public static class TestView extends Component { | ||
|
||
} | ||
|
||
@Test | ||
void viewAccessControlWorksWithoutRequest() { | ||
checker.enable(); | ||
AtomicBoolean accessChecked = new AtomicBoolean(false); | ||
|
||
List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); | ||
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_fake")); | ||
|
||
Mockito.when(authentication.getAuthorities()) | ||
.thenReturn((Collection) grantedAuthorities); | ||
SecurityContextHolder.setContext(new SecurityContext() { | ||
|
||
@Override | ||
public Authentication getAuthentication() { | ||
return authentication; | ||
} | ||
|
||
@Override | ||
public void setAuthentication(Authentication authentication) { | ||
} | ||
|
||
}); | ||
Mockito.when(annotationChecker.hasAccess(Mockito.any(Class.class), | ||
Mockito.any(), Mockito.any())).thenAnswer(answer -> { | ||
Principal principal = answer.getArgument(1); | ||
Function<String, Boolean> roleChecker = answer | ||
.getArgument(2); | ||
|
||
Assert.assertEquals( | ||
"Principal from security context should have been passed to checker", | ||
authentication, principal); | ||
|
||
Assert.assertTrue( | ||
"Role should have been checked from the context holder", | ||
roleChecker.apply("fake")); | ||
Assert.assertFalse( | ||
"Role should have been checked from the context holder", | ||
roleChecker.apply("fake2")); | ||
accessChecked.set(true); | ||
return true; | ||
}); | ||
|
||
BeforeEnterEvent beforeEnterEvent = Mockito | ||
.mock(BeforeEnterEvent.class); | ||
Mockito.when(beforeEnterEvent.getNavigationTarget()) | ||
.thenReturn((Class) TestView.class); | ||
checker.beforeEnter(beforeEnterEvent); | ||
Assert.assertTrue("Annotation checker should have been invoked", | ||
accessChecked.get()); | ||
} | ||
} |