Wednesday, November 14, 2012

Example Shiro setup w/o database backing in grails


When we decided to implement Shiro for our roles based authentication and authorization in our grails app we were initially excited but it turned into a real beast to get into our system. The biggest problem was getting it to work on a system that uses api calls and doesn't directly access the database to auth users.  There was no good example or documentation for this approach out there so it was difficult.  Here are the basics to how we solved it.

First we added some beans to the applicationContext.xml


<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
   <property name="securityManager" ref="securityManager"/>
   <!-- override these for application-specific URLs if you like:-->
   <property name="loginUrl" value="/"/>
   <property name="successUrl" value="/search/newSearch"/>
   <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
   <!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean  -->
   <!-- defined will be automatically acquired and available via its beanName in chain        -->
   <!-- definitions, but you can perform instance overrides or name aliases here if you like: -->
        <property name="filters">
            <map>
                <entry key="authc">
                    <bean class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter"/>
                </entry>
            </map>
        </property>
   <property name="filterChainDefinitions">
       <value>
           # some example chain definitions:
           /search/** = authc
           /session/** = anon
           /internal/** = anon
           /** = anon
           # more URL-to-FilterChain definitions here
       </value>
   </property>
</bean>

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
   <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
   <property name="realm" ref="exampleRealm"/>
   <property name="cacheManager" ref="shiroCacheManager"/>
   <!-- By default the servlet container sessions will be used.  Uncomment this line
        to use shiro's native sessions (see the JavaDoc for more): -->
   <!-- <property name="sessionMode" value="native"/> -->
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

<bean id="allCredentialsMatcher" class="org.apache.shiro.authc.credential.AllowAllCredentialsMatcher" />

<!-- Define the Shiro Realm implementation you want to use to connect to your back-end -->
<!-- security datasource: -->
<bean id="exampleRealm" class="com.ourcompany.otherstuff.ExampleRealm" >
<property name="accountDomain" >
<ref bean="accountDomain" />
</property>
<property name="credentialsMatcher">
<ref local="allCredentialsMatcher" />
</property>
<property name="authorizationCacheName">
<value>shiroAuthCache</value>
</property>
</bean>

<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
            <value>/WEB-INF/ehcache.xml</value>
        </property>
</bean>

<bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManager" ref="ehCacheManager"/>
</bean>

<bean id="permissionLoader" class="com.ourcompany.otherstuff.permission.PermissionLoader" init-method="load">
<constructor-arg ref="shiroCacheManager" />
</bean>


In the web.xml we added the following:



<!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->
<filter>
   <filter-name>shiroFilter</filter-name>
   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
   <init-param>
       <param-name>targetFilterLifecycle</param-name>
       <param-value>true</param-value>
   </init-param>
</filter>

<!-- Shiro mapping should go first so it can function in the next ones -->
<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>
    <filter-mapping>
        <filter-name>charEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>sitemesh</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>



Then we created some classes


The PermissionLoader class basically extended from ApplicationContextAware a springframework class.  It set the cachemanager in the constructor and in the load() method it got the permissions cache and filled it with our permissions listed in a config file.

The heavy hitter here was the ExampleRealm class.  That class is defined as such

class ExampleRealm extends AuthorizingRealm {

public ExampleRealm() {
setName( "ExampleRealm" )
}
@Override
boolean supports( AuthenticationToken token ) {
return true;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authToken ) throws AuthenticationException {
        // this method returns an authorized user as a valid AuthenticationInfo object else it throws an exception
       }

public boolean isPermitted(PrincipalCollection principals, Permission permission) {
             // returns whether the user specified in principals has the passed in permission
        }

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
              // returns a user object as an AuthorizationInfo with their permissions filled in
        }

After that and that was a bunch.  We created a UserService and we defined the auth method something like this:
    def authenticate( email, password ) throws AuthenticationException {
        def authToken = new UsernamePasswordToken(username, password )
        // Perform the actual login. An AuthenticationException
        // will be thrown from ExampleRealm  if the username is unrecognised or the
        // password is incorrect.
        SecurityUtils.subject.login(authToken)
    }


Finally the jsps

After all that we were able to use things like the cool jsp tags like
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

<shiro:authenticated>
<shiro:hasPermission name="Identity:Update:Module">

Good luck out there and stay strong don't let Shiro beat you!


RVM is the only way to do Ruby development

We use apps that require different versions of Ruby and holy hell it'd be a bitch keeping it all straight without rvm.  I recently setup my rvm environment at that is really the only way to reliably run our stuff.

Start here by going to the rvm install page we need 1.9.3 and 1.8.7 here.  One runs our test cases and the other is for using puppet.  Once you get the latest rvm installed be sure to do an

"rvm install 1.9.3"

and an

"rvm install 1.8.7"

One thing we did here too was setup a separate gemset for our puppet app.  You can do that by typing

"rvm gemset create <gemset-name>"

then when you want to use 1.8.7 with say your puppet gemset just do this

"rvm use 1.8.7@puppet"

This is a great way to keep your gems clean and separate.