How to use user ID as parameter for custom authentication annotation?

59 Views Asked by At

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?

1

There are 1 best solutions below

0
On

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.e userId attribute of @CurrentUserAccessLevel) to do some dynamic security configuration something like what you are trying to do. So the userId 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 the getUserCourseDetails() has a method parameter name Long 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 :

@CurrentUserAccessLevel
public UserCourseDetailsDto getUserCourseDetails( Long courseCode, @P("userId") Long studentId){
  
}