authlogic 3.4.6 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +13 -0
  3. data/.github/triage.md +87 -0
  4. data/.gitignore +4 -0
  5. data/.rubocop.yml +127 -0
  6. data/.rubocop_todo.yml +65 -0
  7. data/.travis.yml +18 -10
  8. data/CHANGELOG.md +156 -6
  9. data/CONTRIBUTING.md +71 -3
  10. data/Gemfile +2 -2
  11. data/README.md +386 -0
  12. data/Rakefile +13 -7
  13. data/UPGRADING.md +22 -0
  14. data/authlogic.gemspec +33 -22
  15. data/lib/authlogic.rb +60 -52
  16. data/lib/authlogic/acts_as_authentic/base.rb +40 -26
  17. data/lib/authlogic/acts_as_authentic/email.rb +96 -32
  18. data/lib/authlogic/acts_as_authentic/logged_in_status.rb +36 -12
  19. data/lib/authlogic/acts_as_authentic/login.rb +114 -49
  20. data/lib/authlogic/acts_as_authentic/magic_columns.rb +17 -6
  21. data/lib/authlogic/acts_as_authentic/password.rb +296 -139
  22. data/lib/authlogic/acts_as_authentic/perishable_token.rb +34 -20
  23. data/lib/authlogic/acts_as_authentic/persistence_token.rb +20 -24
  24. data/lib/authlogic/acts_as_authentic/queries/find_with_case.rb +67 -0
  25. data/lib/authlogic/acts_as_authentic/restful_authentication.rb +68 -23
  26. data/lib/authlogic/acts_as_authentic/session_maintenance.rb +128 -85
  27. data/lib/authlogic/acts_as_authentic/single_access_token.rb +41 -25
  28. data/lib/authlogic/acts_as_authentic/validations_scope.rb +8 -8
  29. data/lib/authlogic/authenticates_many/association.rb +22 -14
  30. data/lib/authlogic/authenticates_many/base.rb +35 -16
  31. data/lib/authlogic/config.rb +10 -10
  32. data/lib/authlogic/controller_adapters/abstract_adapter.rb +40 -12
  33. data/lib/authlogic/controller_adapters/rack_adapter.rb +15 -8
  34. data/lib/authlogic/controller_adapters/rails_adapter.rb +42 -22
  35. data/lib/authlogic/controller_adapters/sinatra_adapter.rb +3 -3
  36. data/lib/authlogic/crypto_providers.rb +91 -0
  37. data/lib/authlogic/crypto_providers/aes256.rb +42 -14
  38. data/lib/authlogic/crypto_providers/bcrypt.rb +35 -20
  39. data/lib/authlogic/crypto_providers/md5.rb +11 -9
  40. data/lib/authlogic/crypto_providers/scrypt.rb +26 -13
  41. data/lib/authlogic/crypto_providers/sha1.rb +14 -8
  42. data/lib/authlogic/crypto_providers/sha256.rb +16 -12
  43. data/lib/authlogic/crypto_providers/sha512.rb +8 -24
  44. data/lib/authlogic/crypto_providers/wordpress.rb +44 -15
  45. data/lib/authlogic/i18n.rb +33 -20
  46. data/lib/authlogic/i18n/translator.rb +1 -1
  47. data/lib/authlogic/random.rb +12 -29
  48. data/lib/authlogic/regex.rb +59 -27
  49. data/lib/authlogic/session/activation.rb +36 -23
  50. data/lib/authlogic/session/active_record_trickery.rb +13 -10
  51. data/lib/authlogic/session/base.rb +20 -8
  52. data/lib/authlogic/session/brute_force_protection.rb +87 -56
  53. data/lib/authlogic/session/callbacks.rb +99 -49
  54. data/lib/authlogic/session/cookies.rb +128 -59
  55. data/lib/authlogic/session/existence.rb +29 -19
  56. data/lib/authlogic/session/foundation.rb +70 -16
  57. data/lib/authlogic/session/http_auth.rb +39 -31
  58. data/lib/authlogic/session/id.rb +27 -15
  59. data/lib/authlogic/session/klass.rb +17 -13
  60. data/lib/authlogic/session/magic_columns.rb +78 -59
  61. data/lib/authlogic/session/magic_states.rb +50 -27
  62. data/lib/authlogic/session/params.rb +79 -50
  63. data/lib/authlogic/session/password.rb +197 -118
  64. data/lib/authlogic/session/perishable_token.rb +12 -6
  65. data/lib/authlogic/session/persistence.rb +20 -14
  66. data/lib/authlogic/session/priority_record.rb +20 -16
  67. data/lib/authlogic/session/scopes.rb +63 -33
  68. data/lib/authlogic/session/session.rb +40 -25
  69. data/lib/authlogic/session/timeout.rb +51 -34
  70. data/lib/authlogic/session/unauthorized_record.rb +24 -18
  71. data/lib/authlogic/session/validation.rb +32 -21
  72. data/lib/authlogic/test_case.rb +123 -35
  73. data/lib/authlogic/test_case/mock_controller.rb +14 -13
  74. data/lib/authlogic/test_case/mock_cookie_jar.rb +14 -5
  75. data/lib/authlogic/test_case/mock_logger.rb +1 -1
  76. data/lib/authlogic/test_case/mock_request.rb +9 -4
  77. data/lib/authlogic/test_case/rails_request_adapter.rb +8 -7
  78. data/lib/authlogic/version.rb +21 -0
  79. data/test/acts_as_authentic_test/base_test.rb +1 -1
  80. data/test/acts_as_authentic_test/email_test.rb +80 -63
  81. data/test/acts_as_authentic_test/logged_in_status_test.rb +14 -8
  82. data/test/acts_as_authentic_test/login_test.rb +91 -49
  83. data/test/acts_as_authentic_test/magic_columns_test.rb +13 -13
  84. data/test/acts_as_authentic_test/password_test.rb +82 -60
  85. data/test/acts_as_authentic_test/perishable_token_test.rb +31 -25
  86. data/test/acts_as_authentic_test/persistence_token_test.rb +9 -5
  87. data/test/acts_as_authentic_test/restful_authentication_test.rb +18 -9
  88. data/test/acts_as_authentic_test/session_maintenance_test.rb +86 -22
  89. data/test/acts_as_authentic_test/single_access_test.rb +15 -15
  90. data/test/adapter_test.rb +21 -0
  91. data/test/authenticates_many_test.rb +26 -11
  92. data/test/config_test.rb +9 -9
  93. data/test/crypto_provider_test/aes256_test.rb +3 -3
  94. data/test/crypto_provider_test/bcrypt_test.rb +1 -1
  95. data/test/crypto_provider_test/scrypt_test.rb +2 -2
  96. data/test/crypto_provider_test/sha1_test.rb +4 -4
  97. data/test/crypto_provider_test/sha256_test.rb +2 -2
  98. data/test/crypto_provider_test/sha512_test.rb +3 -3
  99. data/test/crypto_provider_test/wordpress_test.rb +24 -0
  100. data/test/gemfiles/Gemfile.rails-4.2.x +2 -2
  101. data/test/gemfiles/Gemfile.rails-5.0.x +6 -0
  102. data/test/gemfiles/Gemfile.rails-5.1.x +6 -0
  103. data/test/gemfiles/Gemfile.rails-5.2.x +6 -0
  104. data/test/gemfiles/Gemfile.rails-master +6 -0
  105. data/test/i18n_test.rb +9 -9
  106. data/test/libs/affiliate.rb +2 -2
  107. data/test/libs/company.rb +4 -4
  108. data/test/libs/employee.rb +2 -2
  109. data/test/libs/employee_session.rb +1 -1
  110. data/test/libs/ldaper.rb +1 -1
  111. data/test/libs/project.rb +1 -1
  112. data/test/libs/user_session.rb +2 -2
  113. data/test/random_test.rb +9 -38
  114. data/test/session_test/activation_test.rb +7 -7
  115. data/test/session_test/active_record_trickery_test.rb +9 -6
  116. data/test/session_test/brute_force_protection_test.rb +26 -21
  117. data/test/session_test/callbacks_test.rb +10 -4
  118. data/test/session_test/cookies_test.rb +54 -20
  119. data/test/session_test/existence_test.rb +45 -23
  120. data/test/session_test/foundation_test.rb +17 -1
  121. data/test/session_test/http_auth_test.rb +11 -12
  122. data/test/session_test/id_test.rb +3 -3
  123. data/test/session_test/klass_test.rb +2 -2
  124. data/test/session_test/magic_columns_test.rb +15 -17
  125. data/test/session_test/magic_states_test.rb +17 -19
  126. data/test/session_test/params_test.rb +26 -20
  127. data/test/session_test/password_test.rb +11 -12
  128. data/test/session_test/perishability_test.rb +5 -5
  129. data/test/session_test/persistence_test.rb +4 -3
  130. data/test/session_test/scopes_test.rb +15 -9
  131. data/test/session_test/session_test.rb +7 -6
  132. data/test/session_test/timeout_test.rb +16 -14
  133. data/test/session_test/unauthorized_record_test.rb +3 -3
  134. data/test/session_test/validation_test.rb +5 -5
  135. data/test/test_helper.rb +115 -49
  136. metadata +107 -36
  137. data/README.rdoc +0 -232
  138. data/test/gemfiles/Gemfile.rails-3.2.x +0 -7
  139. data/test/gemfiles/Gemfile.rails-4.0.x +0 -7
  140. 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 a user is logged in or not,
4
- # from the database perspective. The best way to do this is to provide a "timeout" based on inactivity.
5
- # So if that user is inactive for a certain amount of time we assume they are logged out. That's what this
6
- # module is all about.
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 if !klass.column_names.include?("last_request_at")
31
+ return unless klass.column_names.include?("last_request_at")
31
32
 
32
33
  klass.class_eval do
33
34
  include InstanceMethods
34
- scope :logged_in, lambda{ where("last_request_at > ? and current_login_at IS NOT NULL", logged_in_timeout.seconds.ago) }
35
- scope :logged_out, lambda{ where("last_request_at is NULL or last_request_at <= ?", logged_in_timeout.seconds.ago) }
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
- raise "Can not determine the records login state because there is no last_request_at column" if !respond_to?(:last_request_at)
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
- def logged_in_timeout
53
- self.class.logged_in_timeout
54
- end
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 field. Allows you to change this however you want.
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 possible, so you can completely replace the hash or
35
- # merge options into it. Checkout the convenience function merge_validates_length_of_login_field_options to merge
36
- # options.</b>
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, {:within => 3..100})
45
+ rw_config(:validates_length_of_login_field_options, value, within: 3..100)
42
46
  end
43
- alias_method :validates_length_of_login_field_options=, :validates_length_of_login_field_options
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 validates_length_of_login_field_options. So instead of:
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 = validates_length_of_login_field_options.merge(:my_option => my_value)
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 = validates_length_of_login_field_options.merge(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 field. Allows you to change this however you want.
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
- # <b>Keep in mind this is ruby. I wanted to keep this as flexible as possible, so you can completely replace the hash or
59
- # merge options into it. Checkout the convenience function merge_validates_format_of_login_field_options to merge
60
- # options.</b>
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(:validates_format_of_login_field_options, value, {:with => Authlogic::Regex.login, :message => I18n.t('error_messages.login_invalid', :default => "should use only letters, numbers, spaces, and .-_@ please.")})
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 :validates_format_of_login_field_options=, :validates_format_of_login_field_options
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, except for validates_format_of_login_field_options
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 = validates_format_of_login_field_options.merge(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 field. Allows you to change this however you want.
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
- # <b>Keep in mind this is ruby. I wanted to keep this as flexible as possible, so you can completely replace the hash or
77
- # merge options into it. Checkout the convenience function merge_validates_format_of_login_field_options to merge
78
- # options.</b>
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(:validates_uniqueness_of_login_field_options, value, {:case_sensitive => false, :scope => validations_scope, :if => "#{login_field}_changed?".to_sym})
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 :validates_uniqueness_of_login_field_options=, :validates_uniqueness_of_login_field_options
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, except for validates_uniqueness_of_login_field_options
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 = validates_uniqueness_of_login_field_options.merge(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 notice, with Active Record you have the
93
- # UniquenessValidator class. They give you a :case_sensitive option. I handle this in the same
94
- # manner that they handle that. If you are using the login field, set false for the :case_sensitive option in
95
- # validates_uniqueness_of_login_field_options and the column doesn't have a case-insensitive collation,
96
- # this method will modify the query to look something like:
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 (with the binary modifier if necessary):
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 need to set the :case_sensitive in
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(login_field, login, validates_uniqueness_of_login_field_options[:case_sensitive] != false)
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(email_field, login, validates_uniqueness_of_email_field_options[:case_sensitive] != false)
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
- def find_with_case(field, value, sensitivity = true)
116
- relation = if not sensitivity
117
- connection.case_insensitive_comparison(arel_table, field.to_s, columns_hash[field.to_s], value)
118
- else
119
- if Gem::Version.new(Rails.version) < Gem::Version.new('4.2')
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 "magically" maintained for
4
- # you. Authlogic has the same thing, but these are maintained on the session side. Please see Authlogic::Session::MagicColumns
5
- # for more details. This module merely adds validations for the magic columns if they exist.
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
- validates_numericality_of :login_count, :only_integer => true, :greater_than_or_equal_to => 0, :allow_nil => true if column_names.include?("login_count")
18
- validates_numericality_of :failed_login_count, :only_integer => true, :greater_than_or_equal_to => 0, :allow_nil => true if column_names.include?("failed_login_count")
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 password, salting it, and verifying it.
4
- # It can also help you transition to a new encryption algorithm. See the Config sub module for configuration options.
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(:crypted_password_field, value, first_column_to_exist(nil, :crypted_password, :encrypted_password, :password_hash, :pw_hash))
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(:password_salt_field, value, first_column_to_exist(nil, :password_salt, :pw_salt, :salt))
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 to confirm their password
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 is blank, but if both of these things
45
- # are met a password is not required. In this case, blank passwords are ignored.
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, including changing their password.
48
- # If they do not want to change their password they just leave the fields blank. This will try to set the password to
49
- # a blank value, in which case is incorrect behavior. As such, Authlogic ignores this. But let's say you have a completely
50
- # separate page for resetting passwords, you might not want to ignore blank passwords. If this is the case for you, then
51
- # just set this value to false.
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 against what's in that object or whats in
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 yet, so most would assume true.
68
- # Other would assume false. So I let you decide by giving you this option.
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. Allows you to change this however you want.
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
- # <b>Keep in mind this is ruby. I wanted to keep this as flexible as possible, so you can completely replace the hash or
89
- # merge options into it. Checkout the convenience function merge_validates_length_of_password_field_options to merge
90
- # options.</b>
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 => 4, :if => :require_password?}
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(:validates_length_of_password_field_options, value, {:minimum => 4, :if => :require_password?})
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 :validates_length_of_password_field_options=, :validates_length_of_password_field_options
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 validates_length_of_login_field_options. So intead of:
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 = validates_length_of_password_field_options.merge(:my_option => my_value)
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 = validates_length_of_password_field_options.merge(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 password field. Allows you to change this however you want.
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
- # <b>Keep in mind this is ruby. I wanted to keep this as flexible as possible, so you can completely replace the hash or
113
- # merge options into it. Checkout the convenience function merge_validates_length_of_password_field_options to merge
114
- # options.</b>
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(:validates_confirmation_of_password_field_options, value, {:if => :require_password?})
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=, :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 validates_confirmation_of_password_field_options
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 = validates_confirmation_of_password_field_options.merge(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 field. Allows you to change this however you want.
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 you can completely replace the hash or
131
- # merge options into it. Checkout the convenience function merge_validates_length_of_password_field_options to merge
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(:validates_length_of_password_confirmation_field_options, value, validates_length_of_password_field_options)
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 :validates_length_of_password_confirmation_field_options=, :validates_length_of_password_confirmation_field_options
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 validates_length_of_password_confirmation_field_options
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 = validates_length_of_password_confirmation_field_options.merge(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 passwords. See the Authlogic::CryptoProviders module for more info
147
- # on the available methods and how to create your own.
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 starting to join the party with MD5 and you want to switch
157
- # to something stronger. No problem, just specify your new and improved algorithm with the crypt_provider option and then let
158
- # Authlogic know you are transitioning from Sha1 using this option. Authlogic will take care of everything, including transitioning
159
- # your users to the new algorithm. The next time a user logs in, they will be granted access using the old algorithm and their
160
- # password will be resaved with the new algorithm. All new users will obviously use the new algorithm as well.
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 crypto providers. So you can transition from as many algorithms
163
- # as you want.
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(:transition_from_crypto_providers, (!value.nil? && [value].flatten.compact) || value, [])
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
- METHODS = [
176
- "before_password_set", "after_password_set",
177
- "before_password_verification", "after_password_verification"
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 *METHODS
258
+ klass.define_callbacks(*METHODS)
183
259
 
184
260
  # If Rails 3, support the new callback syntax
185
- if klass.send(klass.respond_to?(:singleton_class) ? :singleton_class : :metaclass).method_defined?(:set_callback)
261
+ if klass.singleton_class.method_defined?(:set_callback)
186
262
  METHODS.each do |method|
187
- klass.class_eval <<-"end_eval", __FILE__, __LINE__
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
- end_eval
267
+ EOS
192
268
  end
193
269
  end
194
270
  end
195
271
 
196
- private
197
- METHODS.each do |method|
198
- class_eval <<-"end_eval", __FILE__, __LINE__
199
- def #{method}
200
- run_callbacks(:#{method}) { |result, object| result == false }
201
- end
202
- end_eval
203
- end
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 :password, validates_confirmation_of_password_field_options
219
- validates_length_of :password_confirmation, validates_length_of_password_confirmation_field_options
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 create new password salt as well as encrypt
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
- send("#{password_salt_field}=", Authlogic::Random.friendly_token) if password_salt_field
240
- send("#{crypted_password_field}=", crypto_provider.encrypt(*encrypt_arguments(@password, false, act_like_restful_authentication? ? :restful_authentication : nil)))
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 or not. Notice the second argument. That defaults to the value of
246
- # check_passwords_against_database. See that method for more information, but basically it just tells Authlogic to check the password
247
- # against the value in the database or the value in the object.
248
- def valid_password?(attempted_password, check_against_database = check_passwords_against_database?)
249
- crypted = check_against_database && send("#{crypted_password_field}_changed?") ? send("#{crypted_password_field}_was") : send(crypted_password_field)
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
- # The arguments_type of for the transitioning from restful_authentication
255
- arguments_type = (act_like_restful_authentication? && index == 0) ||
256
- (transition_from_restful_authentication? && index > 0 && encryptor == Authlogic::CryptoProviders::Sha1) ?
257
- :restful_authentication : nil
258
-
259
- if encryptor.matches?(crypted, *encrypt_arguments(attempted_password, check_against_database, arguments_type))
260
- transition_password(attempted_password) if transition_password?(index, encryptor, crypted, check_against_database)
261
- after_password_verification
262
- return true
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(:validate => false)
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
- def crypto_providers
290
- [crypto_provider] + transition_from_crypto_providers
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
- def encrypt_arguments(raw_password, check_against_database, arguments_type = nil)
294
- salt = nil
295
- salt = (check_against_database && send("#{password_salt_field}_changed?") ? send("#{password_salt_field}_was") : send(password_salt_field)) if password_salt_field
398
+ def check_passwords_against_database?
399
+ self.class.check_passwords_against_database == true
400
+ end
296
401
 
297
- case arguments_type
298
- when :restful_authentication
299
- [REST_AUTH_SITE_KEY, salt, raw_password, REST_AUTH_SITE_KEY].compact
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
- # Determines if we need to tranisiton the password.
306
- # If the index > 0 then we are using an "transition from" crypto provider.
307
- # If the encryptor has a cost and the cost it outdated.
308
- # If we aren't using database values
309
- # If we are using database values, only if the password hasn't changed so we don't overwrite any changes
310
- def transition_password?(index, encryptor, crypted, check_against_database)
311
- (index > 0 || (encryptor.respond_to?(:cost_matches?) && !encryptor.cost_matches?(send(crypted_password_field)))) &&
312
- (!check_against_database || !send("#{crypted_password_field}_changed?"))
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
- def transition_password(attempted_password)
316
- self.password = attempted_password
317
- save(:validate => false)
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
- def require_password?
321
- new_record? || password_changed? || send(crypted_password_field).blank?
322
- end
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
- def ignore_blank_passwords?
325
- self.class.ignore_blank_passwords == true
326
- end
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
- def password_changed?
329
- @password_changed == true
330
- end
472
+ def transition_password(attempted_password)
473
+ self.password = attempted_password
474
+ save(validate: false)
475
+ end
331
476
 
332
- def reset_password_changed
333
- @password_changed = nil
334
- end
477
+ def require_password?
478
+ new_record? || password_changed? || send(crypted_password_field).blank?
479
+ end
335
480
 
336
- def crypted_password_field
337
- self.class.crypted_password_field
338
- end
481
+ def ignore_blank_passwords?
482
+ self.class.ignore_blank_passwords == true
483
+ end
339
484
 
340
- def password_salt_field
341
- self.class.password_salt_field
342
- end
485
+ def password_changed?
486
+ defined?(@password_changed) && @password_changed == true
487
+ end
343
488
 
344
- def crypto_provider
345
- self.class.crypto_provider
346
- end
489
+ def reset_password_changed
490
+ @password_changed = nil
491
+ end
347
492
 
348
- def transition_from_crypto_providers
349
- self.class.transition_from_crypto_providers
350
- end
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