追記
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) > end
lib/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
つづく。