Moving modules inside respective Models module.
This commit is contained in:
parent
bfd0617a0e
commit
aaddc05f99
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue