I've a REST API:
@CurrentUserAccessLevel(userId = "#studentId")
@GetMapping(value = "/{course-code}/users/{student-id}")
public UserCourseDetailsDto getUserCourseDetails(@PathVariable(value = "course-code") final Long courseCode,
@PathVariable(value = "student-id") final Long studentId) {
return userCourseService.getUserCourseDetails(studentId, courseCode);
}
and custom authentication annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@PreAuthorize("@applicationUserDetailsService.hasAccessToResourceOwnedBy(#userId)")
public @interface CurrentUserAccessLevel {
String userId();
}
the annotation uses method hasAccessToResourceOwnedBy(userId):
public boolean hasAccessToResourceOwnedBy(final Long userId) {
final User currentUser = userService.resolveCurrentUser();
return isAdmin(currentUser) || Objects.equals(currentUser.getId(), userId);
}
but I'm getting null as userId. However, if I replace
@PreAuthorize("@applicationUserDetailsService.hasAccessToResourceOwnedBy(#userId)")
with
@PreAuthorize("@applicationUserDetailsService.hasAccessToResourceOwnedBy(#studentId)")
I obtain requested user id.
Basically, I need this annotation to avoid horizontal escalation of privileges, maybe there is some better solution?
Think that it is because meta-annotation supported by the spring security just simply allowing users to use a self-defined annotation (i.e
@CurrentUserAccessLevel
) to refer a@PreAuthorize(xxxxx)
and there are no fancy behaviour about using the attribute of the self-defined annotation (i.euserId
attribute of@CurrentUserAccessLevel
) to do some dynamic security configuration something like what you are trying to do. So theuserId
of the@CurrentUserAccessLevel
is useless and you can remove it.The
#userId
in the@PreAuthorize
's expression refer to the parameter name of the method that are annotated with@CurrentUserAccessLevel
. So it works if you change it to#studentId
as thegetUserCourseDetails()
has a method parameter nameLong studentId
If you do not want to change the parameter name of the method to
userId
, you can use@P
to specify its value likes :