authlogic 3.4.6 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|