Documentation

You are viewing the documentation for Play 1. The documentation for Play 2 is here.

Adding authentication

Now that we have an administration area we need to plug in some authentication. Fortunately, play has a module for that. It’s called the Secure module.

Enabling the secure module

As for the CRUD module, we have to enable it in the yabe/conf/application.conf file and restart the application.

# Import the secure module
module.secure=${play.path}/modules/secure

After the restart, play should tell that the module is loaded.

The Secure module comes with a set of default routes that you can easily enable in the yabe/conf/routes file (or we could define our own routes as well):

# Import Secure routes
*       /                                       module:secure

Protecting the admins controllers

In fact this module provides a controllers.Secure controller that declare all needed interceptor. Of course we could just inherit this controller but because Java is limited to simple inheritance it would be problematic.

Instead of directly inherit the Secure controller, we can annotate the admin controllers using the @With annotation to tell play to invoke the corresponding interceptors as well:

package controllers;
 
import play.*;
import play.mvc.*;
 
@With(Secure.class)
public class Posts extends CRUD {    
}

Do the same for the Comments, Users and Tags controller.

Now if you try to access any admin action, you should get a login page:

In fact for now you can try any login/password pair, it will just work.

Customizing the authentication process

The application has to provide an instance of controllers.Secure.Security to customize the authentication process. By creating our own version of this class we will be able to specify exactly how users should be authenticated.

Create a yabe/controllers/Security.java file and override the authentify() method:

package controllers;
 
import models.*;
 
public class Security extends Secure.Security {
 
    static boolean authentify(String username, String password) {
        return true;
    }
    
}

As we already have the User objects part of the blog model, it is easy to implement a working version of this method:

static boolean authentify(String username, String password) {
    return User.connect(username, password) != null;
}

Now go to http://localhost:9000/logout to log out and you can try one of the users imported in initial-data.yml file, like [email protected]/secret.

Refactoring the administration area

We’ve started the administration area using the CRUD module, but it is not yet well integrated with the blog UI. We will start to work on a new administration area. This one will give each author access to his/her own posts. The full-featured administration using CRUD will still be available for super administrator users.

Let’s create a new Admin controller for the administration part:

package controllers;
 
import play.*;
import play.mvc.*;
 
import java.util.*;
 
import models.*;
 
@With(Secure.class)
public class Admin extends Controller {
    
    @Before
    static void setConnectedUser() {
        if(Security.isConnected()) {
            User user = User.find("byEmail", Security.connected()).first();
            renderArgs.put("user", user.fullname);
        }
    }
 
    public static void index() {
        render();
    }
    
}

And refactor the routes definition in yabe/conf/routes:

# Administration
GET     /admin/?                                Admin.index
*       /admin                                  module:crud

Now link the ‘Log in to write something’ text from the yabe/app/views/main.html template to this controller:

...
<ul id="tools">
    <li>
        <a href="@{Admin.index()}">Log in to write something</a>
    </li>
</ul>
...

The last thing to make all this new stuff work is to create the yabe/app/views/Admin/index.html template. Let’s start with something simple:

Welcome ${user}!

Now, go to the blog home page, click the ‘Log in to write something’ link and you should get to the new administration area:

Good start! But since we will have several pages in this administration area, we need a super template. Let’s create it in the yabe/app/views/admin.html file:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <title>Administration</title>		
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        #{get 'moreStyles' /}	
        <link rel="stylesheet" type="text/css" media="screen" 
                href="@{'/public/stylesheets/main.css'}" />
        <link rel="shortcut icon" type="image/png" 
                href="@{'/public/images/favicon.png'}" />
        <script src="@{'/public/javascripts/jquery-1.3.2.min.js'}"></script>
        <script src="@{'/public/javascripts/jquery.tools.min.js'}"></script>
    </head>
    <body id="admin">
        
        <div id="header">
            <div id="logo">
                yabe. <span>administration</span>
            </div>
            <ul id="tools">
                <li>
                    <a href="@{Secure.logout()}">Log out</a>
                </li>
            </ul>
        </div>
        
        <div id="main">
            #{doLayout /} 
        </div>
        
        <p id="footer">
            Yabe is a (not so) powerful blog engine built with the 
            <a href="http://www.playframework.org">play framework</a>
            as a tutorial application.
        </p>
        
    </body>
</html>

As you can see, it’s very close to the template used for the front part of the blog engine. You have replaced the Log in link by a Log out link that calls the logout action of the Secure controller provided by the secure module.

Now let’s use it in the yabe/app/views/Admin/index.html template:

#{extends 'admin.html' /}
 
Welcome ${user}!

And refresh!

If you try the log out link it will send you back to the login form:

That’s the default way for the secure module to handle log out events. But it’s easily customizable simply by overriding the onDisconnected() method in the controllers.Security class:

static void onDisconnected() {
    Application.index();
}

Yet, you can do the same for the onAuthenticated() event:

static void onAuthenticated() {
    Admin.index();
}

Adding roles

Actually, we need two administration areas: one for simple editors and another one for super administrator. As you’ve seen the User model object has a isAdmin field that tells if the user has super administrator rights.

The secure module not only provides authentication but authorization management too. It’s called profiles. To create an admin profile, you just need to override the check() method in the controllers.Security class:

static boolean check(String profile) {
    if("admin".equals(profile)) {
        return User.find("byEmail", connected()).<User>first().isAdmin;
    }
    return false;
}

Now we can build an administration menu that depends of the user role. Update the yabe/views/admin.html to integrate a top level menu:

...
<div id="main">
    
    <ul id="adminMenu">
        <li class="${request.controller == 'Admin' ? 'selected' : ''}">
            <a href="@{Admin.index()}">My posts</a>
        </li>
        #{secure.check 'admin'}
            <li class="${request.controller == 'Posts' ? 'selected' : ''}">
                <a href="@{Posts.list()}">Posts</a>
            </li>
            <li class="${request.controller == 'Tags' ? 'selected' : ''}">
                <a href="@{Tags.list()}">Tags</a>
            </li>
            <li class="${request.controller == 'Comments' ? 'selected' : ''}">
                <a href="@{Comments.list()}">Comments</a>
            </li>
            <li class="${request.controller == 'Users' ? 'selected' : ''}">
                <a href="@{Users.list()}">Users</a>
            </li>
        #{/secure.check}
    </ul>
    
    #{doLayout /} 
</div>
...

Look how we used the #{secure.check /} tag to show part of the menu only to admin users.

But it’s not enough to correctly protect the CRUD part! If a user knows the URL he can still access it. We have to protect controllers as well. We can do it easily using the @Check annotation. For example, for the Posts controller:

package controllers;
 
import play.*;
import play.mvc.*;
 
@Check("admin")
@With(Secure.class)
public class Posts extends CRUD {    
}

Do the same for the Tags, Comments and Users controllers. Now try to log out and log in again with a standard user (like [email protected]/secret). You should not see the CRUD admin links. And if you try to access directly the http://localhost:9000/admin/users URL you will get a 403 response.

Customizing the CRUD layout

That’s fun but when we go to the CRUD part of the administration area, we lose the administration layout. That’s because the CRUD module provides it own layout. But we can of course override it. Use this play command:

play crud:ov --layout

And you get a new /yabe/app/views/CRUD/layout.html template. Let’s replace its content to integrate it nicely with our admin.html layout:

#{extends 'admin.html' /}
#{set 'moreStyles'}
    <link rel="stylesheet" type="text/css" media="screen" 
        href="@{'/public/stylesheets/crud.css'}" />
#{/set}
 
<div id="crud">
 
    #{if flash.success}
    	<div class="crudFlash flashSuccess">
    		${flash.success}
    	</div>
    #{/if}
    #{if flash.error || error}
    	<div class="crudFlash flashError">
    		${error ?: flash.error}
    	</div>
    #{/if}
 
    <div id="crudContent">
    	#{doLayout /}
    </div>
 
</div>

As you see, we reuse the crud.css and include it in the admin.html using the get/set template variables mechanism. Now if you try the CRUD part of the administration area, it’s correctly integrated with the administration layout:

Styling the login page

The administration area is almost graphically coherent now. The last thing to style is the login page. As always it is easily customizable by overriding the default css:

play secure:ov --css

We will just keep the CSS as is, but import our main.css at the top. Just add this first line to the yabe/public/stylesheets/secure.css:

@import url(main.css);
...

And customize the login screen messages, by adding these lines to your own yabe/conf/messages file:

secure.username=Your email:
secure.password=Your password:
secure.signin=Log in now

Go to the next part.