devise-security 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +38 -0
- data/.rubocop.yml +42 -0
- data/.travis.yml +14 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +199 -0
- data/LICENSE.txt +20 -0
- data/README.md +263 -0
- data/Rakefile +26 -0
- data/app/controllers/devise/paranoid_verification_code_controller.rb +42 -0
- data/app/controllers/devise/password_expired_controller.rb +48 -0
- data/app/views/devise/paranoid_verification_code/show.html.erb +10 -0
- data/app/views/devise/password_expired/show.html.erb +16 -0
- data/config/locales/de.yml +16 -0
- data/config/locales/en.yml +17 -0
- data/config/locales/es.yml +17 -0
- data/config/locales/it.yml +10 -0
- data/devise-security.gemspec +34 -0
- data/lib/devise-security.rb +106 -0
- data/lib/devise-security/controllers/helpers.rb +96 -0
- data/lib/devise-security/hooks/expirable.rb +10 -0
- data/lib/devise-security/hooks/paranoid_verification.rb +5 -0
- data/lib/devise-security/hooks/password_expirable.rb +5 -0
- data/lib/devise-security/hooks/session_limitable.rb +27 -0
- data/lib/devise-security/models/database_authenticatable_patch.rb +26 -0
- data/lib/devise-security/models/expirable.rb +120 -0
- data/lib/devise-security/models/old_password.rb +4 -0
- data/lib/devise-security/models/paranoid_verification.rb +35 -0
- data/lib/devise-security/models/password_archivable.rb +80 -0
- data/lib/devise-security/models/password_expirable.rb +67 -0
- data/lib/devise-security/models/secure_validatable.rb +100 -0
- data/lib/devise-security/models/security_questionable.rb +18 -0
- data/lib/devise-security/models/session_limitable.rb +21 -0
- data/lib/devise-security/orm/active_record.rb +20 -0
- data/lib/devise-security/patches.rb +21 -0
- data/lib/devise-security/patches/confirmations_controller_captcha.rb +21 -0
- data/lib/devise-security/patches/confirmations_controller_security_question.rb +25 -0
- data/lib/devise-security/patches/controller_captcha.rb +17 -0
- data/lib/devise-security/patches/controller_security_question.rb +20 -0
- data/lib/devise-security/patches/passwords_controller_captcha.rb +20 -0
- data/lib/devise-security/patches/passwords_controller_security_question.rb +24 -0
- data/lib/devise-security/patches/registrations_controller_captcha.rb +33 -0
- data/lib/devise-security/patches/sessions_controller_captcha.rb +24 -0
- data/lib/devise-security/patches/unlocks_controller_captcha.rb +20 -0
- data/lib/devise-security/patches/unlocks_controller_security_question.rb +24 -0
- data/lib/devise-security/rails.rb +17 -0
- data/lib/devise-security/routes.rb +17 -0
- data/lib/devise-security/schema.rb +59 -0
- data/lib/devise-security/version.rb +3 -0
- data/lib/generators/devise-security/install_generator.rb +26 -0
- data/lib/generators/templates/devise-security.rb +38 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/controllers/application_controller.rb +2 -0
- data/test/dummy/app/controllers/captcha/sessions_controller.rb +3 -0
- data/test/dummy/app/controllers/foos_controller.rb +0 -0
- data/test/dummy/app/controllers/security_question/unlocks_controller.rb +3 -0
- data/test/dummy/app/models/.gitkeep +0 -0
- data/test/dummy/app/models/captcha_user.rb +5 -0
- data/test/dummy/app/models/secure_user.rb +3 -0
- data/test/dummy/app/models/security_question_user.rb +6 -0
- data/test/dummy/app/models/user.rb +5 -0
- data/test/dummy/app/views/foos/index.html.erb +0 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +24 -0
- data/test/dummy/config/boot.rb +6 -0
- data/test/dummy/config/database.yml +7 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/test.rb +27 -0
- data/test/dummy/config/initializers/devise.rb +9 -0
- data/test/dummy/config/initializers/migration_class.rb +6 -0
- data/test/dummy/config/routes.rb +10 -0
- data/test/dummy/config/secrets.yml +3 -0
- data/test/dummy/db/migrate/20120508165529_create_tables.rb +33 -0
- data/test/dummy/db/migrate/20150402165590_add_verification_columns.rb +11 -0
- data/test/dummy/db/migrate/20150407162345_add_verification_attempt_column.rb +9 -0
- data/test/dummy/db/migrate/20160320162345_add_security_questions_fields.rb +8 -0
- data/test/test_captcha_controller.rb +58 -0
- data/test/test_helper.rb +13 -0
- data/test/test_install_generator.rb +16 -0
- data/test/test_paranoid_verification.rb +124 -0
- data/test/test_password_archivable.rb +61 -0
- data/test/test_password_expirable.rb +32 -0
- data/test/test_password_expired_controller.rb +29 -0
- data/test/test_secure_validatable.rb +85 -0
- data/test/test_security_question_controller.rb +60 -0
- metadata +315 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
# Updates the last_activity_at fields from the record. Only when the user is active
|
2
|
+
# for authentication and authenticated.
|
3
|
+
# An expiry of the account is only checked on sign in OR on manually setting the
|
4
|
+
# expired_at to the past (see Devise::Models::Expirable for this)
|
5
|
+
Warden::Manager.after_set_user do |record, warden, options|
|
6
|
+
if record && record.respond_to?(:active_for_authentication?) && record.active_for_authentication? &&
|
7
|
+
warden.authenticated?(options[:scope]) && record.respond_to?(:update_last_activity!)
|
8
|
+
record.update_last_activity!
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# After each sign in, update unique_session_id.
|
2
|
+
# This is only triggered when the user is explicitly set (with set_user)
|
3
|
+
# and on authentication. Retrieving the user from session (:fetch) does
|
4
|
+
# not trigger it.
|
5
|
+
Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
|
6
|
+
if record.respond_to?(:update_unique_session_id!) && warden.authenticated?(options[:scope])
|
7
|
+
unique_session_id = Devise.friendly_token
|
8
|
+
warden.session(options[:scope])['unique_session_id'] = unique_session_id
|
9
|
+
record.update_unique_session_id!(unique_session_id)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Each time a record is fetched from session we check if a new session from another
|
14
|
+
# browser was opened for the record or not, based on a unique session identifier.
|
15
|
+
# If so, the old account is logged out and redirected to the sign in page on the next request.
|
16
|
+
Warden::Manager.after_set_user :only => :fetch do |record, warden, options|
|
17
|
+
scope = options[:scope]
|
18
|
+
env = warden.request.env
|
19
|
+
|
20
|
+
if record.respond_to?(:unique_session_id) && warden.authenticated?(scope) && options[:store] != false
|
21
|
+
if record.unique_session_id != warden.session(scope)['unique_session_id'] && !env['devise.skip_session_limitable']
|
22
|
+
warden.raw_session.clear
|
23
|
+
warden.logout(scope)
|
24
|
+
throw :warden, :scope => scope, :message => :session_limited
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Devise
|
2
|
+
module Models
|
3
|
+
module DatabaseAuthenticatablePatch
|
4
|
+
def update_with_password(params, *options)
|
5
|
+
current_password = params.delete(:current_password)
|
6
|
+
|
7
|
+
new_password = params[:password]
|
8
|
+
new_password_confirmation = params[:password_confirmation]
|
9
|
+
|
10
|
+
result = if valid_password?(current_password) && new_password.present? && new_password_confirmation.present?
|
11
|
+
update_attributes(params, *options)
|
12
|
+
else
|
13
|
+
self.assign_attributes(params, *options)
|
14
|
+
self.valid?
|
15
|
+
self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
|
16
|
+
self.errors.add(:password, new_password.blank? ? :blank : :invalid)
|
17
|
+
self.errors.add(:password_confirmation, new_password_confirmation.blank? ? :blank : :invalid)
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
clean_up_passwords
|
22
|
+
result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'devise-security/hooks/expirable'
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Models
|
5
|
+
# Deactivate the account after a configurable amount of time. To be able to
|
6
|
+
# tell, it tracks activity about your account with the following columns:
|
7
|
+
#
|
8
|
+
# * last_activity_at - A timestamp updated when the user requests a page (only signed in)
|
9
|
+
#
|
10
|
+
# == Options
|
11
|
+
# +:expire_after+ - Time interval to expire accounts after
|
12
|
+
#
|
13
|
+
# == Additions
|
14
|
+
# Best used with two cron jobs. One for expiring accounts after inactivity,
|
15
|
+
# and another, that deletes accounts, which have expired for a given amount
|
16
|
+
# of time (for example 90 days).
|
17
|
+
#
|
18
|
+
module Expirable
|
19
|
+
extend ActiveSupport::Concern
|
20
|
+
|
21
|
+
# Updates +last_activity_at+, called from a Warden::Manager.after_set_user hook.
|
22
|
+
def update_last_activity!
|
23
|
+
self.update_column(:last_activity_at, Time.now.utc)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Tells if the account has expired
|
27
|
+
#
|
28
|
+
# @return [bool]
|
29
|
+
def expired?
|
30
|
+
# expired_at set (manually, via cron, etc.)
|
31
|
+
return self.expired_at < Time.now.utc unless self.expired_at.nil?
|
32
|
+
# if it is not set, check the last activity against configured expire_after time range
|
33
|
+
return self.last_activity_at < self.class.expire_after.ago unless self.last_activity_at.nil?
|
34
|
+
# if last_activity_at is nil as well, the user has to be 'fresh' and is therefore not expired
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
# Expire an account. This is for cron jobs and manually expiring of accounts.
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# User.expire!
|
42
|
+
# User.expire! 1.week.from_now
|
43
|
+
# @note +expired_at+ can be in the future as well
|
44
|
+
def expire!(at = Time.now.utc)
|
45
|
+
self.expired_at = at
|
46
|
+
save(:validate => false)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Overwrites active_for_authentication? from Devise::Models::Activatable
|
50
|
+
# for verifying whether a user is active to sign in or not. If the account
|
51
|
+
# is expired, it should never be allowed.
|
52
|
+
#
|
53
|
+
# @return [bool]
|
54
|
+
def active_for_authentication?
|
55
|
+
super && !self.expired?
|
56
|
+
end
|
57
|
+
|
58
|
+
# The message sym, if {#active_for_authentication?} returns +false+. E.g. needed
|
59
|
+
# for i18n.
|
60
|
+
def inactive_message
|
61
|
+
!self.expired? ? super : :expired
|
62
|
+
end
|
63
|
+
|
64
|
+
module ClassMethods
|
65
|
+
::Devise::Models.config(self, :expire_after, :delete_expired_after)
|
66
|
+
|
67
|
+
# Sample method for daily cron to mark expired entries.
|
68
|
+
#
|
69
|
+
# @example You can overide this in your +resource+ model
|
70
|
+
# def self.mark_expired
|
71
|
+
# puts 'overwritten mark_expired'
|
72
|
+
# end
|
73
|
+
def mark_expired
|
74
|
+
all.each do |u|
|
75
|
+
u.expire! if u.expired? && u.expired_at.nil?
|
76
|
+
end
|
77
|
+
return
|
78
|
+
end
|
79
|
+
|
80
|
+
# Scope method to collect all expired users since +time+ ago
|
81
|
+
def expired_for(time = delete_expired_after)
|
82
|
+
where('expired_at < ?', time.seconds.ago)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Sample method for daily cron to delete all expired entries after a
|
86
|
+
# given amount of +time+.
|
87
|
+
#
|
88
|
+
# In your overwritten method you can "blank out" the object instead of
|
89
|
+
# deleting it.
|
90
|
+
#
|
91
|
+
# *Word of warning*: You have to handle the dependent method
|
92
|
+
# on the +resource+ relations (+:destroy+ or +:nullify+) and catch this
|
93
|
+
# behavior (see http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Deleting+from+associations).
|
94
|
+
#
|
95
|
+
# @example
|
96
|
+
# Resource.delete_all_expired_for 90.days
|
97
|
+
# @example You can overide this in your +resource+ model
|
98
|
+
# def self.delete_all_expired_for(time = 90.days)
|
99
|
+
# puts 'overwritten delete call'
|
100
|
+
# end
|
101
|
+
# @example Overwritten version to blank out the object.
|
102
|
+
# def self.delete_all_expired_for(time = 90.days)
|
103
|
+
# expired_for(time).each do |u|
|
104
|
+
# u.update_attributes first_name: nil, last_name: nil
|
105
|
+
# end
|
106
|
+
# end
|
107
|
+
def delete_all_expired_for(time)
|
108
|
+
expired_for(time).delete_all
|
109
|
+
end
|
110
|
+
|
111
|
+
# Version of {#delete_all_expired_for} without arguments (uses
|
112
|
+
# configured +delete_expired_after+ default value).
|
113
|
+
# @see #delete_all_expired_for
|
114
|
+
def delete_all_expired
|
115
|
+
delete_all_expired_for(delete_expired_after)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'devise-security/hooks/paranoid_verification'
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Models
|
5
|
+
# PasswordExpirable takes care of change password after
|
6
|
+
module ParanoidVerification
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def need_paranoid_verification?
|
10
|
+
!!paranoid_verification_code
|
11
|
+
end
|
12
|
+
|
13
|
+
def verify_code(code)
|
14
|
+
attempt = paranoid_verification_attempt
|
15
|
+
|
16
|
+
if (attempt += 1) >= Devise.paranoid_code_regenerate_after_attempt
|
17
|
+
generate_paranoid_code
|
18
|
+
elsif code == paranoid_verification_code
|
19
|
+
attempt = 0
|
20
|
+
update_without_password paranoid_verification_code: nil, paranoid_verified_at: Time.now, paranoid_verification_attempt: attempt
|
21
|
+
else
|
22
|
+
update_without_password paranoid_verification_attempt: attempt
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def paranoid_attempts_remaining
|
27
|
+
Devise.paranoid_code_regenerate_after_attempt - paranoid_verification_attempt
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate_paranoid_code
|
31
|
+
update_without_password paranoid_verification_code: Devise.verification_code_generator.call(), paranoid_verification_attempt: 0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Devise
|
2
|
+
module Models
|
3
|
+
# PasswordArchivable
|
4
|
+
module PasswordArchivable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
has_many :old_passwords, as: :password_archivable, dependent: :destroy
|
9
|
+
before_update :archive_password
|
10
|
+
validate :validate_password_archive
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate_password_archive
|
14
|
+
errors.add(:password, :taken_in_past) if encrypted_password_changed? and password_archive_included?
|
15
|
+
end
|
16
|
+
|
17
|
+
# validate is the password used in the past
|
18
|
+
def password_archive_included?
|
19
|
+
unless deny_old_passwords.is_a? 1.class
|
20
|
+
if deny_old_passwords.is_a? TrueClass and archive_count > 0
|
21
|
+
self.deny_old_passwords = archive_count
|
22
|
+
else
|
23
|
+
self.deny_old_passwords = 0
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if self.class.deny_old_passwords > 0 and not self.password.nil?
|
28
|
+
old_passwords_including_cur_change = self.old_passwords.order(:id).reverse_order.limit(self.class.deny_old_passwords).to_a
|
29
|
+
old_passwords_including_cur_change << OldPassword.new(old_password_params) # include most recent change in list, but don't save it yet!
|
30
|
+
old_passwords_including_cur_change.each do |old_password|
|
31
|
+
dummy = self.class.new
|
32
|
+
dummy.encrypted_password = old_password.encrypted_password
|
33
|
+
return true if dummy.valid_password?(password)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
def password_changed_to_same?
|
41
|
+
pass_change = encrypted_password_change
|
42
|
+
pass_change && pass_change.first == pass_change.last
|
43
|
+
end
|
44
|
+
|
45
|
+
def deny_old_passwords
|
46
|
+
self.class.deny_old_passwords
|
47
|
+
end
|
48
|
+
|
49
|
+
def deny_old_passwords=(count)
|
50
|
+
self.class.deny_old_passwords = count
|
51
|
+
end
|
52
|
+
|
53
|
+
def archive_count
|
54
|
+
self.class.password_archiving_count
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# archive the last password before save and delete all to old passwords from archive
|
60
|
+
def archive_password
|
61
|
+
if encrypted_password_changed?
|
62
|
+
if archive_count.to_i > 0
|
63
|
+
old_passwords.create! old_password_params
|
64
|
+
old_passwords.order(:id).reverse_order.offset(archive_count).destroy_all
|
65
|
+
else
|
66
|
+
old_passwords.destroy_all
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def old_password_params
|
72
|
+
{ encrypted_password: encrypted_password_change.first }
|
73
|
+
end
|
74
|
+
|
75
|
+
module ClassMethods
|
76
|
+
::Devise::Models.config(self, :password_archiving_count, :deny_old_passwords)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'devise-security/hooks/password_expirable'
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Models
|
5
|
+
|
6
|
+
# PasswordExpirable takes care of change password after
|
7
|
+
module PasswordExpirable
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
before_save :update_password_changed
|
12
|
+
end
|
13
|
+
|
14
|
+
# is an password change required?
|
15
|
+
def need_change_password?
|
16
|
+
if expired_password_after_numeric?
|
17
|
+
self.password_changed_at.nil? or self.password_changed_at < self.expire_password_after.seconds.ago
|
18
|
+
else
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# set a fake datetime so a password change is needed and save the record
|
24
|
+
def need_change_password!
|
25
|
+
if expired_password_after_numeric?
|
26
|
+
need_change_password
|
27
|
+
self.save(:validate => false)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# set a fake datetime so a password change is needed
|
32
|
+
def need_change_password
|
33
|
+
if expired_password_after_numeric?
|
34
|
+
self.password_changed_at = self.expire_password_after.seconds.ago
|
35
|
+
end
|
36
|
+
|
37
|
+
# is date not set it will set default to need set new password next login
|
38
|
+
need_change_password if self.password_changed_at.nil?
|
39
|
+
|
40
|
+
self.password_changed_at
|
41
|
+
end
|
42
|
+
|
43
|
+
def expire_password_after
|
44
|
+
self.class.expire_password_after
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# is password changed then update password_cahanged_at
|
50
|
+
def update_password_changed
|
51
|
+
self.password_changed_at = Time.now if (self.new_record? or self.encrypted_password_changed?) and not self.password_changed_at_changed?
|
52
|
+
end
|
53
|
+
|
54
|
+
def expired_password_after_numeric?
|
55
|
+
return @_numeric if defined?(@_numeric)
|
56
|
+
@_numeric ||= self.expire_password_after.is_a?(1.class) ||
|
57
|
+
self.expire_password_after.is_a?(Float)
|
58
|
+
end
|
59
|
+
|
60
|
+
module ClassMethods
|
61
|
+
::Devise::Models.config(self, :expire_password_after)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Devise
|
2
|
+
module Models
|
3
|
+
# SecureValidatable creates better validations with more validation for security
|
4
|
+
#
|
5
|
+
# == Options
|
6
|
+
#
|
7
|
+
# SecureValidatable adds the following options to devise_for:
|
8
|
+
#
|
9
|
+
# * +email_regexp+: the regular expression used to validate e-mails;
|
10
|
+
# * +password_length+: a range expressing password length. Defaults from devise
|
11
|
+
# * +password_regex+: need strong password. Defaults to /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/
|
12
|
+
#
|
13
|
+
module SecureValidatable
|
14
|
+
|
15
|
+
def self.included(base)
|
16
|
+
base.extend ClassMethods
|
17
|
+
assert_secure_validations_api!(base)
|
18
|
+
|
19
|
+
base.class_eval do
|
20
|
+
already_validated_email = false
|
21
|
+
|
22
|
+
# validate login in a strict way if not yet validated
|
23
|
+
unless has_uniqueness_validation_of_login?
|
24
|
+
validation_condition = "#{login_attribute}_changed?".to_sym
|
25
|
+
|
26
|
+
validates login_attribute, :uniqueness => {
|
27
|
+
:scope => authentication_keys[1..-1],
|
28
|
+
:case_sensitive => !!case_insensitive_keys
|
29
|
+
},
|
30
|
+
:if => validation_condition
|
31
|
+
|
32
|
+
already_validated_email = login_attribute.to_s == 'email'
|
33
|
+
end
|
34
|
+
|
35
|
+
unless devise_validation_enabled?
|
36
|
+
validates :email, :presence => true, :if => :email_required?
|
37
|
+
unless already_validated_email
|
38
|
+
validates :email, :uniqueness => true, :allow_blank => true, :if => :email_changed? # check uniq for email ever
|
39
|
+
end
|
40
|
+
|
41
|
+
validates :password, :presence => true, :length => password_length, :confirmation => true, :if => :password_required?
|
42
|
+
end
|
43
|
+
|
44
|
+
# extra validations
|
45
|
+
validates :email, :email => email_validation if email_validation # use rails_email_validator or similar
|
46
|
+
validates :password, :format => { :with => password_regex, :message => :password_format }, :if => :password_required?
|
47
|
+
|
48
|
+
# don't allow use same password
|
49
|
+
validate :current_equal_password_validation
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.assert_secure_validations_api!(base)
|
54
|
+
raise "Could not use SecureValidatable on #{base}" unless base.respond_to?(:validates)
|
55
|
+
end
|
56
|
+
|
57
|
+
def current_equal_password_validation
|
58
|
+
if not self.new_record? and not self.encrypted_password_change.nil?
|
59
|
+
dummy = self.class.new
|
60
|
+
dummy.encrypted_password = self.encrypted_password_change.first
|
61
|
+
dummy.password_salt = self.password_salt_change.first if self.respond_to? :password_salt_change and not self.password_salt_change.nil?
|
62
|
+
self.errors.add(:password, :equal_to_current_password) if dummy.valid_password?(self.password)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
# Checks whether a password is needed or not. For validations only.
|
69
|
+
# Passwords are always required if it's a new record, or if the password
|
70
|
+
# or confirmation are being set somewhere.
|
71
|
+
def password_required?
|
72
|
+
!persisted? || !password.nil? || !password_confirmation.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
def email_required?
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
module ClassMethods
|
80
|
+
Devise::Models.config(self, :password_regex, :password_length, :email_validation)
|
81
|
+
|
82
|
+
private
|
83
|
+
def has_uniqueness_validation_of_login?
|
84
|
+
validators.any? do |validator|
|
85
|
+
validator.kind_of?(ActiveRecord::Validations::UniquenessValidator) &&
|
86
|
+
validator.attributes.include?(login_attribute)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def login_attribute
|
91
|
+
authentication_keys[0]
|
92
|
+
end
|
93
|
+
|
94
|
+
def devise_validation_enabled?
|
95
|
+
self.ancestors.map(&:to_s).include? 'Devise::Models::Validatable'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|