itm / testbed-runtime

WISEBED Wireless Sensor Network Testbed Infrastructure Software
https://github.com/itm/testbed-runtime/wiki
15 stars 11 forks source link

Provide module that can be embedded in Servlet containers that authenticates and authorizes against ShiroSNAA database #309

Closed danbim closed 11 years ago

danbim commented 11 years ago

This module can e.g., be used for #292 but also in other Servlet-based applications that integrate with TR. E.g., the SmartSantander RD could use the module to check authorization of users trying to update the resource descriptions. TR plugins could also use this module if the want to publish web pages (e.g., usage statistics).

This extensive documentation on how to use a Shiro realm implementation in a Servlet container could help.

master-lincoln commented 11 years ago

Shiro needs to added as a filter to the servlet in the service-publisher. Programmatically adding filters is possible from servlet API v3 on (which we are using). I didn't find a place where the ServicePublisher exposes a ServletContext...

danbim commented 11 years ago

I've added a method to expose the ServletContextHandler so you can add filters (see commit https://github.com/itm/service-publisher/commit/c9b1a166b587ddbbbeb4ecf5832b48749d698323). Please be aware that I'm not aware about the lifecycle model here (i.e. if you're allowed to add filters after the servlet has been started). If this does not work we can change the model so that you have to pass in filters when creating the service.

danbim commented 11 years ago

I've implemented a custom Shiro realm that can be configured in a web applications web.xml file as in the following example:

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <!-- ... -->
    <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>
    </filter-mapping>
    <listener>
        <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
    </listener>
    <!-- ... -->
</web-app>

Then, you also need a WEB-INF/shiro.ini file that contains some definitions like in this example:

[main]
  realm=de.uniluebeck.itm.tr.snaa.shiro.ShiroSNAARealm
  realm.jpaProperties=/path/to/tr_snaa.jpa.properties
  myFilter=de.uniluebeck.itm.tr.snaa.shiro.HttpMethodRolesAuthorizationFilter

[urls]
  /index.jsp = anon
  /authenticated.jsp = authcBasic
  /authorized_admin.jsp = authcBasic, roles[ADMINISTRATOR]
  /authorized_experimenter.jsp = authcBasic, roles[EXPERIMENTER]
  /authorized_sp.jsp = authcBasic, roles[SERVICE_PROVIDER]
  /logout = logout
  /rest = authcBasic, myFilter[PUT=SERVICE_PROVIDER&EXPERIMENTER,POST=EXPERIMENTER,DELETE=ADMINISTRATOR]

In the main section the abovementioned custom Shiro realm is initialized that uses the database backend of ShiroSNAA.

The filter myFilter is a custom filter that allows to check authorization for users based on their roles and on the request method (GET, PUT, POST, DELETE ...) that was used. This feature was not implemented in Shiro itself, therefore a custom module was necessary.

The property realm.jpaProperties points to another properties file that contains the configuration on how to connect to the database (should be exactly the same file that is used to run ShiroSNAA itself). An example is given here:

hibernate.connection.url = jdbc:mysql://localhost:3306/tr_snaa
hibernate.connection.driver_class = com.mysql.jdbc.Driver
hibernate.dialect = org.hibernate.dialect.MySQLDialect
hibernate.hbm2ddl.auto = update
hibernate.connection.username = TR
hibernate.connection.password = TR

# configure connection pooling for production servers
hibernate.connection.provider_class=org.hibernate.connection.C3P0ConnectionProvider
hibernate.c3p0.acquire_increment=1
hibernate.c3p0.idle_test_period=60
hibernate.c3p0.min_size=1
hibernate.c3p0.max_size=10
hibernate.c3p0.max_statements=50
hibernate.c3p0.timeout=10
hibernate.c3p0.acquireRetryAttempts=1
hibernate.c3p0.acquireRetryDelay=250

In order to be able to use the aforementioned functionality in your web application you need to include the SNAA JAR file e.g., by adding it to the dependencies section of your POM (in case you're using a Maven-based web app):

<dependencies>
    <!-- ... -->
    <dependency>
        <groupId>de.uniluebeck.itm</groupId>
        <artifactId>tr.snaa</artifactId>
        <version>0.9-SNAPSHOT</version>
    </dependency>
    <!-- ... -->
</dependencies>

as well as the ITM Maven repositories where this dependency can be found

<repository>
    <id>itm-maven-repository-releases</id>
    <url>http://maven.itm.uni-luebeck.de/content/repositories/releases/</url>
    <releases><enabled>true</enabled></releases>
    <snapshots><enabled>false</enabled></snapshots>
</repository>
<repository>
    <id>itm-maven-repository-snapshots</id>
    <url>http://maven.itm.uni-luebeck.de/content/repositories/snapshots/</url>
    <releases><enabled>false</enabled></releases>
    <snapshots><enabled>true</enabled></snapshots>
</repository>
danbim commented 11 years ago

In reality an operation has to be allowed for users with different roles. So e.g., it might be necessary to allow PUT operations for SERVICE_PROVIDERs and ADMINISTRATORs but not for EXPERIMENTERs. With the current implementation this is not possible as only conjunctions of roles are possible (which is probably an extremly rare use case (at least for TR it's non-existent)). So, config should look like this:

/rest = authcBasic, myFilter[PUT=SERVICE_PROVIDER|EXPERIMENTER,POST=EXPERIMENTER,DELETE=ADMINISTRATOR]

Please note the pipe | between SERVICE_PROVIDER and EXPERIMENTER for the PUT case. This indicates that a user is authorized to execute the action if he either is a service provider or an experimenter.

danbim commented 11 years ago

See 3c45a27f743d53e40cbbdf02ebd6d1f1930ef851 for implementation.

danbim commented 11 years ago

One remaining issue is that anon users should be able to e.g., get GET access to resources even if PUT/POST/DELETE/... is forbidden to them. The solution is simply to pass a "permissive" flag to the "authcBasic" filter of Shiro. If given, this filter will let the request through even though the user is not authenticated so the next filter in the chain can decide if anon access should be allowed.

An example shiro.ini file:

[main]
 realm=de.uniluebeck.itm.tr.snaa.shiro.ShiroSNAARealm
 realm.jpaProperties=/Coding/smartsantander/sms-trunk/auth-webapp/tr_snaa.jpa.properties
 httpMethodRoleFilter=de.uniluebeck.itm.tr.snaa.shiro.HttpMethodRolesAuthorizationFilter

[urls]
 /rest = authcBasic[permissive], httpMethodRoleFilter[PUT=SERVICE_PROVIDER|EXPERIMENTER,POST=EXPERIMENTER,DELETE=ADMINISTRATOR]

This will allow anonymous access to /rest for GET but access to PUT/POST/DELETE is only granted for authenticated users that have (one of) the corresponding role(s).