追記
OpenID対応前までをちゃんとやった。
本文
app/model/user.rbを編集する。
% diff user.rb.org user.rb
12c12
< validates_length_of :email, :within => 3..100
---
> validates_length_of :email, :within => 6..100
13a14,17
>
> has_many :permissions
> has_many :roles, :through => :permissions
>
15c19
< before_create :make_activation_code
---
> before_create :make_activation_code
20,25c24,45
< # Activates the user in the database.
< def activate
< @activated = true
< self.activated_at = Time.now.utc
< self.activation_code = nil
< save(false)
---
> class 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
32a53,57
> # Returns true if the user has just been activated.
> def pendings?
> @activated
> end
>
35c60
< u = find :first, :conditions => ['login = ? and activated_at IS NOT NULL', login] # need to get the salt
---
> u = find :first, :conditions => ['login = ?', login] # need to get the salt
54c79
< remember_token_expires_at && Time.now.utc < remember_token_expires_at
---
> remember_token_expires_at && Time.now.utc < remember_token_expires_at
78,80c103,129
< # Returns true if the user has just been activated.
< def recently_activated?
< @activated
---
> def forgot_password
> @forgotten_password = true
> self.make_password_reset_code
> end
>
> def reset_password
> # First update the password_reset_code before setting the
> # reset_password flag to avoicd duplicate email notifications.
> update_attribute(:password_reset_code, nil)
> @reset_password = true
> end
>
> # used in user_observer
> def recently_forgot_password?
> @forgotten_password
> end
>
> def recently_reset_password?
> @reset_password
> end
>
> def self.find_for_forget(email)
> find :first, :conditions => ['email = ? and activated_at IS NOT NULL', email]
> end
>
> def has_role?(rolename)
> self.roles.find_by_rolename(rolename) ? true : false
84c133
< # before filter
---
> # before filter
90c139
<
---
>
94c143
<
---
>
99c148,158
<
---
>
> def make_password_reset_code
> self.password_reset_code = Digest::SHA1.hexdigest( Time.now.to_split(//).sort_by{ rand}.join)
> end
>
> private
>
> def activate!
> @activated = true
> self.update_attribute(:activated_at, Time.now.utc)
> endlib/authenticated_system.rbにメソッド not_logged_in_required, check_role, check_administrator_role, and permission_deniedを付け加える。
% diff authenticated_system.rb.org authenticated_system.rb
9c9
< # Accesses the current user from the session.
---
> # Accesses the current user from the session.
54a55,73
> def not_logged_in_required
> !logged_in? || permission_denied
> end
>
> 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
>
> def check_administrator_role
> check_role('administrator')
> end
>
74a94,121
> 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 permisson 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")
> reander :text => "You don't have permission to complete this action.", :status => '401 Unauthorized'
> end
> end
> end
>
81a129,131
> def store_referer
> session[:refer_to] = request.env["HTTP_REFERER"]
> end
チュートリアルの著者は permission_deniedメソッドは改善が必要だと思っているとのこと。
次はコントローラーを変更する。方針はよりRESTfulにするのが目的らしい。まずはapp/controller/user_controller.rb
% diff users_controller.rb.org users_controller.rb 2,4c2,14 < # Be sure to include AuthenticationSystem in Application Controller instead < include AuthenticatedSystem < --- > layout 'application' > before_filter :not_logged_in_required, :only=>[:new, :create] > before_filter :login_required, :only=>[:show, :edit, :update] > before_filter :check_administrator_role, :only => [:index, :destroy, :enable] > > def index > @users = User.find(:all) > end > > # This show action only allows users to view their own provfile > def show > @user = current_user > end 7a18 > @user = User.new 12c23 < # protects against session fixation attacks, wreaks havoc with --- > # protects against session fixation attacks, wreaks havoc with 17c28,32 < @user.save --- > @user.save! > # Uncomment to have the user 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." >
次にsession_controller.rb
% diff sessions_controller.rb.org sessions_controller.rb
1c1
< # This controller handles the login/logout function of the site.
---
> # This controller handles the login/logout function of the site.
3,4c3,5
< # Be sure to include AuthenticationSystem in Application Controller instead
< include AuthenticatedSystem
---
> layout 'application'
> before_filter :login_required, :only => :destroy
> before_filter :not_logged_in_required, :only => [:new, :create]
11,21c12
< self.current_user = User.authenticate(params[:login], params[:password])
< if logged_in?
< if params[:remember_me] == "1"
< current_user.remember_me unless current_user.remember_token?
< cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
< end
< redirect_back_or_default('/')
< flash[:notice] = "Logged in successfully"
< else
< render :action => 'new'
< end
---
> password_authentication(params[:login], params[:password])
29c20,36
< redirect_back_or_default('/')
---
> redirect_to_ login_path
> end
>
> protected
> #Updated 2/20/08
> def password_authentication(login, password)
> user = User.authenticate(login, password)
> if user == nil
> faild_login("Your username or password is incorrect.")
> elsif user.activated_at.blank?
> faild_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
30a38,59
>
> private
>
> def failed_login(message)
> flash.now[:error] = message
> render :action =>'new'
> end
>
> def successful_login
> if params[:remember_me] == "l"
> self.current_user.remenber_me
> collkies[: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
> end次に二つのコントローラーを作成する。
% ruby script/generate controller Passwords % ruby script/generate controller Accounts
つづく。