Friday, 22 August 2008

Grails - JSecurity plugin

JSecurity is a Grails plugin that adds authentication and authorization to a web application. First we go about creating a project and installing the plugin:

grails create-app project_name
grails install-plugin jsecurity

Then we use the quick-start option, available once the plugin is installed, to generate JSecurity domain classes, controller and realm.

grails quick-start

We can follow the JSecurity Quick Start guide to create a functional security application or use the files present to customize and only take what is required.

Authentication


For the login authentication, we'll need the User class, the Auth controller and the DbRealm.

  • The User class will need a username and password field. The password field must be hashed, SHA1 is the default.

  • For the Auth controller, the important methods are signIn and signOut. For signIn, the important lines are the authToken definition, the jsecSecurityManager login and the try-catch block.

  • Authentication occurs in the DbRealm. The important lines in the authenticate method are the account definition and the credentialMatch. You may customize the authToken class by changing the static line, replacing the default with your own class.

  • credentialMatch defaults to SHA1 hashing, to change it, modify grails-app/conf/spring/resources.groovy and add the line credentialMatcher(org.jsecurity.authc.credential.hashMatcher). Replace hashMatcher with the hash class of your choice.


Roles


Roles depend on the hasRole method of the realm. This method accepts a principal, usually a username, and a rolename. The output is a simple true or false boolean, making the logic inside the method completely up to the programmer. The classes generated by the quick-start are not all necessary. We can easily use a variable in the user class to determine the role, modifying the hasRole method as required, thus no additional classes are needed.

For this project, we are using the default hasRole method and keeping the Role as well as the UserRoleRel class. This, along with the User class, allows us to have users and roles, assigning the users to a role in a many-to-one relationship. Refer to the JSecurity Quick Start guide for the way to bootstrap it.

Authorization


Having users and roles, we can now set up proper access control by creating a security filter file, grails-app/conf/SecurityFilters.groovy. Filter rules cannot overlap in actions, just modify the accessControl to include the desired roles. The rules return true or false, allowing or denying access.

Security Filter Example

import org.jsecurity.SecurityUtils
// Customise the interceptor behaviour by implementing onNotAuthenticated() and onUnauthorized() methods in your filters class.
// Default behaviour remains as before (redirect to login page for an unauthenticated user, redirect to unauthorized page for unauthorized access).
class SecurityFilters {
def filters = {
// Ensure that all controllers and actions require an authenticated user,
// except for a few controllers
auth(controller: "*", action: "*") {
before = {
// Exclude these controllers.
if ((controllerName == "search" && actionName == "unboundSearch") || (controllerName == "business" && actionName == "show") ||
(controllerName == "user" && actionName == "signup")) return true

// This just means that the user must be authenticated. He does
// not need any particular role or permission.
accessControl { true }
}
}

// Roles return true or false
// Add reviews to businesses. Only Administrators or Users can do this
addReview(controller: "review", action: "create") {
before = {
accessControl {
role("Administrator") || role("User")
}
}
}

// Add pets to users
addPets(controller: "pet", action: "(create|save)") {
before = {
accessControl {
def user = User.findByUserName(SecurityUtils.subject.principal)
def allowAccess = false

if (role("Administrator")) {
allowAccess = true
}
else if (params.id.toLong() == user.id) {
allowAccess = role("Business User") || role("User")
}
return allowAccess
}
}
}
}
}

Permissions


Permissions offer finer access control, like read, update, delete. Roles can be considered as named collections of permissions, like administrator, user, superuser. Nothing was done with permissions, it was entirely a role-based affair. Refer to the documentation for usage.

Links