Ruby on Rails: How to require login for some or all actions
Although much of your site may be public, you may want to keep parts of it only for users who have logged in. Specifically, you may want to keep certain changes behind a login screen.
Preparation
No special preparation is required.
Database
You will require a database table for registered users with the following schema:
Column | Type |
---|---|
id | Integer, Not NULL, Auto Increment |
name | VARCHAR(16) (or any other maximum size of user name) |
password | VARCHAR(64) |
User Model
The user model requires Digest
, which provides an MD5 hashing method by which to hash the password:
require "digest" class User < ActiveRecord::Base def cleartext=(rhs) self.password = Digest::MD5.hexdigest(rhs) end def cleartext return "" end def self.authenticate(name, password) find(:first, :conditions => ["name = ? AND password = ?", name, Digest::MD5.hexdigest(password)]) end end
User View
In the form where the user is created or edited, cleartext
should be used in place of password
:
<label for="user_cleartext">Password:</label> <%= f.text_field('cleartext', :size => 32) %>
User Controller
Once the above changes are made to the user model and view, no changes to the controller are required.
Other Models
No changes are required to other models.
Other Views
You will require a login page. The page should be named index.html
for consistency with the login
controller below. The page should include a form submitting to the authenticate
action in the login controller:
<% form_tag 'login/authenticate' do %> <label for="login_name">Name:</label> <%= text_field_tag('name') %> <label for="login_password">Password:</label> <%= password_field_tag('password') %> <%= submit_tag "Log In" %> <% end %>
Other Controllers
Your application controller should include the following method. It checks whether a valid session exists. If not, it stores the requested page in the session, and redirects to the login
controller.
protected def authenticate unless (nil != session && nil != session[:user]) session[:return_to] = request.request_uri redirect_to :controller => "login" return false end end
The login controller’s index
method will merely display the login page. Submission of the login form will call the authenticate
action. If the user is successfully authenticated, then this action redirects to the page requested before authentication.
class LoginController < ActionController::Base def index end def authenticate if session[:user] = User.authenticate(params["name"], params["password"]) if session[:return_to] redirect_to session[:return_to] session[:return_to] = nil else redirect_to :controller => "summaries" end else flash[:alert] = "Login failed." redirect_to :action => "index" end end def logout reset_session flash[:alert] = "Logged out." redirect_to :action => "index" end end
For controllers that you want protected behind a login page, add the following line:
before_filter :authenticate, :only => [:new, :edit, :update, :create, :destroy]
This prevents public access to controller methods that write to the database. To prevent access to all methods, omit the :only
parameter.