Joe Murray
Joe Murray

Reputation: 125

using shiro with stormpath for jax-rs rbac

I'm attempting to adapt this excellent stormpath post by Brian Demers - https://stormpath.com/blog/protecting-jax-rs-resources-rbac-apache-shiro - to my own purposes and so far it works pretty well - except that now I want to add stormpath for user/role management rather then having the users in a shiro-ini file.

I'm using Apache Shiro shiro-jaxrs 1.4.0-RC to secure a REST endpoint using jax-rs. It works fine. I'm able to selectively secure the endpoints using a @RequiresPermissions tag like so:

@Path("/scan")
@Produces("application/json")
public class ScanService {

final static Logger logger = Logger.getLogger(ScanService.class);

@GET
@Path("/gettest")
@RequiresPermissions("troopers:read")
public List<Barcode> gettest() throws Exception {

ArrayList<Barcode> listofstrings = new ArrayList<Barcode>();
    Barcode b = new Barcode();
    b.setBarcode("this is a big barcode");
    listofstrings.add(b );

    return listofstrings;

}


@GET
@Produces( MediaType.APPLICATION_JSON  )
@Path("/gettest2")
public List<Barcode> gettest2() throws Exception {
    ArrayList<Barcode> listofstrings = new ArrayList<Barcode>();
    Barcode b = new Barcode();
    b.setBarcode("this is a BIGGER barcode");
    listofstrings.add(b );

    return listofstrings;
}

I also have an application class to add my resource and the ShiroFeature class like so:

package ca.odell.erbscan;
import ca.odell.erbscan.ws.ScanService;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

import org.apache.shiro.web.jaxrs.ShiroFeature;
import com.stormpath.shiro.jaxrs.StormpathShiroFeature;


@ApplicationPath("/")
public class ERBApplication extends Application {

@Override
public Set<Class<?>> getClasses() {
    Set<Class<?>> classes = new HashSet<Class<?>>();

    // register Shiro
    classes.add( ShiroFeature.class);
    // register resources
    classes.add(ScanService.class);

    return classes;
}
}

and my web.xml to init my Application class like so:

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


    <servlet-name>ERBRest</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>ca.odell.erbscan</param-value>
    </init-param>

    <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>ca.odell.erbscan.ERBApplication</param-value>
    </init-param>


    <load-on-startup>1</load-on-startup>
</servlet>



<servlet-mapping>
    <servlet-name>ERBRest</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>

<filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
</filter-mapping>


</web-app>

and finally my shiro.ini

[main]



cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $cacheManager



sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager = $sessionManager
securityManager.sessionManager.sessionIdCookieEnabled = false
securityManager.sessionManager.sessionIdUrlRewritingEnabled = false


[urls]
/** = noSessionCreation, authcBasic[permissive]

[users]
# format: username = password, role1, role2, ..., roleN
root = secret,admin
emperor = secret,admin
officer = secret,officer
guest = secret

[roles]

admin = *
officer = troopers:create, troopers:read, troopers:update

What I want to do next is add Stormpath for RBAC rather then having users and roles in a file. My feeling is there's a simple way to do this and that I'm overthinking it.

I thought it would be a fairly straightforward manner of adding in my shiro.ini:

stormpathClient = com.stormpath.shiro.client.ClientFactory
stormpathClient.cacheManager = $cacheManager

stormpath.application.href=http://....

But I was wrong. Could someone point me in the right direction?

Upvotes: 1

Views: 272

Answers (2)

Brian Demers
Brian Demers

Reputation: 2080

thanks for reading that post!

A couple things I want to point out:

  • Use this feature com.stormpath.shiro.jaxrs.StormpathShiroFeature instead of ShiroFeature
  • Your shiro.ini could look something like:
[main]
cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $cacheManager

sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager = $sessionManager
securityManager.sessionManager.sessionIdCookieEnabled = false
securityManager.sessionManager.sessionIdUrlRewritingEnabled = false

[urls]
/** = noSessionCreation, authcBasic[permissive]

[stormpath]
stormpath.application.href=http://....
  • Permissions can be stored as user or role Custom Data, you can update the Custom Data in the Stormpath admin console:
{
    … your other custom data fields …,
    "apacheShiroPermissions": [
        "troopers:create",
        "troopers:read",
        "troopers:update"
    ]
}

This blog post covers the custom data bit, it is a little older, but still relevant. I'll be updating the doc on this in the near future, so feedback welcome.

If this doesn't help you can also ping support, and we will get you going!

Upvotes: 1

Joe Murray
Joe Murray

Reputation: 125

I'm going to answer my own question here. I don't think this is the best solution, but it something I managed to get to work.

I followed this web app tutorial off of the shiro site.

https://shiro.apache.org/webapp-tutorial.html

I checked out step6 of the project and copied the [main] section of the shiro.ini as follows: Note I added the

https://api.stormpath.com/v1/applications/$STORMPATH_APPLICATION_ID

at the bottom the [main] section.

cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $cacheManager

stormpathClient = com.stormpath.shiro.client.ClientFactory
stormpathClient.cacheManager = $cacheManager

# we can disable session tracking completely, and have Stormpath manage        it for us.
sessionManager =   org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager = $sessionManager
securityManager.sessionManager.sessionIdCookieEnabled = false
securityManager.sessionManager.sessionIdUrlRewritingEnabled = false

stormpathRealm = com.stormpath.shiro.realm.ApplicationRealm
stormpathRealm.client = $stormpathClient

stormpathRealm.groupRoleResolver.modeNames = name
securityManager.realm = $stormpathRealm

stormpathRealm.applicationRestUrl = https://api.stormpath.com/v1/applications/$STORMPATH_APPLICATION_ID

I then completely removed the [users] section of the shiro.ini. Since it's now wired up to Stormpath, I need to add users and groups there. My ScanService ( as above ) has a method called gettest decorated thusly:

@GET
    @Path("/gettest")
    @RequiresPermissions("trooper:read")
    public List<Barcode> gettest() throws Exception {
.
.
.

so I need to added an account, a group and permissions in stormpath to match the permissions on the above resource. In order to do this, I need to add an account in Stormpath ( I already have the Application setup ) under my existing test application. I also added a group called officer1. The under this group I added Custom Data an array called apacheShiroPermissions - I added a string key/value pair 'trooper:read' to the apacheShiroPermissions - the JSON is below

{
  "apacheShiroPermissions": [
    "trooper:read"
  ]
}

Then I simply made sure my account - in this case jlpicard was part of the officer1 group.

Testing with curl

curl --user jlpicard:Changeme1 http://localhost:8080/JPA1_Web_exploded/rest/scan/gettest

Confirms jlpicard has access on the permission level. Adding and removing the strings entry's to the apacheShiroPermission array i.e. allows that fine grained access.

Also removing jlpicard from the officer1 or adding another account to it works as expected.

There is undoubtedly a better way to do this but this has what was worked for me so far.

Upvotes: 0

Related Questions