-
Notifications
You must be signed in to change notification settings - Fork 43
Description
Bug Report
Summary
AuthorityService.getAuthoritiesFromRoles() only maps privilege names to GrantedAuthority objects — it does not include the role names themselves (e.g., ROLE_ADMIN). This causes the framework's own @PreAuthorize("hasRole('ADMIN')") annotations (on methods like UserEmailService.initiateAdminPasswordReset()) to always deny access, even for users who have the ROLE_ADMIN role.
Root Cause
In AuthorityService.getAuthoritiesFromRoles(), the stream pipeline does:
roles.stream()
.flatMap(role -> role.getPrivileges().stream()) // only privileges
.map(Privilege::getName)
.map(SimpleGrantedAuthority::new)
.collect(toSet());
This produces authorities like ADMIN_PRIVILEGE, LOGIN_PRIVILEGE, etc. — but never ROLE_ADMIN or ROLE_USER.
Meanwhile, UserEmailService.initiateAdminPasswordReset() (all overloads) is annotated with @PreAuthorize("hasRole('ADMIN')"), which checks for ROLE_ADMIN as a granted authority. Since ROLE_ADMIN is never granted, the call always fails with AuthorizationDeniedException: Access Denied.
Impact
Any consumer using UserEmailService.initiateAdminPasswordReset() gets a 500 error. The only workaround is to add ROLE_ADMIN as an explicit privilege name for the ROLE_ADMIN role in the config:
user:
roles:
roles-and-privileges:
"[ROLE_ADMIN]":
- ROLE_ADMIN # workaround: needed for framework's hasRole() checks
- ADMIN_PRIVILEGESuggested Fix
AuthorityService.getAuthoritiesFromRoles() should include both role names and privilege names as granted authorities:
public Collection<? extends GrantedAuthority> getAuthoritiesFromRoles(Collection<Role> roles) {
Set<GrantedAuthority> authorities = new HashSet<>();
for (Role role : roles) {
// Include the role itself (needed for hasRole() checks)
authorities.add(new SimpleGrantedAuthority(role.getName()));
// Include all privileges for the role
for (Privilege privilege : role.getPrivileges()) {
authorities.add(new SimpleGrantedAuthority(privilege.getName()));
}
}
return authorities;
}This is the standard Spring Security convention — UserDetailsService implementations typically include both role names and privilege names as granted authorities.
Environment
- DS Spring User Framework version: 4.3.0
- Spring Security version: 7.0.3
- Discovered in: MagicMenu admin password reset feature (MM-207)