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,7 +1,10 @@
1
1
  module Authlogic
2
2
  module Session
3
- # Handles all authentication that deals with cookies, such as persisting, saving, and destroying.
3
+ # Handles all authentication that deals with cookies, such as persisting,
4
+ # saving, and destroying.
4
5
  module Cookies
6
+ VALID_SAME_SITE_VALUES = [nil, "Lax", "Strict"].freeze
7
+
5
8
  def self.included(klass)
6
9
  klass.class_eval do
7
10
  extend Config
@@ -14,8 +17,10 @@ module Authlogic
14
17
 
15
18
  # Configuration for the cookie feature set.
16
19
  module Config
17
- # The name of the cookie or the key in the cookies hash. Be sure and use a unique name. If you have multiple sessions and they use the same cookie it will cause problems.
18
- # Also, if a id is set it will be inserted into the beginning of the string. Exmaple:
20
+ # The name of the cookie or the key in the cookies hash. Be sure and use
21
+ # a unique name. If you have multiple sessions and they use the same
22
+ # cookie it will cause problems. Also, if a id is set it will be
23
+ # inserted into the beginning of the string. Example:
19
24
  #
20
25
  # session = UserSession.new
21
26
  # session.cookie_key => "user_credentials"
@@ -48,25 +53,42 @@ module Authlogic
48
53
  end
49
54
  alias_method :remember_me_for=, :remember_me_for
50
55
 
51
- # Should the cookie be set as secure? If true, the cookie will only be sent over SSL connections
56
+ # Should the cookie be set as secure? If true, the cookie will only be sent over
57
+ # SSL connections
52
58
  #
53
- # * <tt>Default:</tt> false
59
+ # * <tt>Default:</tt> true
54
60
  # * <tt>Accepts:</tt> Boolean
55
61
  def secure(value = nil)
56
- rw_config(:secure, value, false)
62
+ rw_config(:secure, value, true)
57
63
  end
58
64
  alias_method :secure=, :secure
59
65
 
60
- # Should the cookie be set as httponly? If true, the cookie will not be accessable from javascript
66
+ # Should the cookie be set as httponly? If true, the cookie will not be
67
+ # accessible from javascript
61
68
  #
62
- # * <tt>Default:</tt> false
69
+ # * <tt>Default:</tt> true
63
70
  # * <tt>Accepts:</tt> Boolean
64
71
  def httponly(value = nil)
65
- rw_config(:httponly, value, false)
72
+ rw_config(:httponly, value, true)
66
73
  end
67
74
  alias_method :httponly=, :httponly
68
75
 
69
- # Should the cookie be signed? If the controller adapter supports it, this is a measure against cookie tampering.
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
+
90
+ # Should the cookie be signed? If the controller adapter supports it, this is a
91
+ # measure against cookie tampering.
70
92
  def sign_cookie(value = nil)
71
93
  if value && !controller.cookies.respond_to?(:signed)
72
94
  raise "Signed cookies not supported with #{controller.class}!"
@@ -76,7 +98,8 @@ module Authlogic
76
98
  alias_method :sign_cookie=, :sign_cookie
77
99
  end
78
100
 
79
- # The methods available for an Authlogic::Session::Base object that make up the cookie feature set.
101
+ # The methods available for an Authlogic::Session::Base object that make up the
102
+ # cookie feature set.
80
103
  module InstanceMethods
81
104
  # Allows you to set the remember_me option when passing credentials.
82
105
  def credentials=(value)
@@ -84,10 +107,12 @@ module Authlogic
84
107
  values = value.is_a?(Array) ? value : [value]
85
108
  case values.first
86
109
  when Hash
87
- self.remember_me = values.first.with_indifferent_access[:remember_me] if values.first.with_indifferent_access.key?(:remember_me)
110
+ if values.first.with_indifferent_access.key?(:remember_me)
111
+ self.remember_me = values.first.with_indifferent_access[:remember_me]
112
+ end
88
113
  else
89
- r = values.find { |value| value.is_a?(TrueClass) || value.is_a?(FalseClass) }
90
- 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?
91
116
  end
92
117
  end
93
118
 
@@ -97,7 +122,9 @@ module Authlogic
97
122
  @remember_me = self.class.remember_me
98
123
  end
99
124
 
100
- # Accepts a boolean as a flag to remember the session or not. Basically to expire the cookie at the end of the session or keep it for "remember_me_until".
125
+ # Accepts a boolean as a flag to remember the session or not. Basically
126
+ # to expire the cookie at the end of the session or keep it for
127
+ # "remember_me_until".
101
128
  def remember_me=(value)
102
129
  @remember_me = value
103
130
  end
@@ -107,13 +134,15 @@ module Authlogic
107
134
  remember_me == true || remember_me == "true" || remember_me == "1"
108
135
  end
109
136
 
110
- # How long to remember the user if remember_me is true. This is based on the class level configuration: remember_me_for
137
+ # How long to remember the user if remember_me is true. This is based on the class
138
+ # level configuration: remember_me_for
111
139
  def remember_me_for
112
140
  return unless remember_me?
113
141
  self.class.remember_me_for
114
142
  end
115
143
 
116
- # When to expire the cookie. See remember_me_for configuration option to change this.
144
+ # When to expire the cookie. See remember_me_for configuration option to change
145
+ # this.
117
146
  def remember_me_until
118
147
  return unless remember_me?
119
148
  remember_me_for.from_now
@@ -131,7 +160,8 @@ module Authlogic
131
160
  @secure = self.class.secure
132
161
  end
133
162
 
134
- # Accepts a boolean as to whether the cookie should be marked as secure. If true the cookie will only ever be sent over an SSL connection.
163
+ # Accepts a boolean as to whether the cookie should be marked as secure. If true
164
+ # the cookie will only ever be sent over an SSL connection.
135
165
  def secure=(value)
136
166
  @secure = value
137
167
  end
@@ -141,13 +171,14 @@ module Authlogic
141
171
  secure == true || secure == "true" || secure == "1"
142
172
  end
143
173
 
144
- # If the cookie should be marked as httponly (not accessable via javascript)
174
+ # If the cookie should be marked as httponly (not accessible via javascript)
145
175
  def httponly
146
176
  return @httponly if defined?(@httponly)
147
177
  @httponly = self.class.httponly
148
178
  end
149
179
 
150
- # Accepts a boolean as to whether the cookie should be marked as httponly. If true, the cookie will not be accessable from javascript
180
+ # Accepts a boolean as to whether the cookie should be marked as
181
+ # httponly. If true, the cookie will not be accessible from javascript
151
182
  def httponly=(value)
152
183
  @httponly = value
153
184
  end
@@ -157,13 +188,29 @@ module Authlogic
157
188
  httponly == true || httponly == "true" || httponly == "1"
158
189
  end
159
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
+
160
206
  # If the cookie should be signed
161
207
  def sign_cookie
162
208
  return @sign_cookie if defined?(@sign_cookie)
163
209
  @sign_cookie = self.class.sign_cookie
164
210
  end
165
211
 
166
- # Accepts a boolean as to whether the cookie should be signed. If true the cookie will be saved and verified using a signature.
212
+ # Accepts a boolean as to whether the cookie should be signed. If true
213
+ # the cookie will be saved and verified using a signature.
167
214
  def sign_cookie=(value)
168
215
  @sign_cookie = value
169
216
  end
@@ -174,53 +221,75 @@ module Authlogic
174
221
  end
175
222
 
176
223
  private
177
- def cookie_key
178
- build_key(self.class.cookie_key)
179
- end
180
224
 
181
- def cookie_credentials
182
- if self.class.sign_cookie
183
- cookie = controller.cookies.signed[cookie_key]
184
- else
185
- cookie = controller.cookies[cookie_key]
186
- end
187
- cookie && cookie.split("::")
188
- end
225
+ def cookie_key
226
+ build_key(self.class.cookie_key)
227
+ end
189
228
 
190
- # Tries to validate the session from information in the cookie
191
- def persist_by_cookie
192
- persistence_token, record_id = cookie_credentials
193
- if persistence_token.present?
194
- record = search_for_record("find_by_#{klass.primary_key}", record_id)
195
- self.unauthorized_record = record if record && record.persistence_token == persistence_token
196
- valid?
197
- else
198
- false
199
- end
229
+ # Returns an array of cookie elements. See cookie format in
230
+ # `generate_cookie_for_saving`. If no cookie is found, returns nil.
231
+ def cookie_credentials
232
+ cookie = cookie_jar[cookie_key]
233
+ cookie && cookie.split("::")
234
+ end
235
+
236
+ # The third element of the cookie indicates whether the user wanted
237
+ # to be remembered (Actually, it's a timestamp, `remember_me_until`)
238
+ # See cookie format in `generate_cookie_for_saving`.
239
+ def cookie_credentials_remember_me?
240
+ !cookie_credentials.nil? && !cookie_credentials[2].nil?
241
+ end
242
+
243
+ def cookie_jar
244
+ if self.class.sign_cookie
245
+ controller.cookies.signed
246
+ else
247
+ controller.cookies
200
248
  end
249
+ end
201
250
 
202
- def save_cookie
203
- if sign_cookie?
204
- controller.cookies.signed[cookie_key] = generate_cookie_for_saving
205
- else
206
- controller.cookies[cookie_key] = generate_cookie_for_saving
251
+ # Tries to validate the session from information in the cookie
252
+ def persist_by_cookie
253
+ persistence_token, record_id = cookie_credentials
254
+ if persistence_token.present?
255
+ record = search_for_record("find_by_#{klass.primary_key}", record_id)
256
+ if record && record.persistence_token == persistence_token
257
+ self.unauthorized_record = record
207
258
  end
259
+ valid?
260
+ else
261
+ false
208
262
  end
263
+ end
209
264
 
210
- def generate_cookie_for_saving
211
- remember_me_until_value = "::#{remember_me_until.iso8601}" if remember_me?
212
- {
213
- :value => "#{record.persistence_token}::#{record.send(record.class.primary_key)}#{remember_me_until_value}",
214
- :expires => remember_me_until,
215
- :secure => secure,
216
- :httponly => httponly,
217
- :domain => controller.cookie_domain
218
- }
265
+ def save_cookie
266
+ if sign_cookie?
267
+ controller.cookies.signed[cookie_key] = generate_cookie_for_saving
268
+ else
269
+ controller.cookies[cookie_key] = generate_cookie_for_saving
219
270
  end
271
+ end
220
272
 
221
- def destroy_cookie
222
- controller.cookies.delete cookie_key, :domain => controller.cookie_domain
223
- end
273
+ def generate_cookie_for_saving
274
+ value = format(
275
+ "%s::%s%s",
276
+ record.persistence_token,
277
+ record.send(record.class.primary_key),
278
+ remember_me? ? "::#{remember_me_until.iso8601}" : ""
279
+ )
280
+ {
281
+ value: value,
282
+ expires: remember_me_until,
283
+ secure: secure,
284
+ httponly: httponly,
285
+ same_site: same_site,
286
+ domain: controller.cookie_domain
287
+ }
288
+ end
289
+
290
+ def destroy_cookie
291
+ controller.cookies.delete cookie_key, domain: controller.cookie_domain
292
+ end
224
293
  end
225
294
  end
226
295
  end
@@ -1,13 +1,19 @@
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
+
11
17
  def self.included(klass)
12
18
  klass.class_eval do
13
19
  extend ClassMethods
@@ -15,9 +21,9 @@ module Authlogic
15
21
  attr_accessor :new_session, :record
16
22
  end
17
23
  end
18
-
24
+
19
25
  module ClassMethods
20
- # A convenince method. The same as:
26
+ # A convenience method. The same as:
21
27
  #
22
28
  # session = UserSession.new(*args)
23
29
  # session.save
@@ -30,7 +36,7 @@ module Authlogic
30
36
  session.save(&block)
31
37
  session
32
38
  end
33
-
39
+
34
40
  # Same as create but calls create!, which raises an exception when validation fails.
35
41
  def create!(*args)
36
42
  session = new(*args)
@@ -38,10 +44,11 @@ module Authlogic
38
44
  session
39
45
  end
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 requring
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
@@ -50,17 +57,19 @@ module Authlogic
50
57
  after_destroy
51
58
  true
52
59
  end
53
-
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.
60
+
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
-
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 erorrs will be stored in the errors object.
63
- def save(&block)
66
+
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
@@ -90,4 +100,4 @@ module Authlogic
90
100
  end
91
101
  end
92
102
  end
93
- end
103
+ end
@@ -1,8 +1,8 @@
1
1
  module Authlogic
2
2
  module Session
3
- # Sort of like an interface, it sets the foundation for the class, such as the required methods. This also allows
4
- # other modules to overwrite methods and call super on them. It's also a place to put "utility" methods used
5
- # throughout Authlogic.
3
+ # Sort of like an interface, it sets the foundation for the class, such as the
4
+ # required methods. This also allows other modules to overwrite methods and call super
5
+ # on them. It's also a place to put "utility" methods used throughout Authlogic.
6
6
  module Foundation
7
7
  def self.included(klass)
8
8
  klass.class_eval do
@@ -12,39 +12,93 @@ 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
18
49
 
19
- # The credentials you passed to create your session. See credentials= for more info.
50
+ # The credentials you passed to create your session. See credentials= for more
51
+ # info.
20
52
  def credentials
21
53
  []
22
54
  end
23
55
 
24
- # Set your credentials before you save your session. You can pass a hash of credentials:
56
+ # Set your credentials before you save your session. There are many
57
+ # method signatures.
25
58
  #
26
- # 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 }
27
62
  #
28
- # or you can pass an array of objects:
63
+ # # You must pass an actual Hash, `ActionController::Parameters` is
64
+ # # specifically not allowed.
29
65
  #
30
- # session.credentials = [my_user_object, true]
66
+ # # You can pass an array of objects:
67
+ # session.credentials = [my_user_object, true]
31
68
  #
32
- # and if you need to set an id, just pass it last. This value need be the last item in the array you pass, since the id is something that
33
- # you control yourself, 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]
34
78
  #
35
- # session.credentials = [{:login => "my login", :password => "my password", :remember_me => true}, :my_id]
36
- # session.credentials = [my_user_object, true, :my_id]
79
+ # # Finally, there's priority_record
80
+ # [{ priority_record: my_object }, :my_id]
81
+ # ```
37
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
38
87
  end
39
88
 
40
89
  def inspect
41
- "#<#{self.class.name}: #{credentials.blank? ? "no credentials provided" : credentials.inspect}>"
90
+ format(
91
+ "#<%s: %s>",
92
+ self.class.name,
93
+ credentials.blank? ? "no credentials provided" : credentials.inspect
94
+ )
42
95
  end
43
96
 
44
97
  private
45
- def build_key(last_part)
46
- last_part
47
- end
98
+
99
+ def build_key(last_part)
100
+ last_part
101
+ end
48
102
  end
49
103
  end
50
104
  end