2

In order to use the security annotations of JSR-250 (RolesAllowed, PermitAll, DenyAll):

  • In Jersey, you would register the RolesAllowedDynamicFeature class.
  • In RESTeasy, you would use the web.xml config:

    <context-param>
       <param-name>resteasy.role.based.security</param-name>
       <param-value>true</param-value>
    </context-param>
    

Both of these rely on the implementation of SecurityContext.isUserInRole(), but it seems that WebSphere Liberty Profile does not.

How do we get this to work in WebSphere Liberty Profile (WLP)?

I used a minimal example:

  1. Create a resource class/method with @RolesAllowed:

    @Path("/rest")
    public class HelloWorld {
        @GET
        @RolesAllowed("ANYTHING")
        public Response hello() {
            return Response.ok("Hello World").build();
        }
    }
    
  2. Set your SecurityContextImpl in a filter, overriding isUserInRole() to always returns true;
  3. Enable "role-based security" for the JAX-RS implementation. (Jersey or RESTeasy, etc as above. For WLP, I had to add the appSecurity-2.0 feature)
  4. And you should have a working example.

However, it appears that WebSphere Liberty Profile returns 403 Forbidden even though isUserInRole returns true.

Does anyone know how to properly use the @RolesAllowed annotation in Liberty and what I might be missing?

Code

@ApplicationPath("/")
public class MyApplication extends Application {
    public MyApplication() {}
}

@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthFilter implements ContainerRequestFilter {
    @Override
    public void filter(ContainerRequestContext ctx) throws IOException {
        System.out.println("Setting SecurityContext..");
        ctx.setSecurityContext(new MySecurityContext("someuser", "anyrole"));
    }
}

public class MySecurityContext implements SecurityContext {

    private String user;
    private String role;

    public static class MyPrincipal implements Principal {
        private String name;

        public MyPrincipal(String name) { this.name = name; }
        @Override public String getName() { return name; }
    }

    public MySecurityContext(String user, String role) {
        this.user = user;
        this.role = role;
    }

    @Override public String getAuthenticationScheme() { return "BASIC"; }
    @Override public Principal getUserPrincipal() { return new MyPrincipal(user); }
    @Override public boolean isSecure() { return true; }

    @Override
    public boolean isUserInRole(String role) {
        return true;
    }
}

@Path("/test")
public class HelloWorld {
    @GET
    @RolesAllowed("doesntmatter")
    public Response hello() {
        return Response.ok("Hello World").build();
    }
}

pom.xml (dependencies only)

<dependencies>
    <dependency>
        <groupId>javax.ws.rs</groupId>
        <artifactId>javax.ws.rs-api</artifactId>
        <version>2.0.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.annotation</groupId>
        <artifactId>javax.annotation-api</artifactId>
        <version>1.2</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

server.xml

Code works with the appSecurity feature disabled. Does not work with it enabled.

<server description="test">
    <featureManager>
        <feature>jaxrs-2.0</feature>
        <feature>localConnector-1.0</feature>
        <!--  <feature>appSecurity-2.0</feature> -->
    </featureManager>

    <webApplication id="RoleTest" location="RoleTest.war" name="RoleTest"/>
    <httpEndpoint httpPort="9081" httpsPort="9444" id="defaultHttpEndpoint"/>

    <!-- below lines are required when appSecurity feature is loaded -->
    <!--  
    <keyStore id="defaultKeyStore" password="{xor}Lz4sLCgwLTtu"/>
    <basicRegistry id="basic" realm="BasicRegistry"> 
        <user name="username" password="password" />
    </basicRegistry>
    -->
</server>
gb.
  • 168
  • 3
  • 11
  • It would be helpful if you provided the server.xml for the liberty server. – Alasdair Jan 08 '16 at 22:24
  • 1. Do you know how the authorization is done? You mentioned with what components RESTeasy and Jersey do it with, but not with WS. 2. Did you mark your authentication filter with an early priority, .e.g `@Priority(Priorities.AUTHENTICATED)`. 3. Did you [set the Principal](http://stackoverflow.com/a/34497750/2587435) in the `SecurityContext`? – Paul Samsotha Jan 09 '16 at 01:54
  • @peeskillet 1. Maybe I've been hitting the wrong documentation, but the only thing I have found says to enable the "appSecurity-2.0" feature. 2. See edit for code example. 3. See edit for code example. – gb. Jan 09 '16 at 02:53
  • Maybe just try to disable the websphere, and just copy either Jersey's or RESTeasy's implementation support for this. For RESTeasy, see [`RoleBasedSecurityFeature` and `RoleBasedSecurityFilter`](https://github.com/resteasy/Resteasy/tree/master/jaxrs/resteasy-jaxrs/src/main/java/org/jboss/resteasy/plugins/interceptors). You'll need to register the former, or annotate with `@Provider` to be discovered. For Jersey's impl, see [`RolesAllowedDynamicFeature`](https://github.com/jersey/jersey/blob/master/core-server/src/main/java/org/glassfish/jersey/server/filter/RolesAllowedDynamicFeature.java#L84) – Paul Samsotha Jan 09 '16 at 03:01
  • If using the Jersey impl, you will also need to register the feature or just annotate with `@Provider` to be discovered. Both features work pretty much the same way. The main difference I would say is that the RESTeasy impl uses all standard JAX-RS classes, so you don't need to make any adjustments, though it shouldn't be difficult to figure out Jersey's impl if you know reflection. – Paul Samsotha Jan 09 '16 at 03:04

1 Answers1

1

May be you can try this:

1 server.xml

<server description="test">
    <featureManager>
        <feature>jaxrs-2.0</feature>
        <feature>appSecurity-2.0</feature>
    </featureManager>

    <webApplication id="RoleTest" location="RoleTest.war" name="RoleTest">
        <application-bnd>
            <security-role name="ANYTHING">
                <user name="username" />
            </security-role>
            <security-role name="AuthenticationRole">
                <user name="username" />
            </security-role>
            <security-role name="AllAuthenticated">
                <special-subject type="ALL_AUTHENTICATED_USERS" />
            </security-role>
        </application-bnd>
    </webApplication>

    <httpEndpoint httpPort="9081" httpsPort="9444" id="defaultHttpEndpoint" />

    <basicRegistry id="basic" realm="BasicRegistry">
        <user name="username" password="password" />
    </basicRegistry>
</server>

2 Java Code Create a MyApplication class and a resource class/method with @RolesAllowed:

@ApplicationPath("/")
public class MyApplication extends Application {
    public MyApplication() {}
    public Set<Class<?>> getClasses(){
      Set<Class<?>> classes = new HashSet();
      classes.add(HelloWorld.class);

      return classes;
   }
}


@Path("/rest")
public class HelloWorld {
    @GET
    @RolesAllowed("ANYTHING")
    public Response hello() {
        return Response.ok("Hello World").build();
    }
}

3 web.xml

<web-app xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee web-app_3_0.xsd"
    version="3.0">

  <display-name>Test Application</display-name>
  <description>blablabla</description>

    <servlet>
        <servlet-name>MyApplication</servlet-name>
        <servlet-class>com.ibm.websphere.jaxrs.server.IBMRestServlet</servlet-class>
        <init-param>
            <param-name>requestProcessorAttribute</param-name>
            <param-value>requestProcessorAttribute_webcontainer</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet>
        <servlet-name>com.xxx.MyApplication</servlet-name>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>SecurityContextApp</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>com.xxx.MyApplication</servlet-name>
        <url-pattern>/xxx/*</url-pattern>
    </servlet-mapping>


    <security-constraint id="SecurityConstraint_2">
        <web-resource-collection id="WebResourceCollection_2">
            <web-resource-name>com.xxx.MyApplication
            </web-resource-name>
            <description>Protection area for Rest Servlet</description>
            <url-pattern>/xxx/rest</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <user-data-constraint id="UserDataConstraint_2">
            <transport-guarantee>NONE</transport-guarantee>
        </user-data-constraint>
        <auth-constraint id="AuthConstraint_2">
            <role-name>AuthenticationRole</role-name>
        </auth-constraint>
    </security-constraint>    


    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>test</realm-name>
    </login-config>
    <security-role id="SecurityRole_1">
        <description>blabla</description>
        <role-name>ANYTHING</role-name>
    </security-role>

    <security-role id="SecurityRole_2">
        <role-name>AuthenticationRole</role-name>
    </security-role>

</web-app>

Any other issues, leave me a message.

Qian Du
  • 11
  • 2