How to install RESTful Authentication on a Ruby on Rails 2.0 application
It’s time to upgrade your Rails 2.0 application with user authentication, and we hear that RESTful Authentication is the way to go, but all the instructions out there (even on the plugin repository sites) are out of date or don’t completely work or only work on Rails 1.2.x. In this tutorial, however, we’ll go step-by-step to install a complete RESTful authentication suite with all the trimmings your advanced Rails 2.0 application requires. Best of all, we’ll maintain complete source control of our user administration code.
Please note that the code in this tutorial is borrowed from a railsforum.com post by a very smart user named “activefx”. activefx has miraculously put together his research on Rick Olson’s brilliant restful_authentication plugin to create a pretty robust user authentication process. This post is just a bit more tailored for my Rails MVC+ architectural approach, which is to 1) install my plugins (if any), 2) use the Rails generators to generate the resources I think I’ll need, 3) generate my database migrations, add 4) my models (and library modules, if any), then 5) my controllers, and finally 6) my views. This tutorial has also been reorganized and optimized for quick availability to copy and paste into new Ruby on Rails projects.
——————————————
# PLUGIN INSTALLATION
# To start, add the techno-weenie plugin repository to your local resource list,
# then install restful_athentication plugin into your rails application.
# in terminal: cd ~/my/rails/application/root
script/plugin source http://svn.techno-weenie.net/projects/plugins
script/plugin install restful_authentication
——————————————
# GENERATORS
# The restful_authentication plugin will generate our “User” model, controller, and view,
# as well as our “Sessions” controller and view (no Session model needed).
# We’ll create the models, controllers, and views for 1 new resource: “Roles”
# Then we’ll create 1 new join model for: “Permissions”
# Then we’ll create 2 new controllers: “Passwords” and “UserAccounts”
script/generate authenticated user sessions - -include-activation
script/generate resource Role
script/generate model Permission
script/generate controller Passwords
script/generate controller UserAccounts
——————————————
# ROUTES
# Call me crazy, but I like to “map” out my application routes before I start coding my MCV architecture.
# This way I get a clearer sense of what Models, Controllers, and Views I’m going to need to configure.
# I also get a more friendly, routes-related error message if I miss a section or
# misspell a filename or something, instead of getting some puzzling error from the actual code.
# In other words, at least I know the code is actually there or not before I start scratching my head.
# (If you’re pre-rails, old school, or dyslexic: I’m speaking of the MVC paradigm that Rails employs.)
# open file: config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.root :controller => “sessions”, :action => “new”
# begin restful_authentication routes …
map.login ‘/login’, :controller => ’sessions’, :action => ‘new’
map.logout ‘/logout’, :controller => ’sessions’, :action => ‘destroy’
map.signup ‘/signup’, :controller => ‘users’, :action => ‘new’
map.activate ‘/activate/:id’, :controller => ‘user_accounts’, :action => ’show’
map.change_password ‘/change_password’, :controller => ‘user_accounts’, :action => ‘edit’
map.forgot_password ‘/forgot_password’, :controller => ‘passwords’, :action => ‘new’
map.reset_password ‘/reset_password/:id’, :controller => ‘passwords’, :action => ‘edit’map.resources :users, :member => { :enable => :put } do |users|
users.resource :user_account
users.resources :roles
endmap.resource :session
map.resource :password
# … end restful_authentication routes# Install the default routes as the lowest priority.
map.connect ‘:controller/:action/:id’
map.connect ‘:controller/:action/:id.:format’end
——————————————
# DB MIGRATIONS
# As intelligent and agile developers, we’ve planned ahead
# and already mapped out our application routes.
# But before we can start working with our Rails models,
# we need to generate our database migrations
# so Rails can generate/update our database schema
# when we run the “rake db:migrate” command afterwards.
# open file: db/migrate/xxx_create_users.rb
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users, :force => true do |t|
t.string :login
t.string :email
t.string :crypted_password, :limit => 40
t.string :salt, :limit => 40
t.string :remember_token
t.datetime :remember_token_expires_at
t.string :activation_code, :limit => 40
t.datetime :activated_at
t.string :password_reset_code, :limit => 40
t.boolean :enabled, :default => true
t.integer :updated_by
t.timestamps
end
enddef self.down
drop_table :users
end
end
# open file: db/migrate/xxx_create_roles.rb
class CreateRoles < ActiveRecord::Migration
def self.up
create_table :roles do |t|
t.string :name
t.integer :updated_by
t.timestamps
end
enddef self.down
drop_table :roles
end
end
# open file: db/migrate/xxx_create_permissions.rb
class CreatePermissions < ActiveRecord::Migration
def self.up
create_table :permissions do |t|
t.integer :role_id, :user_id, :null => false
t.integer :updated_by
t.timestamps
end# Make sure the role migration file was generated first
Role.create(:name => ’super user’)
# Then, add default admin user
# Be sure change the password later or in this migration file
user = User.new
user.login = “super”
user.email = “super@mydomain1.com”
user.password = “password”
user.password_confirmation = “password”
user.save(false)
user.send(:activate!)role = Role.find_by_name(’super user’)
user = User.find_by_login(’super’)permission = Permission.new
permission.role = role
permission.user = user
permission.save(false)
enddef self.down
drop_table :permissions
Role.find_by_name(’super user’).destroy
User.find_by_login(’super’).destroy
end
end
# Now that we’ve created our database migrations, we’re ready to run our rake task
# which will add our User, Role, and Permission models to our db
rake db:migrate
——————————————
# MODELS
# Now that we’ve created the main resources that we’re going to be working with,
# we can start manipulating our models.
# open file: app/models/role.rb
class Role < ActiveRecord::Base
has_many :permissions
has_many :users, :through => :permissions
end
# open file: app/models/permission.rb
class Permission < ActiveRecord::Base
belongs_to :user
belongs_to :role
end
# open file: app/models/user.rb
require ‘digest/sha1′
class User < ActiveRecord::Base
# Virtual attribute for the unencrypted password
attr_accessor :passwordvalidates_presence_of :login, :email
validates_presence_of :password, :if => :password_required?
validates_presence_of :password_confirmation, :if => :password_required?
validates_length_of :password, :within => 4..40, :if => :password_required?
validates_confirmation_of :password, :if => :password_required?
validates_length_of :login, :within => 3..40
validates_length_of :email, :within => 3..100
validates_uniqueness_of :login, :email, :case_sensitive => false
validates_format_of :email, :with => /(^([^@\s]+)@((?:[-_a-z0-9]+\.)+[a-z]{2,})$)|(^$)/ihas_many :permissions
has_many :roles, :through => :permissionsbefore_save :encrypt_password
before_create :make_activation_code# prevents a user from submitting a crafted form that bypasses activation
# anything else you want your user to change should be added here.
attr_accessible :login, :email, :password, :password_confirmationclass ActivationCodeNotFound < StandardError; end
class AlreadyActivated < StandardError
attr_reader :user, :message;
def initialize(user, message=nil)
@message, @user = message, user
end
end# Finds the user with the corresponding activation code, activates their account and returns the user.
#
# Raises:
# +User::ActivationCodeNotFound+ if there is no user with the corresponding activation code
# +User::AlreadyActivated+ if the user with the corresponding activation code has already activated their account
def self.find_and_activate!(activation_code)
raise ArgumentError if activation_code.nil?
user = find_by_activation_code(activation_code)
raise ActivationCodeNotFound if !user
raise AlreadyActivated.new(user) if user.active?
user.send(:activate!)
user
enddef active?
# the presence of an activation date means they have activated
!activated_at.nil?
end# Returns true if the user has just been activated.
def pending?
@activated
end# Authenticates a user by their login name and unencrypted password. Returns the user or nil.
# Updated 2/20/08
def self.authenticate(login, password)
u = find :first, :conditions => [’login = ?’, login] # need to get the salt
u && u.authenticated?(password) ? u : nil
end# Encrypts some data with the salt.
def self.encrypt(password, salt)
Digest::SHA1.hexdigest(”–#{salt}–#{password}–”)
end# Encrypts the password with the user salt
def encrypt(password)
self.class.encrypt(password, salt)
enddef authenticated?(password)
crypted_password == encrypt(password)
enddef remember_token?
remember_token_expires_at && Time.now.utc < remember_token_expires_at
end# These create and unset the fields required for remembering users between browser closes
def remember_me
remember_me_for 2.weeks
enddef remember_me_for(time)
remember_me_until time.from_now.utc
enddef remember_me_until(time)
self.remember_token_expires_at = time
self.remember_token = encrypt(”#{email}–#{remember_token_expires_at}”)
save(false)
enddef forget_me
self.remember_token_expires_at = nil
self.remember_token = nil
save(false)
enddef forgot_password
@forgotten_password = true
self.make_password_reset_code
enddef reset_password
# First update the password_reset_code before setting the
# reset_password flag to avoid duplicate email notifications.
update_attribute(:password_reset_code, nil)
@reset_password = true
end# used in user_observer
def recently_forgot_password?
@forgotten_password
enddef recently_reset_password?
@reset_password
enddef self.find_for_forget(email)
find :first, :conditions => [’email = ? and activated_at IS NOT NULL’, email]
enddef has_role?(name)
self.roles.find_by_name(name) ? true : false
endprotected
# before filter
def encrypt_password
return if password.blank?
self.salt = Digest::SHA1.hexdigest(”–#{Time.now.to_s}–#{login}–”) if new_record?
self.crypted_password = encrypt(password)
enddef password_required?
crypted_password.blank? || !password.blank?
enddef make_activation_code
self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
enddef make_password_reset_code
self.password_reset_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
endprivate
def activate!
@activated = true
self.update_attribute(:activated_at, Time.now.utc)
endend
# create file: app/models/user_mailer.rb
class UserMailer < ActionMailer::Base
def signup_notification(user)
setup_email(user)
@subject += ‘Please activate your new account’
@body[:url] = “http://localhost:3000/activate/#{user.activation_code}”
enddef activation(user)
setup_email(user)
@subject += ‘Your account has been activated!’
@body[:url] = “http://localhost:3000/”
enddef forgot_password(user)
setup_email(user)
@subject += ‘You have requested to change your password’
@body[:url] = “http://localhost:3000/reset_password/#{user.password_reset_code}”
enddef reset_password(user)
setup_email(user)
@subject += ‘Your password has been reset.’
endprotected
def setup_email(user)
@recipients = “#{user.email}”
@from = “mail@mydomain1.com”
@subject = “mydomain1.com email setup”
@sent_on = Time.now
@body[:user] = user
end
end
# create file: apps/models/user_observer.rb
class UserObserver < ActiveRecord::Observer
def after_create(user)
UserMailer.deliver_signup_notification(user)
enddef after_save(user)
UserMailer.deliver_activation(user) if user.pending?
UserMailer.deliver_forgot_password(user) if user.recently_forgot_password?
UserMailer.deliver_reset_password(user) if user.recently_reset_password?
endend
——————————————
# CONFIGURATION
# Let’s quickly configure our smtp server and update our application environment
# for use with restful_authentication:
# create file: config/initializers/mail.rb
# Email settings
ActionMailer::Base.delivery_method = :smtp # or :sendmail
ActionMailer::Base.smtp_settings = {
:address => “mail.domain.com”,
:port => 25,
:domain => “domain.com”,
:authentication => :login,
:user_name => “mail@domain.com”,
:password => “uh”
}
# open file: config/environment.rb
Rails::Initializer.run do |config|
…
config.active_record.observers = :user_observer
…
end
——————————————
# AUTHENTICATED_SYSTEM LIBRARY MODULE
# Now that our models are setup, let’s update our
# AuthenticatedSystem module with 6 new objects
# 1) not_logged_in_required
# 2) check_role
# 3) check_super_user_role
# 4) permission_denied
# 5) store_referer
# 6) redirect_to_referer_or_default
# open file: lib/authenticated_system.rb
module AuthenticatedSystem
protected
# Returns true or false if the user is logged in.
# Preloads @current_user with the user model if they’re logged in.
def logged_in?
!!current_user
end# Accesses the current user from the session.
# Future calls avoid the database because nil is not equal to false.
def current_user
@current_user ||= (login_from_session || login_from_basic_auth || login_from_cookie) unless @current_user == false
end# Store the given user id in the session.
def current_user=(new_user)
session[:user_id] = new_user ? new_user.id : nil
@current_user = new_user || false
end# Check if the user is authorized
#
# Override this method in your controllers if you want to restrict access
# to only a few actions or if you want to check if the user
# has the correct rights.
#
# Example:
#
# # only allow nonbobs
# def authorized?
# current_user.login != “bob”
# end
def authorized?
logged_in?
end# Filter method to enforce a login requirement.
#
# To require logins for all actions, use this in your controllers:
#
# before_filter :login_required
#
# To require logins for specific actions, use this in your controllers:
#
# before_filter :login_required, :only => [ :edit, :update ]
#
# To skip this in a subclassed controller:
#
# skip_before_filter :login_required
#
def login_required
authorized? || access_denied
end# added
def not_logged_in_required
!logged_in? || permission_denied
end# added
def check_role(role)
unless logged_in? && @current_user.has_role?(role)
if logged_in?
permission_denied
else
store_referer
access_denied
end
end
end# added
def check_super_user_role
check_role(’super user’)
end# Redirect as appropriate when an access request fails.
#
# The default action is to redirect to the login screen.
#
# Override this method in your controllers if you want to have special
# behavior in case the user is not authorized
# to access the requested action. For example, a popup window might
# simply close itself.
def access_denied
respond_to do |format|
format.html do
store_location
redirect_to new_session_path
end
format.any do
request_http_basic_authentication ‘Web Password’
end
end
end# added
def permission_denied
respond_to do |format|
format.html do
#Put your domain name here ex. http://www.example.com
domain_name = “http://localhost:3000″
http_referer = session[:refer_to]
if http_referer.nil?
store_referer
http_referer = ( session[:refer_to] || domain_name )
end
flash[:error] = “You don’t have permission to complete that action.”
#The [0..20] represents the 21 characters in http://localhost:3000
#You have to set that to the number of characters in your domain name
if http_referer[0..20] != domain_name
session[:refer_to] = nil
redirect_to root_path
else
redirect_to_referer_or_default(root_path)
end
end
format.xml do
headers[”Status”] = “Unauthorized”
headers[”WWW-Authenticate”] = %(Basic realm=”Web Password”)
render :text => “You don’t have permission to complete this action.”, :status => ‘401 Unauthorized’
end
end
end# Store the URI of the current request in the session.
# We can return to this location by calling #redirect_back_or_default.
def store_location
session[:return_to] = request.request_uri
end# added
def store_referer
session[:refer_to] = request.env[”HTTP_REFERER”]
end# Redirect to the URI stored by the most recent store_location call or
# to the passed default.
def redirect_back_or_default(default)
redirect_to(session[:return_to] || default)
session[:return_to] = nil
end# added
def redirect_to_referer_or_default(default)
redirect_to(session[:refer_to] || default)
session[:refer_to] = nil
end# Inclusion hook to make #current_user and #logged_in?
# available as ActionView helper methods.
def self.included(base)
base.send :helper_method, :current_user, :logged_in?
end# Called from #current_user. First attempt to login by the user id stored in the session.
def login_from_session
self.current_user = User.find_by_id(session[:user_id]) if session[:user_id]
end# Called from #current_user. Now, attempt to login by basic authentication information.
def login_from_basic_auth
authenticate_with_http_basic do |username, password|
self.current_user = User.authenticate(username, password)
end
end# Called from #current_user. Finaly, attempt to login by an expiring token in the cookie.
def login_from_cookie
user = cookies[:auth_token] && User.find_by_remember_token(cookies[:auth_token])
if user && user.remember_token?
cookies[:auth_token] = { :value => user.remember_token, :expires => user.remember_token_expires_at }
self.current_user = user
end
end
end
——————————————
# CONTROLLERS
# Now that we’ve configured our Rails models, we’ll work on our Rails controllers
# open file:
# add this line to the ApplicationController class
# remember the file we just edited? this command is to include it here:
include AuthenticatedSystem
# open file: app/controllers/users_controller.rb
class UsersController < ApplicationController
layout ‘application’
before_filter :not_logged_in_required, :only => [:new, :create]
before_filter :login_required, :only => [:show, :edit, :update]
before_filter :check_super_user_role, :only => [:index, :destroy, :enable]def index
@users = User.find(:all)
end# this show action only allows users to view their own profile
def show
@user = current_user
end# render new.rhtml
def new
@user = User.new
enddef create
cookies.delete :auth_token
@user = User.new(params[:user])
@user.save!
# Uncomment to have the user automatically
# logged in after creating an account - Not Recommended
# self.current_user = @user
flash[:notice] = “Thanks for signing up! Please check your email to activate your account before logging in.”
redirect_to login_path
rescue ActiveRecord::RecordInvalid
flash[:error] = “There was a problem creating your account.”
render :action => ‘new’
enddef edit
@user = current_user
enddef update
@user = User.find(current_user)
if @user.update_attributes(params[:user])
flash[:notice] = “User updated”
redirect_to :action => ’show’, :id => current_user
else
render :action => ‘edit’
end
enddef destroy
@user = User.find(params[:id])
if @user.update_attribute(:enabled, false)
flash[:notice] = “User disabled”
else
flash[:error] = “There was a problem disabling this user.”
end
redirect_to :action => ‘index’
enddef enable
@user = User.find(params[:id])
if @user.update_attribute(:enabled, true)
flash[:notice] = “User enabled”
else
flash[:error] = “There was a problem enabling this user.”
end
redirect_to :action => ‘index’
enddef activate
@user = User.find_by_activation_code(params[:id])
if @user and @user.activate
self.current_user = @user
redirect_back_or_default(:controller => ‘/user_account’, :action => ‘index’)
flash[:notice] = “Your account has been activated.”
end
redirect_to :action => ‘index’
endend
# open file: app/controllers/sessions_controller.rb
# This controller handles the login/logout function of the site.
class SessionsController < ApplicationController
layout ‘application’
before_filter :login_required, :only => :destroy
before_filter :not_logged_in_required, :only => [:new, :create]# render new.rhtml
def new
enddef create
password_authentication(params[:login], params[:password])
enddef destroy
self.current_user.forget_me if logged_in?
cookies.delete :auth_token
reset_session
flash[:notice] = “You have been logged out.”
redirect_to login_path
endprotected
def password_authentication(login, password)
user = User.authenticate(login, password)
if user == nil
failed_login(”Your username or password is incorrect.”)
elsif user.activated_at.blank?
failed_login(”Your account is not active, please check your email for the activation code.”)
elsif user.enabled == false
failed_login(”Your account has been disabled.”)
else
self.current_user = user
successful_login
end
endprivate
def failed_login(message)
flash.now[:error] = message
render :action => ‘new’
enddef successful_login
if params[:remember_me] == “1″
self.current_user.remember_me
cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
end
flash[:notice] = “Logged in successfully”
return_to = session[:return_to]
if return_to.nil?
redirect_to user_path(self.current_user)
else
redirect_to return_to
end
endend
# open file: app/controllers/roles_controller.rb
class RolesController < ApplicationController
layout ‘application’
before_filter :check_super_user_roledef index
@user = User.find(params[:user_id])
@all_roles = Role.find(:all)
enddef update
@user = User.find(params[:user_id])
@role = Role.find(params[:id])
unless @user.has_role?(@role.name)
@user.roles << @role
end
redirect_to :action => ‘index’
enddef destroy
@user = User.find(params[:user_id])
@role = Role.find(params[:id])
if @user.has_role?(@role.name)
@user.roles.delete(@role)
else
redirect_to :action => ‘index’
endend
# open file: app/controllers/permissions_controller.rb
# remember, there is no Permissions controller, just a Permission join model that links roles with users using the “has_many :y, :through => :z” relationship in both of those joined models.
# open file: app/controllers/passwords_controller.rb
class PasswordsController < ApplicationController
layout ‘application’
before_filter :not_logged_in_required, :only => [:new, :create]# Enter email address to recover password
def new
end# Forgot password action
def create
return unless request.post?
if @user = User.find_for_forget(params[:email])
@user.forgot_password
@user.save
flash[:notice] = “A password reset link has been sent to your email address.”
redirect_to login_path
else
flash[:notice] = “Could not find a user with that email address.”
render :action => ‘new’
end
end# Action triggered by clicking on the /reset_password/:id link recieved via email
# Makes sure the id code is included
# Checks that the id code matches a user in the database
# Then if everything checks out, shows the password reset fields
def edit
if params[:id].nil?
render :action => ‘new’
return
end
@user = User.find_by_password_reset_code(params[:id]) if params[:id]
raise if @user.nil?
rescue
logger.error “Invalid Reset Code entered.”
flash[:notice] = “Sorry - That is an invalid password reset code. Please check your code and try again. (Perhaps your email client inserted a carriage return?)”
#redirect_back_or_default(’/')
redirect_to new_user_path
end# Reset password action /reset_password/:id
# Checks once again that an id is included and makes sure that the password field isn’t blank
def update
if params[:id].nil?
render :action => ‘new’
return
end
if params[:password].blank?
flash[:notice] = “Password field cannot be blank.”
render :action => ‘edit’, :id => params[:id]
return
end
@user = User.find_by_password_reset_code(params[:id]) if params[:id]
raise if @user.nil?
return if @user unless params[:password]
if (params[:password] == params[:password_confirmation])
#Uncomment and comment lines with @user to have the user logged in after reset - not recommended
#self.current_user = @user #for the next two lines to work
#current_user.password_confirmation = params[:password_confirmation]
#current_user.password = params[:password]
#@user.reset_password
#flash[:notice] = current_user.save ? “Password reset” : “Password not reset”
@user.password_confirmation = params[:password_confirmation]
@user.password = params[:password]
@user.reset_password
flash[:notice] = @user.save ? “Password reset.” : “Password not reset.”
else
flash[:notice] = “Password mismatch.”
render :action => ‘edit’, :id => params[:id]
return
end
redirect_to login_path
rescue
logger.error “Invalid Reset Code entered”
flash[:notice] = “Sorry - That is an invalid password reset code. Please check your code and try again. (Perhaps your email client inserted a carriage return?)”
redirect_to new_user_path
endend
# open file: app/controllers/user_accounts_controller.rb
class UserAccountsController < ApplicationController
layout ‘application’
before_filter :login_required, :except => :show
before_filter :not_logged_in_required, :only => :show# Activate action
def show
# Uncomment and change paths to have user logged in after activation - not recommended
#self.current_user = User.find_and_activate!(params[:id])
User.find_and_activate!(params[:id])
flash[:notice] = “Your account has been activated! You can now login.”
redirect_to login_path
rescue User::ArgumentError
flash[:notice] = ‘Activation code not found. Please try creating a new account.’
redirect_to new_user_path
rescue User::ActivationCodeNotFound
flash[:notice] = ‘Activation code not found. Please try creating a new account.’
redirect_to new_user_path
rescue User::AlreadyActivated
flash[:notice] = ‘Your account has already been activated. You can log in below.’
redirect_to login_path
enddef edit
end# Change password action
def update
return unless request.post?
if User.authenticate(current_user.login, params[:old_password])
if ((params[:password] == params[:password_confirmation]) && !params[:password_confirmation].blank?)
current_user.password_confirmation = params[:password_confirmation]
current_user.password = params[:password]
if current_user.save
flash[:notice] = “Password successfully updated.”
redirect_to root_path #profile_url(current_user.login)
else
flash[:error] = “An error occured, your password was not changed.”
render :action => ‘edit’
end
else
flash[:error] = “New password does not match the password confirmation.”
@old_password = params[:old_password]
render :action => ‘edit’
end
else
flash[:error] = “Your old password is incorrect.”
render :action => ‘edit’
end
endend
——————————————
# VIEWS
# To complete our MVC architecture, we’ll configure our application Views
# open/create file: app/views/layouts/application.html.erb
# add this navigation as your eyes sees fit
<ul>
<% if logged_in? %>
<li>Logged in as:</li>
<li><%= link_to h(current_user.login.capitalize), user_path(current_user) %></li>
<ul>
<li><%= link_to ‘Edit Profile’, edit_user_path(current_user) %></li>
<li><%= link_to ‘Change Password’, change_password_path %></li>
<li><%= link_to ‘Log Out’, logout_url %></li>
</ul>
<% if current_user.has_role?(’administrator’) %>
<li><%= link_to ‘Administer Users’, users_path %></li>
<% end %>
<% else %>
<li><%= link_to ‘Log In’, new_session_path %></li>
<li><%= link_to ‘Sign Up’, new_user_path %></li>
<li><%= link_to ‘Forgot Password?’, forgot_password_path %></li>
<% end %>
</ul><%= yield %>
# create file: app/views/roles/_role.html.erb
<li>
<%= role.name %>
<% if @user.has_role?(role.name) %>
<%= link_to ‘remove role’, user_role_url(:id => role.id, :user_id => @user.id), :method => :delete %>
<% else %>
<%= link_to ‘assign role’, user_role_url(:id => role.id, :user_id => @user.id), :method => :put %>
<% end %>
</li>
# create file: app/views/roles/index.html.erb
<h2>Roles for <%=h @user.login.capitalize %></h2>
<h3>Roles assigned:</h3>
<ul><%= render :partial => ‘role’, :collection => @user.roles %></ul><h3>Roles available:</h3>
<ul><%= render :partial => ‘role’, :collection => (@all_roles - @user.roles) %></ul>
# open file: app/views/sessions/new.html.erb
<h2>Login with User ID and Password:</h2>
<% form_tag session_path do %>
<p><label for=”login”>Login</label><br/>
<%= text_field_tag ‘login’ %></p><p><label for=”password”>Password</label><br />
<%= password_field_tag ‘password’ %></p><p><label for=”remember_me”>Remember me:</label>
<%= check_box_tag ‘remember_me’ %></p><p><%= submit_tag ‘Log in’ %><%= link_to “Sign Up”, new_user_path %></p>
<% end %>
# create file: app/views/users/_user.html.erb
<tr class=”<%= cycle(’odd’, ‘even’) %>”>
<td><%=h user.login %></td>
<td><%=h user.email %></td>
<td><%= user.enabled ? ‘yes’ : ‘no’ %>
<% unless user == current_user %>
<% if user.enabled %>
<%= link_to(’disable’, user_path(user.id), :method => :delete) %>
<% else %>
<%= link_to(’enable’, enable_user_path(user.id), :method => :put) %>
<% end %>
<% end %>
</td>
<td><%= link_to ‘edit roles’, user_roles_path(user) %>]</td>
</tr>
# create file: app/views/users/edit.html.erb
<h2>Edit Your Account</h2>
<p><%= link_to ‘Show Profile’, user_path(@user) %> | <%= link_to ‘Change Password’, change_password_path %></p>
<%= error_messages_for :user %>
<% form_for :user, :url => user_url(@user), :html => { :method => :put } do |f| %>
<p>Email:<br /><%= f.text_field :email, :size => 60 %></p><%= submit_tag ‘Save’ %>
<% end %>
# create file: app/views/users/index.html.erb
<h2>All Users</h2>
<table>
<tr>
<th>Username</th>
<th>Email</th>
<th>Enabled?</th>
<th>Roles</th>
</tr>
<%= render :partial => ‘user’, :collection => @users %>
</table>
# open file: app/views/users/new.html.erb
# no necessary changes need to be made to this file at this time
# create file: app/views/users/show.html.erb
<h2>User: <%=h @user.login %></h2>
<p>Joined on: <%= @user.created_at.to_s(:long) %></p>
# create folder: app/views/user_mailers
# create file: app/views/user_mailers/activation.html.erb
<%=h @user.login %>, your account has been activated. To visit the site, follow the link below:
<%= @url %>
# create file: app/views/user_mailers/forgot_password.html.erb
<%=h @user.login %>, to reset your password, please visit
<%= @url %>
# create file: app/views/user_mailers/reset_password.html.erb
<%=h @user.login %>, your password has been reset.
# create file: app/views/user_mailers/signup_notification.html.erb
Your account has been created.
Username: <%=h @user.login %>
Visit this url to activate your account:
<%= @url %>
# create file: app/views/passwords/edit.html.erb
<% form_tag url_for(:action => “update”, :id => params[:id]) do %>
Password:<br />
<%= password_field_tag :password %><br />Confirm Password:<br />
<%= password_field_tag :password_confirmation %><br /><%= submit_tag “Reset Your Password” %>
<% end %>
# create file: app/views/passwords/new.html.erb
<h2>Forgot Password</h2>
<% form_tag url_for(:action => ‘create’) do %>
What is the email address used to create your account?<br />
<%= text_field_tag :email, “”, :size => 50 %><br />
<%= submit_tag ‘Reset Password’ %>
<% end %>
# create file: app/views/user_accounts/edit.html.erb
<% form_tag url_for(:action => “update”) do %>
<p><label for=”old_password” class=”block”>Old Password</label><br />
<%= password_field_tag ‘old_password’, @old_password, :size => 45 %></p><p><label for=”password” class=”block”>New Password</label><br />
<%= password_field_tag ‘password’, {}, :size => 45 %><br />
<small>Between 4 and 40 characters</small></p><p><label for=”password_confirmation” class=”block”>Confirm new password</label><br />
<%= password_field_tag ‘password_confirmation’, {}, :size => 45 %></p><%= submit_tag ‘Change password’ %>
<% end %>
——————————————
# Start ‘er up!
ruby script/server -p 3000
# Done!
























You must be logged in to post a comment.