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.