Grails Integration Plugin

Name Grails Integration Plugin
Version 0.3
Product Versions Crowd 1.3
Author(s) CODEGEIST:Graham Bakay
Homepage Grails website
Codehaus website (better theme)
Price gratis
License BSD
JavaDocs TBD
IssueTracking Codehaus JIRA
Download Zip grails-crowd-0.3.zip
View Source Codehaus Fisheye
Download Source Codehaus Subversion
Now a Grails Plugin
In order to have first-class integration with Grails, I have moved all the development to Codehaus. Please visit the plugin page for all the latest info.
For the Codegeist Judges
For the purposes of Codegeist, I am copying the current Codehaus wiki page here. Additionally, a copy of the Grails project (containing all sources, etc.) for the plugin CODEGEIST:is attached, and a screenshot of the default login screen (woo!) is at the bottom of the page.

Crowd Plugin

Table of Contents

CODEGEIST:Crowd Plugin

Description/Features

Plugin that adds support for Atlassian Crowd authentication and authorization to a Grails webapp.

Welcome Codegeisters!

Sure, you can wire up Grails with Acegi and the new Crowd-Acegi integration APIs released with v1.3, as Katie has done here, but that is way too much work. This Grails plugin has no dependency on Acegi, and talks to Crowd directly for its authentication and authorization requirements. The plugin borrows ideas from Grails Authorize for the simple auth/auth configuration, and from Grails Acegi for a GSP taglib.

The plugin provides a Grails webapp with simple hooks for Grails controllers and actions to require authentication and authorization. Pages requiring authentication will redirect a non-authenticated user to a supplied Login controller (with a customizable view). Logouts are handled by a supplied Logout controller. Authentication is fully integrated with Crowd's single sign-on capabilities; if a user is already logged into Crowd, the user will be authenticated to the webapp automatically.

Pages requiring authorization will return to a non-authorized user a http 503 FORBIDDEN error. The logic and/or view handling that error is also fully customizable within Grails.

The plugin also provides some Grails tag libraries to manipulate views depending on: whether the user is authenticated, and whether the user is authorized, and at what level. A tag library to easily grab information about the currently authenticated user is provided. As well, a tag library is provided for easily retrieving SOAPAttribute values from a Crowd SOAPEntity.

Finally, several utility classes provide the same functionality of the tag libraries in Groovy code. This is very useful for logic written in the controller and service areas.

Usage

Achtung!
This is BETA SOFTWARE. There are bound to be some issues. The plugin shouldn't eat your app, but make a backup anyway. Bug fixes welcome!

Crowd Requirements

The plugin requires an Atlassian Crowd server, running version 1.3. You will need to configure an application within Crowd. Don't forget to specify the remote IP address. That gets me every time

Install and Configure the Plugin

  1. Install the into a Grails webapp:
    grails install-plugin crowd
  2. When installing, choose 'y' when asked to add the following configuration to your Grails webapp Config.groovy:
    PROJECT_ROOT/grails-app/conf/Config.groovy
    crowd {
      application.name= 'test'                                             //Replace with name of Crowd Application
      application.password= 'password'                                     //Replace with password
      application.login.url= 'http://localhost:8080/appname/login'         //Replace with your webapp login url
      crowd.crowd.server.url= 'https://crowd.example.com/crowd/services/'  //Replace with Crowd server url
    }

    A list of all the configurable options (along with the default) is listed in PROJECT_ROOT/grails-app/conf/CrowdAuthConfig.groovy.

  3. When installing, choose 'y' when asked to add default internationalization messages to your Grails webapp PROJECT_ROOT/grails-app/i18n/messages.properties.
    Further localizations will be added as they are created. Submissions welcome!
  4. If you are using Grails 1.0.2 (possibly earlier), this bug (fixed in 1.0.3, whenever that arrives) requires you to copy the default Login view from the Plugin to your webapp:
    grails create-login-view

Secure a Grails Controller or Action

Authentication:

Authentication is defined via the static authenticate variable in your controller

A Boolean will enable or disable authentication for all actions:

def static authenticate= true

An 'only' list with enable authentication only for the listed actions:

def static authenticate = [only:['index', 'list']]

An 'except' list with enable authentication for all actions except those listed:

def static authenticate = [except:['index', 'list']]

Authorization:

Authorization is defined via the static authorize variable in your controller

A String will require the user belong the Crowd group for all actions:

def static authorize = "admin"

A List will require the user to belong to all listed Crowd groups for all actions:

def static authorize = ["customer", "buyer"]

A Map will require the user to belong to all listed Crowd groups for specified actions:

def static authorize = [all:["admin"], list:["guest", "customer"]]

Taglibs

Authentication:

crowdAuth:isAuthenticated will render its body if the user is authenticated.
crowdAuth:isNotAuthenticated will render its body if the user is not authenticated.
crowdAuth:authenticatedUserInfo will render information from the Crowd principal speficied in a property attribute. Valid properties are: username, name (full name), firstName, lastName, displayName, email, telephoneNumber, faxNumber.

Authorization:

crowdAuth:ifAllGranted will render its body if the user belongs to all of the comma-separated Crowd group/role names specified in a group/role attribute.
crowdAuth:ifAnyGranted will render its body if the user belongs to one of the comma-separated Crowd group/role names specified in a group/role attribute.
crowdAuth:ifNotAllGranted will render its body if the user does not belong to all of the Crowd group/role names specified in a group/role attribute.
crowdAuth:ifNotAnyGranted will render its body if the user does not belong to one of the Crowd group/role names specified in a group/role attribute.

Crowd Utility:

crowd:attributeValue will retrieve the first value of the attribute specified in an attribute named attribute from a SOAPEntity supplied in an attribute named entity.

Utility Classes

There are also some utility classes which provide an API for wrestling with the internals of the plugin:

static class CrowdAuthUtils {
  static SOAPPrincipal getAuthenticatedPrincipal(request);
  // Returns the SOAPPrincipal of a currently logged-in user

  static List<String> getAuthenticatedPrincipalGroups(request);
  // Returns a List of Group names the logged-in user belongs to

  static List<String> getAuthenticatedPrincipalRoles(request);
  // Returns a List of Role names the logged-in user belongs to

  static Boolean isAuthenticated(request);
  // Returns true if a user is logged-in, false otherwise
}
static class CrowdUtils {
  getAttributeValues(SOAPEntity entity, String attribute)
  // Returns null if not found, a String if one value, or a List<String> if multiple values
}

Tutorial

The tutorial below assume Crowd to have a user named George Harrison who belongs to a group called Guitarists. There should also be some other groups (which George is not a member of) called Bassists, Drummers, and Lead Singers.

Create a Grails webapp:

grails create-app rockstars

CODEGEIST:Install and configure the Crowd plugin.

Create the following Grails Controller:

grails create-controller home

Edit the controller with the following:

PROJECT_ROOT/grails-app/controllers/HomeController.groovy
class HomeController {
  def static authenticate= false
  def static authorize= []

  def index = {
  }
}

Create a new file for the view:

PROJECT_ROOT/grails-app/views/home/index.gsp
<html>
    <head>
        <meta name="layout" content="main"/>
        <title>Home</title>
    </head>
    <body>
        <div class="body">
            <h1>Home</h1>

            <crowdAuth:isAuthenticated>
                <p>I am authenticated!</p>
                <p>According to Crowd:<br/>
                    My Name is: <crowdAuth:authenticatedUserInfo property="name"/><br/>
                    My Email address is: <crowdAuth:authenticatedUserInfo property="email"/><br/>
                    My Username is: <crowdAuth:authenticatedUserInfo property="username"/><br/>
                </p>
                <p>
                    <crowdAuth:ifAllGranted group="Guitarists">
                        I am a guitarist.<br/>
                    </crowdAuth:ifAllGranted>
                    <crowdAuth:ifAnyGranted group="Guitarists,Lead Singers">
                        I am either a guitarist or a Lead Singer.<br/>
                    </crowdAuth:ifAnyGranted>
                    <crowdAuth:ifAllGranted group="Guitarists,Lead Singers">
                        I am a guitarist and a Lead Singer.<br/>
                    </crowdAuth:ifAllGranted>
                    <crowdAuth:ifAllNotGranted group="Guitarists">
                        I am not a guitarist.<br/>
                    </crowdAuth:ifAllNotGranted>
                    <crowdAuth:ifAllNotGranted group="Drummers">
                        I am not a drummer.<br/>
                    </crowdAuth:ifAllNotGranted>
                    <crowdAuth:ifAllNotGranted group="Drummers,Lead Singers">
                        I am not a drummer, nor a Lead Singer.<br/>
                    </crowdAuth:ifAllNotGranted>
                    <crowdAuth:ifAnyNotGranted group="Drummers,Guitarists">
                        I am either not a drummer, or not a guitarist, or both.<br/>
                    </crowdAuth:ifAnyNotGranted>
                </p>
            </crowdAuth:isAuthenticated>
            <crowdAuth:isNotAuthenticated>
                <p>I am not authenticated! Perhaps I should <g:link controller="login">Login</g:link>.</p>
            </crowdAuth:isNotAuthenticated>
        </div>
    </body>
</html>

Start your webapp:

grails run-app

In your browser, from the webapp home page, click on HomeController. If you are logged into Crowd already, you have been logged into the application automatically (via Crowd's single sign-on). If not, click on the Login link to be sent to the Login screen. Try logging in. For invalid logins, the error messages are customizable in PROJECT_ROOT/grails-app/i18n/messages.properties. The default messages were copied there on installation. Once logged in, Go back to the HomeController. Note how the screen now says you're logged in and displays some information about the user.

Things to try:

  • Change HomeController's authenticate variable to true. This will make the controller automatically redirect you to the Login screen if you are not authenticated.
  • Change HomeController's authorize variable to a map requiring a group to which George doesn't belong. The controller will redirect you to a 503 FORBIDDEN page.
  • Change some of the tags in the view. The text should be rendered only if the conditions of the tag are met.

You get the idea!

Issue Tracking

jira.codehaus.org (8 issues)
T Key Summary Assignee Reporter Pr Status Res Created Updated Due
Improvement GRAILSPLUGINS-360 Implement Crowd Role-based Authorization Graham Bakay Graham Bakay Major Open UNRESOLVED May 08, 2008 May 08, 2008
Improvement GRAILSPLUGINS-351 Have a default 503 error page for authz failures Graham Bakay Graham Bakay Major Open UNRESOLVED May 05, 2008 May 08, 2008
Test GRAILSPLUGINS-361 Improve Unit Tests and Integration Tests Graham Bakay Graham Bakay Major Open UNRESOLVED May 08, 2008 May 08, 2008
Wish GRAILSPLUGINS-407 Crowd 1.4 Support Graham Bakay James William Dumay Major In Progress UNRESOLVED Jun 10, 2008 Aug 19, 2008
Bug GRAILSPLUGINS-375 Compilation error in Crowd plugin - version 0.3: "static not allowed here" Graham Bakay Nick Pellow Major In Progress UNRESOLVED May 18, 2008 Aug 19, 2008
New Feature GRAILSPLUGINS-387 Provide a callback on successful login Graham Bakay Nick Pellow Major Open UNRESOLVED May 27, 2008 Aug 19, 2008
Task GRAILSPLUGINS-308 Create documentation in wiki Graham Bakay Graham Bakay Minor In Progress UNRESOLVED Apr 03, 2008 Apr 04, 2008
Improvement GRAILSPLUGINS-317 Make variable names used to store principal data in session scope customizable Graham Bakay Graham Bakay Minor Open UNRESOLVED Apr 07, 2008 May 08, 2008


Labels

codegeist_2008_crowd codegeist_2008_crowd Delete
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.