OpenLiberty / open-liberty

Open Liberty is a highly composable, fast to start, dynamic application server runtime environment
https://openliberty.io
Eclipse Public License 2.0
1.14k stars 587 forks source link

RolesAllowed("**") not working with Jakarta Rest #26539

Open rdean-csx opened 11 months ago

rdean-csx commented 11 months ago

Describe the bug
I have a Jakarta RestfulWS method that is annotated with @RolesAllowed("**"). This should be equated to the ALL_AUTHENTICATED_USERS special subject, but BuiltinAuthorizationService.isAllAuthenticatedGranted(...) is returning false for the check.

Steps to Reproduce

  1. Configure Liberty with mpJwt-2.1 feature
  2. Create a Jakarta RestfulWS resource class and annotate an operation method with @RolesAllowed("**")
    @GET
    @Path("/{message}")
    @Produces(MediaType.APPLICATION_JSON)
    @RolesAllowed("**")
    public Response getMsg(@Context SecurityContext context, @PathParam("message") String msg) {
        String output = "Message requested: " + msg;
        // Simply return the parameter passed as message
        return Response.status(200).entity(output).build();
    }
  3. Invoke an HTTP request, sending a valid JWT.

Expected behavior
Liberty returns a 200 OK response code with the requested content.

Actual behavior Liberty returns a 403 Forbidden response code.

Diagnostic information:

 - This is an out-of-context snippet from the trace log, with company info redacted:

[10/6/23, 23:39:43:937 EDT] 000000a0 id=024dda94 y.authorization.builtin.internal.BuiltinAuthorizationService > isAllAuthenticatedGranted Entry
[[[my-app-name]]] [starstar] Subject: [[[REDACTED]]]

[10/6/23, 23:39:43:937 EDT] 000000a0 id=024dda94 y.authorization.builtin.internal.BuiltinAuthorizationService > getRolesForSpecialSubject Entry
[[[my-app-name]]] ALL_AUTHENTICATED_USERS [10/6/23, 23:39:43:937 EDT] 000000a0 id=6380f293 security.feature.internal.FeatureWebSecurityCollaboratorImpl > getFeatureAuthzRoleHeaderValue Entry [10/6/23, 23:39:43:937 EDT] 000000a0 id=6380f293 .ibm.ws.webcontainer.security.WebAppSecurityCollaboratorImpl > getWebAppConfig Entry [10/6/23, 23:39:43:937 EDT] 000000a0 id=6380f293 .ibm.ws.webcontainer.security.WebAppSecurityCollaboratorImpl < getWebAppConfig Exit
[[[my-app-name]]] [10/6/23, 23:39:43:937 EDT] 000000a0 id=6380f293 security.feature.internal.FeatureWebSecurityCollaboratorImpl < getFeatureAuthzRoleHeaderValue Exit
null [10/6/23, 23:39:43:937 EDT] 000000a0 id=445a32f8 com.ibm.ws.security.authorization.RoleSet > toTraceString Entry [10/6/23, 23:39:43:937 EDT] 000000a0 id=445a32f8 com.ibm.ws.security.authorization.RoleSet < toTraceString Exit
[] [10/6/23, 23:39:43:937 EDT] 000000a0 id=024dda94 y.authorization.builtin.internal.BuiltinAuthorizationService < getRolesForSpecialSubject Exit
[] [10/6/23, 23:39:43:937 EDT] 000000a0 id=445a32f8 com.ibm.ws.security.authorization.RoleSet > toTraceString Entry [10/6/23, 23:39:43:937 EDT] 000000a0 id=445a32f8 com.ibm.ws.security.authorization.RoleSet < toTraceString Exit
[] [10/6/23, 23:39:43:937 EDT] 000000a0 id=04f84b6f .authorization.builtin.internal.BuiltinAccessDecisionService > isGranted Entry
[[[my-app-name]]] [starstar] [] Subject: [[[REDACTED]]]

[10/6/23, 23:39:43:937 EDT] 000000a0 id=445a32f8 com.ibm.ws.security.authorization.RoleSet > contains Entry
** [10/6/23, 23:39:43:937 EDT] 000000a0 id=445a32f8 com.ibm.ws.security.authorization.RoleSet < contains Exit
false [10/6/23, 23:39:43:937 EDT] 000000a0 id=04f84b6f .authorization.builtin.internal.BuiltinAccessDecisionService < isGranted Exit
false [10/6/23, 23:39:43:937 EDT] 000000a0 id=024dda94 y.authorization.builtin.internal.BuiltinAuthorizationService < isAllAuthenticatedGranted Exit
false

WhiteCat22 commented 11 months ago

Hi @rdean-csx,

Unfortunately, @RolesAllowed("**") isn't supported.

To protect an endpoint with ALL_AUTHENTICATED_USERS, you can configure your web.xml with an auth-constraint:

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>all-resources</web-resource-name>
            <url-pattern>/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>auth-user</role-name>
        </auth-constraint>
    </security-constraint>
    <security-role>
        <role-name>auth-user</role-name>
    </security-role>

and then give the role special-subject ALL_AUTHENTICATED_USERS in your server.xml

<webApplication location="system.war" contextRoot="/">
    <!-- remove this binding for token to work , and remove constraints in web.xml -->
    <application-bnd>
    <security-role name="auth-user">
      <special-subject type="ALL_AUTHENTICATED_USERS" />
    </security-role>
  </application-bnd> 
  </webApplication>

At that point you should be able to remove the @RolesAllowed annotation from your resource - security constraints for the endpoint will be handled by the web.xml

rdean-csx commented 11 months ago

I've seen that commentary before, but I raised the issue because the lack of support appears to be unintentional based on the code for BuiltinAuthorizationService, which does an explicit check for AllAuthenticatedUsers that only failed because the role value had been transformed to the string _starstar_. Based on a plain reading of Jakarta Annotations and Jakarta Enterprise Beans, such support wouldn't be prohibited by spec, and doing so would provide a consistent meaning to RolesAllowed regardless of context.

Using authorization checks through web.xml is misaligned to our usage of MicroProfile JWT. One of the benefits is that it automates the mapping of group claims in the JWT to roles checked by @RolesAllowed or isUserInRole(...).

jim-krueger commented 11 months ago

Moving to the Security team as a potential enhancement.