authlogic 3.4.6 → 4.2.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 +5 -5
- data/.github/ISSUE_TEMPLATE.md +13 -0
- data/.github/triage.md +87 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +127 -0
- data/.rubocop_todo.yml +65 -0
- data/.travis.yml +18 -10
- data/CHANGELOG.md +156 -6
- data/CONTRIBUTING.md +71 -3
- data/Gemfile +2 -2
- data/README.md +386 -0
- data/Rakefile +13 -7
- data/UPGRADING.md +22 -0
- data/authlogic.gemspec +33 -22
- data/lib/authlogic.rb +60 -52
- data/lib/authlogic/acts_as_authentic/base.rb +40 -26
- data/lib/authlogic/acts_as_authentic/email.rb +96 -32
- data/lib/authlogic/acts_as_authentic/logged_in_status.rb +36 -12
- data/lib/authlogic/acts_as_authentic/login.rb +114 -49
- data/lib/authlogic/acts_as_authentic/magic_columns.rb +17 -6
- data/lib/authlogic/acts_as_authentic/password.rb +296 -139
- data/lib/authlogic/acts_as_authentic/perishable_token.rb +34 -20
- data/lib/authlogic/acts_as_authentic/persistence_token.rb +20 -24
- data/lib/authlogic/acts_as_authentic/queries/find_with_case.rb +67 -0
- data/lib/authlogic/acts_as_authentic/restful_authentication.rb +68 -23
- data/lib/authlogic/acts_as_authentic/session_maintenance.rb +128 -85
- data/lib/authlogic/acts_as_authentic/single_access_token.rb +41 -25
- data/lib/authlogic/acts_as_authentic/validations_scope.rb +8 -8
- data/lib/authlogic/authenticates_many/association.rb +22 -14
- data/lib/authlogic/authenticates_many/base.rb +35 -16
- data/lib/authlogic/config.rb +10 -10
- data/lib/authlogic/controller_adapters/abstract_adapter.rb +40 -12
- data/lib/authlogic/controller_adapters/rack_adapter.rb +15 -8
- data/lib/authlogic/controller_adapters/rails_adapter.rb +42 -22
- data/lib/authlogic/controller_adapters/sinatra_adapter.rb +3 -3
- data/lib/authlogic/crypto_providers.rb +91 -0
- data/lib/authlogic/crypto_providers/aes256.rb +42 -14
- data/lib/authlogic/crypto_providers/bcrypt.rb +35 -20
- data/lib/authlogic/crypto_providers/md5.rb +11 -9
- data/lib/authlogic/crypto_providers/scrypt.rb +26 -13
- data/lib/authlogic/crypto_providers/sha1.rb +14 -8
- data/lib/authlogic/crypto_providers/sha256.rb +16 -12
- data/lib/authlogic/crypto_providers/sha512.rb +8 -24
- data/lib/authlogic/crypto_providers/wordpress.rb +44 -15
- data/lib/authlogic/i18n.rb +33 -20
- data/lib/authlogic/i18n/translator.rb +1 -1
- data/lib/authlogic/random.rb +12 -29
- data/lib/authlogic/regex.rb +59 -27
- data/lib/authlogic/session/activation.rb +36 -23
- data/lib/authlogic/session/active_record_trickery.rb +13 -10
- data/lib/authlogic/session/base.rb +20 -8
- data/lib/authlogic/session/brute_force_protection.rb +87 -56
- data/lib/authlogic/session/callbacks.rb +99 -49
- data/lib/authlogic/session/cookies.rb +128 -59
- data/lib/authlogic/session/existence.rb +29 -19
- data/lib/authlogic/session/foundation.rb +70 -16
- data/lib/authlogic/session/http_auth.rb +39 -31
- data/lib/authlogic/session/id.rb +27 -15
- data/lib/authlogic/session/klass.rb +17 -13
- data/lib/authlogic/session/magic_columns.rb +78 -59
- data/lib/authlogic/session/magic_states.rb +50 -27
- data/lib/authlogic/session/params.rb +79 -50
- data/lib/authlogic/session/password.rb +197 -118
- data/lib/authlogic/session/perishable_token.rb +12 -6
- data/lib/authlogic/session/persistence.rb +20 -14
- data/lib/authlogic/session/priority_record.rb +20 -16
- data/lib/authlogic/session/scopes.rb +63 -33
- data/lib/authlogic/session/session.rb +40 -25
- data/lib/authlogic/session/timeout.rb +51 -34
- data/lib/authlogic/session/unauthorized_record.rb +24 -18
- data/lib/authlogic/session/validation.rb +32 -21
- data/lib/authlogic/test_case.rb +123 -35
- data/lib/authlogic/test_case/mock_controller.rb +14 -13
- data/lib/authlogic/test_case/mock_cookie_jar.rb +14 -5
- data/lib/authlogic/test_case/mock_logger.rb +1 -1
- data/lib/authlogic/test_case/mock_request.rb +9 -4
- data/lib/authlogic/test_case/rails_request_adapter.rb +8 -7
- data/lib/authlogic/version.rb +21 -0
- data/test/acts_as_authentic_test/base_test.rb +1 -1
- data/test/acts_as_authentic_test/email_test.rb +80 -63
- data/test/acts_as_authentic_test/logged_in_status_test.rb +14 -8
- data/test/acts_as_authentic_test/login_test.rb +91 -49
- data/test/acts_as_authentic_test/magic_columns_test.rb +13 -13
- data/test/acts_as_authentic_test/password_test.rb +82 -60
- data/test/acts_as_authentic_test/perishable_token_test.rb +31 -25
- data/test/acts_as_authentic_test/persistence_token_test.rb +9 -5
- data/test/acts_as_authentic_test/restful_authentication_test.rb +18 -9
- data/test/acts_as_authentic_test/session_maintenance_test.rb +86 -22
- data/test/acts_as_authentic_test/single_access_test.rb +15 -15
- data/test/adapter_test.rb +21 -0
- data/test/authenticates_many_test.rb +26 -11
- data/test/config_test.rb +9 -9
- data/test/crypto_provider_test/aes256_test.rb +3 -3
- data/test/crypto_provider_test/bcrypt_test.rb +1 -1
- data/test/crypto_provider_test/scrypt_test.rb +2 -2
- data/test/crypto_provider_test/sha1_test.rb +4 -4
- data/test/crypto_provider_test/sha256_test.rb +2 -2
- data/test/crypto_provider_test/sha512_test.rb +3 -3
- data/test/crypto_provider_test/wordpress_test.rb +24 -0
- data/test/gemfiles/Gemfile.rails-4.2.x +2 -2
- data/test/gemfiles/Gemfile.rails-5.0.x +6 -0
- data/test/gemfiles/Gemfile.rails-5.1.x +6 -0
- data/test/gemfiles/Gemfile.rails-5.2.x +6 -0
- data/test/gemfiles/Gemfile.rails-master +6 -0
- data/test/i18n_test.rb +9 -9
- data/test/libs/affiliate.rb +2 -2
- data/test/libs/company.rb +4 -4
- data/test/libs/employee.rb +2 -2
- data/test/libs/employee_session.rb +1 -1
- data/test/libs/ldaper.rb +1 -1
- data/test/libs/project.rb +1 -1
- data/test/libs/user_session.rb +2 -2
- data/test/random_test.rb +9 -38
- data/test/session_test/activation_test.rb +7 -7
- data/test/session_test/active_record_trickery_test.rb +9 -6
- data/test/session_test/brute_force_protection_test.rb +26 -21
- data/test/session_test/callbacks_test.rb +10 -4
- data/test/session_test/cookies_test.rb +54 -20
- data/test/session_test/existence_test.rb +45 -23
- data/test/session_test/foundation_test.rb +17 -1
- data/test/session_test/http_auth_test.rb +11 -12
- data/test/session_test/id_test.rb +3 -3
- data/test/session_test/klass_test.rb +2 -2
- data/test/session_test/magic_columns_test.rb +15 -17
- data/test/session_test/magic_states_test.rb +17 -19
- data/test/session_test/params_test.rb +26 -20
- data/test/session_test/password_test.rb +11 -12
- data/test/session_test/perishability_test.rb +5 -5
- data/test/session_test/persistence_test.rb +4 -3
- data/test/session_test/scopes_test.rb +15 -9
- data/test/session_test/session_test.rb +7 -6
- data/test/session_test/timeout_test.rb +16 -14
- data/test/session_test/unauthorized_record_test.rb +3 -3
- data/test/session_test/validation_test.rb +5 -5
- data/test/test_helper.rb +115 -49
- metadata +107 -36
- data/README.rdoc +0 -232
- data/test/gemfiles/Gemfile.rails-3.2.x +0 -7
- data/test/gemfiles/Gemfile.rails-4.0.x +0 -7
- data/test/gemfiles/Gemfile.rails-4.1.x +0 -7
@@ -1,9 +1,10 @@
|
|
1
1
|
module Authlogic
|
2
2
|
module ActsAsAuthentic
|
3
|
-
# Since web applications are stateless there is not sure fire way to tell if
|
4
|
-
# from the database perspective. The best way to
|
5
|
-
#
|
6
|
-
#
|
3
|
+
# Since web applications are stateless there is not sure fire way to tell if
|
4
|
+
# a user is logged in or not, from the database perspective. The best way to
|
5
|
+
# do this is to provide a "timeout" based on inactivity. So if that user is
|
6
|
+
# inactive for a certain amount of time we assume they are logged out.
|
7
|
+
# That's what this module is all about.
|
7
8
|
module LoggedInStatus
|
8
9
|
def self.included(klass)
|
9
10
|
klass.class_eval do
|
@@ -27,19 +28,41 @@ module Authlogic
|
|
27
28
|
# All methods for the logged in status feature seat.
|
28
29
|
module Methods
|
29
30
|
def self.included(klass)
|
30
|
-
return
|
31
|
+
return unless klass.column_names.include?("last_request_at")
|
31
32
|
|
32
33
|
klass.class_eval do
|
33
34
|
include InstanceMethods
|
34
|
-
scope
|
35
|
-
|
35
|
+
scope(
|
36
|
+
:logged_in,
|
37
|
+
lambda do
|
38
|
+
where(
|
39
|
+
"last_request_at > ? and current_login_at IS NOT NULL",
|
40
|
+
logged_in_timeout.seconds.ago
|
41
|
+
)
|
42
|
+
end
|
43
|
+
)
|
44
|
+
scope(
|
45
|
+
:logged_out,
|
46
|
+
lambda do
|
47
|
+
where(
|
48
|
+
"last_request_at is NULL or last_request_at <= ?",
|
49
|
+
logged_in_timeout.seconds.ago
|
50
|
+
)
|
51
|
+
end
|
52
|
+
)
|
36
53
|
end
|
37
54
|
end
|
38
55
|
|
56
|
+
# :nodoc:
|
39
57
|
module InstanceMethods
|
40
58
|
# Returns true if the last_request_at > logged_in_timeout.
|
41
59
|
def logged_in?
|
42
|
-
|
60
|
+
unless respond_to?(:last_request_at)
|
61
|
+
raise(
|
62
|
+
"Can not determine the records login state because " \
|
63
|
+
"there is no last_request_at column"
|
64
|
+
)
|
65
|
+
end
|
43
66
|
!last_request_at.nil? && last_request_at > logged_in_timeout.seconds.ago
|
44
67
|
end
|
45
68
|
|
@@ -49,11 +72,12 @@ module Authlogic
|
|
49
72
|
end
|
50
73
|
|
51
74
|
private
|
52
|
-
|
53
|
-
|
54
|
-
|
75
|
+
|
76
|
+
def logged_in_timeout
|
77
|
+
self.class.logged_in_timeout
|
78
|
+
end
|
55
79
|
end
|
56
80
|
end
|
57
81
|
end
|
58
82
|
end
|
59
|
-
end
|
83
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "authlogic/acts_as_authentic/queries/find_with_case"
|
2
|
+
|
1
3
|
module Authlogic
|
2
4
|
module ActsAsAuthentic
|
3
5
|
# Handles everything related to the login field.
|
@@ -29,102 +31,165 @@ module Authlogic
|
|
29
31
|
end
|
30
32
|
alias_method :validate_login_field=, :validate_login_field
|
31
33
|
|
32
|
-
# A hash of options for the validates_length_of call for the login
|
34
|
+
# A hash of options for the validates_length_of call for the login
|
35
|
+
# field. Allows you to change this however you want.
|
33
36
|
#
|
34
|
-
# <b>Keep in mind this is ruby. I wanted to keep this as flexible as
|
35
|
-
#
|
36
|
-
#
|
37
|
+
# <b>Keep in mind this is ruby. I wanted to keep this as flexible as
|
38
|
+
# possible, so you can completely replace the hash or merge options into
|
39
|
+
# it. Checkout the convenience function
|
40
|
+
# merge_validates_length_of_login_field_options to merge options.</b>
|
37
41
|
#
|
38
42
|
# * <tt>Default:</tt> {:within => 3..100}
|
39
43
|
# * <tt>Accepts:</tt> Hash of options accepted by validates_length_of
|
40
44
|
def validates_length_of_login_field_options(value = nil)
|
41
|
-
rw_config(:validates_length_of_login_field_options, value,
|
45
|
+
rw_config(:validates_length_of_login_field_options, value, within: 3..100)
|
42
46
|
end
|
43
|
-
alias_method
|
47
|
+
alias_method(
|
48
|
+
:validates_length_of_login_field_options=,
|
49
|
+
:validates_length_of_login_field_options
|
50
|
+
)
|
44
51
|
|
45
|
-
# A convenience function to merge options into the
|
52
|
+
# A convenience function to merge options into the
|
53
|
+
# validates_length_of_login_field_options. So instead of:
|
46
54
|
#
|
47
|
-
# self.validates_length_of_login_field_options =
|
55
|
+
# self.validates_length_of_login_field_options =
|
56
|
+
# validates_length_of_login_field_options.merge(:my_option => my_value)
|
48
57
|
#
|
49
58
|
# You can do this:
|
50
59
|
#
|
51
60
|
# merge_validates_length_of_login_field_options :my_option => my_value
|
52
61
|
def merge_validates_length_of_login_field_options(options = {})
|
53
|
-
self.validates_length_of_login_field_options =
|
62
|
+
self.validates_length_of_login_field_options =
|
63
|
+
validates_length_of_login_field_options.merge(options)
|
54
64
|
end
|
55
65
|
|
56
|
-
# A hash of options for the validates_format_of call for the login
|
66
|
+
# A hash of options for the validates_format_of call for the login
|
67
|
+
# field. Allows you to change this however you want.
|
68
|
+
#
|
69
|
+
# <b>Keep in mind this is ruby. I wanted to keep this as flexible as
|
70
|
+
# possible, so you can completely replace the hash or merge options into
|
71
|
+
# it. Checkout the convenience function
|
72
|
+
# merge_validates_format_of_login_field_options to merge options.</b>
|
73
|
+
#
|
74
|
+
# * <tt>Default:</tt>
|
57
75
|
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
76
|
+
# {
|
77
|
+
# :with => Authlogic::Regex.login,
|
78
|
+
# :message => lambda {
|
79
|
+
# I18n.t(
|
80
|
+
# 'error_messages.login_invalid',
|
81
|
+
# :default => "should use only letters, numbers, spaces, and .-_@+ please."
|
82
|
+
# )
|
83
|
+
# }
|
84
|
+
# }
|
61
85
|
#
|
62
|
-
# * <tt>Default:</tt> {:with => Authlogic::Regex.login, :message => lambda {I18n.t('error_messages.login_invalid', :default => "should use only letters, numbers, spaces, and .-_@ please.")}}
|
63
86
|
# * <tt>Accepts:</tt> Hash of options accepted by validates_format_of
|
64
87
|
def validates_format_of_login_field_options(value = nil)
|
65
|
-
rw_config(
|
88
|
+
rw_config(
|
89
|
+
:validates_format_of_login_field_options,
|
90
|
+
value,
|
91
|
+
with: Authlogic::Regex::LOGIN,
|
92
|
+
message: proc do
|
93
|
+
I18n.t(
|
94
|
+
"error_messages.login_invalid",
|
95
|
+
default: "should use only letters, numbers, spaces, and .-_@+ please."
|
96
|
+
)
|
97
|
+
end
|
98
|
+
)
|
66
99
|
end
|
67
|
-
alias_method
|
100
|
+
alias_method(
|
101
|
+
:validates_format_of_login_field_options=,
|
102
|
+
:validates_format_of_login_field_options
|
103
|
+
)
|
68
104
|
|
69
|
-
# See merge_validates_length_of_login_field_options. The same thing,
|
105
|
+
# See merge_validates_length_of_login_field_options. The same thing,
|
106
|
+
# except for validates_format_of_login_field_options
|
70
107
|
def merge_validates_format_of_login_field_options(options = {})
|
71
|
-
self.validates_format_of_login_field_options =
|
108
|
+
self.validates_format_of_login_field_options =
|
109
|
+
validates_format_of_login_field_options.merge(options)
|
72
110
|
end
|
73
111
|
|
74
|
-
# A hash of options for the validates_uniqueness_of call for the login
|
112
|
+
# A hash of options for the validates_uniqueness_of call for the login
|
113
|
+
# field. Allows you to change this however you want.
|
114
|
+
#
|
115
|
+
# <b>Keep in mind this is ruby. I wanted to keep this as flexible as
|
116
|
+
# possible, so you can completely replace the hash or merge options into
|
117
|
+
# it. Checkout the convenience function
|
118
|
+
# merge_validates_format_of_login_field_options to merge options.</b>
|
119
|
+
#
|
120
|
+
# * <tt>Default:</tt>
|
75
121
|
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
122
|
+
# {
|
123
|
+
# :case_sensitive => false,
|
124
|
+
# :scope => validations_scope,
|
125
|
+
# :if => "#{login_field}_changed?".to_sym
|
126
|
+
# }
|
79
127
|
#
|
80
|
-
# * <tt>Default:</tt> {:case_sensitive => false, :scope => validations_scope, :if => "#{login_field}_changed?".to_sym}
|
81
128
|
# * <tt>Accepts:</tt> Hash of options accepted by validates_uniqueness_of
|
82
129
|
def validates_uniqueness_of_login_field_options(value = nil)
|
83
|
-
rw_config(
|
130
|
+
rw_config(
|
131
|
+
:validates_uniqueness_of_login_field_options,
|
132
|
+
value,
|
133
|
+
case_sensitive: false,
|
134
|
+
scope: validations_scope,
|
135
|
+
if: "#{login_field}_changed?".to_sym
|
136
|
+
)
|
84
137
|
end
|
85
|
-
alias_method
|
138
|
+
alias_method(
|
139
|
+
:validates_uniqueness_of_login_field_options=,
|
140
|
+
:validates_uniqueness_of_login_field_options
|
141
|
+
)
|
86
142
|
|
87
|
-
# See merge_validates_length_of_login_field_options. The same thing,
|
143
|
+
# See merge_validates_length_of_login_field_options. The same thing,
|
144
|
+
# except for validates_uniqueness_of_login_field_options
|
88
145
|
def merge_validates_uniqueness_of_login_field_options(options = {})
|
89
|
-
self.validates_uniqueness_of_login_field_options =
|
146
|
+
self.validates_uniqueness_of_login_field_options =
|
147
|
+
validates_uniqueness_of_login_field_options.merge(options)
|
90
148
|
end
|
91
149
|
|
92
|
-
# This method allows you to find a record with the given login. If you
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
150
|
+
# This method allows you to find a record with the given login. If you
|
151
|
+
# notice, with Active Record you have the UniquenessValidator class.
|
152
|
+
# They give you a :case_sensitive option. I handle this in the same
|
153
|
+
# manner that they handle that. If you are using the login field, set
|
154
|
+
# false for the :case_sensitive option in
|
155
|
+
# validates_uniqueness_of_login_field_options and the column doesn't
|
156
|
+
# have a case-insensitive collation, this method will modify the query
|
157
|
+
# to look something like:
|
97
158
|
#
|
98
159
|
# "LOWER(#{quoted_table_name}.#{login_field}) = LOWER(#{login})"
|
99
160
|
#
|
100
|
-
# If you don't specify this it just uses a regular case-sensitive search
|
161
|
+
# If you don't specify this it just uses a regular case-sensitive search
|
162
|
+
# (with the binary modifier if necessary):
|
101
163
|
#
|
102
164
|
# "BINARY #{login_field} = #{login}"
|
103
165
|
#
|
104
|
-
# The above also applies for using email as your login, except that you
|
166
|
+
# The above also applies for using email as your login, except that you
|
167
|
+
# need to set the :case_sensitive in
|
105
168
|
# validates_uniqueness_of_email_field_options to false.
|
169
|
+
#
|
170
|
+
# @api public
|
106
171
|
def find_by_smart_case_login_field(login)
|
107
172
|
if login_field
|
108
|
-
find_with_case(
|
173
|
+
find_with_case(
|
174
|
+
login_field,
|
175
|
+
login,
|
176
|
+
validates_uniqueness_of_login_field_options[:case_sensitive] != false
|
177
|
+
)
|
109
178
|
else
|
110
|
-
find_with_case(
|
179
|
+
find_with_case(
|
180
|
+
email_field,
|
181
|
+
login,
|
182
|
+
validates_uniqueness_of_email_field_options[:case_sensitive] != false
|
183
|
+
)
|
111
184
|
end
|
112
185
|
end
|
113
186
|
|
114
187
|
private
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
value = connection.case_sensitive_modifier(value)
|
121
|
-
else
|
122
|
-
value = connection.case_sensitive_modifier(value, field.to_s)
|
123
|
-
end
|
124
|
-
relation = arel_table[field.to_s].eq(value)
|
125
|
-
end
|
126
|
-
where(relation).first
|
127
|
-
end
|
188
|
+
|
189
|
+
# @api private
|
190
|
+
def find_with_case(field, value, sensitive)
|
191
|
+
Queries::FindWithCase.new(self, field, value, sensitive).execute
|
192
|
+
end
|
128
193
|
end
|
129
194
|
|
130
195
|
# All methods relating to the login field
|
@@ -1,21 +1,32 @@
|
|
1
1
|
module Authlogic
|
2
2
|
module ActsAsAuthentic
|
3
|
-
# Magic columns are like ActiveRecord's created_at and updated_at columns. They are
|
4
|
-
# you. Authlogic has the same thing, but these are
|
5
|
-
#
|
3
|
+
# Magic columns are like ActiveRecord's created_at and updated_at columns. They are
|
4
|
+
# "magically" maintained for you. Authlogic has the same thing, but these are
|
5
|
+
# maintained on the session side. Please see Authlogic::Session::MagicColumns for more
|
6
|
+
# details. This module merely adds validations for the magic columns if they exist.
|
6
7
|
module MagicColumns
|
7
8
|
def self.included(klass)
|
8
9
|
klass.class_eval do
|
9
10
|
add_acts_as_authentic_module(Methods)
|
10
11
|
end
|
11
12
|
end
|
12
|
-
|
13
|
+
|
13
14
|
# Methods relating to the magic columns
|
14
15
|
module Methods
|
15
16
|
def self.included(klass)
|
16
17
|
klass.class_eval do
|
17
|
-
|
18
|
-
|
18
|
+
if column_names.include?("login_count")
|
19
|
+
validates_numericality_of :login_count,
|
20
|
+
only_integer: true,
|
21
|
+
greater_than_or_equal_to: 0,
|
22
|
+
allow_nil: true
|
23
|
+
end
|
24
|
+
if column_names.include?("failed_login_count")
|
25
|
+
validates_numericality_of :failed_login_count,
|
26
|
+
only_integer: true,
|
27
|
+
greater_than_or_equal_to: 0,
|
28
|
+
allow_nil: true
|
29
|
+
end
|
19
30
|
end
|
20
31
|
end
|
21
32
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
module Authlogic
|
2
2
|
module ActsAsAuthentic
|
3
|
-
# This module has a lot of neat functionality. It is responsible for encrypting your
|
4
|
-
# It can also help you transition to a new
|
3
|
+
# This module has a lot of neat functionality. It is responsible for encrypting your
|
4
|
+
# password, salting it, and verifying it. It can also help you transition to a new
|
5
|
+
# encryption algorithm. See the Config sub module for configuration options.
|
5
6
|
module Password
|
6
7
|
def self.included(klass)
|
7
8
|
klass.class_eval do
|
@@ -18,7 +19,17 @@ module Authlogic
|
|
18
19
|
# * <tt>Default:</tt> :crypted_password, :encrypted_password, :password_hash, or :pw_hash
|
19
20
|
# * <tt>Accepts:</tt> Symbol
|
20
21
|
def crypted_password_field(value = nil)
|
21
|
-
rw_config(
|
22
|
+
rw_config(
|
23
|
+
:crypted_password_field,
|
24
|
+
value,
|
25
|
+
first_column_to_exist(
|
26
|
+
nil,
|
27
|
+
:crypted_password,
|
28
|
+
:encrypted_password,
|
29
|
+
:password_hash,
|
30
|
+
:pw_hash
|
31
|
+
)
|
32
|
+
)
|
22
33
|
end
|
23
34
|
alias_method :crypted_password_field=, :crypted_password_field
|
24
35
|
|
@@ -27,12 +38,16 @@ module Authlogic
|
|
27
38
|
# * <tt>Default:</tt> :password_salt, :pw_salt, :salt, nil if none exist
|
28
39
|
# * <tt>Accepts:</tt> Symbol
|
29
40
|
def password_salt_field(value = nil)
|
30
|
-
rw_config(
|
41
|
+
rw_config(
|
42
|
+
:password_salt_field,
|
43
|
+
value,
|
44
|
+
first_column_to_exist(nil, :password_salt, :pw_salt, :salt)
|
45
|
+
)
|
31
46
|
end
|
32
47
|
alias_method :password_salt_field=, :password_salt_field
|
33
48
|
|
34
|
-
# Whether or not to require a password confirmation. If you don't want your users
|
35
|
-
# just set this to false.
|
49
|
+
# Whether or not to require a password confirmation. If you don't want your users
|
50
|
+
# to confirm their password just set this to false.
|
36
51
|
#
|
37
52
|
# * <tt>Default:</tt> true
|
38
53
|
# * <tt>Accepts:</tt> Boolean
|
@@ -41,14 +56,17 @@ module Authlogic
|
|
41
56
|
end
|
42
57
|
alias_method :require_password_confirmation=, :require_password_confirmation
|
43
58
|
|
44
|
-
# By default passwords are required when a record is new or the crypted_password
|
45
|
-
# are met a password is not required. In
|
59
|
+
# By default passwords are required when a record is new or the crypted_password
|
60
|
+
# is blank, but if both of these things are met a password is not required. In
|
61
|
+
# this case, blank passwords are ignored.
|
46
62
|
#
|
47
|
-
# Think about a profile page, where the user can edit all of their information,
|
48
|
-
# If they do not want to change their password
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
63
|
+
# Think about a profile page, where the user can edit all of their information,
|
64
|
+
# including changing their password. If they do not want to change their password
|
65
|
+
# they just leave the fields blank. This will try to set the password to a blank
|
66
|
+
# value, in which case is incorrect behavior. As such, Authlogic ignores this. But
|
67
|
+
# let's say you have a completely separate page for resetting passwords, you might
|
68
|
+
# not want to ignore blank passwords. If this is the case for you, then just set
|
69
|
+
# this value to false.
|
52
70
|
#
|
53
71
|
# * <tt>Default:</tt> true
|
54
72
|
# * <tt>Accepts:</tt> Boolean
|
@@ -57,15 +75,16 @@ module Authlogic
|
|
57
75
|
end
|
58
76
|
alias_method :ignore_blank_passwords=, :ignore_blank_passwords
|
59
77
|
|
60
|
-
# When calling valid_password?("some pass") do you want to check that password
|
61
|
-
# the database. Take this example:
|
78
|
+
# When calling valid_password?("some pass") do you want to check that password
|
79
|
+
# against what's in that object or whats in the database. Take this example:
|
62
80
|
#
|
63
81
|
# u = User.first
|
64
82
|
# u.password = "new pass"
|
65
83
|
# u.valid_password?("old pass")
|
66
84
|
#
|
67
|
-
# Should the last line above return true or false? The record hasn't been saved
|
68
|
-
# Other would assume false. So I let you decide by
|
85
|
+
# Should the last line above return true or false? The record hasn't been saved
|
86
|
+
# yet, so most would assume true. Other would assume false. So I let you decide by
|
87
|
+
# giving you this option.
|
69
88
|
#
|
70
89
|
# * <tt>Default:</tt> true
|
71
90
|
# * <tt>Accepts:</tt> Boolean
|
@@ -83,124 +102,184 @@ module Authlogic
|
|
83
102
|
end
|
84
103
|
alias_method :validate_password_field=, :validate_password_field
|
85
104
|
|
86
|
-
# A hash of options for the validates_length_of call for the password field.
|
105
|
+
# A hash of options for the validates_length_of call for the password field.
|
106
|
+
# Allows you to change this however you want.
|
87
107
|
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
108
|
+
# **Keep in mind this is ruby. I wanted to keep this as flexible as
|
109
|
+
# possible, so you can completely replace the hash or merge options into
|
110
|
+
# it. Checkout the convenience function
|
111
|
+
# merge_validates_length_of_password_field_options to merge options.**
|
91
112
|
#
|
92
|
-
# * <tt>Default:</tt> {:minimum =>
|
113
|
+
# * <tt>Default:</tt> {:minimum => 8, :if => :require_password?}
|
93
114
|
# * <tt>Accepts:</tt> Hash of options accepted by validates_length_of
|
94
115
|
def validates_length_of_password_field_options(value = nil)
|
95
|
-
rw_config(
|
116
|
+
rw_config(
|
117
|
+
:validates_length_of_password_field_options,
|
118
|
+
value,
|
119
|
+
minimum: 8,
|
120
|
+
if: :require_password?
|
121
|
+
)
|
96
122
|
end
|
97
|
-
alias_method
|
123
|
+
alias_method(
|
124
|
+
:validates_length_of_password_field_options=,
|
125
|
+
:validates_length_of_password_field_options
|
126
|
+
)
|
98
127
|
|
99
|
-
# A convenience function to merge options into the
|
128
|
+
# A convenience function to merge options into the
|
129
|
+
# validates_length_of_login_field_options. So instead of:
|
100
130
|
#
|
101
|
-
# self.validates_length_of_password_field_options =
|
131
|
+
# self.validates_length_of_password_field_options =
|
132
|
+
# validates_length_of_password_field_options.merge(:my_option => my_value)
|
102
133
|
#
|
103
134
|
# You can do this:
|
104
135
|
#
|
105
136
|
# merge_validates_length_of_password_field_options :my_option => my_value
|
106
137
|
def merge_validates_length_of_password_field_options(options = {})
|
107
|
-
self.validates_length_of_password_field_options =
|
138
|
+
self.validates_length_of_password_field_options =
|
139
|
+
validates_length_of_password_field_options.merge(options)
|
108
140
|
end
|
109
141
|
|
110
|
-
# A hash of options for the validates_confirmation_of call for the
|
142
|
+
# A hash of options for the validates_confirmation_of call for the
|
143
|
+
# password field. Allows you to change this however you want.
|
111
144
|
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
145
|
+
# **Keep in mind this is ruby. I wanted to keep this as flexible as
|
146
|
+
# possible, so you can completely replace the hash or merge options into
|
147
|
+
# it. Checkout the convenience function
|
148
|
+
# merge_validates_length_of_password_field_options to merge options.**
|
115
149
|
#
|
116
150
|
# * <tt>Default:</tt> {:if => :require_password?}
|
117
151
|
# * <tt>Accepts:</tt> Hash of options accepted by validates_confirmation_of
|
118
152
|
def validates_confirmation_of_password_field_options(value = nil)
|
119
|
-
rw_config(
|
153
|
+
rw_config(
|
154
|
+
:validates_confirmation_of_password_field_options,
|
155
|
+
value,
|
156
|
+
if: :require_password?
|
157
|
+
)
|
120
158
|
end
|
121
|
-
alias_method :validates_confirmation_of_password_field_options=,
|
159
|
+
alias_method :validates_confirmation_of_password_field_options=,
|
160
|
+
:validates_confirmation_of_password_field_options
|
122
161
|
|
123
|
-
# See merge_validates_length_of_password_field_options. The same thing, except for
|
162
|
+
# See merge_validates_length_of_password_field_options. The same thing, except for
|
163
|
+
# validates_confirmation_of_password_field_options
|
124
164
|
def merge_validates_confirmation_of_password_field_options(options = {})
|
125
|
-
self.validates_confirmation_of_password_field_options =
|
165
|
+
self.validates_confirmation_of_password_field_options =
|
166
|
+
validates_confirmation_of_password_field_options.merge(options)
|
126
167
|
end
|
127
168
|
|
128
|
-
# A hash of options for the validates_length_of call for the password_confirmation
|
169
|
+
# A hash of options for the validates_length_of call for the password_confirmation
|
170
|
+
# field. Allows you to change this however you want.
|
129
171
|
#
|
130
|
-
# <b>Keep in mind this is ruby. I wanted to keep this as flexible as possible, so
|
131
|
-
# merge options into it. Checkout the
|
172
|
+
# <b>Keep in mind this is ruby. I wanted to keep this as flexible as possible, so
|
173
|
+
# you can completely replace the hash or merge options into it. Checkout the
|
174
|
+
# convenience function merge_validates_length_of_password_field_options to merge
|
132
175
|
# options.</b>
|
133
176
|
#
|
134
177
|
# * <tt>Default:</tt> validates_length_of_password_field_options
|
135
178
|
# * <tt>Accepts:</tt> Hash of options accepted by validates_length_of
|
136
179
|
def validates_length_of_password_confirmation_field_options(value = nil)
|
137
|
-
rw_config(
|
180
|
+
rw_config(
|
181
|
+
:validates_length_of_password_confirmation_field_options,
|
182
|
+
value,
|
183
|
+
validates_length_of_password_field_options
|
184
|
+
)
|
138
185
|
end
|
139
|
-
alias_method
|
186
|
+
alias_method(
|
187
|
+
:validates_length_of_password_confirmation_field_options=,
|
188
|
+
:validates_length_of_password_confirmation_field_options
|
189
|
+
)
|
140
190
|
|
141
|
-
# See merge_validates_length_of_password_field_options. The same thing, except for
|
191
|
+
# See merge_validates_length_of_password_field_options. The same thing, except for
|
192
|
+
# validates_length_of_password_confirmation_field_options
|
142
193
|
def merge_validates_length_of_password_confirmation_field_options(options = {})
|
143
|
-
self.validates_length_of_password_confirmation_field_options =
|
194
|
+
self.validates_length_of_password_confirmation_field_options =
|
195
|
+
validates_length_of_password_confirmation_field_options.merge(options)
|
144
196
|
end
|
145
197
|
|
146
|
-
# The class you want to use to encrypt and verify your encrypted
|
147
|
-
#
|
198
|
+
# The class you want to use to encrypt and verify your encrypted
|
199
|
+
# passwords. See the Authlogic::CryptoProviders module for more info on
|
200
|
+
# the available methods and how to create your own.
|
201
|
+
#
|
202
|
+
# The family of adaptive hash functions (BCrypt, SCrypt, PBKDF2) is the
|
203
|
+
# best choice for password storage today. We recommend SCrypt. Other
|
204
|
+
# one-way functions like SHA512 are inferior, but widely used.
|
205
|
+
# Reverisbile functions like AES256 are the worst choice.
|
206
|
+
#
|
207
|
+
# You can use the `transition_from_crypto_providers` option to gradually
|
208
|
+
# transition to a better crypto provider without causing your users any
|
209
|
+
# pain.
|
148
210
|
#
|
149
211
|
# * <tt>Default:</tt> CryptoProviders::SCrypt
|
150
212
|
# * <tt>Accepts:</tt> Class
|
151
213
|
def crypto_provider(value = nil)
|
214
|
+
CryptoProviders::Guidance.new(value).impart_wisdom
|
152
215
|
rw_config(:crypto_provider, value, CryptoProviders::SCrypt)
|
153
216
|
end
|
154
217
|
alias_method :crypto_provider=, :crypto_provider
|
155
218
|
|
156
|
-
# Let's say you originally encrypted your passwords with Sha1. Sha1 is
|
157
|
-
# to
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
219
|
+
# Let's say you originally encrypted your passwords with Sha1. Sha1 is
|
220
|
+
# starting to join the party with MD5 and you want to switch to
|
221
|
+
# something stronger. No problem, just specify your new and improved
|
222
|
+
# algorithm with the crypt_provider option and then let Authlogic know
|
223
|
+
# you are transitioning from Sha1 using this option. Authlogic will take
|
224
|
+
# care of everything, including transitioning your users to the new
|
225
|
+
# algorithm. The next time a user logs in, they will be granted access
|
226
|
+
# using the old algorithm and their password will be resaved with the
|
227
|
+
# new algorithm. All new users will obviously use the new algorithm as
|
228
|
+
# well.
|
161
229
|
#
|
162
|
-
# Lastly, if you want to transition again, you can pass an array of
|
163
|
-
# as you
|
230
|
+
# Lastly, if you want to transition again, you can pass an array of
|
231
|
+
# crypto providers. So you can transition from as many algorithms as you
|
232
|
+
# want.
|
164
233
|
#
|
165
234
|
# * <tt>Default:</tt> nil
|
166
235
|
# * <tt>Accepts:</tt> Class or Array
|
167
236
|
def transition_from_crypto_providers(value = nil)
|
168
|
-
rw_config(
|
237
|
+
rw_config(
|
238
|
+
:transition_from_crypto_providers,
|
239
|
+
(!value.nil? && [value].flatten.compact) || value,
|
240
|
+
[]
|
241
|
+
)
|
169
242
|
end
|
170
243
|
alias_method :transition_from_crypto_providers=, :transition_from_crypto_providers
|
171
244
|
end
|
172
245
|
|
173
246
|
# Callbacks / hooks to allow other modules to modify the behavior of this module.
|
174
247
|
module Callbacks
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
248
|
+
# Does the order of this array matter?
|
249
|
+
METHODS = %w[
|
250
|
+
before_password_set
|
251
|
+
after_password_set
|
252
|
+
before_password_verification
|
253
|
+
after_password_verification
|
254
|
+
].freeze
|
179
255
|
|
180
256
|
def self.included(klass)
|
181
257
|
return if klass.crypted_password_field.nil?
|
182
|
-
klass.define_callbacks
|
258
|
+
klass.define_callbacks(*METHODS)
|
183
259
|
|
184
260
|
# If Rails 3, support the new callback syntax
|
185
|
-
if klass.
|
261
|
+
if klass.singleton_class.method_defined?(:set_callback)
|
186
262
|
METHODS.each do |method|
|
187
|
-
klass.class_eval <<-
|
263
|
+
klass.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
188
264
|
def self.#{method}(*methods, &block)
|
189
265
|
set_callback :#{method}, *methods, &block
|
190
266
|
end
|
191
|
-
|
267
|
+
EOS
|
192
268
|
end
|
193
269
|
end
|
194
270
|
end
|
195
271
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
272
|
+
# TODO: Ideally, once this module is included, the included copies of
|
273
|
+
# the following methods would be private. This cannot be accomplished
|
274
|
+
# by using calling `private` here in the module. Maybe we can set the
|
275
|
+
# privacy inside `included`?
|
276
|
+
METHODS.each do |method|
|
277
|
+
class_eval <<-EOS, __FILE__, __LINE__ + 1
|
278
|
+
def #{method}
|
279
|
+
run_callbacks(:#{method}) { |result, object| result == false }
|
280
|
+
end
|
281
|
+
EOS
|
282
|
+
end
|
204
283
|
end
|
205
284
|
|
206
285
|
# The methods related to the password field.
|
@@ -215,8 +294,14 @@ module Authlogic
|
|
215
294
|
validates_length_of :password, validates_length_of_password_field_options
|
216
295
|
|
217
296
|
if require_password_confirmation
|
218
|
-
validates_confirmation_of
|
219
|
-
|
297
|
+
validates_confirmation_of(
|
298
|
+
:password,
|
299
|
+
validates_confirmation_of_password_field_options
|
300
|
+
)
|
301
|
+
validates_length_of(
|
302
|
+
:password_confirmation,
|
303
|
+
validates_length_of_password_confirmation_field_options
|
304
|
+
)
|
220
305
|
end
|
221
306
|
end
|
222
307
|
|
@@ -224,43 +309,62 @@ module Authlogic
|
|
224
309
|
end
|
225
310
|
end
|
226
311
|
|
312
|
+
# :nodoc:
|
227
313
|
module InstanceMethods
|
228
314
|
# The password
|
229
315
|
def password
|
316
|
+
return nil unless defined?(@password)
|
230
317
|
@password
|
231
318
|
end
|
232
319
|
|
233
|
-
# This is a virtual method. Once a password is passed to it, it will
|
234
|
-
# the password.
|
320
|
+
# This is a virtual method. Once a password is passed to it, it will
|
321
|
+
# create new password salt as well as encrypt the password.
|
235
322
|
def password=(pass)
|
236
323
|
return if ignore_blank_passwords? && pass.blank?
|
237
324
|
before_password_set
|
238
325
|
@password = pass
|
239
|
-
|
240
|
-
|
326
|
+
if password_salt_field
|
327
|
+
send("#{password_salt_field}=", Authlogic::Random.friendly_token)
|
328
|
+
end
|
329
|
+
encryptor_args_type = act_like_restful_authentication? ? :restful_authentication : nil
|
330
|
+
send(
|
331
|
+
"#{crypted_password_field}=",
|
332
|
+
crypto_provider.encrypt(
|
333
|
+
*encrypt_arguments(@password, false, encryptor_args_type)
|
334
|
+
)
|
335
|
+
)
|
241
336
|
@password_changed = true
|
242
337
|
after_password_set
|
243
338
|
end
|
244
339
|
|
245
|
-
# Accepts a raw password to determine if it is the correct password
|
246
|
-
#
|
247
|
-
#
|
248
|
-
|
249
|
-
|
340
|
+
# Accepts a raw password to determine if it is the correct password.
|
341
|
+
#
|
342
|
+
# - attempted_password [String] - password entered by user
|
343
|
+
# - check_against_database [boolean] - Should we check the password
|
344
|
+
# against the value in the database or the value in the object?
|
345
|
+
# Default taken from config option check_passwords_against_database.
|
346
|
+
# See config method for more information.
|
347
|
+
def valid_password?(
|
348
|
+
attempted_password,
|
349
|
+
check_against_database = check_passwords_against_database?
|
350
|
+
)
|
351
|
+
crypted = crypted_password_to_validate_against(check_against_database)
|
250
352
|
return false if attempted_password.blank? || crypted.blank?
|
251
353
|
before_password_verification
|
252
354
|
|
253
355
|
crypto_providers.each_with_index do |encryptor, index|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
356
|
+
next unless encryptor_matches?(
|
357
|
+
crypted,
|
358
|
+
encryptor,
|
359
|
+
index,
|
360
|
+
attempted_password,
|
361
|
+
check_against_database
|
362
|
+
)
|
363
|
+
if transition_password?(index, encryptor, check_against_database)
|
364
|
+
transition_password(attempted_password)
|
263
365
|
end
|
366
|
+
after_password_verification
|
367
|
+
return true
|
264
368
|
end
|
265
369
|
|
266
370
|
false
|
@@ -277,77 +381,130 @@ module Authlogic
|
|
277
381
|
# Resets the password to a random friendly token and then saves the record.
|
278
382
|
def reset_password!
|
279
383
|
reset_password
|
280
|
-
save_without_session_maintenance(:
|
384
|
+
save_without_session_maintenance(validate: false)
|
281
385
|
end
|
282
386
|
alias_method :randomize_password!, :reset_password!
|
283
387
|
|
284
388
|
private
|
285
|
-
def check_passwords_against_database?
|
286
|
-
self.class.check_passwords_against_database == true
|
287
|
-
end
|
288
389
|
|
289
|
-
|
290
|
-
|
390
|
+
def crypted_password_to_validate_against(check_against_database)
|
391
|
+
if check_against_database && send("#{crypted_password_field}_changed?")
|
392
|
+
send("#{crypted_password_field}_was")
|
393
|
+
else
|
394
|
+
send(crypted_password_field)
|
291
395
|
end
|
396
|
+
end
|
292
397
|
|
293
|
-
|
294
|
-
|
295
|
-
|
398
|
+
def check_passwords_against_database?
|
399
|
+
self.class.check_passwords_against_database == true
|
400
|
+
end
|
296
401
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
else
|
301
|
-
[raw_password, salt].compact
|
302
|
-
end
|
303
|
-
end
|
402
|
+
def crypto_providers
|
403
|
+
[crypto_provider] + transition_from_crypto_providers
|
404
|
+
end
|
304
405
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
406
|
+
# Returns an array of arguments to be passed to a crypto provider, either its
|
407
|
+
# `matches?` or its `encrypt` method.
|
408
|
+
def encrypt_arguments(raw_password, check_against_database, arguments_type = nil)
|
409
|
+
salt = nil
|
410
|
+
if password_salt_field
|
411
|
+
salt =
|
412
|
+
if check_against_database && send("#{password_salt_field}_changed?")
|
413
|
+
send("#{password_salt_field}_was")
|
414
|
+
else
|
415
|
+
send(password_salt_field)
|
416
|
+
end
|
313
417
|
end
|
314
418
|
|
315
|
-
|
316
|
-
|
317
|
-
|
419
|
+
case arguments_type
|
420
|
+
when :restful_authentication
|
421
|
+
[REST_AUTH_SITE_KEY, salt, raw_password, REST_AUTH_SITE_KEY].compact
|
422
|
+
when nil
|
423
|
+
[raw_password, salt].compact
|
424
|
+
else
|
425
|
+
raise "Invalid encryptor arguments_type: #{arguments_type}"
|
318
426
|
end
|
427
|
+
end
|
319
428
|
|
320
|
-
|
321
|
-
|
322
|
-
|
429
|
+
# Given `encryptor`, does `attempted_password` match the `crypted` password?
|
430
|
+
def encryptor_matches?(
|
431
|
+
crypted,
|
432
|
+
encryptor,
|
433
|
+
index,
|
434
|
+
attempted_password,
|
435
|
+
check_against_database
|
436
|
+
)
|
437
|
+
# The arguments_type for the transitioning from restful_authentication
|
438
|
+
acting_restful = act_like_restful_authentication? && index.zero?
|
439
|
+
transitioning = transition_from_restful_authentication? &&
|
440
|
+
index > 0 &&
|
441
|
+
encryptor == Authlogic::CryptoProviders::Sha1
|
442
|
+
restful = acting_restful || transitioning
|
443
|
+
arguments_type = restful ? :restful_authentication : nil
|
444
|
+
encryptor_args = encrypt_arguments(
|
445
|
+
attempted_password,
|
446
|
+
check_against_database,
|
447
|
+
arguments_type
|
448
|
+
)
|
449
|
+
encryptor.matches?(crypted, *encryptor_args)
|
450
|
+
end
|
323
451
|
|
324
|
-
|
325
|
-
|
326
|
-
|
452
|
+
# Determines if we need to transition the password.
|
453
|
+
#
|
454
|
+
# - If the index > 0 then we are using an "transition from" crypto
|
455
|
+
# provider.
|
456
|
+
# - If the encryptor has a cost and the cost it outdated.
|
457
|
+
# - If we aren't using database values
|
458
|
+
# - If we are using database values, only if the password hasn't
|
459
|
+
# changed so we don't overwrite any changes
|
460
|
+
def transition_password?(index, encryptor, check_against_database)
|
461
|
+
(
|
462
|
+
index > 0 ||
|
463
|
+
(encryptor.respond_to?(:cost_matches?) &&
|
464
|
+
!encryptor.cost_matches?(send(crypted_password_field)))
|
465
|
+
) &&
|
466
|
+
(
|
467
|
+
!check_against_database ||
|
468
|
+
!send("#{crypted_password_field}_changed?")
|
469
|
+
)
|
470
|
+
end
|
327
471
|
|
328
|
-
|
329
|
-
|
330
|
-
|
472
|
+
def transition_password(attempted_password)
|
473
|
+
self.password = attempted_password
|
474
|
+
save(validate: false)
|
475
|
+
end
|
331
476
|
|
332
|
-
|
333
|
-
|
334
|
-
|
477
|
+
def require_password?
|
478
|
+
new_record? || password_changed? || send(crypted_password_field).blank?
|
479
|
+
end
|
335
480
|
|
336
|
-
|
337
|
-
|
338
|
-
|
481
|
+
def ignore_blank_passwords?
|
482
|
+
self.class.ignore_blank_passwords == true
|
483
|
+
end
|
339
484
|
|
340
|
-
|
341
|
-
|
342
|
-
|
485
|
+
def password_changed?
|
486
|
+
defined?(@password_changed) && @password_changed == true
|
487
|
+
end
|
343
488
|
|
344
|
-
|
345
|
-
|
346
|
-
|
489
|
+
def reset_password_changed
|
490
|
+
@password_changed = nil
|
491
|
+
end
|
347
492
|
|
348
|
-
|
349
|
-
|
350
|
-
|
493
|
+
def crypted_password_field
|
494
|
+
self.class.crypted_password_field
|
495
|
+
end
|
496
|
+
|
497
|
+
def password_salt_field
|
498
|
+
self.class.password_salt_field
|
499
|
+
end
|
500
|
+
|
501
|
+
def crypto_provider
|
502
|
+
self.class.crypto_provider
|
503
|
+
end
|
504
|
+
|
505
|
+
def transition_from_crypto_providers
|
506
|
+
self.class.transition_from_crypto_providers
|
507
|
+
end
|
351
508
|
end
|
352
509
|
end
|
353
510
|
end
|