restlet / restlet-framework-java

The first REST API framework for Java
https://restlet.talend.com
646 stars 284 forks source link

RFC: Loosen access restrictions on ServerResource methods that deal with MethodAnnotationInfo #1410

Closed qsiebers closed 4 months ago

qsiebers commented 5 months ago

This PR loosens the access restrictions on 4 ServerResource methods, from private to protected. This allows implementations to extend the runtime annotation processing and method parameter resolving performed in ServerResource#doHandle(MethodAnnotationInfo, Variant).

qsiebers commented 5 months ago

There are a few more private methods in ServerResource that could be set to protected, but I have not included them in this PR to keep it as simple as possible

kerbymart commented 5 months ago

@qsiebers I'm curious about your use case, is it something like this:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequireRole {
    String value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ContentLanguage {
    String value();
}

// ...

public class BookResource extends ServerResource {

    @Get("json")
    @RequireRole("admin")
    @ContentLanguage("en-US")
    public Representation getBook() {
        // This method will only be accessible for admin users and will provide content in English (US)
        // Fetch and return book details
        // ...
    }

    // ... Other methods, potentially with different roles and content languages ...
}

// ...

@Override
protected Representation doHandle(MethodAnnotationInfo annotationInfo, Variant variant) throws ResourceException {
    Method method = annotationInfo.getMethod();

    // Check for the RequireRole annotation and enforce role check
    if (method.isAnnotationPresent(RequireRole.class)) {
        RequireRole roleAnnotation = method.getAnnotation(RequireRole.class);
        if (!currentUserHasRole(roleAnnotation.value())) {
            throw new ResourceException(Status.CLIENT_ERROR_FORBIDDEN, "User does not have the required role.");
        }
    }

    // Check for the ContentLanguage annotation and handle accordingly
    if (method.isAnnotationPresent(ContentLanguage.class)) {
        ContentLanguage languageAnnotation = method.getAnnotation(ContentLanguage.class);
        // Logic to set the response language based on the annotation value
        getResponse().getHeaders().add("Content-Language", languageAnnotation.value());
    }

    return super.doHandle(annotationInfo, variant);
}

// Helper method to check if the current user has the required role
private boolean currentUserHasRole(String role) {
    // Assume we have a user service that can verify the role of the current user
    User currentUser = getCurrentUser();
    return userService.userHasRole(currentUser, role);
}

// Helper method to retrieve the current user
private User getCurrentUser() {
    // Retrieve and return the current user, possibly from the security context
    // ...
}

// ...
qsiebers commented 5 months ago

Yea something like that indeed, good to see I'm not the only one ;)

thboileau commented 4 months ago

thanks @qsiebers for your contribution. it looks good for me. It will be part of incoming 2.4.4.