Skip to content

Commit cf4abb0

Browse files
authored
feat: Add methods for checking roles and authorities (#18700)
1 parent 8dad559 commit cf4abb0

File tree

3 files changed

+507
-7
lines changed

3 files changed

+507
-7
lines changed

vaadin-spring/src/main/java/com/vaadin/flow/spring/security/AuthenticationContext.java

Lines changed: 222 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,29 @@
1818
import jakarta.servlet.ServletException;
1919
import jakarta.servlet.http.HttpServletRequest;
2020
import jakarta.servlet.http.HttpServletResponse;
21-
22-
import java.io.IOException;
23-
import java.security.Principal;
24-
import java.util.List;
25-
import java.util.Optional;
26-
2721
import org.slf4j.Logger;
2822
import org.slf4j.LoggerFactory;
2923
import org.springframework.security.authentication.AnonymousAuthenticationToken;
3024
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3125
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
3226
import org.springframework.security.core.Authentication;
27+
import org.springframework.security.core.GrantedAuthority;
3328
import org.springframework.security.core.context.SecurityContext;
3429
import org.springframework.security.core.context.SecurityContextHolder;
3530
import org.springframework.security.web.authentication.logout.CompositeLogoutHandler;
3631
import org.springframework.security.web.authentication.logout.LogoutHandler;
3732
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
3833

34+
import java.io.IOException;
35+
import java.security.Principal;
36+
import java.util.Collection;
37+
import java.util.Collections;
38+
import java.util.List;
39+
import java.util.Optional;
40+
import java.util.Set;
41+
import java.util.stream.Collectors;
42+
import java.util.stream.Stream;
43+
3944
import com.vaadin.flow.component.UI;
4045
import com.vaadin.flow.server.VaadinServletRequest;
4146
import com.vaadin.flow.server.VaadinServletResponse;
@@ -63,6 +68,8 @@ public class AuthenticationContext {
6368

6469
private CompositeLogoutHandler logoutHandler;
6570

71+
private VaadinRolePrefixHolder rolePrefixHolder;
72+
6673
/**
6774
* Gets an {@link Optional} with an instance of the current user if it has
6875
* been authenticated, or empty if the user is not authenticated.
@@ -140,6 +147,198 @@ public void logout() {
140147
});
141148
}
142149

150+
/**
151+
* Gets the authorities granted to the current authenticated user.
152+
*
153+
* @return an unmodifiable collection of {@link GrantedAuthority}s or an
154+
* empty collection if there is no authenticated user.
155+
*/
156+
public Collection<? extends GrantedAuthority> getGrantedAuthorities() {
157+
return getAuthentication().filter(Authentication::isAuthenticated)
158+
.map(Authentication::getAuthorities)
159+
.orElse(Collections.emptyList());
160+
}
161+
162+
private Stream<String> getGrantedAuthoritiesStream() {
163+
return getGrantedAuthorities().stream()
164+
.map(GrantedAuthority::getAuthority);
165+
}
166+
167+
/**
168+
* Gets the roles granted to the current authenticated user.
169+
*
170+
* @return an unmodifiable collection of role names (without the role
171+
* prefix) or an empty collection if there is no authenticated user.
172+
*/
173+
public Collection<String> getGrantedRoles() {
174+
return getGrantedRolesStream().collect(Collectors.toSet());
175+
}
176+
177+
private Stream<String> getGrantedRolesStream() {
178+
var rolePrefix = getRolePrefix();
179+
return getGrantedAuthoritiesStream()
180+
.filter(ga -> ga.startsWith(rolePrefix))
181+
.map(ga -> ga.substring(rolePrefix.length()));
182+
}
183+
184+
/**
185+
* Checks whether the current authenticated user has the given role.
186+
*
187+
* @param role
188+
* the role to check.
189+
* @return {@literal true} if the user holds the given role, otherwise
190+
* {@literal false}.
191+
*/
192+
public boolean hasRole(String role) {
193+
return getGrantedRolesStream().anyMatch(role::equals);
194+
}
195+
196+
/**
197+
* Checks whether the current authenticated user has any of the given roles.
198+
*
199+
* @param roles
200+
* a collection containing at least one role.
201+
* @return {@literal true} if the user holds at least one of the given
202+
* roles, otherwise {@literal false}.
203+
* @throws IllegalArgumentException
204+
* if the given collection is empty.
205+
*/
206+
public boolean hasAnyRole(Collection<String> roles) {
207+
if (roles.isEmpty()) {
208+
throw new IllegalArgumentException(
209+
"Must provide at least one role to check");
210+
}
211+
return getGrantedRolesStream().anyMatch(roles::contains);
212+
}
213+
214+
/**
215+
* Checks whether the current authenticated user has any of the given roles.
216+
*
217+
* @param roles
218+
* an array containing at least one role.
219+
* @return {@literal true} if the user holds at least one of the given
220+
* roles, otherwise {@literal false}.
221+
* @throws IllegalArgumentException
222+
* if the given array is empty.
223+
*/
224+
public boolean hasAnyRole(String... roles) {
225+
return hasAnyRole(Set.of(roles));
226+
}
227+
228+
/**
229+
* Checks whether the current authenticated user has all the given roles.
230+
*
231+
* @param roles
232+
* a collection containing at least one role.
233+
* @return {@literal true} if the user holds all the given roles, otherwise
234+
* {@literal false}.
235+
* @throws IllegalArgumentException
236+
* if the given collection is empty.
237+
*/
238+
public boolean hasAllRoles(Collection<String> roles) {
239+
if (roles.isEmpty()) {
240+
throw new IllegalArgumentException(
241+
"Must provide at least one role to check");
242+
}
243+
return getGrantedRolesStream().collect(Collectors.toSet())
244+
.containsAll(roles);
245+
}
246+
247+
/**
248+
* Checks whether the current authenticated user has all the given roles.
249+
*
250+
* @param roles
251+
* an array containing at least one role.
252+
* @return {@literal true} if the user holds all the given roles, otherwise
253+
* {@literal false}.
254+
* @throws IllegalArgumentException
255+
* if the given array is empty.
256+
*/
257+
public boolean hasAllRoles(String... roles) {
258+
return hasAllRoles(Set.of(roles));
259+
}
260+
261+
/**
262+
* Checks whether the current authenticated user has the given authority.
263+
*
264+
* @param authority
265+
* the authority to check.
266+
* @return {@literal true} if the user holds the given authority, otherwise
267+
* {@literal false}.
268+
*/
269+
public boolean hasAuthority(String authority) {
270+
return getGrantedAuthoritiesStream().anyMatch(authority::equals);
271+
}
272+
273+
/**
274+
* Checks whether the current authenticated user has any of the given
275+
* authorities.
276+
*
277+
* @param authorities
278+
* a collection containing at least one authority.
279+
* @return {@literal true} if the user holds at least one of the given
280+
* authorities, otherwise {@literal false}.
281+
* @throws IllegalArgumentException
282+
* if the given collection is empty.
283+
*/
284+
public boolean hasAnyAuthority(Collection<String> authorities) {
285+
if (authorities.isEmpty()) {
286+
throw new IllegalArgumentException(
287+
"Must provide at least one authority to check");
288+
}
289+
return getGrantedAuthoritiesStream().anyMatch(authorities::contains);
290+
}
291+
292+
/**
293+
* Checks whether the current authenticated user has any of the given
294+
* authorities.
295+
*
296+
* @param authorities
297+
* an array containing at least one authority.
298+
* @return {@literal true} if the user holds at least one of the given
299+
* authorities, otherwise {@literal false}.
300+
* @throws IllegalArgumentException
301+
* if the given array is empty.
302+
*/
303+
public boolean hasAnyAuthority(String... authorities) {
304+
return hasAnyAuthority(Set.of(authorities));
305+
}
306+
307+
/**
308+
* Checks whether the current authenticated user has all the given
309+
* authorities.
310+
*
311+
* @param authorities
312+
* a collection containing at least one authority.
313+
* @return {@literal true} if the user holds all the given authorities,
314+
* otherwise {@literal false}.
315+
* @throws IllegalArgumentException
316+
* if the given collection is empty.
317+
*/
318+
public boolean hasAllAuthorities(Collection<String> authorities) {
319+
if (authorities.isEmpty()) {
320+
throw new IllegalArgumentException(
321+
"Must provide at least one authority to check");
322+
}
323+
return getGrantedAuthoritiesStream().collect(Collectors.toSet())
324+
.containsAll(authorities);
325+
}
326+
327+
/**
328+
* Checks whether the current authenticated user has all the given
329+
* authorities.
330+
*
331+
* @param authorities
332+
* an array containing at least one authority.
333+
* @return {@literal true} if the user holds all the given authorities,
334+
* otherwise {@literal false}.
335+
* @throws IllegalArgumentException
336+
* if the given array is empty.
337+
*/
338+
public boolean hasAllAuthorities(String... authorities) {
339+
return hasAllAuthorities(Set.of(authorities));
340+
}
341+
143342
/**
144343
* Sets component to handle logout process.
145344
*
@@ -154,6 +353,18 @@ void setLogoutHandlers(LogoutSuccessHandler logoutSuccessHandler,
154353
this.logoutHandler = new CompositeLogoutHandler(logoutHandlers);
155354
}
156355

356+
/**
357+
* Sets the role prefix holder to use when checking the current user's
358+
* roles.
359+
*
360+
* @param rolePrefixHolder
361+
* {@link VaadinRolePrefixHolder} instance, or {@literal null} to
362+
* revert to the default {@code ROLE_} prefix.
363+
*/
364+
void setRolePrefixHolder(VaadinRolePrefixHolder rolePrefixHolder) {
365+
this.rolePrefixHolder = rolePrefixHolder;
366+
}
367+
157368
private static Optional<Authentication> getAuthentication() {
158369
return Optional.of(SecurityContextHolder.getContext())
159370
.map(SecurityContext::getAuthentication)
@@ -170,6 +381,11 @@ CompositeLogoutHandler getLogoutHandler() {
170381
return logoutHandler;
171382
}
172383

384+
private String getRolePrefix() {
385+
return Optional.ofNullable(rolePrefixHolder)
386+
.map(VaadinRolePrefixHolder::getRolePrefix).orElse("ROLE_");
387+
}
388+
173389
/**
174390
* Augments the given {@link AuthenticationContext} with Spring Security.
175391
*
@@ -188,7 +404,6 @@ public static void applySecurityConfiguration(HttpSecurity httpSecurity,
188404
.getConfigurer(LogoutConfigurer.class);
189405
authCtx.setLogoutHandlers(logoutConfigurer.getLogoutSuccessHandler(),
190406
logoutConfigurer.getLogoutHandlers());
191-
192407
}
193408

194409
}

vaadin-spring/src/main/java/com/vaadin/flow/spring/security/VaadinWebSecurity.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ public abstract class VaadinWebSecurity {
129129
@PostConstruct
130130
void afterPropertiesSet() {
131131
accessControl = accessControlProvider.getIfAvailable();
132+
authenticationContext.setRolePrefixHolder(vaadinRolePrefixHolder);
132133
}
133134

134135
private final AuthenticationContext authenticationContext = new AuthenticationContext();

0 commit comments

Comments
 (0)