Home arrow Ruby-on-Rails arrow Page 5 - Controlling Information Access with the Rails Action Controller
RUBY-ON-RAILS

Controlling Information Access with the Rails Action Controller


In this conclusion to a four-part series on the Rails Action Controller, you will learn how to restrict access to controller methods, use filters for authentication, and more. This article is excerpted from chapter four of the Rails Cookbook, written by Rob Orsini (O'Reilly, 2007; ISBN: 0596527314). Copyright © 2007 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Author Info:
By: O'Reilly Media
Rating: 5 stars5 stars5 stars5 stars5 stars / 4
February 14, 2008
TABLE OF CONTENTS:
  1. · Controlling Information Access with the Rails Action Controller
  2. · 4.13 Sending Files or Data Streams to the Browser
  3. · 4.14 Storing Session Information in a Database
  4. · 4.15 Tracking Information with Sessions
  5. · 4.16 Using Filters for Authentication

print this article
SEARCH DEVARTICLES

TOOLS YOU CAN USE

advertisement
Controlling Information Access with the Rails Action Controller - 4.16 Using Filters for Authentication
(Page 5 of 5 )

Problem

You want to authenticate users before they’re allowed to use certain areas of your application; you wish to redirect unauthenticated users to a login page. Furthermore, you want to remember the page that the user requested and, if authentication succeeds, redirect them to that page once they’ve authenticated. Finally, once a user has logged in, you want to remember his credentials and let him move around the site without having to re-authenticate.

Solution

Implement an authentication system, and apply it to selected controller actions using before_filter.

First, create a user database to store user account information and login credentials. Always store passwords as hashed strings in your database, in case your server is compromised.

db/migrate/001_create_users.rb: 

  class CreateUsers
< ActiveRecord::Migration
    def self.up
     
create_table :users do |t|
        t.column :first_name,        :string
        t.column :last_name,         :string
        t.column :username,          :string
       
t.column :hashed_password,   :string
      end

      User.create :first_name => 'Rob',
                  :last_name => 'Orisni',
                  :username => 'rorsini',
                  :hashed_password =>
  '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8'
    end

    def self.down
      drop_table :users
    end
  end

In your ApplicationController, define an authenticate method that checks if a user is logged in and stores the URL of the page the user initially requested:

app/controllers/application.rb:

  # Filters added to this controller will be
run for all controllers in the
  # application. 
  # Likewise, all the methods added will be
available for all controllers.
  class ApplicationController
< ActionController::Base
    def authenticate
     
if session['user'].nil?
        session['initial_uri'] = request.request_uri     
        redirect_to :controller => "users",
:action => "login
     
end
    end
 end

To make sure the authenticate method is invoked, pass the symbol :authenticate to before_filter in each controller that gives access to pages requiring authentication. Here’s how to make sure that users are authenticated before they can access anything governed by the ArticlesController or the BooksController:

app/controllers/articles_controller.rb:

  class ArticlesController
< ApplicationController

    before_filter :authenticate

    def admin
    end
  end

app/controllers/books_controller.rb:

  class BooksController
< ApplicationController

    before_filter :authenticate

    def admin
    end
  end

Now, create a login form template to collect user credentials:

app/views/users/login.rhtml:

  <% if flash['notice'] %>
    <p style="color: red;"><%= flash['notice'] %></p>
  <% end %>

  <% form_tag :action => 'verify' do %>

    <p><label
for="user_username">Username</label>;
    <%= text_field 'user', 'username' %> </p>  

    <p><label
for="user_hashed_password">Password</label>;
    <%=  password_field 'user', 'hashed_password' %> </p> 

    <%= submit_tag "Login" %>
  <% end %>

The UsersController defines login, verify, and logout methods to handle the authentication of new users:

app/controllers/users_controller.rb:

  class UsersController
< ApplicationController

    def login
    end

    def verify
      hash_pass =
Digest::SHA1.hexdigest(params[:user][:hashed_password])[0..39]
      user = User.find(:first,:conditions =>
                        ["username = ?
and hashed_password = ?",
                   params[:user][:username],
hash_pass ])
     
if user
        session['user'] = user
        redirect_to session['initial_uri']
      else
        flash['notice'] = "Bad username/password!"  
        redirect_to :controller => "users",
:action => "login" 
      end
    end

    def logout
      reset_session 
      # Redirect users to Books#admin,
which in turn sends them to 
      # Users#login, with a refering url
of Books#admin:

      redirect_to :controller => "books", :action => "admin"
    end
  end

Next, provide a mechanism for users to log themselves out if they’re not comfortable letting their session time out on its own. Create a "logout" link with a named route using logout_url:

app/views/articles/admin.rhtml:

  <h1>Articles Admin</h1>

  <%= link_to "logout", :logout_url %>

app/views/books/admin.rhtml:

  <h1>Books Admin</h1>

  <%= link_to "logout", :logout_url %>

Finally, define the "logout" named route with its URL mapping:

config/routes.rb:

  ActionController::Routing::Routes.draw
do |map|

    map.logout '/logout', :controller
=> "users", :action => "logout" 

    # Install the default route as the
lowest priority.
    map.connect ':controller/:action/:id'
  end

Discussion

Adding authentication to a site is one of the most common tasks in web development. Almost any site that does anything meaningful requires some level of security, or at least a way to differentiate between site visitors.

The Rails before_filter lends itself perfectly to the task of access control by invoking an authentication method just before controller actions are executed. Code that is declared as a filter with before_filter has access to all the same objects as the controller, including the request and response objects, and the params and session hashes.

The solution places the authenticate filter in the Book and Article controllers. Every request to either controller first executes the code in authenticate. This code checks for the existence of a user object in the session hash, under the key of user. If that session key is empty, the URL of the request is stored in its own session key, and the request is redirected to the login method of the User controller.

The login form submits the username and password to the Login controller, which looks for a match in the database. If a user is found with that username and a matching hashed password, the request is redirected to the URL that was stored in the session earlier.

When the user wishes to log out, the logout action of the User controller calls reset_session, clearing out all the objects stored in the session. The user is then redirected to the login screen.

See Also


DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.

blog comments powered by Disqus
RUBY-ON-RAILS ARTICLES

- Adding Style with Action Pack
- Handling HTML in Templates with Action Pack
- Filters, Controllers and Helpers in Action P...
- Action Pack and Controller Filters
- Action Pack Categories and Events
- Logging Out, Events and Templates with Actio...
- Action Pack Sessions and Architecture
- More on Action Pack Partial Templates
- Action Pack Partial Templates
- Displaying Error Messages with the Action Pa...
- Action Pack Request Parameters
- Creating an Action Pack Registration Form
- Ruby on Rails Templates and Layouts
- Action Pack Controller Creation
- Writing an Action Pack Controller

Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 



© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 1 - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials