Developing the Login Management Module
Even though Login and session handling are separate functionalities from User
management, they depend on the same table—user. Also, the functionalities are
more alike than different. Hence, instead of creating a new Controller, we will be
using the UserController itself as the Controller for the Login module. Keeping this
point in mind, let us look at the steps involved in developing the Login Management,
- Creating the Login page
- Implementing the Authentication Method
- Setting up the Session
- Applying Authorization
Leaving aside the first step, all other steps mainly focus on the Controller. Here
Creating the Login Page
We need a login page with textboxes for user name and password in which users can
put their credentials and submit to the login authenticator (fancy name for the action
method that will contain the logic to authenticate the user). That’s what we are going
to create now. The convention for any website is to show the login page when the user
enters the URL without any specific page in mind. RoR also follows this convention.
For example, if you enter the URL as http://localhost:3000/user, it displays the
list of users. The reason is that the index action method of the UserController class
calls the list method whenever the aforementioned URL is used. From this, we can
understand two things—first, the default action method is index, and second, the
first page to be shown is changeable if we change the index method.
What we need is to show the login page whenever a user enters the URL
http://localhost:3000/user. So let’s change the index method. Open the
user_controller.rb file from the app/views/user folder and remove all the
statements from the body of the index method so that it looks like as follows:
def index end
Next, let us create an index.rhtml file, which will be shown when the index
method is called. This file will be the login page. In the app/views/user folder,
create an index.rhtml file. It will be as follows:
<%= form_tag :action=> 'authenticate'%> <table > <tr align="center" class="tablebody"> <td>User name:</td> <td><%= text_field("user", "user_name",:size=>"15" ) %></td> </tr> <tr align="center" class="tablebody"> <td>Password:</td> <td><%= password_field("user", "password",:size=>"17" ) %></td> </tr> <tr align="center" class="tablebody"> <td></td> <td><input type="submit" value=" LOGIN " /></td> </tr> </tabale>
It uses two new form helpers—text_field and password_field. The text_field
creates a text field with the name passed as the parameter, and the password_
field creates a password field again with the name passed as the parameter. We
have passed the authenticate method as the action parameter so that the form is
submitted to the authenticate method. That completes the login page creation.
Next, we will work on the authenticate method.
Implementing the Authenticate method
Authenticating a user essentially means checking whether the user name and
password given by the user corresponds to the one in database or not. In our case,
the user gives us the user name and password through the login page. What we will
be doing is checking whether the user is in database and does the password that we
got corresponds to the password stored in the database for the user? Here, we will be
working on two levels:
We can put the data access part in the action method that being the Controller itself.
But it will create problems in the future if we want to add something extra to the user
name/password checking code. That’s why we are going to put (or delegate) the
data access part into Model.
We will be modifying the User class by adding a method that will check whether
the user name and password provided by the user is correct or not. The name of the
method is login. It is as follows:
def self.login(name,password) find(:first,:conditions => ["user_name = ? and password = ?",name, password]) end
It is defined as a singleton method of the User class by using the self keyword. The
singleton methods are special class-level methods. The conditions parameter of the
find method takes an array of condition and the corresponding values. The find
method generates an SQL statement from the passed parameters. Here, the find
method finds the first record that matches the provided user_name and password.
Now, let us create the method that the Controller will call to check the validity of the
user. Let us name it check_login. The definition is as follows:
def check_login User.login(self.user_name, self.password) end
This function calls the login method. Now if you observe closely, check_login calls
the login function. One more point to remember—if a method ‘test’ returns a value
and you call ‘test’ from another method ‘test1,’ then you don’t need to say ‘return test’
from within ‘test1′.The value returned from ‘test’ will be returned by ‘test1′ implicitly.
That completes the changes to be done at the Model level. Now let us see the changes
at the Controller-level.
In the Controller for User—UserController—add a new method named
authenticate. The method will first create a User object based on the user name
and password. Then it will invoke check_login on the newly created User object. If
check_login is successful, that is, it does not return nil, then the user is redirected to
the list view of Tales. Otherwise, the user is redirected to the login page itself. Here is
what the method will look like:
def authenticate @user = User.new(params[:user]) valid_user = @user.check_login if logged_in_user flash[:note]="Welcome "+logged_in_user.name redirect_to(:controller=>'tale',:action => "list") else flash[:notice] = "Invalid User/Password" redirect_to :action=> "index" end end
The redirect_to method accepts two parameters—the name of the Controller
and the method within the Controller. If the user is valid, then the list method of
TaleController is called, or in other words, the user is redirected to the list of tales.
Next, let us make it more robust by checking for the get method. If a user directly
types a URL to an action, then the get method is received by the method. If any user
does that, we want him/her to be redirected to the login page. To do this, we wrap
up the user validation logic in an if/else block. The code will be the following:
def authenticate if request.get? render :action=> 'index' else @user = User.new(params[:user]) valid_user = @user.check_login if valid_user flash[:note]="Welcome "+valid_user.user_name redirect_to(:controller=>'tale',:action => 'list') else flash[:notice] = "Invalid User/Password" redirect_to :action=> 'index' end end end
The get? method returns true if the URL has the GET method else it returns false.
That completes the login authentication part. Next, let us set up the session.
In Ruby, any method that returns a Boolean value—true or false—is suffixed with a question mark (?). The get method of the request object returns a boolean value. So it is suffixed with a question mark (?).
Setting up the Session
Once a user is authenticated, the next step is to set up the session to track the user.
Session, by definition, is the conversation between the user and the server from
the moment the user logs in to the moment the user logs out. A conversation is a
pair of requests by the user and the response from the server. In RoR, the session
can be tracked either by using cookies or the session object. The session is an object
provided by RoR. The session object can hold objects where as cookies cannot.
Therefore, we will be using the session object. The session object is a hash like
structure, which can hold the key and the corresponding value. Setting up a session
is as easy as providing a key to the session object and assigning it a value. The
following code illustrates this aspect:
def authenticate if request.get? render :action=> 'index' else @user = User.new(params[:user]) valid_user = @user.check_login if valid_user session[:user_id]=valid_user.id flash[:note]="Welcome "+valid_user.user_name redirect_to(:controller=>'tale',:action => 'list') else flash[:notice] = "Invalid User/Password" redirect_to :action=> 'index' end end end
That completes setting up the session part. That brings us to the last
Until now, we have authenticated the user and set up a session for him/her.
However, we still haven’t ensured that only the authenticated users can access the
different functionalities of TaleWiki. This is where authorization comes into the
picture. Authorization has two levels—coarse grained and fine grained. Coarse grained
authorization looks at the whole picture whereas the fine grained authorization looks
at the individual ‘pixels’ of the picture. Ensuring that only the authenticated users
can get into TaleWiki is a part of coarse grained authorization while checking the
privileges for each functionality comes under the fine grained authorization. In this
chapter, we will be working with the coarse grained authorization.
The best place to apply the coarse grained authorization is the Controller as it is the
central point of data exchange. Just like other aspects, RoR provides a functionality
to easily apply any kind of logic on the Controller as a whole in the form of filters. To
jog your memory, a filter contains a set of statements that need to be executed before,
after (or before and after) the methods within the Controllers are executed.
Our problem is to check whether the user is authenticated or not, before any
method in a Controller is executed. The solution to our problem is using a ‘before
filter’. But we have to apply authorization to all the Controllers. Hence, the filter
should be callable from any of the Controller. If you look at the definition of
a Controller, you can find such a place. Each Controller is inherited from the
ApplicationController. Anything placed in ApplicationController
will be callable from other Controllers. In other words, any method placed in
ApplicationController becomes global to all the Controllers within your application.
So, we will place the method containing the filter logic in ApplicationController.
To check whether a user is authentic or not, the simplest way is to check whether a
session exists for that person or not. If it exists, then we can continue with the normal
execution. Let us name it check_authentic_user. The implementation will be
def check_authentic_user unless session[:user_id] flash[:notice] = "Please log in" redirect_to(:controller => "user", :action => "index") end end
It checks for the user_id key in a session. If it is not present, the user is redirected
to the login page. Place the code in the application.rb file as a method
of ApplicationController. Next, let us use it as a filter. First, we will tell
UserController to apply the filter for all the action methods except index and
authenticate methods. Add the following statement to the UserController. It
should be the first statement after the starting of the Controller class.
class UserController < ApplicationController before_filter :check_authentic_user, :except =>[ :index, :authenticate ] : : end
Similarly, we will place the filter in other Controllers as well. However, in their case,
there are no exceptions. So TaleController will have:
class TaleController < ApplicationController before_filter :check_authentic_user : : end
GenreController and RoleController will be the same as TaleController. Thus,
we have completed the ‘applying authorization’ part for the time being. Now, let’s tie
up one loose end—the problem of adding a new tale.