CHAPTER 14
Security

Security is a broad topic that is applicable across multiple layers of your application. From the view layer to the database, making your application immune to the various forms of attack is a nontrivial task. Scary things like cross-site scripting (XSS) and SQL injection attacks require careful attention when building your application. As well as covering techniques that help avoid such attacks, in this chapter we'll cover how you can secure your application through authentication and authorization.

Authentication refers to the act of establishing a client's identity. The ubiquitous login form is typically used to establish identity in web applications. Authorization, on the other hand, is about granting a client specific rights (often referred to as privileges or permissions).

Of course, there is no point in reinventing the wheel, so we'll cover how you can use one of the security frameworks already available to implement a more generic solution for authentication and authorization.

Securing Against Attacks

Hacking Internet sites has become a challenge for not just malicious individuals but also for security firms that research potential holes in an application's makeup. The more media coverage an application has, the more likely it is subject to such attacks. Banks and large Internet sites are at particular risk.

When developing an application, you should pay careful attention to the security requirements. Is it exposed to the outside world, or is it an intranet application? What are the implications of a breach? An application with heightened security requirements will take longer to develop and require more user acceptance testing and probing.

As for Grails, some vulnerabilities are completely beyond its control. No matter how cautious you are, Grails won't save you if there is a vulnerability at the operating system or web server level. Having said that, Grails does provide you with the tools to implement application-layer security, but ultimately it is up to you to keep security at the forefront of your mind. Unit and functional testing can help you spot problems in this area. Your application can be breached in many ways. In the next few sections, we'll cover some of those ways and how you can help avoid any issues occurring in the first place.

SQL or HQL Injection

One way to launch a Denial of Service (DoS) attack is to use SQL or HQL injection. Essentially, if you use HQL that is built up from values obtained from request parameters, it is possible for an attacker to modify the incoming parameters to give the HQL a different meaning. This may cause invalid data to be returned from the HQL query or, worse still, data held in the database to be removed or changed! To illustrate the problem, consider the code in Listing 14-1.

Listing 14-1. An Action Vulnerable to HQL Injection

def search = {
    Album.findAll("from Album as a where a.title='"+ params.title +"'")
}

With the code in Listing 14-1, an attacker could pass a value as the title parameter that could compromise the query and lead to a DoS attack. For example, say the attacker decided to send a request parameter with the following value:

' or a.title not null

This would result in the following HQL query:

from Album as a where a.title='' or a.title not null

The result is that instead of returning only a few records, the query could return thousands or millions of records, causing a potential OutOfMemoryError. Worse still, if the attacker initiates 10,000 requests using the same parameters, you could get threads blocking while these long-running queries execute. With no threads left in the pool, your server will become unresponsive, and the hacker will have successfully completed a DoS attack.

Of course, this phenomenon is not specific to Grails; any application that builds HQL or SQL up dynamically comes up against it. So, how do you prevent such an attack? The secret is never, ever to build up queries from String values, as you saw in Listing 14-1. Instead, use either named or ordinal parameters for the query or, even better, criteria queries. Listing 14-2 shows four possible alternatives to the query from Listing 14-1.

Listing 14-2. Alternatives That Avoid HQL Injection

// using ordinal parameters
Album.findAll("from Album as a where a.title = ?", [params.title])

// using named parameters
Album.findAll("from Album as a where a.title = :title", [title:params.title])

// using criteria
Album.withCriteria {
    eq('title', params.title)
}

// using a dynamic finder
Album.findAllByTitle(params.title)

In all the examples from Listing 14-2, Hibernate will automatically deal with escaping the values passed into the query, making it impossible to execute an HQL injection attack. In the next section, we'll show another potential avenue for attack that is specific to Groovy and Grails— Groovy injection.

Groovy Injection

HQL injection vulnerabilities are dangerous for sure, but the unguarded parsing of Groovy scripts from user input could be even more harmful. Called Groovy injection, this involves accepting input from a user that is then executed as a Groovy script. Listing 14-3 shows an example of this technique.

Listing 14-3. Groovy Injection

def execute = {
    new GroovyShell().evaluate(params.script)
}

Writing code like that shown in Listing 14-3 is, to be blunt, not the smartest thing to do. Bringing the whole container down is a simple matter of sending a parameter with the following value:

System.exit(1)

Or worse, the user could send code that modifies key system files, corrupting the operating system. The GroovyShell class places no restrictions on what code the user is able to run. Generally, as is the case with other dynamic languages such as Ruby and JavaScript, it is not advisable to dynamically evaluate user input in this manner. If you really must have this functionality, then you need to make sure the GroovyShell instance is set up with the appropriate Java security permissions. The Groovy website has good documentation on how to achieve this at http://groovy.codehaus.org/Security.

Cross-Site Scripting (XSS)

XSS attacks are probably the most well known but least understood security exploit. The technique involves injecting JavaScript written by the attacker into the page. An attacker able to control the JavaScript on your site is an incredibly dangerous scenario. She could do all manner of things, from stealing a user's cookie to changing a login form so that it sends requests to another server that captures usernames and passwords.

XSS attacks are amazingly common; the site xssed.com even keeps an up-to-date list of the latest known vulnerabilities in major public sites. You'll notice many prominent industry names there; as you can see, even some of the most well-known companies in the software industry make mistakes. The main reason XSS attacks are so common is that they are very hard to test for. Automated testing in most cases is insufficient to trace every potential XSS problem. In fact, the current implementation of the gTunes application already has an XSS vulnerability that we left in there on purpose (honest!). To reproduce it, try the following:

  1. Click the "Signup now" link to load the register form.
  2. Enter a valid login, password, email, and last name.
  3. For the "First Name" field, enter the text <script type="text/javascript">alert ('hello')</script>.
  4. Click the "Register" button.

Figure 14-1 shows the form populated with the data from these steps.

image

Figure 14-1. Entering malicious data into the registration form

When you click the "Register" button, you'll see an alert box pop up with the message "hello." The JavaScript you entered into the "First Name" field has been executed! The gTunes application is currently vulnerable to an XSS attack. Figure 14-2 shows an example of the message box appearing in Firefox.

image

Figure 14-2. An XSS vulnerability in action

But why? The reason for the vulnerability lies in the grails-app/views/user/_welcomeMessage.gsp template. If you look at the code for the template, it has the following snippet of HTML:

Welcome back <span id="userFirstName">${session?.user?.firstName}!</span><br><br>

Using the GSP expression syntax ${..} on the first name simply dumps out the value; there is no HTML escaping happening here. So, what is the solution? A robust and future-proof solution would be to make all ${..} expressions HTML escaped by default using the grails.views.default.codec setting in grails-app/conf/Config.groovy:

grails.views.default.codec="html"

By setting the default codec Grails uses to encode data in GSP views to HTML, you can ensure all GSP expressions are HTML escaped by default. The downside of this approach is that if you're using GSPs to produce any format other than HTML, such as JSON or raw text, then this may be problematic since the setting is global. An alternative is to use the defaultCodec page directive to enable HTML escaping on a page-by-page basis:

<%@ defaultCodec="html" %>

By inserting the previous line of code at the top of a GSP, you can enable escaping all expressions for only the current page. Finally, you can also use the encodeAsHTML() method provided by Grails to explicitly encode the data, as shown in Listing 14-4.

Listing 14-4. Using encodeAsHTML to HTML Escape a Value

    Welcome back
<span id="userFirstName">${session?.user?.firstName?.encodeAsHTML()}!</span><br><br>

Another important thing to note is that Grails' built-in form tags, such as <g:textField>, automatically use the encodeAsHTML() method for you. So, you need to be concerned only when the data is being used outside of Grails' built-in tags.

XSS and URL Escaping

In the previous section, you saw how a user can launch an XSS exploit if you don't correctly encode data as HTML by calling the encodeAsHTML() method. However, when creating URLs programmatically from user input, it is equally important to URL encode the data used to make up a link. If you're using Grails' built-in <g:link> tag and all the other built-in tags that use URLs, then you don't have to worry. Grails will ensure all the data is appropriately URL encoded.

However, if you decide to bypass the built-in tags and do your own link creation, maybe through a tag library, then it is critical you URL escape the programmatically created links. Listing 14-5 shows an example of a potentially vulnerable link.

Simply by fiddling with the title parameter in a GET request an attacker could perform an XSS attack. To avoid this problem, you can call the encodeAsURL() method on any data to be included in the URL. Listing 14-6 shows an example of this.

Listing 14-6. Escaping URLs

<a href="/gTunes/albums?title=${params.title?.encodeAsURL()}">Show Album</a>

You'll be learning more about the encodeAsHTML() and encodeAsURL() methods in the section "Using Dynamic Codecs." For now, let's stay on the topic of vulnerabilities with a further look into DoS attacks.

Denial of Service (DoS)

You've already seen how HQL injection can be used to cause a DoS attack and bring your system down. However, there are other ways you can be vulnerable to a DoS attack even if you avoid using String concatenation to build queries. One of the most common ways is through pagination. As you'll recall, GORM methods like list and the dynamic finders accept parameters such as offset and max that allow you to paginate through the records available in the database. Listing 14-7 presents an example of a simple list action that does this.

Listing 14-7. Listing All Albums

def list = {
    if(!params.max) params.max = 10
    [albumList: Album.list(params)]
}

As innocent as it may seem, the code in Listing 14-7 is vulnerable to a DoS attack. The reason is that the code doesn't set the maximum value of the max argument. An attacker could pass a max value of 1000000, and you could end up with a million records loading and the same OutOfMemoryError and thread blocking issues we mentioned earlier. Ouch!

A better solution is to ensure that you constrain the value of the max parameter passed to a query to not exceed a specific value. Listing 14-8 shows an example implementation that ensures the max parameter can only ever reach 100.

Listing 14-8. Constraining the Maximum Value for Pagination

def list = {
    params.max = Math.min( params.max?.toInteger() ?: 0, 100)
    [albumList: Album.list(params)]
}

As you can see from the code in Listing 14-8, you can use the Math.min method to get a safe maximum value to use when paginating data. We're not done with potential vulnerabilities just yet, though. In the next section, you'll look at one that affects data binding.

Batch Data Binding Vulnerability

Many web frameworks, including Grails, allow you to bind the data of incoming request parameters to objects. In the case of Grails, these are typically domain instances. Data binding was covered in depth in Chapter 4, but just as a reminder, with Grails it can be done with the following constructor:

def album = new Album(params)

or alternatively using the properties property of an existing domain instance:

def album = Album.get(params.id)
album.properties = params

In many scenarios, this is not a problem, because a trusted source may be performing the update. However, in some cases, using this technique can be undesirable. Consider, for example, a scenario where you used a simple flag on a User domain class to signify whether the User is an administrator:

class User {
    ...
    boolean administrator
}

Administrators have far-reaching powers over the system that only a select few are allowed to have. To set the scene further, say you had a profile page where a user can change her password, phone number, and various personal details. Listing 14-9 shows the server-side code to update the User instance.

Listing 14-9. Vulnerable Controller Action

def update = {
    def user = User.get(params.id)
    user.properties = params
    if(user.save()) {
        redirect(action:"profile", id:user.id)
    }
    ...
}

The form that sends the request to the update action in Listing 14-9 has fields that only the User is allowed to edit. However, a particularly malicious individual could spoof a request so that it sent a parameter called administrator with a value of true. The result would be the User gaining newfound powers and, potentially, compromising your system.

In this scenario, you should make sure you are explicit about what properties can be updated. Listing 14-10 shows a corrected version of the code in Listing 14-9 that uses the subscript operator on the properties property to specify which properties are subject to data binding.

Listing 14-10. Correcting the Data Binding Vulnerability

def update = {
    def user = User.get(params.id)
    user.properties['firstName', 'lastName', 'phoneNumber','password'] = params
    if(user.save()) {
        redirect(action:"profile", id:user.id)
    }
    ...
}

The key message with all these attacks is to make sure that when you accept input from the user, you are aware of the risks of doing so. Grails provides you with all the tools necessary to avoid attacks but will not magically save you from writing vulnerable code. So far in this chapter, you've seen the use of encodeAsURL() and encodeAsHTML(); in the next section, we'll cover how these methods came about and how you can add your own custom versions.

Using Dynamic Codecs

Throughout the course of the chapter so far, you've seen examples of the encodeAsHTML() and encodeAsURL() methods. These methods didn't magically appear out of nowhere; codec classes that ship with Grails provide them. For example, the encodeAsHTML() method is implemented in Grails as shown in Listing 14-11.

Listing 14-11. An Example Codec Class

import org.springframework.web.util.HtmlUtils
class HTMLCodec {
    static encode( theTarget ) {
        HtmlUtils.htmlEscape(theTarget?.toString())
    }

    static decode( theTarget ) {
        HtmlUtils.htmlUnescape(theTarget?.toString())
    }
}

Essentially, a codec class is one that ends with the convention Codec and includes encode and/or decode methods. Grails will automatically create encodeAsHTML() and decodeHTML() methods that delegate to the HTMLCodec class in Listing 14-11 at runtime. The interesting thing is that you can provide your own custom codecs. For example, say you wanted to provide the ability to encrypt data using the Blowfish encryption algorithm that is part of the Java Cryptography Extension (JCE) provided by Sun at http://java.sun.com/javase/technologies/security/. Thanks to custom codecs, this is pretty easy: all you need to do is create a new codec class in the grails-app/utils directory called BlowfishCodec.groovy and populate it with the code in Listing 14-12.

Listing 14-12. A Blowfish Encryption Codec Class

import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH

import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;

class BlowfishCodec {
    static encode(target) {
         def cipher = getCipher(Cipher.ENCRYPT_MODE)
         return cipher.doFinal(target.bytes).encodeBase64()
    }

    static decode(target) {
         def cipher = getCipher(Cipher.DECRYPT_MODE)
         return new String(cipher.doFinal(target.decodeBase64()))
    }

    private static getCipher(mode) {
         def keySpec = new PBEKeySpec(getPassword())
         def cipher = Cipher.getInstance("Blowfish")
         def keyFactory = SecretKeyFactory.getInstance("Blowfish")
         cipher.init(mode, keyFactory.generateSecret(keySpec))
    }
    private static getPassword() { CH.config.secret.key.toCharArray() }
}

The BlowfishCodec implementation shown in Listing 14-12 uses the Java cryptography APIs to construct a Cipher using a password set in grails-app/conf/Config.groovy. The method getPassword() inspects the config object provided by importing the org.codehaus.groovy.grails.commons.ConfigurationHolder class:

private static getPassword() { CH.config.secret.key.toCharArray() }

The getCipher(mode) then uses the getPassword() method to construct an instance of the javax.crypto.spec.PBEKeySpec class that is used for password-based encryption. A javax.crypto.Cipher instance is then obtained using the Blowfish algorithm and initialized using the appropriate mode:

private static getCipher(mode) {
        def keySpec = new PBEKeySpec(getPassword())
        def cipher = Cipher.getInstance("Blowfish")
        def keyFactory = SecretKeyFactory.getInstance("Blowfish")
        cipher.init(mode, keyFactory.generateSecret(keySpec))
}

Finally, the encode and decode closures then use the cipher to encrypt and decrypt the necessary bytes. Notice how this codec is actually using the Base64Codec built into Grails to return the byte[] as a Base-64 encoded String. Now to encrypt data, you can simply call the encodeAsBlowfish() method:

def encrypted = "This is some secret info".encodeAsBlowfish()

And to perform the associated decryption, you can call the decodeBlowfish() method:

def unencrypted = encrypted.decodeBlowfish()

We'll leave to your imagination what else might be possible with codec classes. They're certainly a pretty powerful way to provide common encoding and decoding methods across your application and yet another example of the use of conventions in Grails to enhance behavior. In the next section, we'll take a diversion into the topic of authentication and authorization, including coverage of the available security plugins for Grails.

Authentication and Authorization

Application-layer security, which consists of authenticating users at login and authorizing authenticated users to perform certain functions, is used in most nontrivial applications. In Chapter 4, you saw how to roll your own authentication mechanism with the UserController class, a trivial implementation that simply checks that a user exists in the database. Until now, however, we have not explained how authorization works through roles and permissions.

As simple as it is to implement your own login mechanism, as your application grows you'll feel the need for more complex security rules. You could use roles to distinguish access to parts of the system—for example, is the user an administrator or a regular user? You may also want fine-grained permission access to individual resources. Typically, but not always, a role consists of multiple permissions.

Rolling your own solution for all of these, potentially complex, security scenarios is rather wasteful given the abundance of security frameworks available for Grails. Currently, three widely used plugins offer security features to Grails:

In the next section, we'll cover filters, a feature of Grails that underpins all of these frameworks. After that, we'll dive headfirst into integrating the JSecurity plugin into the gTunes application.

Grails Filters

Security is one of those problems that Aspect-Oriented Programming (AOP) advocates often point to as a prime example of a crosscutting concern. In other words, security rules often apply to multiple URIs, classes, and even methods across an application. Getting your security logic mixed in with your business logic is definitely undesirable. Typically, you need to authorize a user to execute certain methods, which can result in security logic being mixed with application logic.

In Grails, you can use filters to execute code before and after a controller action. To add a set of filters in Grails, you need to create a class that ends with the convention Filters in your application. A typical place to do this is in the grails-app/conf directory. For example, Listing 14-13 shows a LoggingFilters implementation that logs request information before and after each request.

Listing 14-13. An Example Filters Class

class LoggingFilters {
    static filters = {
         all(controller:"*", action:"*") {
             before = {
                 log.debug "Parameters: ${params.inspect()}"
            }
            after = { model ->
                log.debug "Model: ${model?.inspect()}"
            }
        }
    }
}

As you can see from Listing 14-13, within the LoggingFilters definition you define a single static property called filters that is assigned a block of code. Then, within the body of this block of code, you can define one or more filters. The example in Listing 14-13 defines a single filter called all that applies to all actions within all controllers:

all(controller:"*", action:"*") {

Notice the usage of the wildcard (*) character to signify that this filter applies to all actions and controllers. Instead of a wildcard, you can also define a specific controller and/or action:

secure(controller:"admin", action:"*") {

Alternatively, if you prefer URI-based filters, then you can use the uri argument:

secure(uri:"/admin/**") {

In addition, the values you pass to any of the arguments, such as controller and action, are actually just regular expressions. Hence, if you need to apply a filter to multiple controllers, you can use regex:

secure(controller:"(admin|secure)", action:"*") {

The last argument of each filter definition is a block of code that you can use to define a before filter:

before = {
        log.debug "Parameters: ${params.inspect()}"
}

A before filter can also return false, which signifies that the intercepted action should not be executed, something that is critical for security plugins. As well as the before filter, there is also an after filter:

after = { model ->
        log.debug "Model: ${model?.inspect()}"
}

As you can see, the after filter is a little special because it gets passed the model that the view will use to render. Note also that the after filter gets executed before view rendering. If you want to execute a filter after the view has rendered, you can use the afterView filter, as shown in Listing 14-14.

Listing 14-14. Using the afterView Filter

after = {
    request.currentTime = System.currentTimeMillis()
}
afterView = {
    log.debug "View took ${System.currentTimeMillis()-request.currentTime}ms"
}

Listing 14-14 shows an example that profiles how long it takes for view rendering to complete. As you can see, filters provide an excellent mechanism for implementing crosscutting concerns, because they can be applied across multiple controllers and/or actions. For example, Listing 14-15 shows a very trivial security filter that checks whether a user is logged in.

Listing 14-15. A Security Filter

class SecurityFilters {
   def filters = {
       loginCheck(controller:'*', action:'*') {
           before = {
              if(!session.user && actionName != 'login') {
                  redirect(action:'login')
                  return false
               }
            }
} } }

The security plugins available for Grails make extensive usage of its filters mechanism. In the next section, we'll talk about the JSecurity plugin as an example.

The JSecurity Plugin

The JSecurity plugin builds on the excellent JSecurity library (http://www.jsecurity.org/) to provide authentication and authorization to a Grails application. The JSecurity plugin works by combining a set of one or more security filters with a security realm. The realm is the bridge between JSecurity and Grails, and it provides methods that you can implement to facilitate authentication and authorization. To get started with JSecurity, you have to install the plugin by running the install-plugin command, as shown in Listing 14-16.

Listing 14-16. Running the install-plugin command


$ grails install-plugin jsecurity
...
Plugin jsecurity-0.2.1 installed
Plug-in provides the following new scripts:
------------------------------------------
grails create-auth-controller
grails create-db-realm
grails create-ldap-realm
grails quick-start

As you can see from the output in Listing 14-16, the JSecurity plugin provides various additional commands that help you integrate it with Grails, the details of which are listed here:

  • create-auth-controller: This creates a controller that implements logging in and logging out using JSecurity APIs.
  • create-db-realm: If you don't already have a domain model that represents users and roles, this command will create one that uses GORM to store user information to the database.
  • create-ldap-realm: This creates a realm that authenticates users against a configured LDAP server.
  • quick-start: This combines the create-db-realm and create-auth-controller commands to set up JSecurity in a single command.

Authentication Realms

Both the create-db-realm and create-ldap-realm classes set up a realm class that deals with rights management. In other words, the realms dictate who can access your system, as well as what roles and permissions they have once the user has authenticated. A realm class is a class that lives in the grails-app/realms directory and that ends with the convention Realm. Although there are no further requirements, for realm classes to be useful they should implement some or all of the methods shown in Listing 14-17.

Listing 14-17. Methods of a Realm

def authenticate(authToken)
def hasRole(principal, roleName)
def isPermitted(principal, permission)

The authenticate method is called when a user tries to sign in to your application. The argument passed to the authenticate method is an instance of the org.jsecurity.authc.AuthenticationToken interface. The default implementation assumed by JSecurity is org.jsecurity.authc.UsernamePasswordToken, which uses username/password-based authentication. However, you can change the authentication token mechanism used by setting the authTokenClass static property of the realm class:

static authTokenClass = org.jsecurity.authc.UsernamePasswordToken

The hasRole and isPermitted methods both accept a principal, which is the unique value used to identify the user returned by the authenticate method. We'll be returning to roles and permissions in a moment; first we'll address the notion of subjects and principals.

Subjects and Principals

A subject, in JSecurity terms, is a person or entity who is currently accessing your application. Modeled by the class org.jsecurity.subject.Subject, a subject does not have to be logged in and can be in one of three states:

  • Unknown: The application doesn't know who the user is.
  • Remembered: The application remembers the user from a previous session.
  • Authenticated: The user has successfully logged in, by entering their credentials, and the application knows who they are.

You can obtain the current Subject instance at any time using the org.jsecurity.SecurityUtils class, which has a static getSubject() method, as shown in Listing 14-18.

Listing 14-18. Using SecurityUtils to Obtain the Subject

def subject = org.jsecurity.SecurityUtils.getSubject() println "User ${subject.principal} is authenticated? ${subject.authenticated}"

As shown in Listing 14-18, the Subject has a getPrincipal() method that returns the principal. As mentioned, the principal is the unique identity of a user such as a login name, email address, or Social Security number.

Roles and Permissions

In a role-based system, a role represents a function or set of responsibilities a user may have. If you were developing a content management system (CMS), you might have roles such as Administrators, Editors, and Users. Although an Administrator would have overall access to all parts of the system, lesser beings such as simple Users would be able to access only a limited set of functions. Permissions, on the other hand, represent a much finer-grained level of control. In fact, a role typically consists of a collection of permissions. For example, considering the gTunes application permissions might include asking the following questions:

  • Can the user create blog entries?
  • Can the user play music from Album X?
  • Can the user upload new music to the system?

In JSecurity, a permission is modeled by the org.jsecurity.authz.Permission interface shown in Listing 14-19.

Listing 14-19. The org.jsecurity.authz.Permission Interface

package org.jsecurity.authz

interface Permission {
    boolean implies(Permission p)
}

As you can see from Listing 14-19, the Permission interface defines a single method called implies(Permission). The idea here is that typically a user gets assigned a collection of permissions. If one of the permissions assigned to that user allows access to the Permission passed as an argument to the implies(Permission) method, then true will be returned. As an example, to create an Administrator user, you could use the org.jsecurity.authz.permission.AllPermission permission, which implies access to all other permissions by always returning true from the implies(Permission) method.

The validation of roles and permissions can and should occur at multiple levels, from the view to the controller layer. In the next section, we'll show how to apply these ideas to the gTunes application by using JSecurity to secure access to various parts of the application.

JSecurity in Action

As mentioned previously, JSecurity comes with built-in commands that allow you to generate a domain model and authentication realm. However, since you already have a domain model and because it will help you understand the intricacies of JSecurity, you're going to build a custom JSecurity realm class.

Implementing Authentication with JSecurity

To get started, you need to create new realm class in the grails-app/realms directory. In Figure 14-3 you can see we've created a new realm class called AuthRealm in the com.g2one.gtunes package.

image

Figure 14-3. The AuthRealm class

The next step is to implement the authenticate method for the AuthRealm class, as shown in Listing 14-20.

Listing 14-20. The authenticate Method of the AuthRealm

class AuthRealm {
    static authTokenClass = org.jsecurity.authc.UsernamePasswordToken

    CredentialsMatcher credentialMatcher

    def authenticate(authToken) {
        ...
    }
}

As you can see, the code uses the org.jsecurity.authc.UsernamePasswordToken class for the authToken instance that will be passed to the authenticate method. Since there is already an existing com.g2one.gtunes.User domain class that contains login and password properties, the UsernamePasswordToken class is a logical choice here.

Another thing you'll note from the code in Listing 14-20 is the credentialMatcher property. This property is injected by Spring at runtime. The default implementation used is the org.jsecurity.authc.credential.Sha1CredentialsMatcher, which expects that credentials that are stored are SHA hashed, a pretty common practice.

You can, however, replace the CredentialsMatcher implementation simply by defining a new Spring bean in the grails-app/conf/spring/resources.groovy file called credentialMatcher. For example, the following code defines a CredentialsMatcher that uses MD5 hashing instead:

credentialMatcher(org.jsecurity.authc.credential.Md5CredentialsMatcher)

Returning to the authenticate method, the UsernamePasswordToken instance defines a username property that you can use to obtain the username of the user, as shown in Listing 14-21.

Listing 14-21. Obtaining the Username of a User

def username = authToken.username

  // Null username is invalid
  if (username == null) {
      throw new AccountException('Null usernames are not allowed by this realm.')
  }

As you can see from Listing 14-21, if the username is null, an org.jsecurity.authc.AccountException is thrown. JSecurity provides a number of built-in exception types within the org.jsecurity.authc package, the examples of which are listed here:

  • AccountException: Thrown because of a problem with the account under which an authentication attempt is being executed
  • ConcurrentAccessException: Thrown when an authentication attempt has been received for an account that has already been authenticated
  • DisabledAccountException: Thrown when attempting to authenticate and the corresponding account has been disabled for some reason
  • ExcessiveAttemptsException: Thrown when a system is configured to allow only a certain number of authentication attempts over a period of time and the current session has failed to authenticate successfully within that number
  • ExpiredCredentialsException: Thrown during the authentication process when the system determines the submitted credentials has expired and will not allow a login
  • IncorrectCredentialsException: Thrown when attempting to authenticate with credential(s) that do not match the actual credentials associated with the account principal
  • UnknownAccountException: Thrown when attempting to authenticate with a principal that doesn't exist in the system (for example, by specifying a username that doesn't relate to a user account)

It is up to your implementation of the authenticate method to throw the appropriate exceptions for each reason, but as you can see, JSecurity provides exception types for most common cases. For example, Listing 14-22 shows the next step needed to implement authenticate appropriately by throwing an UnknownAccountException if the User instance is not found for the specified username.

Listing 14-22. Throwing an UnknownAccountException If a User Is Not Found

def user = User.findByLogin(username)

if (!user) {
    throw new UnknownAccountException("No account found for user $username")
}

As you can see from Listing 14-22, it is at this point that you are able to connect JSecurity with your existing domain model. However, if a User instance is found, you want to make sure that said user's password is correct. Listing 14-23 shows an example of how to achieve this.

Listing 14-23. Validating User Credentials

def account = new SimpleAccount(username, user.password, "gTunesRealm")
if (!credentialMatcher.doCredentialsMatch(authToken, account)) {
    throw new IncorrectCredentialsException("Invalid password for $username")
}

Notice how in Listing 14-23 you need to construct an instance of the org.jsecurity.authc.SimpleAccount class, which takes the principal (in this case the username), the credentials, and the name of the realm. Once constructed, you can then use the credentialMatcher instance's doCredentialsMatch method to validate the user's authentication token. If the token is not valid, an IncorrectCredentialsException is thrown. If all is well, the final thing to do is to return the user's principal:

return username

And with that, you've completed the implementation of the authenticate method. Listing 14-24 shows the full code listing from the authenticate method.

Listing 14-24. The authenticate Method

def authenticate(authToken) {
    def username = authToken.username

    // Null username is invalid
    if (username == null) {
        throw new AccountException('Null usernames are not allowed by this realm.')
    }

    // Get the user with the given username. If the user is not
    // found, then they don't have an account and we throw an
    // exception.
    def user = User.findByLogin(username)
    if (!user) {
        throw new UnknownAccountException("No account found for $username")
    }
    // Now check the user's password against the hashed value stored
    // in the database.
    def account = new SimpleAccount(username, user.password, "gTunesRealm")
    if (!credentialMatcher.doCredentialsMatch(authToken, account)) {
        throw new IncorrectCredentialsException("Invalid password for $username")
    }
    return username
}

Now all that is left to do is implement a controller that can take advantage of the realm. A simple way to do this is to run the create-auth-controller command, which will generate a controller that uses JSecurity to authenticate. However, since the gTunes application already has a UserController, you're going to modify that instead and at the same time get a chance to explore JSecurity's APIs.

To authenticate with JSecurity, you need a reference to the org.jsecurity.mgt.SecurityManager instance, the interface for which is shown in Listing 14-25.

Listing 14-25. The org.jsecurity.mgt.SecurityManager Interface

interface SecurityManager {
    Subject getSubject()
    Subject login(AuthenticationToken authenticationToken)
    void logout(PrincipalCollection subjectIdentifier)
}

To obtain a reference to the SecurityManager, you need to use dependency injection via Spring using a bean called jsecSecurityManager. If you recall, the current UserController uses a command object, called LoginCommand, to handle login processing. Command objects can participate in dependency injection using Spring by simply declaring a property within the command class that matches the bean name:

def jsecSecurityManager

Using the SecurityManager instance's login(AuthenticationToken) method, you can then authenticate users based on the parameters bound to the LoginCommand. Listing 14-26 shows the updated LoginCommand class that uses the jsecSecurityManager for authentication.

Listing 14-26. A LoginCommand Definition That Uses JSecurity for Authentication

class LoginCommand {
    String login
    String password

    def jsecSecurityManager

    boolean authenticate() {
          def authToken = new UsernamePasswordToken(login, password)
          try{
              this.jsecSecurityManager.login(authToken)
              return true
          }
          catch (AuthenticationException ex){
             return false
          }
     }
     static constraints = {
         login blank:false, validator:{ val, cmd ->
             if(!cmd.authenticate())
                 return "user.invalid.login"
         }
         password blank:false
     }
}

You can see the guts of the logic in the authenticate() method of the LoginCommand in Listing 14-26. Initially, a new UsernamePasswordToken instance is constructed and passed to the jsecSecurityManager bean's login(AuthenticationToken) method. If the login(AuthenticationToken) method completes without an exception being thrown, the authenticate() method returns true, signaling a successful login. Otherwise, if an exception is thrown, false is returned. The other major change to the LoginCommand is that the login constraint now calls the command's authenticate() method and returns a code called user.invalid.login if authentication failed.

You could write code to handle specific AuthenticationException instances, such as UnknownAccountException, and return different error codes based on each exception. Nevertheless, the code serves to demonstrate how to use a command object to authenticate via JSecurity. As for the login action of the UserController, it doesn't need any changes since the command object itself encapsulates the logic of logging in.

However, what does need a change is the register action. This currently stores passwords in plain-text form, but JSecurity is expecting an SHA1 hash of the password by default. Listing 14-27 shows the changes made to the register action to provide an SHA1 hash of the password.

Listing 14-27. Hashing a Password with SHA1

import org.jsecurity.crypto.hash.Sha1Hash

class UserController {
    ...
    def register = {
        if(request.method == 'POST') {
           ...
              if(u.validate()) {
                 u.password = new Sha1Hash(u.password).toHex()
                  u.save()
                  ...
                }
           ...
        }
    }
    ...
}
Securing Your Site with JSecurity Filters

Adding the ability to authenticate users wouldn't be of much use if you didn't have the ability to secure areas of a Grails application that require authentication. The JSecurity plugin for Grails uses the filters mechanism discussed earlier in the chapter in order to authenticate users. To begin with, you need to define a filters class. For example, you could create an AuthFilters class, as shown in Figure 14-4.

image

Figure 14-4. The grails-app/conf/com/g2one/tunes/AuthFilters.groovy file

With the filters class in place, you need to define a static filters property that is assigned a block of code, the body of which will contain the filter definitions. Listing 14-28 shows the AuthFilters class with the filters static property in place.

Listing 14-28. The AuthFilters Class

package com.g2one.gtunes

class AuthFilters {
    static filters = {
        ...
    }
}

By default JSecurity allows all requests through without authentication, so you need to define which controllers, actions, and/or URIs require authentication. You can do so by calling the accessControl method within the definition of a before filter. For example, users are required to log in to purchase music, so you need to secure the buy action of the StoreController, as shown in Listing 14-29.

Listing 14-29. Securing an Action

static filters = {
    purchasing(controller:"store", action:"buy") {
        before = {
            accessControl()
       }
   }
   ...
}

To deal with authentication failures that arise from a filters class, you need to implement the onNotAuthenticated(Subject, controller) method. Listing 14-30 shows the implementation used by the gTunes application.

Listing 14-30. Implementing the onNotAuthenticated Method

def onNotAuthenticated(subject, d) {
    if (d.request.xhr) {
        d.render(template:"/user/loginForm", model:[message:"user.not.logged.in"])
    }
    else {
        // Redirect to login page.
        d.flash.message = "user.not.logged.in"
        if(d.actionName == 'buy') {
           d.redirect(controller:"album", action:"display", id:d.params.id)
        }
        else {
            d.redirect(controller:"store", action:"shop")
        }
    }
}

The logic here is a bit more convoluted because it deals both with Ajax requests, by checking the request.xhr property, and with regular requests. Additionally, if the actionName is the buy action, then there is some logic in there to take the user back to the Album they were trying to buy. You could, of course, redirect to the original URI using the forwardURI property, but since there isn't a use case yet for this in the gTunes application, the implementation in Listing 14-30 will do fine.

If you need to secure access for a specific role, you can pass a block to the accessControl method that contains a call to the method role(name) that defines the role. For example, the blogging feature you added via a plugin in Chapter 13 can be secured so that only administrators can access the feature using the following syntax:

blogEditing(controller:"blog", action:"(create|save)") {
    before = {
        accessControl {
            role('ADMINISTRATOR')
        }
    }
}

Notice how the previous filter applies to both the create and save actions of the BlogController. Currently the role(name) method is being called to allow access to a role named ADMINISTRATOR, but if you wanted to allow access to more than one role, you can use the | operator:

accessControl {
       role('ADMINISTRATOR') | role('EDITOR')
    }

Here users who are in either the ADMINISTRATOR role or the EDITOR role can create blog posts. Of course, you have not yet implemented the hasRole method in the AuthRealm class, so no one at this point has access to these areas of the gTunes site. In the next section, you'll rectify that by implementing role-based security.

Implementing Role-Based Security

Currently, the gTunes domain model does not define the concept of a role. To correct this, you need to create a new domain class called Role using the create-domain-class command:

$ grails create-domain-class com.g2one.gtunes.Role

Once complete, you'll end up with a new domain class in the grails-app/domain/com/g2one/gtunes directory called Role.groovy. A good way to implement a role is using a type-safe enum. Listing 14-31 shows the code for the Role class that uses an enum called RoleName containing the different role names.

Listing 14-31. The Role Domain Class

package com.g2one.gtunes

class Role implements Serializable {
    RoleName name
}
enum RoleName {
    USER, EDITOR, ADMINISTRATOR
}

The next step is to update the com.g2one.gtunes.User domain class to associate a user with a set of roles. Listing 14-32 shows the changes to the User domain class with the addition of a roles association.

Listing 14-32. Adding a roles Association to the User Domain Class

class User implements Serializable{
    ...
    static hasMany = [ purchasedAlbums:Album,
                       purchasedSongs:Song,
                       roles:Role ]
}

In addition, users who register with the gTunes site should be assigned the default role of USER. To achieve this, you can update the register action, as shown in Listing 14-33, to call the addToRoles method, passing the RoleName as an argument.

Listing 14-33. Updating the register Action to Include Roles

     def register = {
         ...
         if(u.validate()) {
             u.password = new Sha1Hash(u.password).toHex()
             u.addToRoles(name:RoleName.USER)
             u.save()
             ...
        }
        ...
    }

Now it is time to consider the AuthRealm, which currently does not implement the hasRole method. Listing 14-34 shows a simple implementation that inspects the roles association of the User domain class.

Listing 14-34. Using Criteria to Query User Roles

def hasRole(principal, roleName) {
        def user = User.findByLogin(principal, [fetch:[roles:'join']])
        return user.roles.any { it.name == RoleName.valueOf(roleName) }
}

Notice how in Listing 14-34 you use the principal argument passed to the hasRole method to look up the User based on their unique login name. If the User doesn't have a Role within its roles association that matches the specified RoleName, then the hasRole method will return false. With this in place, the controller actions secured with the accessControl method in the AuthFilters class will not allow users to access those controller actions unless the User has the specified role.

Securing the View

In addition to securing via the AuthFilters class, you can also secure the view layer using a variety of tags provided by the JSecurity plugin. You may recall that previously the gTunes application checked whether a User existed within the session object to control the state of the view. As a refresher, the code in question can be found within the grails-app/views/layouts/main.gsp layout, as shown in Listing 14-35.

Listing 14-35. The Old Way of Securing the View

      <div id="loginBox" class="loginBox">
          <g:if test="${session?.user}">
              <g:render template="/user/welcomeMessage"></g:render>
          </g:if>
          <g:else>
              <g:render template="/user/loginForm"></g:render>
          </g:else>
      </div>

In the example in Listing 14-35, the loginBox <div> displays different content depending on whether the user is logged in. Using JSecurity, there are two equivalents tags to achieve this: <jsec:isLoggedIn> and <jsec:isNotLoggedIn>. Listing 14-36 shows the code updated to use the JSecurity model.

Listing 14-36. Checking Whether a User Is Authenticated with JSecurity

<div id="loginBox" class="loginBox">
    <jsec:isLoggedIn>
        <g:render template="/user/welcomeMessage"></g:render>
    </jsec:isLoggedIn>
    <jsec:isNotLoggedIn>
        <g:render template="/user/loginForm"></g:render>
    </jsec:isNotLoggedIn>
</div>

Tip If you're not keen on the naming of the <jsec:isLoggedIn> and <jsec:isNotLoggedIn> tags, you may want to use <jsec:authenticated> and <jsec:notAuthenticated>, which mean the same thing.


Additionally, the grails-app/views/user/_welcomeMessage.gsp template was particularly reliant on the existence of a user object within the session with the following snippet of code:

Welcome back <span id="userFirstName">${session?.user?.firstName}!</span><br><br>

If you merely want to output the currently logged in user's login name, then you could use the <jsec:principal /> tag instead:

Welcome back <span id="userFirstName"><jsec:principal />!</span><br><br>

However, in this case, you really want to print the user's first name. To facilitate this, you may want to add another filter that makes the actual User instance available to the request, as shown in Listing 14-37.

Listing 14-37. Making the User Object Available in the Request

userInRequest(controller:"*", action:"*") {
   before = {
        def subject = SecurityUtils.getSubject()
        if(subject && subject?.principal) {
          request.user = User.findByLogin(subject.principal)
        }
    }
}

As you can see from Listing 14-37, you can use the SecurityUtils class to get a reference to the Subject and then, using the principal, look up the User instance and place it within the request. As you saw in Chapter 10, with a good caching policy in place, you can avoid hitting the database in most cases. Now within the _welcomeMessage.gsp template you can use the user object held in the request to output the User instance's firstName property:

Welcome back <span id="userFirstName">${request?.user?.firstName}!</span><br><br>

Returning to roles, as well as the <jsec:isLoggedIn> tag, you can also check whether a User has a particular Role within the view using the <jsec:hasRole> or <jsec:hasAllRoles> tag. Listing 14-38 shows an example of using the <jsec:hasRole> tag.

Listing 14-38. Restricting Access Based on Role

<jsec:hasRole name="ADMINISTRATOR">
    <g:link controller="blog" action="create">Create Blog Entry</g:link>
</jsec:hasRole>
<jsec:hasRole in="['ADMINISTRATOR', 'USER']">
    <g:link controller="blog" action="list">Show Blog Entries</g:link>
</jsec:hasRole>

Now it is time to try something a little more fun. In the next section, you're going to implement the "My Music" section of the gTunes application that will allow you to play the music you have purchased. Of course, this has to be dealt with in a secure manner, because users should be able to play only the music they have actually purchased. Luckily, JSecurity has great support for implementing permission-based security, which will help solve this problem.

Implementing the "My Music" Section with Permissions

As it stands at the moment, the gTunes application is capable of allowing users to purchase albums. However, there is currently no way to play the music the User has purchased, which is not particularly useful. To fix this problem, you're going to implement the "My Music" section, which will show the currently logged in user's collection of music and allow them to play individual songs.

To do so, first create a link to the "My Music" section by editing the grails-app/views/layouts/main.gsp layout and modifying the navButtons <div>, as shown in Listing 14-39.

Listing 14-39. Adding the "My Music" Link

<div id="navPane">
    <div id="navButtons" style="display:${request.user? 'block' :'none'}">
        <ul>
            <li><g:link controller="user" action="music">My Music</g:link></li>
            <li><g:link controller="store" action="shop">The Store</g:link></a></li>
        </ul>
      </div>
     ...
</div>

The added <g:link> tag links to a new music action of the UserController. The music action is responsible for building up a model representing the user's library of music. Listing 14-40 shows an example implementation.

Listing 14-40. Obtaining Information About the User's Music Collection

def music = {
    def albumList = AlbumPayment.withCriteria {
        projections {
            property "album"
        }
        eq("user", request.user)
     }
     def artistList = albumList.collect { it.artist }.unique()
     return [artists:artistList, albums:albumList ]
}

As you can see, you can obtain information about the user's purchases using the AlbumPayment class. In Listing 14-40, a projection is used to select the album property of each AlbumPayment instance in the criteria query. Projections were discussed in more detail in Chapter 10. With a list of albums and artists in hand, you can then use a view to render this information appropriately. Listing 14-41 shows the grails-app/views/user/music.gsp view that goes through each Artist instance and displays an album art link to each album.

Listing 14-41. The music.gsp View

<g:applyLayout name="libraryLayout">
    <div id="musicLibrary" class="musicLibrary">
        <g:if test="${!artists}">
            You haven't purchased any music just yet.
            Why don't you take a <g:link controller="store"
             action="shop">look at the store</g:link>
             to see what's available.
        </g:if>
        <g:each var="artist" in="${artists}">
            <div id="artist${artist.id}" class="artist">
                <h2>${artist.name}</h2>
                <g:each var="album"
                         in="${albums.findAll { it.artist.name == artist.name}}">
                    <span class="purchasedAlbum">
                        <g:remoteLink update="musicLibrary"
                                                 controller="album"
                                                 action="display"
                                                 id="${album.id}">
                            <music:albumArt artist="${artist.name}"
                                                         album="${album.title}"
                                                         alt="${album.title}"/>
                        </g:remoteLink>
                    </span>

                </g:each>
            </div>
        </g:each>
    </div>
</g:applyLayout>

Notice that in Listing 14-41 the music.gsp view is using a new layout called libraryLayout. This makes sense, since typically you don't want the same information about the store within your music library. You can see the grails-app/views/layouts/libraryLayout.gsp file in Listing 14-42.

Listing 14-42. The libraryLayout.gsp View

<html>
    <head>
        <meta http-equiv="Content-type" content="text/html; charset=utf-8">
        <meta name="layout" content="main">
        <title>gTunes Store</title>
    </head>
    <body id="body">
        <h1>Your Music</h1>
        <div id="musicPanel">
            <g:layoutBody />
       </div>
   </body>

</html>

Currently, the libraryLayout.gsp view in Listing 14-42 is pretty simple, but you could easily augment it with additional functionality such as recommendations based on the user's current collection of music, and so on. All in all, after applying a few CSS tweaks, the new "My Music" section looks like Figure 14-5.

image

Figure 14-5. The "My Music" section of the gTunes application

Next, since this section of the gTunes application relates specifically to personal data of individual users, you need to ensure that said users are logged in before accessing the music action of the SongController. To do this, add a new filter definition in the AuthFilters class that secures the music action, as shown in Listing 14-43.

Listing 14-43. Securing the music Action

library(controller:"user", action:"music") {
    before = {
        accessControl()
    }
}

If you refer to the code in Listing 14-41, you'll notice that the <g:remoteLink> tag used links to the display action of the AlbumController. Currently, this will just render an Album exactly as shown in the store. Figure 14-6 shows an example of the current behavior.

image

Figure 14-6. The current presentation of Album information

As you can see, even if you purchased the Album, the gTunes application is still showing the price, the "Buy" button, and so on. Somehow you need to give the user permission to access this Album. It is in use cases like this that JSecurity's permissions mechanism comes in handy. To model permissions, you're going to need to create a new com.g2one.gtunes.Permission class using the create-domain-class command:

$ grails create-domain-class com.g2one.gtunes.Permission

The Permission domain class is going to implement the org.jsecurity.authz.Permission interface, providing some default behavior. Listing 14-44 shows the code for the Permission domain class.

Listing 14-44. The Permission Domain Class

package com.g2one.gtunes

class Permission implements org.jsecurity.authz.Permission, Serializable{

    static belongsTo = [user:User]

    boolean implies(org.jsecurity.authz.Permission p) { false }
}

As you can see from Listing 14-44, the Permission domain class is also associated with an individual User using a belongsTo static property. To make this a bidirectional relationship, you can add a hasMany definition on the User side of the association, as shown in Listing 14-45.

Listing 14-45. Updating the User Class with the Permissions Association

class User implements Serializable{
    ...
    static hasMany = [ purchasedAlbums:Album,
                       purchasedSongs:Song,
                       roles:Role,
                       permissions:Permission]
}

Returning to Listing 14-44, the default behavior is to return false from the implies(Permission) method, granting the User no additional permissions. To provide additional behavior, you can subclass the Permission domain class. As an example, currently you need to restrict the access users have to Album instances they have purchased. To do this, you can implement an AlbumPermission by extending the Permission class. Simply run the create-domain-class command again to create the AlbumPermission class:

grails create-domain-class com.g2one.gtunes.AlbumPermission

With that done, you need to extend the com.g2one.gtunes.Permission class and add the necessary behavior to restrict access to individual Album instances. Listing 14-46 shows a sample implementation.

Listing 14-46. The AlbumPermission Class

package com.g2one.gtunes

class AlbumPermission extends Permission {
    Album album

    boolean implies(org.jsecurity.authz.Permission p) {
       if(p instanceof AlbumPermission) {
           if(album.id == p.album?.id) {
               return true
           }
        }
        return false
    }
    String toString() { "Album Permission: ${album}"}
}

As you can see from Listing 14-46, each AlbumPermission is associated with an Album instance. If the Permission supplied to the implies(Permission) method contains the same Album instance, then the User has permission to access the Album and true is returned; otherwise, false is returned.

To finalize permission handling, you need to add code to the purchaseAlbum method of the StoreService you created in Chapter 11 to associate an AlbumPermission with a User when they purchase an Album. Listing 14-47 shows how you can use the addToPermissions method to achieve this in the StoreService class.

Listing 14-47. Assigning an AlbumPermission to a User

class StoreService {

    static transactional = true

    Payment purchaseAlbums(User user, creditCard, List albumPayments) {
        // Once payment taken update user profile
        for(ap in albumPayments) {
            ...
            user.addToPurchasedAlbums(ap.album)
            user.addToPermissions(new AlbumPermission(album:ap.album))
        }
        ...
    }

At this point, you need to consider the AuthRealm, which currently implements the hasRole method, but not the isPermitted method that is necessary for permission handling. Listing 14-48 shows an example implementation that obtains a list of Permission instances for a User and verifies them against the supplied Permission instance.

Listing 14-48. Implementing isPermitted in the AuthRealm Class

def isPermitted(principal, requiredPermission) {
    if(requiredPermission instanceof com.g2one.gtunes.Permission) {
        def permissions = Permission.withCriteria {
            user {
                eq('login', principal)
            }
        }
        return permissions.any { permission ->
               permission.implies(requiredPermission)
        }
    }
    else {
        return true
    }
}

Notice, in Listing 14-48, the usage of the any method on the list of permissions. The any method will return true if any of the expressions within the passed closure evaluate to true. The result is that if any of the Permission instances return true from the implies(Permission) method, then the any method will return true.

Now it is time to return to the grails-app/views/album/_album.gsp template that is currently showing the price and insisting users purchase the Album again, even if they have already purchased it. To resolve this situation, you can create a new permission variable using the AlbumPermission class with the <g:set> tag:

<g:set var="permission"
           value="${new com.g2one.gtunes.AlbumPermission(album:album)}" />

Then you can use the <jsec:lacksPermission> tag to display information only if the user doesn't have permission to access the Album. For example, the price shouldn't be shown if the user has already purchased the Album, as shown in Listing 14-49.

Listing 14-49. Using the <jsec:lacksPermission> Tag to Restrict Access

<div class="albumInfo">
    Genre: ${album.genre ?: 'Other'}<br>
    Year: ${album.year}<br>
    <jsec:lacksPermission permission="${permission}">
        <strong>Price: $ ${album.price}</strong>
    </jsec:lacksPermission>
</div>

You can also at this point supply a link that allows the user to play an individual Song if they have purchased the Album using a combination of the <jsec:lacksPermission> and <jsec:hasPermission> tags, as shown in Listing 14-50.

Listing 14-50. Using the <jsec:hasPermission> Tag to Allow Access

<g:each in="${album.songs}" var="song">
    <li>
       <jsec:lacksPermission permission="${permission}">
              ${song.title}
      </jsec:lacksPermission>
       <jsec:hasPermission permission="${permission}">
           <g:link controller="song" action="play"
               id="${song.id}">${song.title}</g:link>
       </jsec:hasPermission>
   </li>
</g:each>

As you can see from Listing 14-50, the code links to the play action of the SongController. We'll talk about implementing this action in a moment; for now, the last step in updating the _album.gsp template is to disable the "Buy" button if the user has already purchased the Album, as shown in Listing 14-51.

Listing 14-51. Disabling the "Buy" Button

<div id="buttons" style="float:right;">
    <jsec:hasPermission permission="${permission}">
        <g:link controller="user" action="music">Back to My Music</g:link>
    </jsec:hasPermission>
    <jsec:lacksPermission permission="${permission}">
        <g:link controller="store" action="buy" id="${album.id}">
              <img src="${createLinkTo(dir:'images',file:'buy-button.gif')}"
                        border="0">
          </g:link>
    </jsec:lacksPermission>
</div>

You'll notice from the GSP code in Listing 14-51 that if the User does have access to the Album, then a "Back to My Music" link is displayed instead, allowing the user to navigate easily back to their music. Figure 14-7 shows the updated interface in place with the AlbumPermission class having the desired effect.

image

Figure 14-7. The updated _album.gsp template with permissions working

At this point, you need to consider how to enable users to play music they have purchased. You could leverage various technologies that allow the streaming of media. From Windows Media Player to Flash, each has its own advantages and disadvantages. For the gTunes application, the powers that be have decided on QuickTime (http://www.apple.com/quicktime/) as the preferred technology, since it works well on most mainstream platforms (even on Unix-flavors via WINE; see http://appdb.winehq.org/appview.php?appId=1029) and is simple to use.

To allow embedding of QuickTime audio easily, it would be good to wrap the functionality of QuickTime into a tag library. To do so, run the grails create-tag-lib command as follows:

$ grails create-tag-lib com.g2one.gtunes.Streaming

This will create a new tag library called StreamingTagLib.groovy at the location grails-app/com/g2one/gtunes, as shown in Figure 14-8.

image

Figure 14-8. The StreamingTagLib.groovy file

We chose a generic name on purpose, just in case the requirement to add support for other media players, such as Flash, arises. Nevertheless, Listing 14-52 shows the code that embeds a QuickTime movie using the StreamingTagLib.

Listing 14-52. The StreamingTagLib Implementation

package com.g2one.gtunes

class StreamingTagLib {

    static namespace = "media"

    def player = { attrs, body ->
        def userAgent = request.getHeader('User-Agent')
        def src = attrs.src
        def width = attrs.width ?: 100
        def height = attrs.height ?: 100
        def autoplay = attrs.autoplay ?: false
        out.write """
<OBJECT CLASSID="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
                  WIDTH="${width}"
                  HEIGHT="${height}"
                  CODEBASE="http://www.apple.com/qtactivex/qtplugin.cab">
<PARAM name="SRC" VALUE="${src}">
<PARAM name="AUTOPLAY" VALUE="${autoplay}">
<EMBED SRC="${src}"
                 WIDTH="${width}"
                  HEIGHT="${height}"
                  AUTOPLAY="${autoplay}"
                  CONTROLLER="true"
                  LOOP="false"
                  PLUGINSPAGE="http://www.apple.com/quicktime/download/">
</EMBED>
</OBJECT>"""
    }
}

There are a couple of interesting things to note about the code in Listing 14-52. First, as you can see, the media namespace is used for this tag library, making the name of the tag <media:player>.

Second, notice the usage of Groovy multiline Strings to easily write out a bunch of markup. If you wanted, you could refactor this out to a separate GSP template, but for now this simple solution will do. Now let's take advantage of the <media:player> tag by implementing the play action of the SongController.

Of course, you don't want users who don't have permission to be able to play a Song from an Album they have purchased. Luckily, using the ability to call tags as methods in Grails, you can use the same <jsec:hasPermission> and <jsec:lacksPermission> tags in a controller. Listing 14-53 shows this in action.

Listing 14-53. The play Action of the SongController

def play = {
    def song = Song.get(params.id)
    if(song) {
        def albumPermission = new AlbumPermission(album:song.album)
        jsec.hasPermission(permission:albumPermission) {
            render(view:"play", model:[song:song])
        }
        jsec.lacksPermission(permission:albumPermission) {
            response.sendError 401
        }
    }
    else {
        response.sendError 404
    }
}

As demonstrated by Listing 14-53, you can construct an instance of the AlbumPermission class and then use it as an argument to the jsec.hasPermission method. The closure, which is equivalent to the tag body in GSP, will be invoked only if the user has permission. In the case where the User lacks permission, an HTTP 401 error is sent back signaling that the User is forbidden from accessing this resource.

Otherwise, if all is well, a new view called grails-app/views/song/play.gsp is rendered. Listing 14-54 shows the GSP markup for the play.gsp view, which takes advantage of the <media:player> tag you developed earlier.

Listing 14-54. The play.gsp View

<g:applyLayout name="libraryLayout">
    <div id="musicLibrary" class="musicLibrary">
        <div class="songPlayer">
            <h2>${song.artist.name} - ${song.title}</h2>
            <div class="albumArt">
                <music:albumArt artist="${song.artist.name}"
                                album="${song.album.title}" />
            </div>
            <div class="player">
                <media:player src="${createLink(controller:'song',
                                                action:'stream',
                                                id:song.id)}"
                              autoplay="true"
                              height="20"
                              width="200" />
            </div>
            <div class="links" style="float:right;">
                    <g:remoteLink controller="album"
                                             action="display"
                                             id="${song.album.id}"
                                             update="musicLibrary">
                                  Back to Album
                     </g:remoteLink><br>
                    <g:link controller="user"
                                 action="music">
                                   Back to My Music<
                    </g:link><br>
            </div>
        </div>
    </div>
</g:applyLayout>

You'll notice from the code in Listing 14-54 that the src attribute of the <media:player> tag is another action called stream. The stream action is responsible for sending back the music file. Of course, at the moment, there isn't any music! To rectify that, add a new file property to the Song domain class, as shown in Listing 14-55.

Listing 14-55. Adding a file Property to the Song Class

class Song implements Serializable {
    String file
    ...
    static constraints = {
        ...
        file validator:{ val ->
            if(!new File(val).exists())
                return "song.does.not.exist"
        }
    }
}

As you can see, the file property uses a custom validator that ensures you can't add a Song that doesn't exist on the file system. Now all you need to do is stream the data from the file back to the User. Listing 14-56 shows an example implementation that uses Java I/O techniques.

Listing 14-56. Implementing the stream Action

1 static final BUFFER_SIZE = 2048
2 def stream = {
3     def song = Song.get(params.id)
4    if(song) {
5        def albumPermission = new AlbumPermission(album:song.album)
6        jsec.hasPermission(permission:albumPermission) {
7            try {
8                def file = new File(song.file)
9                def type = file.name[-3..-1]
10                response.contentType = "audio/x-${type}"
11                def out = response.outputStream
12                def bytes = new byte[BUFFER_SIZE]
13                file.withInputStream { inp ->
14                    while( inp.read(bytes) != −1) {
15                        out.write(bytes)
16                        out.flush()
17                    }
18                }
19            }
20            catch(Exception e) {
21                log.error "Error streaming song $file: $e.message", e
22                response.sendError 500
23            }
24
25       }
26       jsec.lacksPermission(permission:albumPermission) {
27           response.sendError 401
28       }
29   }
30   else {
31       response.sendError 404
32   }
33 }

Notice that in Listing 14-56, the code once again secures access to the Song using the AlbumPermission you created earlier and the <jsec:hasPermission> and <jsec:lacksPermission> tags. If the User does have permission, then a new java.io.File is created, and the response contentType is set based on the file extension on lines 8 to 10:

8         def file = new File(song.file)
9         def type = file.name[-3..-1]
10        response.contentType = "audio/x-${type}"

Note The technique of using the file extension to produce the MIME type for the contentType works for MP3 and M4A formats but may be a little naive if the application later needs to support other formats like WMA and so on.


With that done, the next step is to obtain the java.io.OutputStream to write to and from the response and create a buffer to read bytes from the file with the following:

11         def out = response.outputStream
12         def bytes = new byte[BUFFER_SIZE]

A trivial way to read the bytes of the File would be to call the readBytes() method. However, this reads the entire contents of the File into memory and, since audio files are quite large, may not scale too well.


Note Speaking of scaling, a better solution may be to use something like Amazon's Simple Storage Service (S3) to serve the files from the cloud instead. You can find an Amazon S3 plugin for Grails that can help simplify this task at http://grails.org/Amazon+S3+Plugin.


Instead, the code in Listing 14-56 uses a 2KB buffer to read and stream parts of the file back to the User on lines 13 to 18:

13         file.withInputStream { inp ->
14             while( inp.read(bytes) != −1) {
15                 out.write(bytes)
16                 out.flush()
17             }
18         }

And with that, you've completed the "My Music" section of the gTunes application and allowed users to securely stream the music they have purchased! Figure 14-9 shows the interface that allows users to play their music.

image

Figure 14-9. Streaming music with QuickTime

In the next section, you'll learn how having a better understanding of your URL mappings will enable you to keep an eye on how users can access your application.

Limiting Access Through URL Mappings

A good technique to adopt when considering securing your application is to have greater control over the way URLs map onto controllers. The default URL mapping scheme that Grails uses is dynamic in that the parameters in the URI dictate what action is executed (see Listing 14-57).

Listing 14-57. The Default URL Mapping Scheme

"/$controller/$action?/$id?"()

It is easy with a URL mapping like the one in Listing 14-57 to accidentally expose an action that should be secured. If security is of a high priority, we recommend you take control of your URL mappings and create mapping rules for each URL that is exposed. Listing 14-58 shows an example grails-app/conf/UrlMappings.groovy file for the gTunes application that provides mappings for each exposed controller.

Listing 14-58. Fine-Grained URL Mapping Configuration

// User access
"/your/music"(controller:"user", action:"music")
"/login"(controller:"user",action:"login")
"/logout"(controller:"user", action:"logout")
"/register"(controller:"user", action:"register")
"/stream/$id"(controller:"song", action:"stream")
"/play/$id"(controller:"song", action:"play")
"/buy/$id"(controller:"store", action:"buy")

// Anonymous browsing
"/"(controller:"store")
"/album/$id"(controller:"album", action:"display")
"/song/$id"(controller:"song", action:"display")
"/artist/$id"(controller:"artist", action:"display")
"/store"(controller:"store", action:"shop")
"/search"(controller:"store", action:"search")
"/genre/$name"(controller:"store", action:"genre")
"/blog"(controller:"blog", action:"list")

Another advantage of this approach is that you can then configure a dynamic URL mapping purely for administrator access, as shown in Listing 14-59.

Listing 14-59. Administrator URL Mappings

// Administrator access
"/admin/$controller/$action?/$id?"()

As you can see from Listing 14-59, all URIs that start with /admin can now be used for administrator access. If you then secure this URI within the AuthFilters class, as shown in Listing 14-60, you have created an area of the site that is accessible only to administrators.

Listing 14-60. Securing the /admin URI

admin(uri:'/admin/*') {
      before = {
            accessControl {
                   role("ADMINISTRATOR")
            }
      }
}

If you want to add some quick administrative features, then you could take advantage of dynamic scaffolding, a topic covered in Chapter 2. As an example, try adding the following line to the AlbumController class:

def scaffold = Album

Now if you go to the URL http://localhost:8080/gTunes/admin/album/create, you can create new Album instances using the CRUD interface provided. You can also go to the URL http://localhost:8080/gTunes/admin/album/list to get a list of existing Album instances in case you need to modify any of them. Thanks to Grails' scaffolding feature, you have managed to add a basic admin facility to the gTunes application that is secured with JSecurity in only a few lines of code!

Summary

In this chapter, you explored the importance of security when developing a Grails application. From preventing malicious users from penetrating your application to cross-site scripting and DoS attacks to implementing authentication and authorization using a framework such as JSecurity, we covered a lot of ground. Security is, however, a domain-specific topic, and a variety of options are available to you when developing a Grails application. If JSecurity doesn't fulfill your requirements, then you have the Acegi plugin or the Authentication plugin to try. Alternatively, you could continue the "home-grown" security model. It is really up to you.

In the next chapter, we'll cover how to implement web services in Grails. Using key technologies such as SOAP, REST, and RSS/Atom, there is a lot to cover in this particularly interesting area of web development. Don't go away!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset