authlogic 3.8.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/.github/triage.md +87 -0
  3. data/.gitignore +2 -1
  4. data/.rubocop.yml +62 -6
  5. data/.rubocop_todo.yml +51 -267
  6. data/.travis.yml +4 -26
  7. data/CHANGELOG.md +226 -2
  8. data/CONTRIBUTING.md +15 -5
  9. data/Gemfile +2 -2
  10. data/README.md +183 -91
  11. data/Rakefile +1 -1
  12. data/UPGRADING.md +20 -0
  13. data/authlogic.gemspec +25 -16
  14. data/lib/authlogic.rb +45 -45
  15. data/lib/authlogic/acts_as_authentic/base.rb +18 -11
  16. data/lib/authlogic/acts_as_authentic/email.rb +32 -28
  17. data/lib/authlogic/acts_as_authentic/logged_in_status.rb +1 -1
  18. data/lib/authlogic/acts_as_authentic/login.rb +32 -42
  19. data/lib/authlogic/acts_as_authentic/magic_columns.rb +6 -6
  20. data/lib/authlogic/acts_as_authentic/password.rb +53 -31
  21. data/lib/authlogic/acts_as_authentic/perishable_token.rb +18 -17
  22. data/lib/authlogic/acts_as_authentic/persistence_token.rb +7 -12
  23. data/lib/authlogic/acts_as_authentic/queries/find_with_case.rb +64 -0
  24. data/lib/authlogic/acts_as_authentic/restful_authentication.rb +11 -3
  25. data/lib/authlogic/acts_as_authentic/session_maintenance.rb +30 -10
  26. data/lib/authlogic/acts_as_authentic/single_access_token.rb +4 -4
  27. data/lib/authlogic/authenticates_many/association.rb +3 -3
  28. data/lib/authlogic/authenticates_many/base.rb +2 -2
  29. data/lib/authlogic/config.rb +0 -1
  30. data/lib/authlogic/controller_adapters/abstract_adapter.rb +11 -4
  31. data/lib/authlogic/controller_adapters/rack_adapter.rb +7 -3
  32. data/lib/authlogic/controller_adapters/rails_adapter.rb +2 -0
  33. data/lib/authlogic/crypto_providers/aes256.rb +1 -1
  34. data/lib/authlogic/crypto_providers/bcrypt.rb +1 -1
  35. data/lib/authlogic/crypto_providers/scrypt.rb +6 -6
  36. data/lib/authlogic/crypto_providers/sha1.rb +10 -5
  37. data/lib/authlogic/crypto_providers/sha256.rb +11 -8
  38. data/lib/authlogic/crypto_providers/wordpress.rb +2 -2
  39. data/lib/authlogic/i18n.rb +4 -2
  40. data/lib/authlogic/random.rb +10 -28
  41. data/lib/authlogic/regex.rb +11 -8
  42. data/lib/authlogic/session/activation.rb +6 -3
  43. data/lib/authlogic/session/active_record_trickery.rb +13 -9
  44. data/lib/authlogic/session/base.rb +15 -4
  45. data/lib/authlogic/session/brute_force_protection.rb +14 -7
  46. data/lib/authlogic/session/callbacks.rb +53 -30
  47. data/lib/authlogic/session/cookies.rb +57 -16
  48. data/lib/authlogic/session/existence.rb +21 -11
  49. data/lib/authlogic/session/foundation.rb +56 -10
  50. data/lib/authlogic/session/http_auth.rb +15 -8
  51. data/lib/authlogic/session/klass.rb +7 -5
  52. data/lib/authlogic/session/magic_columns.rb +24 -11
  53. data/lib/authlogic/session/magic_states.rb +11 -4
  54. data/lib/authlogic/session/params.rb +6 -2
  55. data/lib/authlogic/session/password.rb +46 -73
  56. data/lib/authlogic/session/persistence.rb +11 -7
  57. data/lib/authlogic/session/priority_record.rb +7 -4
  58. data/lib/authlogic/session/scopes.rb +15 -6
  59. data/lib/authlogic/session/session.rb +20 -10
  60. data/lib/authlogic/session/timeout.rb +2 -2
  61. data/lib/authlogic/session/unauthorized_record.rb +1 -1
  62. data/lib/authlogic/session/validation.rb +1 -1
  63. data/lib/authlogic/test_case.rb +65 -2
  64. data/lib/authlogic/test_case/mock_controller.rb +5 -4
  65. data/lib/authlogic/test_case/mock_cookie_jar.rb +11 -2
  66. data/lib/authlogic/test_case/mock_request.rb +5 -1
  67. data/lib/authlogic/test_case/rails_request_adapter.rb +3 -2
  68. data/lib/authlogic/version.rb +16 -0
  69. data/test/acts_as_authentic_test/email_test.rb +33 -34
  70. data/test/acts_as_authentic_test/logged_in_status_test.rb +1 -1
  71. data/test/acts_as_authentic_test/login_test.rb +73 -78
  72. data/test/acts_as_authentic_test/password_test.rb +30 -18
  73. data/test/acts_as_authentic_test/perishable_token_test.rb +9 -3
  74. data/test/acts_as_authentic_test/persistence_token_test.rb +4 -0
  75. data/test/acts_as_authentic_test/session_maintenance_test.rb +66 -14
  76. data/test/adapter_test.rb +21 -0
  77. data/test/gemfiles/Gemfile.rails-4.2.x +2 -2
  78. data/test/gemfiles/Gemfile.rails-5.0.x +2 -2
  79. data/test/gemfiles/Gemfile.rails-master +6 -0
  80. data/test/i18n_test.rb +1 -1
  81. data/test/libs/company.rb +2 -2
  82. data/test/random_test.rb +7 -37
  83. data/test/session_test/active_record_trickery_test.rb +4 -3
  84. data/test/session_test/brute_force_protection_test.rb +8 -8
  85. data/test/session_test/callbacks_test.rb +1 -1
  86. data/test/session_test/cookies_test.rb +27 -4
  87. data/test/session_test/existence_test.rb +15 -4
  88. data/test/session_test/foundation_test.rb +16 -0
  89. data/test/session_test/http_auth_test.rb +3 -1
  90. data/test/session_test/magic_columns_test.rb +10 -12
  91. data/test/session_test/params_test.rb +4 -1
  92. data/test/session_test/password_test.rb +7 -7
  93. data/test/session_test/persistence_test.rb +1 -0
  94. data/test/session_test/scopes_test.rb +7 -7
  95. data/test/session_test/session_test.rb +2 -2
  96. data/test/session_test/timeout_test.rb +1 -1
  97. data/test/session_test/unauthorized_record_test.rb +1 -1
  98. data/test/test_helper.rb +111 -103
  99. metadata +68 -64
  100. data/test/gemfiles/Gemfile.rails-3.2.x +0 -7
  101. data/test/gemfiles/Gemfile.rails-4.0.x +0 -7
  102. data/test/gemfiles/Gemfile.rails-4.1.x +0 -7
@@ -1,7 +1,18 @@
1
1
  module Authlogic
2
2
  module Session # :nodoc:
3
- # This is the base class Authlogic, where all modules are included. For information on functionality see the various
4
- # sub modules.
3
+ # This is the most important class in Authlogic. You will inherit this class
4
+ # for your own eg. `UserSession`.
5
+ #
6
+ # Code is organized topically. Each topic is represented by a module. So, to
7
+ # learn about password-based authentication, read the `Password` module.
8
+ #
9
+ # It is common for methods (.initialize and #credentials=, for example) to
10
+ # be implemented in multiple mixins. Those methods will call `super`, so the
11
+ # order of `include`s here is important.
12
+ #
13
+ # Also, to fully understand such a method (like #credentials=) you will need
14
+ # to mentally combine all of its definitions. This is perhaps the primary
15
+ # disadvantage of topical organization using modules.
5
16
  class Base
6
17
  include Foundation
7
18
  include Callbacks
@@ -15,8 +26,8 @@ module Authlogic
15
26
  include Session
16
27
  include HttpAuth
17
28
 
18
- # Included in a specific order so magic states gets ran after a record is found
19
- # TODO: What does "magic states gets ran" mean? Be specific.
29
+ # Included in a specific order so magic states gets run after a record is found
30
+ # TODO: What does "magic states gets run" mean? Be specific.
20
31
  include Password
21
32
  include UnauthorizedRecord
22
33
  include MagicStates
@@ -25,8 +25,8 @@ module Authlogic
25
25
  klass.class_eval do
26
26
  extend Config
27
27
  include InstanceMethods
28
- validate :reset_failed_login_count, :if => :reset_failed_login_count?
29
- validate :validate_failed_logins, :if => :being_brute_force_protected?
28
+ validate :reset_failed_login_count, if: :reset_failed_login_count?
29
+ validate :validate_failed_logins, if: :being_brute_force_protected?
30
30
  end
31
31
  end
32
32
 
@@ -73,15 +73,22 @@ module Authlogic
73
73
  # with configuration. By default they will be banned for 2 hours. During
74
74
  # that 2 hour period this method will return true.
75
75
  def being_brute_force_protected?
76
- exceeded_failed_logins_limit? && (failed_login_ban_for <= 0 ||
77
- (attempted_record.respond_to?(:updated_at) && attempted_record.updated_at >= failed_login_ban_for.seconds.ago))
76
+ exceeded_failed_logins_limit? &&
77
+ (
78
+ failed_login_ban_for <= 0 ||
79
+ attempted_record.respond_to?(:updated_at) &&
80
+ attempted_record.updated_at >= failed_login_ban_for.seconds.ago
81
+ )
78
82
  end
79
83
 
80
84
  private
81
85
 
82
86
  def exceeded_failed_logins_limit?
83
- !attempted_record.nil? && attempted_record.respond_to?(:failed_login_count) && consecutive_failed_logins_limit > 0 &&
84
- attempted_record.failed_login_count && attempted_record.failed_login_count >= consecutive_failed_logins_limit
87
+ !attempted_record.nil? &&
88
+ attempted_record.respond_to?(:failed_login_count) &&
89
+ consecutive_failed_logins_limit > 0 &&
90
+ attempted_record.failed_login_count &&
91
+ attempted_record.failed_login_count >= consecutive_failed_logins_limit
85
92
  end
86
93
 
87
94
  def reset_failed_login_count?
@@ -100,7 +107,7 @@ module Authlogic
100
107
  :base,
101
108
  I18n.t(
102
109
  'error_messages.consecutive_failed_logins_limit_exceeded',
103
- :default => "Consecutive failed logins limit exceeded, account has been" +
110
+ default: "Consecutive failed logins limit exceeded, account has been" +
104
111
  (failed_login_ban_for == 0 ? "" : " temporarily") +
105
112
  " disabled."
106
113
  )
@@ -1,15 +1,18 @@
1
1
  module Authlogic
2
2
  module Session
3
- # Between these callbacks and the configuration, this is the contract between me and you to safely
4
- # modify Authlogic's behavior. I will do everything I can to make sure these do not change.
3
+ # Between these callbacks and the configuration, this is the contract between me and
4
+ # you to safely modify Authlogic's behavior. I will do everything I can to make sure
5
+ # these do not change.
5
6
  #
6
- # Check out the sub modules of Authlogic::Session. They are very concise, clear, and to the point. More
7
- # importantly they use the same API that you would use to extend Authlogic. That being said, they are great
8
- # examples of how to extend Authlogic and add / modify behavior to Authlogic. These modules could easily be pulled out
9
- # into their own plugin and become an "add on" without any change.
7
+ # Check out the sub modules of Authlogic::Session. They are very concise, clear, and
8
+ # to the point. More importantly they use the same API that you would use to extend
9
+ # Authlogic. That being said, they are great examples of how to extend Authlogic and
10
+ # add / modify behavior to Authlogic. These modules could easily be pulled out into
11
+ # their own plugin and become an "add on" without any change.
10
12
  #
11
- # Now to the point of this module. Just like in ActiveRecord you have before_save, before_validation, etc.
12
- # You have similar callbacks with Authlogic, see the METHODS constant below. The order of execution is as follows:
13
+ # Now to the point of this module. Just like in ActiveRecord you have before_save,
14
+ # before_validation, etc. You have similar callbacks with Authlogic, see the METHODS
15
+ # constant below. The order of execution is as follows:
13
16
  #
14
17
  # before_persisting
15
18
  # persist
@@ -38,9 +41,10 @@ module Authlogic
38
41
  # destroy
39
42
  # after_destroy
40
43
  #
41
- # Notice the "save record if changed?" lines above. This helps with performance. If you need to make
42
- # changes to the associated record, there is no need to save the record, Authlogic will do it for you.
43
- # This allows multiple modules to modify the record and execute as few queries as possible.
44
+ # Notice the "save record if changed?" lines above. This helps with performance. If
45
+ # you need to make changes to the associated record, there is no need to save the
46
+ # record, Authlogic will do it for you. This allows multiple modules to modify the
47
+ # record and execute as few queries as possible.
44
48
  #
45
49
  # **WARNING**: unlike ActiveRecord, these callbacks must be set up on the class level:
46
50
  #
@@ -50,38 +54,57 @@ module Authlogic
50
54
  # # ..etc
51
55
  # end
52
56
  #
53
- # You can NOT define a "before_validation" method, this is bad practice and does not allow Authlogic
54
- # to extend properly with multiple extensions. Please ONLY use the method above.
57
+ # You can NOT define a "before_validation" method, this is bad practice and does not
58
+ # allow Authlogic to extend properly with multiple extensions. Please ONLY use the
59
+ # method above.
55
60
  module Callbacks
56
61
  METHODS = [
57
- "before_persisting", "persist", "after_persisting",
58
- "before_validation", "before_validation_on_create", "before_validation_on_update", "validate",
59
- "after_validation_on_update", "after_validation_on_create", "after_validation",
60
- "before_save", "before_create", "before_update", "after_update", "after_create", "after_save",
61
- "before_destroy", "after_destroy"
62
- ]
62
+ "before_persisting",
63
+ "persist",
64
+ "after_persisting",
65
+ "before_validation",
66
+ "before_validation_on_create",
67
+ "before_validation_on_update",
68
+ "validate",
69
+ "after_validation_on_update",
70
+ "after_validation_on_create",
71
+ "after_validation",
72
+ "before_save",
73
+ "before_create",
74
+ "before_update",
75
+ "after_update",
76
+ "after_create",
77
+ "after_save",
78
+ "before_destroy",
79
+ "after_destroy"
80
+ ].freeze
63
81
 
64
82
  def self.included(base) #:nodoc:
65
83
  base.send :include, ActiveSupport::Callbacks
66
84
  if Gem::Version.new(ActiveSupport::VERSION::STRING) >= Gem::Version.new('5')
67
- base.define_callbacks *METHODS + [{ :terminator => ->(target, result_lambda) { result_lambda.call == false } }]
68
- base.define_callbacks *['persist', { :terminator => ->(target, result_lambda) { result_lambda.call == true } }]
85
+ base.define_callbacks(
86
+ *METHODS + [{ terminator: ->(_target, result_lambda) { result_lambda.call == false } }]
87
+ )
88
+ base.define_callbacks(
89
+ 'persist',
90
+ terminator: ->(_target, result_lambda) { result_lambda.call == true }
91
+ )
69
92
  elsif Gem::Version.new(ActiveSupport::VERSION::STRING) >= Gem::Version.new('4.1')
70
- base.define_callbacks *METHODS + [{ :terminator => ->(target, result) { result == false } }]
71
- base.define_callbacks *['persist', { :terminator => ->(target, result) { result == true } }]
93
+ base.define_callbacks(*METHODS + [{ terminator: ->(_target, result) { result == false } }])
94
+ base.define_callbacks('persist', terminator: ->(_target, result) { result == true })
72
95
  else
73
- base.define_callbacks *METHODS + [{ :terminator => 'result == false' }]
74
- base.define_callbacks *['persist', { :terminator => 'result == true' }]
96
+ base.define_callbacks(*METHODS + [{ terminator: 'result == false' }])
97
+ base.define_callbacks('persist', terminator: 'result == true')
75
98
  end
76
99
 
77
100
  # If Rails 3, support the new callback syntax
78
101
  if base.singleton_class.method_defined?(:set_callback)
79
102
  METHODS.each do |method|
80
- base.class_eval <<-"end_eval", __FILE__, __LINE__
103
+ base.class_eval <<-EOS, __FILE__, __LINE__
81
104
  def self.#{method}(*methods, &block)
82
105
  set_callback :#{method}, *methods, &block
83
106
  end
84
- end_eval
107
+ EOS
85
108
  end
86
109
  end
87
110
  end
@@ -89,16 +112,16 @@ module Authlogic
89
112
  private
90
113
 
91
114
  METHODS.each do |method|
92
- class_eval <<-"end_eval", __FILE__, __LINE__
115
+ class_eval <<-EOS, __FILE__, __LINE__
93
116
  def #{method}
94
117
  run_callbacks(:#{method})
95
118
  end
96
- end_eval
119
+ EOS
97
120
  end
98
121
 
99
122
  def save_record(alternate_record = nil)
100
123
  r = alternate_record || record
101
- r.save_without_session_maintenance(:validate => false) if r && r.changed? && !r.readonly?
124
+ r.save_without_session_maintenance(validate: false) if r && r.changed? && !r.readonly?
102
125
  end
103
126
  end
104
127
  end
@@ -3,6 +3,8 @@ module Authlogic
3
3
  # Handles all authentication that deals with cookies, such as persisting,
4
4
  # saving, and destroying.
5
5
  module Cookies
6
+ VALID_SAME_SITE_VALUES = [nil, 'Lax', 'Strict'].freeze
7
+
6
8
  def self.included(klass)
7
9
  klass.class_eval do
8
10
  extend Config
@@ -54,23 +56,37 @@ module Authlogic
54
56
  # Should the cookie be set as secure? If true, the cookie will only be sent over
55
57
  # SSL connections
56
58
  #
57
- # * <tt>Default:</tt> false
59
+ # * <tt>Default:</tt> true
58
60
  # * <tt>Accepts:</tt> Boolean
59
61
  def secure(value = nil)
60
- rw_config(:secure, value, false)
62
+ rw_config(:secure, value, true)
61
63
  end
62
64
  alias_method :secure=, :secure
63
65
 
64
66
  # Should the cookie be set as httponly? If true, the cookie will not be
65
67
  # accessible from javascript
66
68
  #
67
- # * <tt>Default:</tt> false
69
+ # * <tt>Default:</tt> true
68
70
  # * <tt>Accepts:</tt> Boolean
69
71
  def httponly(value = nil)
70
- rw_config(:httponly, value, false)
72
+ rw_config(:httponly, value, true)
71
73
  end
72
74
  alias_method :httponly=, :httponly
73
75
 
76
+ # Should the cookie be prevented from being send along with cross-site
77
+ # requests?
78
+ #
79
+ # * <tt>Default:</tt> nil
80
+ # * <tt>Accepts:</tt> String, one of nil, 'Lax' or 'Strict'
81
+ def same_site(value = nil)
82
+ unless VALID_SAME_SITE_VALUES.include?(value)
83
+ msg = "Invalid same_site value: #{value}. Valid: #{VALID_SAME_SITE_VALUES.inspect}"
84
+ raise ArgumentError.new(msg)
85
+ end
86
+ rw_config(:same_site, value)
87
+ end
88
+ alias_method :same_site=, :same_site
89
+
74
90
  # Should the cookie be signed? If the controller adapter supports it, this is a
75
91
  # measure against cookie tampering.
76
92
  def sign_cookie(value = nil)
@@ -95,8 +111,8 @@ module Authlogic
95
111
  self.remember_me = values.first.with_indifferent_access[:remember_me]
96
112
  end
97
113
  else
98
- r = values.find { |value| value.is_a?(TrueClass) || value.is_a?(FalseClass) }
99
- self.remember_me = r if !r.nil?
114
+ r = values.find { |val| val.is_a?(TrueClass) || val.is_a?(FalseClass) }
115
+ self.remember_me = r unless r.nil?
100
116
  end
101
117
  end
102
118
 
@@ -172,6 +188,21 @@ module Authlogic
172
188
  httponly == true || httponly == "true" || httponly == "1"
173
189
  end
174
190
 
191
+ # If the cookie should be marked as SameSite with 'Lax' or 'Strict' flag.
192
+ def same_site
193
+ return @same_site if defined?(@same_site)
194
+ @same_site = self.class.same_site(nil)
195
+ end
196
+
197
+ # Accepts nil, 'Lax' or 'Strict' as possible flags.
198
+ def same_site=(value)
199
+ unless VALID_SAME_SITE_VALUES.include?(value)
200
+ msg = "Invalid same_site value: #{value}. Valid: #{VALID_SAME_SITE_VALUES.inspect}"
201
+ raise ArgumentError.new(msg)
202
+ end
203
+ @same_site = value
204
+ end
205
+
175
206
  # If the cookie should be signed
176
207
  def sign_cookie
177
208
  return @sign_cookie if defined?(@sign_cookie)
@@ -196,12 +227,16 @@ module Authlogic
196
227
  end
197
228
 
198
229
  def cookie_credentials
230
+ cookie = cookie_jar[cookie_key]
231
+ cookie && cookie.split("::")
232
+ end
233
+
234
+ def cookie_jar
199
235
  if self.class.sign_cookie
200
- cookie = controller.cookies.signed[cookie_key]
236
+ controller.cookies.signed
201
237
  else
202
- cookie = controller.cookies[cookie_key]
238
+ controller.cookies
203
239
  end
204
- cookie && cookie.split("::")
205
240
  end
206
241
 
207
242
  # Tries to validate the session from information in the cookie
@@ -225,18 +260,24 @@ module Authlogic
225
260
  end
226
261
 
227
262
  def generate_cookie_for_saving
228
- remember_me_until_value = "::#{remember_me_until.iso8601}" if remember_me?
263
+ value = format(
264
+ "%s::%s%s",
265
+ record.persistence_token,
266
+ record.send(record.class.primary_key),
267
+ remember_me? ? "::#{remember_me_until.iso8601}" : ""
268
+ )
229
269
  {
230
- :value => "#{record.persistence_token}::#{record.send(record.class.primary_key)}#{remember_me_until_value}",
231
- :expires => remember_me_until,
232
- :secure => secure,
233
- :httponly => httponly,
234
- :domain => controller.cookie_domain
270
+ value: value,
271
+ expires: remember_me_until,
272
+ secure: secure,
273
+ httponly: httponly,
274
+ same_site: same_site,
275
+ domain: controller.cookie_domain
235
276
  }
236
277
  end
237
278
 
238
279
  def destroy_cookie
239
- controller.cookies.delete cookie_key, :domain => controller.cookie_domain
280
+ controller.cookies.delete cookie_key, domain: controller.cookie_domain
240
281
  end
241
282
  end
242
283
  end
@@ -1,10 +1,16 @@
1
1
  module Authlogic
2
2
  module Session
3
- # Provides methods to create and destroy objects. Basically controls their "existence".
3
+ # Provides methods to create and destroy objects. Basically controls their
4
+ # "existence".
4
5
  module Existence
5
6
  class SessionInvalidError < ::StandardError # :nodoc:
6
7
  def initialize(session)
7
- super("Your session is invalid and has the following errors: #{session.errors.full_messages.to_sentence}")
8
+ message = I18n.t(
9
+ 'error_messages.session_invalid',
10
+ default: "Your session is invalid and has the following errors:"
11
+ )
12
+ message += " #{session.errors.full_messages.to_sentence}"
13
+ super message
8
14
  end
9
15
  end
10
16
 
@@ -40,8 +46,9 @@ module Authlogic
40
46
  end
41
47
 
42
48
  module InstanceMethods
43
- # Clears all errors and the associated record, you should call this terminate a session, thus requiring
44
- # the user to authenticate again if it is needed.
49
+ # Clears all errors and the associated record, you should call this
50
+ # terminate a session, thus requiring the user to authenticate again if
51
+ # it is needed.
45
52
  def destroy
46
53
  before_destroy
47
54
  save_record
@@ -51,16 +58,18 @@ module Authlogic
51
58
  true
52
59
  end
53
60
 
54
- # Returns true if the session is new, meaning no action has been taken on it and a successful save
55
- # has not taken place.
61
+ # Returns true if the session is new, meaning no action has been taken
62
+ # on it and a successful save has not taken place.
56
63
  def new_session?
57
64
  new_session != false
58
65
  end
59
66
 
60
- # After you have specified all of the details for your session you can try to save it. This will
61
- # run validation checks and find the associated record, if all validation passes. If validation
62
- # does not pass, the save will fail and the errors will be stored in the errors object.
63
- def save(&block)
67
+ # After you have specified all of the details for your session you can
68
+ # try to save it. This will run validation checks and find the
69
+ # associated record, if all validation passes. If validation does not
70
+ # pass, the save will fail and the errors will be stored in the errors
71
+ # object.
72
+ def save
64
73
  result = nil
65
74
  if valid?
66
75
  self.record = attempted_record
@@ -81,7 +90,8 @@ module Authlogic
81
90
  result
82
91
  end
83
92
 
84
- # Same as save but raises an exception of validation errors when validation fails
93
+ # Same as save but raises an exception of validation errors when
94
+ # validation fails
85
95
  def save!
86
96
  result = save
87
97
  raise SessionInvalidError.new(self) unless result
@@ -12,6 +12,37 @@ module Authlogic
12
12
  end
13
13
 
14
14
  module InstanceMethods
15
+ E_AC_PARAMETERS = <<-EOS.strip_heredoc.freeze
16
+ Passing an ActionController::Parameters to Authlogic is not allowed.
17
+
18
+ In Authlogic 3, especially during the transition of rails to Strong
19
+ Parameters, it was common for Authlogic users to forget to `permit`
20
+ their params. They would pass their params into Authlogic, we'd call
21
+ `to_h`, and they'd be surprised when authentication failed.
22
+
23
+ In 2018, people are still making this mistake. We'd like to help them
24
+ and make authlogic a little simpler at the same time, so in Authlogic
25
+ 3.7.0, we deprecated the use of ActionController::Parameters. Instead,
26
+ pass a plain Hash. Please replace:
27
+
28
+ UserSession.new(user_session_params)
29
+ UserSession.create(user_session_params)
30
+
31
+ with
32
+
33
+ UserSession.new(user_session_params.to_h)
34
+ UserSession.create(user_session_params.to_h)
35
+
36
+ And don't forget to `permit`!
37
+
38
+ We discussed this issue thoroughly between late 2016 and early
39
+ 2018. Notable discussions include:
40
+
41
+ - https://github.com/binarylogic/authlogic/issues/512
42
+ - https://github.com/binarylogic/authlogic/pull/558
43
+ - https://github.com/binarylogic/authlogic/pull/577
44
+ EOS
45
+
15
46
  def initialize(*args)
16
47
  self.credentials = args
17
48
  end
@@ -22,22 +53,37 @@ module Authlogic
22
53
  []
23
54
  end
24
55
 
25
- # Set your credentials before you save your session. You can pass a hash of
26
- # credentials:
56
+ # Set your credentials before you save your session. There are many
57
+ # method signatures.
27
58
  #
28
- # session.credentials = {:login => "my login", :password => "my password", :remember_me => true}
59
+ # ```
60
+ # # A hash of credentials is most common
61
+ # session.credentials = { login: "foo", password: "bar", remember_me: true }
29
62
  #
30
- # or you can pass an array of objects:
63
+ # # You must pass an actual Hash, `ActionController::Parameters` is
64
+ # # specifically not allowed.
31
65
  #
32
- # session.credentials = [my_user_object, true]
66
+ # # You can pass an array of objects:
67
+ # session.credentials = [my_user_object, true]
33
68
  #
34
- # and if you need to set an id, just pass it last. This value need be the last
35
- # item in the array you pass, since the id is something that you control yourself,
36
- # it should never be set from a hash or a form. Examples:
69
+ # # If you need to set an id (see `Authlogic::Session::Id`) pass it
70
+ # # last. It needs be the last item in the array you pass, since the id
71
+ # # is something that you control yourself, it should never be set from
72
+ # # a hash or a form. Examples:
73
+ # session.credentials = [
74
+ # {:login => "foo", :password => "bar", :remember_me => true},
75
+ # :my_id
76
+ # ]
77
+ # session.credentials = [my_user_object, true, :my_id]
37
78
  #
38
- # session.credentials = [{:login => "my login", :password => "my password", :remember_me => true}, :my_id]
39
- # session.credentials = [my_user_object, true, :my_id]
79
+ # # Finally, there's priority_record
80
+ # [{ priority_record: my_object }, :my_id]
81
+ # ```
40
82
  def credentials=(values)
83
+ normalized = Array.wrap(values)
84
+ if normalized.first.class.name == "ActionController::Parameters"
85
+ raise TypeError.new(E_AC_PARAMETERS)
86
+ end
41
87
  end
42
88
 
43
89
  def inspect