Moving modules inside respective Models module.

This commit is contained in:
Carlos A. da Silva 2009-10-09 08:30:25 -03:00
parent bfd0617a0e
commit aaddc05f99
9 changed files with 265 additions and 252 deletions

View File

@ -14,10 +14,9 @@ end
require 'devise/initializers/warden'
require 'devise/models/authenticable'
require 'devise/models/confirmable'
require 'devise/models/recoverable'
require 'devise/models/validatable'
module Devise
end
ActionController::Base.send :include, Devise::Controllers::Authenticable
ActionView::Base.send :include, DeviseHelper
ActionController::Base.send :include, Devise::Controllers::Authenticable
#ActiveRecord::Base.send :include, Devise

View File

@ -2,6 +2,10 @@ module Devise
module Controllers
module Authenticable
# Helper for use in before_filters where no authentication is required:
# Example:
# before_filter :require_no_authentication, :only => :new
#
def require_no_authentication
redirect_to root_path if authenticated?
end

View File

@ -1,76 +1,78 @@
module Devise
module Authenticable
require 'digest/sha1'
module Models
module Authenticable
require 'digest/sha1'
mattr_accessor :pepper, :stretches
# Pepper for encrypting password
self.pepper = '23c64df433d9b08e464db5c05d1e6202dd2823f0'
# Encrypt password as many times as possible
self.stretches = 10
mattr_accessor :pepper, :stretches
# Pepper for encrypting password
self.pepper = '23c64df433d9b08e464db5c05d1e6202dd2823f0'
# Encrypt password as many times as possible
self.stretches = 10
def self.included(base)
base.class_eval do
extend ClassMethods
def self.included(base)
base.class_eval do
extend ClassMethods
before_save :generate_salt
before_save :encrypt_password
before_save :generate_salt
before_save :encrypt_password
attr_accessor :password, :password_confirmation
attr_accessible :email, :password, :password_confirmation
end
end
# Verifies whether an incoming_password (ie from login) is the user password
#
def valid_password?(incoming_password)
password_digest(incoming_password) == encrypted_password
end
private
# Generate password salt using SHA1 based on password and Time.now
#
def generate_salt
self.password_salt = secure_digest(Time.now.utc, random_string, password) if password_salt.blank?
attr_accessor :password, :password_confirmation
attr_accessible :email, :password, :password_confirmation
end
end
# Encrypt password using SHA1
# Verifies whether an incoming_password (ie from login) is the user password
#
def encrypt_password
self.encrypted_password = password_digest(password) unless password.blank?
def valid_password?(incoming_password)
password_digest(incoming_password) == encrypted_password
end
# Gererates a default password digest based on salt, pepper and the
# incoming password
#
def password_digest(password_to_digest)
digest = pepper
stretches.times { digest = secure_digest(password_salt, digest, password_to_digest, pepper)}
digest
end
private
# Generate a SHA1 digest joining args. Generated token is something like
#
# --arg1--arg2--arg3--argN--
#
def secure_digest(*tokens)
::Digest::SHA1.hexdigest('--' << tokens.flatten.join('--') << '--')
end
# Generate password salt using SHA1 based on password and Time.now
#
def generate_salt
self.password_salt = secure_digest(Time.now.utc, random_string, password) if password_salt.blank?
end
# Generate a string randomically based on rand method
#
def random_string
(1..10).map{ rand.to_s }
end
# Encrypt password using SHA1
#
def encrypt_password
self.encrypted_password = password_digest(password) unless password.blank?
end
module ClassMethods
# Gererates a default password digest based on salt, pepper and the
# incoming password
#
def password_digest(password_to_digest)
digest = pepper
stretches.times { digest = secure_digest(password_salt, digest, password_to_digest, pepper)}
digest
end
# Authenticate a user based on email and password. Returns the
# authenticated user if it's valid or nil
#
def authenticate(email, password)
authenticable = self.find_by_email(email)
authenticable if authenticable.valid_password?(password) unless authenticable.nil?
# Generate a SHA1 digest joining args. Generated token is something like
#
# --arg1--arg2--arg3--argN--
#
def secure_digest(*tokens)
::Digest::SHA1.hexdigest('--' << tokens.flatten.join('--') << '--')
end
# Generate a string randomically based on rand method
#
def random_string
(1..10).map{ rand.to_s }
end
module ClassMethods
# Authenticate a user based on email and password. Returns the
# authenticated user if it's valid or nil
#
def authenticate(email, password)
authenticable = self.find_by_email(email)
authenticable if authenticable.valid_password?(password) unless authenticable.nil?
end
end
end
end

View File

@ -1,91 +1,93 @@
module Devise
module Confirmable
require 'devise/models/perishable'
module Models
module Confirmable
require 'devise/models/perishable'
def self.included(base)
base.class_eval do
include ::Devise::Perishable
extend ClassMethods
def self.included(base)
base.class_eval do
include ::Devise::Models::Perishable
extend ClassMethods
after_create :send_confirmation_instructions
before_update :reset_confirmation, :if => :email_changed?
after_update :send_confirmation_instructions, :if => :email_changed?
after_create :send_confirmation_instructions
before_update :reset_confirmation, :if => :email_changed?
after_update :send_confirmation_instructions, :if => :email_changed?
end
end
end
# Confirm a user by setting it's confirmed_at to actual time. If the user
# is already confirmed, add en error to email field
#
def confirm!
unless confirmed?
update_attribute(:confirmed_at, Time.now)
else
errors.add(:email, :already_confirmed, :default => 'already confirmed')
false
end
end
# Verifies whether a user is confirmed or not
#
def confirmed?
!new_record? && confirmed_at?
end
# Send confirmation instructions by email
#
def send_confirmation_instructions
::Notifier.deliver_confirmation_instructions(self)
end
# Remove confirmation date and send confirmation instructions, to ensure
# after sending these instructions the user won't be able to sign in without
# confirming it's account
#
def reset_confirmation!
reset_confirmation
reset_perishable_token!
send_confirmation_instructions
end
private
# Remove confirmation date from the user, ensuring after a user update it's
# email, it won't be able to sign in without confirming it.
# Confirm a user by setting it's confirmed_at to actual time. If the user
# is already confirmed, add en error to email field
#
def reset_confirmation
self.confirmed_at = nil
def confirm!
unless confirmed?
update_attribute(:confirmed_at, Time.now)
else
errors.add(:email, :already_confirmed, :default => 'already confirmed')
false
end
end
module ClassMethods
# Hook default authenticate to test whether the account is confirmed or not
# Returns the authenticated_user if it's confirmed, otherwise returns nil
# Verifies whether a user is confirmed or not
#
def authenticate(email, password)
confirmable = super
confirmable if confirmable.confirmed? unless confirmable.nil?
def confirmed?
!new_record? && confirmed_at?
end
# Attempt to find a user by it's email. If a record is found, send new
# confirmation instructions to it. If not user is found, returns a new user
# with an email not found error.
# Options must contain the user email
# Send confirmation instructions by email
#
def send_confirmation_instructions(options={})
confirmable = find_or_initialize_with_error_by_email(options[:email])
confirmable.reset_confirmation! unless confirmable.new_record?
confirmable
def send_confirmation_instructions
::Notifier.deliver_confirmation_instructions(self)
end
# Find a user by it's confirmation token and try to confirm it.
# If no user is found, returns a new user
# If the user is already confirmed, create an error for the user
# Options must have the perishable_token
# Remove confirmation date and send confirmation instructions, to ensure
# after sending these instructions the user won't be able to sign in without
# confirming it's account
#
def confirm!(options={})
confirmable = find_or_initialize_with_error_by_perishable_token(options[:perishable_token])
confirmable.confirm! unless confirmable.new_record?
confirmable
def reset_confirmation!
reset_confirmation
reset_perishable_token!
send_confirmation_instructions
end
private
# Remove confirmation date from the user, ensuring after a user update it's
# email, it won't be able to sign in without confirming it.
#
def reset_confirmation
self.confirmed_at = nil
end
module ClassMethods
# Hook default authenticate to test whether the account is confirmed or not
# Returns the authenticated_user if it's confirmed, otherwise returns nil
#
def authenticate(email, password)
confirmable = super
confirmable if confirmable.confirmed? unless confirmable.nil?
end
# Attempt to find a user by it's email. If a record is found, send new
# confirmation instructions to it. If not user is found, returns a new user
# with an email not found error.
# Options must contain the user email
#
def send_confirmation_instructions(options={})
confirmable = find_or_initialize_with_error_by_email(options[:email])
confirmable.reset_confirmation! unless confirmable.new_record?
confirmable
end
# Find a user by it's confirmation token and try to confirm it.
# If no user is found, returns a new user
# If the user is already confirmed, create an error for the user
# Options must have the perishable_token
#
def confirm!(options={})
confirmable = find_or_initialize_with_error_by_perishable_token(options[:perishable_token])
confirmable.confirm! unless confirmable.new_record?
confirmable
end
end
end
end

View File

@ -1,48 +1,50 @@
module Devise
module Perishable
module Models
module Perishable
def self.included(base)
base.class_eval do
extend ClassMethods
def self.included(base)
base.class_eval do
extend ClassMethods
before_create :reset_perishable_token
end
end
# Generates a new random token for confirmation, based on actual Time and salt
#
def reset_perishable_token
self.perishable_token = secure_digest(Time.now.utc, random_string, password)
end
# Resets the perishable token with and save the record without validating
#
def reset_perishable_token!
reset_perishable_token and save(false)
end
module ClassMethods
# Attempt to find a user by and incoming perishable_token. If no user is
# found, initialize a new one and adds an :invalid error to perishable_token
#
def find_or_initialize_with_error_by_perishable_token(perishable_token)
perishable = find_or_initialize_by_perishable_token(perishable_token)
if perishable.new_record?
perishable.errors.add(:perishable_token, :invalid, :default => 'invalid confirmation')
before_create :reset_perishable_token
end
perishable
end
# Attempt to find a user by it's email. If not user is found, returns a
# new user with an email not found error.
# Generates a new random token for confirmation, based on actual Time and salt
#
def find_or_initialize_with_error_by_email(email)
perishable = find_or_initialize_by_email(email)
if perishable.new_record?
perishable.errors.add(:email, :not_found, :default => 'not found')
def reset_perishable_token
self.perishable_token = secure_digest(Time.now.utc, random_string, password)
end
# Resets the perishable token with and save the record without validating
#
def reset_perishable_token!
reset_perishable_token and save(false)
end
module ClassMethods
# Attempt to find a user by and incoming perishable_token. If no user is
# found, initialize a new one and adds an :invalid error to perishable_token
#
def find_or_initialize_with_error_by_perishable_token(perishable_token)
perishable = find_or_initialize_by_perishable_token(perishable_token)
if perishable.new_record?
perishable.errors.add(:perishable_token, :invalid, :default => 'invalid confirmation')
end
perishable
end
# Attempt to find a user by it's email. If not user is found, returns a
# new user with an email not found error.
#
def find_or_initialize_with_error_by_email(email)
perishable = find_or_initialize_by_email(email)
if perishable.new_record?
perishable.errors.add(:email, :not_found, :default => 'not found')
end
perishable
end
perishable
end
end
end

View File

@ -1,57 +1,59 @@
module Devise
module Recoverable
require 'devise/models/perishable'
module Models
module Recoverable
require 'devise/models/perishable'
def self.included(base)
base.class_eval do
include ::Devise::Perishable
extend ClassMethods
end
end
# Update password
#
def reset_password(new_password, new_password_confirmation)
self.password = new_password
self.password_confirmation = new_password_confirmation
end
# Update password saving the record
#
def reset_password!(new_password, new_password_confirmation)
reset_password(new_password, new_password_confirmation) and save
end
# Resets perishable token and send reset password instructions by email
#
def send_reset_password_instructions
reset_perishable_token!
::Notifier.deliver_reset_password_instructions(self)
end
module ClassMethods
# Attempt to find a user by it's email. If a record is found, send new
# password instructions to it. If not user is found, returns a new user
# with an email not found error.
# Options must contain the user email
#
def send_reset_password_instructions(options={})
recoverable = find_or_initialize_with_error_by_email(options[:email])
recoverable.send_reset_password_instructions unless recoverable.new_record?
recoverable
def self.included(base)
base.class_eval do
include ::Devise::Models::Perishable
extend ClassMethods
end
end
# Attempt to find a user by it's perishable_token to reset it's password.
# If a user is found, reset it's password and automatically try saving the
# record. If not user is found, returns a new user containing an error
# in perishable_token attribute.
# Options must contain perishable_token, password and confirmation
# Update password
#
def reset_password!(options={})
recoverable = find_or_initialize_with_error_by_perishable_token(options[:perishable_token])
recoverable.reset_password!(options[:password], options[:password_confirmation]) unless recoverable.new_record?
recoverable
def reset_password(new_password, new_password_confirmation)
self.password = new_password
self.password_confirmation = new_password_confirmation
end
# Update password saving the record
#
def reset_password!(new_password, new_password_confirmation)
reset_password(new_password, new_password_confirmation) and save
end
# Resets perishable token and send reset password instructions by email
#
def send_reset_password_instructions
reset_perishable_token!
::Notifier.deliver_reset_password_instructions(self)
end
module ClassMethods
# Attempt to find a user by it's email. If a record is found, send new
# password instructions to it. If not user is found, returns a new user
# with an email not found error.
# Options must contain the user email
#
def send_reset_password_instructions(options={})
recoverable = find_or_initialize_with_error_by_email(options[:email])
recoverable.send_reset_password_instructions unless recoverable.new_record?
recoverable
end
# Attempt to find a user by it's perishable_token to reset it's password.
# If a user is found, reset it's password and automatically try saving the
# record. If not user is found, returns a new user containing an error
# in perishable_token attribute.
# Options must contain perishable_token, password and confirmation
#
def reset_password!(options={})
recoverable = find_or_initialize_with_error_by_perishable_token(options[:perishable_token])
recoverable.reset_password!(options[:password], options[:password_confirmation]) unless recoverable.new_record?
recoverable
end
end
end
end

View File

@ -1,29 +1,31 @@
module Devise
module Validatable
module Models
module Validatable
# Email regex used to validate email formats
#
EMAIL_REGEX = /\A[\w\.%\+\-]+@(?:[A-Z0-9\-]+\.)+(?:[A-Z]{2,4}|museum|travel)\z/i
def self.included(base)
base.class_eval do
validates_presence_of :email
validates_uniqueness_of :email, :allow_blank => true
validates_format_of :email, :with => EMAIL_REGEX, :allow_blank => true
validates_presence_of :password, :if => :password_required?
validates_confirmation_of :password, :if => :password_required?
validates_length_of :password, :within => 6..20, :allow_blank => true, :if => :password_required?
end
end
private
# Checks whether a password is needed or not. For validations only.
# Email regex used to validate email formats
#
def password_required?
new_record? || !password.nil? || !password_confirmation.nil?
EMAIL_REGEX = /\A[\w\.%\+\-]+@(?:[A-Z0-9\-]+\.)+(?:[A-Z]{2,4}|museum|travel)\z/i
def self.included(base)
base.class_eval do
validates_presence_of :email
validates_uniqueness_of :email, :allow_blank => true
validates_format_of :email, :with => EMAIL_REGEX, :allow_blank => true
validates_presence_of :password, :if => :password_required?
validates_confirmation_of :password, :if => :password_required?
validates_length_of :password, :within => 6..20, :allow_blank => true, :if => :password_required?
end
end
private
# Checks whether a password is needed or not. For validations only.
#
def password_required?
new_record? || !password.nil? || !password_confirmation.nil?
end
end
end
end

View File

@ -84,8 +84,8 @@ class AuthenticableTest < ActiveSupport::TestCase
end
test 'should encrypt password using a sha1 hash' do
Devise::Authenticable.pepper = 'pepper'
Devise::Authenticable.stretches = 1
Devise::Models::Authenticable.pepper = 'pepper'
Devise::Models::Authenticable.stretches = 1
user = create_user
expected_password = ::Digest::SHA1.hexdigest("--#{user.password_salt}--pepper--123456--pepper--")
assert_equal expected_password, user.encrypted_password

View File

@ -1,6 +1,6 @@
class User < ActiveRecord::Base
include Devise::Authenticable
include Devise::Confirmable
include Devise::Recoverable
include Devise::Validatable
include Devise::Models::Authenticable
include Devise::Models::Confirmable
include Devise::Models::Recoverable
include Devise::Models::Validatable
end