authlogic 3.8.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +28 -0
- data/.github/ISSUE_TEMPLATE/feature_proposal.md +32 -0
- data/.github/triage.md +86 -0
- data/.gitignore +4 -3
- data/.rubocop.yml +109 -9
- data/.rubocop_todo.yml +38 -355
- data/.travis.yml +11 -35
- data/CHANGELOG.md +345 -2
- data/CONTRIBUTING.md +45 -14
- data/Gemfile +3 -2
- data/README.md +244 -90
- data/Rakefile +10 -10
- data/UPGRADING.md +22 -0
- data/authlogic.gemspec +34 -21
- data/doc/use_normal_rails_validation.md +82 -0
- data/gemfiles/Gemfile.rails-4.2.x +6 -0
- data/{test/gemfiles → gemfiles}/Gemfile.rails-5.1.x +2 -2
- data/{test/gemfiles → gemfiles}/Gemfile.rails-5.2.x +2 -2
- data/lib/authlogic/acts_as_authentic/base.rb +36 -24
- data/lib/authlogic/acts_as_authentic/email.rb +65 -31
- data/lib/authlogic/acts_as_authentic/logged_in_status.rb +14 -9
- data/lib/authlogic/acts_as_authentic/login.rb +61 -45
- data/lib/authlogic/acts_as_authentic/magic_columns.rb +6 -6
- data/lib/authlogic/acts_as_authentic/password.rb +267 -146
- data/lib/authlogic/acts_as_authentic/perishable_token.rb +24 -19
- data/lib/authlogic/acts_as_authentic/persistence_token.rb +10 -15
- data/lib/authlogic/acts_as_authentic/queries/find_with_case.rb +67 -0
- data/lib/authlogic/acts_as_authentic/restful_authentication.rb +50 -14
- data/lib/authlogic/acts_as_authentic/session_maintenance.rb +88 -60
- data/lib/authlogic/acts_as_authentic/single_access_token.rb +23 -11
- data/lib/authlogic/acts_as_authentic/validations_scope.rb +9 -6
- data/lib/authlogic/authenticates_many/association.rb +7 -7
- data/lib/authlogic/authenticates_many/base.rb +37 -21
- data/lib/authlogic/config.rb +21 -10
- data/lib/authlogic/controller_adapters/abstract_adapter.rb +38 -11
- data/lib/authlogic/controller_adapters/rack_adapter.rb +9 -5
- data/lib/authlogic/controller_adapters/rails_adapter.rb +12 -7
- data/lib/authlogic/controller_adapters/sinatra_adapter.rb +2 -2
- data/lib/authlogic/crypto_providers/aes256.rb +37 -32
- data/lib/authlogic/crypto_providers/bcrypt.rb +21 -15
- data/lib/authlogic/crypto_providers/md5.rb +4 -2
- data/lib/authlogic/crypto_providers/scrypt.rb +22 -17
- data/lib/authlogic/crypto_providers/sha1.rb +11 -5
- data/lib/authlogic/crypto_providers/sha256.rb +13 -9
- data/lib/authlogic/crypto_providers/sha512.rb +0 -21
- data/lib/authlogic/crypto_providers/wordpress.rb +32 -3
- data/lib/authlogic/crypto_providers.rb +91 -0
- data/lib/authlogic/i18n.rb +26 -19
- data/lib/authlogic/random.rb +10 -28
- data/lib/authlogic/regex.rb +59 -28
- data/lib/authlogic/session/activation.rb +10 -7
- data/lib/authlogic/session/active_record_trickery.rb +13 -9
- data/lib/authlogic/session/base.rb +15 -4
- data/lib/authlogic/session/brute_force_protection.rb +40 -33
- data/lib/authlogic/session/callbacks.rb +94 -46
- data/lib/authlogic/session/cookies.rb +130 -45
- data/lib/authlogic/session/existence.rb +21 -11
- data/lib/authlogic/session/foundation.rb +64 -14
- data/lib/authlogic/session/http_auth.rb +35 -28
- data/lib/authlogic/session/id.rb +9 -4
- data/lib/authlogic/session/klass.rb +15 -12
- data/lib/authlogic/session/magic_columns.rb +58 -55
- data/lib/authlogic/session/magic_states.rb +25 -19
- data/lib/authlogic/session/params.rb +42 -28
- data/lib/authlogic/session/password.rb +130 -120
- data/lib/authlogic/session/perishable_token.rb +5 -4
- data/lib/authlogic/session/persistence.rb +18 -12
- data/lib/authlogic/session/priority_record.rb +15 -12
- data/lib/authlogic/session/scopes.rb +51 -32
- data/lib/authlogic/session/session.rb +38 -28
- data/lib/authlogic/session/timeout.rb +13 -13
- data/lib/authlogic/session/unauthorized_record.rb +18 -13
- data/lib/authlogic/session/validation.rb +9 -9
- data/lib/authlogic/test_case/mock_controller.rb +5 -4
- data/lib/authlogic/test_case/mock_cookie_jar.rb +47 -3
- data/lib/authlogic/test_case/mock_request.rb +6 -3
- data/lib/authlogic/test_case/rails_request_adapter.rb +3 -2
- data/lib/authlogic/test_case.rb +70 -2
- data/lib/authlogic/version.rb +21 -0
- data/lib/authlogic.rb +51 -49
- data/test/acts_as_authentic_test/base_test.rb +3 -1
- data/test/acts_as_authentic_test/email_test.rb +43 -42
- data/test/acts_as_authentic_test/logged_in_status_test.rb +6 -4
- data/test/acts_as_authentic_test/login_test.rb +77 -80
- data/test/acts_as_authentic_test/magic_columns_test.rb +3 -1
- data/test/acts_as_authentic_test/password_test.rb +51 -37
- data/test/acts_as_authentic_test/perishable_token_test.rb +13 -5
- data/test/acts_as_authentic_test/persistence_token_test.rb +7 -1
- data/test/acts_as_authentic_test/restful_authentication_test.rb +14 -3
- data/test/acts_as_authentic_test/session_maintenance_test.rb +69 -15
- data/test/acts_as_authentic_test/single_access_test.rb +3 -1
- data/test/adapter_test.rb +23 -0
- data/test/authenticates_many_test.rb +3 -1
- data/test/config_test.rb +11 -9
- data/test/crypto_provider_test/aes256_test.rb +3 -1
- data/test/crypto_provider_test/bcrypt_test.rb +3 -1
- data/test/crypto_provider_test/scrypt_test.rb +3 -1
- data/test/crypto_provider_test/sha1_test.rb +3 -1
- data/test/crypto_provider_test/sha256_test.rb +3 -1
- data/test/crypto_provider_test/sha512_test.rb +3 -1
- data/test/crypto_provider_test/wordpress_test.rb +26 -0
- data/test/fixtures/companies.yml +2 -2
- data/test/fixtures/employees.yml +1 -1
- data/test/i18n_test.rb +6 -4
- data/test/libs/affiliate.rb +2 -0
- data/test/libs/company.rb +4 -2
- data/test/libs/employee.rb +2 -0
- data/test/libs/employee_session.rb +2 -0
- data/test/libs/ldaper.rb +2 -0
- data/test/libs/project.rb +2 -0
- data/test/libs/user.rb +2 -0
- data/test/libs/user_session.rb +4 -2
- data/test/random_test.rb +10 -38
- data/test/session_test/activation_test.rb +3 -1
- data/test/session_test/active_record_trickery_test.rb +7 -4
- data/test/session_test/brute_force_protection_test.rb +11 -9
- data/test/session_test/callbacks_test.rb +12 -4
- data/test/session_test/cookies_test.rb +48 -5
- data/test/session_test/existence_test.rb +18 -5
- data/test/session_test/foundation_test.rb +19 -1
- data/test/session_test/http_auth_test.rb +11 -7
- data/test/session_test/id_test.rb +3 -1
- data/test/session_test/klass_test.rb +3 -1
- data/test/session_test/magic_columns_test.rb +13 -13
- data/test/session_test/magic_states_test.rb +3 -1
- data/test/session_test/params_test.rb +13 -5
- data/test/session_test/password_test.rb +10 -8
- data/test/session_test/perishability_test.rb +3 -1
- data/test/session_test/persistence_test.rb +4 -1
- data/test/session_test/scopes_test.rb +16 -8
- data/test/session_test/session_test.rb +6 -4
- data/test/session_test/timeout_test.rb +4 -2
- data/test/session_test/unauthorized_record_test.rb +4 -2
- data/test/session_test/validation_test.rb +3 -1
- data/test/test_helper.rb +84 -45
- metadata +87 -73
- data/.github/ISSUE_TEMPLATE.md +0 -13
- data/test/gemfiles/Gemfile.rails-3.2.x +0 -7
- data/test/gemfiles/Gemfile.rails-4.0.x +0 -7
- data/test/gemfiles/Gemfile.rails-4.1.x +0 -7
- data/test/gemfiles/Gemfile.rails-4.2.x +0 -7
- data/test/gemfiles/Gemfile.rails-5.0.x +0 -6
@@ -6,7 +6,7 @@ module Authlogic
|
|
6
6
|
klass.class_eval do
|
7
7
|
extend Config
|
8
8
|
include InstanceMethods
|
9
|
-
validate :validate_by_password, :
|
9
|
+
validate :validate_by_password, if: :authenticating_with_password?
|
10
10
|
|
11
11
|
class << self
|
12
12
|
attr_accessor :configured_password_methods
|
@@ -119,7 +119,7 @@ module Authlogic
|
|
119
119
|
# should be an instance method. It should also be prepared to accept a
|
120
120
|
# raw password and a crytped password.
|
121
121
|
#
|
122
|
-
# * <tt>Default:</tt> "valid_password?"
|
122
|
+
# * <tt>Default:</tt> "valid_password?" defined in acts_as_authentic/password.rb
|
123
123
|
# * <tt>Accepts:</tt> Symbol or String
|
124
124
|
def verify_password_method(value = nil)
|
125
125
|
rw_config(:verify_password_method, value, "valid_password?")
|
@@ -127,45 +127,14 @@ module Authlogic
|
|
127
127
|
alias_method :verify_password_method=, :verify_password_method
|
128
128
|
end
|
129
129
|
|
130
|
-
# Password
|
130
|
+
# Password related instance methods
|
131
131
|
module InstanceMethods
|
132
|
-
E_AC_PARAMETERS = <<-STR.strip_heredoc.freeze
|
133
|
-
You have passed an ActionController::Parameters to Authlogic 3. That's
|
134
|
-
OK for now, but in Authlogic 4, it will raise an error. Please
|
135
|
-
replace:
|
136
|
-
|
137
|
-
UserSession.new(user_session_params)
|
138
|
-
UserSession.create(user_session_params)
|
139
|
-
|
140
|
-
with
|
141
|
-
|
142
|
-
UserSession.new(user_session_params.to_h)
|
143
|
-
UserSession.create(user_session_params.to_h)
|
144
|
-
|
145
|
-
And don't forget to `permit`!
|
146
|
-
|
147
|
-
During the transition of rails to Strong Parameters, it has been
|
148
|
-
common for Authlogic users to forget to `permit` their params. They
|
149
|
-
would pass their params into Authlogic, we'd call `to_h`, and they'd
|
150
|
-
be surprised when authentication failed.
|
151
|
-
|
152
|
-
In 2018, people are still making this mistake. We'd like to help them
|
153
|
-
and make authlogic a little simpler at the same time, so in Authlogic
|
154
|
-
3.7.0, we deprecated the use of ActionController::Parameters.
|
155
|
-
|
156
|
-
We discussed this issue thoroughly between late 2016 and early
|
157
|
-
2018. Notable discussions include:
|
158
|
-
|
159
|
-
- https://github.com/binarylogic/authlogic/issues/512
|
160
|
-
- https://github.com/binarylogic/authlogic/pull/558
|
161
|
-
- https://github.com/binarylogic/authlogic/pull/577
|
162
|
-
STR
|
163
|
-
|
164
132
|
def initialize(*args)
|
165
|
-
|
133
|
+
unless self.class.configured_password_methods
|
166
134
|
configure_password_methods
|
167
135
|
self.class.configured_password_methods = true
|
168
136
|
end
|
137
|
+
instance_variable_set("@#{password_field}", nil)
|
169
138
|
super
|
170
139
|
end
|
171
140
|
|
@@ -184,14 +153,23 @@ module Authlogic
|
|
184
153
|
|
185
154
|
# Accepts the login_field / password_field credentials combination in
|
186
155
|
# hash form.
|
156
|
+
#
|
157
|
+
# You must pass an actual Hash, `ActionController::Parameters` is
|
158
|
+
# specifically not allowed.
|
159
|
+
#
|
160
|
+
# See `Authlogic::Session::Foundation#credentials=` for an overview of
|
161
|
+
# all method signatures.
|
187
162
|
def credentials=(value)
|
188
163
|
super
|
189
|
-
values =
|
190
|
-
|
164
|
+
values = Array.wrap(value)
|
191
165
|
if values.first.is_a?(Hash)
|
192
|
-
|
193
|
-
|
194
|
-
|
166
|
+
sliced = values
|
167
|
+
.first
|
168
|
+
.with_indifferent_access
|
169
|
+
.slice(login_field, password_field)
|
170
|
+
sliced.each do |field, val|
|
171
|
+
next if val.blank?
|
172
|
+
send("#{field}=", val)
|
195
173
|
end
|
196
174
|
end
|
197
175
|
end
|
@@ -202,106 +180,138 @@ module Authlogic
|
|
202
180
|
|
203
181
|
private
|
204
182
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
self.class.send(:define_method, password_field) {} if !respond_to?(password_field)
|
214
|
-
|
215
|
-
# The password should not be accessible publicly. This way forms
|
216
|
-
# using form_for don't fill the password with the attempted
|
217
|
-
# password. To prevent this we just create this method that is
|
218
|
-
# private.
|
219
|
-
self.class.class_eval <<-"end_eval", __FILE__, __LINE__
|
220
|
-
private
|
221
|
-
def protected_#{password_field}
|
222
|
-
@#{password_field}
|
223
|
-
end
|
224
|
-
end_eval
|
225
|
-
end
|
183
|
+
def add_invalid_password_error
|
184
|
+
if generalize_credentials_error_messages?
|
185
|
+
add_general_credentials_error
|
186
|
+
else
|
187
|
+
errors.add(
|
188
|
+
password_field,
|
189
|
+
I18n.t("error_messages.password_invalid", default: "is not valid")
|
190
|
+
)
|
226
191
|
end
|
192
|
+
end
|
227
193
|
|
228
|
-
|
229
|
-
|
194
|
+
def add_login_not_found_error
|
195
|
+
if generalize_credentials_error_messages?
|
196
|
+
add_general_credentials_error
|
197
|
+
else
|
198
|
+
errors.add(
|
199
|
+
login_field,
|
200
|
+
I18n.t("error_messages.login_not_found", default: "is not valid")
|
201
|
+
)
|
230
202
|
end
|
203
|
+
end
|
231
204
|
|
232
|
-
|
233
|
-
|
205
|
+
def authenticating_with_password?
|
206
|
+
login_field && (!send(login_field).nil? || !send("protected_#{password_field}").nil?)
|
207
|
+
end
|
234
208
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
if send("protected_#{password_field}").blank?
|
240
|
-
errors.add(password_field, I18n.t('error_messages.password_blank', :default => "cannot be blank"))
|
241
|
-
end
|
242
|
-
return if errors.count > 0
|
209
|
+
def configure_password_methods
|
210
|
+
define_login_field_methods
|
211
|
+
define_password_field_methods
|
212
|
+
end
|
243
213
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
return
|
250
|
-
end
|
214
|
+
def define_login_field_methods
|
215
|
+
return unless login_field
|
216
|
+
self.class.send(:attr_writer, login_field) unless respond_to?("#{login_field}=")
|
217
|
+
self.class.send(:attr_reader, login_field) unless respond_to?(login_field)
|
218
|
+
end
|
251
219
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
220
|
+
def define_password_field_methods
|
221
|
+
return unless password_field
|
222
|
+
self.class.send(:attr_writer, password_field) unless respond_to?("#{password_field}=")
|
223
|
+
self.class.send(:define_method, password_field) {} unless respond_to?(password_field)
|
224
|
+
|
225
|
+
# The password should not be accessible publicly. This way forms
|
226
|
+
# using form_for don't fill the password with the attempted
|
227
|
+
# password. To prevent this we just create this method that is
|
228
|
+
# private.
|
229
|
+
self.class.class_eval(
|
230
|
+
<<-EOS, __FILE__, __LINE__ + 1
|
231
|
+
private
|
232
|
+
def protected_#{password_field}
|
233
|
+
@#{password_field}
|
234
|
+
end
|
235
|
+
EOS
|
236
|
+
)
|
237
|
+
end
|
261
238
|
|
262
|
-
|
239
|
+
# In keeping with the metaphor of ActiveRecord, verification of the
|
240
|
+
# password is referred to as a "validation".
|
241
|
+
def validate_by_password
|
242
|
+
self.invalid_password = false
|
243
|
+
validate_by_password__blank_fields
|
244
|
+
return if errors.count > 0
|
245
|
+
self.attempted_record = search_for_record(find_by_login_method, send(login_field))
|
246
|
+
if attempted_record.blank?
|
247
|
+
add_login_not_found_error
|
248
|
+
return
|
249
|
+
end
|
250
|
+
validate_by_password__invalid_password
|
251
|
+
end
|
263
252
|
|
264
|
-
|
265
|
-
|
253
|
+
def validate_by_password__blank_fields
|
254
|
+
if send(login_field).blank?
|
255
|
+
errors.add(
|
256
|
+
login_field,
|
257
|
+
I18n.t("error_messages.login_blank", default: "cannot be blank")
|
258
|
+
)
|
259
|
+
end
|
260
|
+
if send("protected_#{password_field}").blank?
|
261
|
+
errors.add(
|
262
|
+
password_field,
|
263
|
+
I18n.t("error_messages.password_blank", default: "cannot be blank")
|
264
|
+
)
|
266
265
|
end
|
266
|
+
end
|
267
267
|
|
268
|
-
|
269
|
-
|
268
|
+
# Verify the password, usually using `valid_password?` in
|
269
|
+
# `acts_as_authentic/password.rb`. If it cannot be verified, we
|
270
|
+
# refer to it as "invalid".
|
271
|
+
def validate_by_password__invalid_password
|
272
|
+
unless attempted_record.send(
|
273
|
+
verify_password_method,
|
274
|
+
send("protected_#{password_field}")
|
275
|
+
)
|
276
|
+
self.invalid_password = true
|
277
|
+
add_invalid_password_error
|
270
278
|
end
|
279
|
+
end
|
280
|
+
|
281
|
+
attr_accessor :invalid_password
|
282
|
+
|
283
|
+
def find_by_login_method
|
284
|
+
self.class.find_by_login_method
|
285
|
+
end
|
286
|
+
|
287
|
+
def login_field
|
288
|
+
self.class.login_field
|
289
|
+
end
|
271
290
|
|
272
|
-
|
273
|
-
|
291
|
+
def add_general_credentials_error
|
292
|
+
error_message =
|
274
293
|
if self.class.generalize_credentials_error_messages.is_a? String
|
275
294
|
self.class.generalize_credentials_error_messages
|
276
295
|
else
|
277
296
|
"#{login_field.to_s.humanize}/Password combination is not valid"
|
278
297
|
end
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
end
|
298
|
+
errors.add(
|
299
|
+
:base,
|
300
|
+
I18n.t("error_messages.general_credentials_error", default: error_message)
|
301
|
+
)
|
302
|
+
end
|
285
303
|
|
286
|
-
|
287
|
-
|
288
|
-
|
304
|
+
def generalize_credentials_error_messages?
|
305
|
+
self.class.generalize_credentials_error_messages
|
306
|
+
end
|
289
307
|
|
290
|
-
|
291
|
-
|
292
|
-
|
308
|
+
def password_field
|
309
|
+
self.class.password_field
|
310
|
+
end
|
293
311
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
def parse_param_val(value)
|
298
|
-
if value.first.class.name == "ActionController::Parameters"
|
299
|
-
ActiveSupport::Deprecation.warn(E_AC_PARAMETERS)
|
300
|
-
[value.first.to_h]
|
301
|
-
else
|
302
|
-
value.is_a?(Array) ? value : [value]
|
303
|
-
end
|
304
|
-
end
|
312
|
+
def verify_password_method
|
313
|
+
self.class.verify_password_method
|
314
|
+
end
|
305
315
|
end
|
306
316
|
end
|
307
317
|
end
|
@@ -13,11 +13,12 @@ module Authlogic
|
|
13
13
|
|
14
14
|
private
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
record.
|
19
|
-
|
16
|
+
def reset_perishable_token!
|
17
|
+
if record.respond_to?(:reset_perishable_token) &&
|
18
|
+
!record.disable_perishable_token_maintenance?
|
19
|
+
record.reset_perishable_token
|
20
20
|
end
|
21
|
+
end
|
21
22
|
end
|
22
23
|
end
|
23
24
|
end
|
@@ -10,9 +10,10 @@ module Authlogic
|
|
10
10
|
end
|
11
11
|
|
12
12
|
module ClassMethods
|
13
|
-
# This is how you persist a session. This finds the record for the
|
14
|
-
# a variety of methods. It basically tries to "log
|
15
|
-
# to explicitly log in. Check out
|
13
|
+
# This is how you persist a session. This finds the record for the
|
14
|
+
# current session using a variety of methods. It basically tries to "log
|
15
|
+
# in" the user without the user having to explicitly log in. Check out
|
16
|
+
# the other Authlogic::Session modules for more information.
|
16
17
|
#
|
17
18
|
# The best way to use this method is something like:
|
18
19
|
#
|
@@ -28,30 +29,35 @@ module Authlogic
|
|
28
29
|
# @current_user = current_user_session && current_user_session.user
|
29
30
|
# end
|
30
31
|
#
|
31
|
-
# Also, this method accepts a single parameter as the id, to find
|
32
|
+
# Also, this method accepts a single parameter as the id, to find
|
33
|
+
# session that you marked with an id:
|
32
34
|
#
|
33
35
|
# UserSession.find(:secure)
|
34
36
|
#
|
35
37
|
# See the id method for more information on ids.
|
36
38
|
def find(id = nil, priority_record = nil)
|
37
|
-
session = new({ :
|
39
|
+
session = new({ priority_record: priority_record }, id)
|
38
40
|
session.priority_record = priority_record
|
39
41
|
if session.persisting?
|
40
42
|
session
|
41
|
-
else
|
42
|
-
nil
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
47
|
module InstanceMethods
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
48
|
+
# Returns boolean indicating if the session is being persisted or not,
|
49
|
+
# meaning the user does not have to explicitly log in in order to be
|
50
|
+
# logged in.
|
51
|
+
#
|
52
|
+
# If the session has no associated record, it will try to find a record
|
53
|
+
# and persist the session.
|
54
|
+
#
|
55
|
+
# This is the method that the class level method find uses to ultimately
|
56
|
+
# persist the session.
|
51
57
|
def persisting?
|
52
|
-
return true
|
58
|
+
return true unless record.nil?
|
53
59
|
self.attempted_record = nil
|
54
|
-
self.remember_me =
|
60
|
+
self.remember_me = cookie_credentials_remember_me?
|
55
61
|
before_persisting
|
56
62
|
persist
|
57
63
|
ensure_authentication_attempted
|
@@ -1,8 +1,10 @@
|
|
1
1
|
module Authlogic
|
2
2
|
module Session
|
3
|
-
# The point of this module is to avoid the StaleObjectError raised when
|
4
|
-
# We accomplish this by using a
|
5
|
-
#
|
3
|
+
# The point of this module is to avoid the StaleObjectError raised when
|
4
|
+
# lock_version is implemented in ActiveRecord. We accomplish this by using a
|
5
|
+
# "priority record". Meaning this record is used if possible, it gets
|
6
|
+
# priority. This way we don't save a record behind the scenes thus making an
|
7
|
+
# object being used stale.
|
6
8
|
module PriorityRecord
|
7
9
|
def self.included(klass)
|
8
10
|
klass.class_eval do
|
@@ -10,7 +12,8 @@ module Authlogic
|
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
13
|
-
# Setting priority record if it is passed. The only way it can be passed
|
15
|
+
# Setting priority record if it is passed. The only way it can be passed
|
16
|
+
# is through an array:
|
14
17
|
#
|
15
18
|
# session.credentials = [real_user_object, priority_user_object]
|
16
19
|
def credentials=(value)
|
@@ -21,15 +24,15 @@ module Authlogic
|
|
21
24
|
|
22
25
|
private
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
def attempted_record=(value)
|
28
|
+
value = priority_record if value == priority_record
|
29
|
+
super
|
30
|
+
end
|
28
31
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
32
|
+
def save_record(alternate_record = nil)
|
33
|
+
r = alternate_record || record
|
34
|
+
super if r != priority_record
|
35
|
+
end
|
33
36
|
end
|
34
37
|
end
|
35
38
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "request_store"
|
2
2
|
|
3
3
|
module Authlogic
|
4
4
|
module Session
|
@@ -25,10 +25,10 @@ module Authlogic
|
|
25
25
|
RequestStore.store[:authlogic_scope]
|
26
26
|
end
|
27
27
|
|
28
|
-
# What with_scopes focuses on is scoping the query when finding the
|
29
|
-
# name of the cookie / session. It works very similar to
|
30
|
-
# ActiveRecord::Base#with_scopes. It accepts a hash with any of the
|
31
|
-
# options:
|
28
|
+
# What with_scopes focuses on is scoping the query when finding the
|
29
|
+
# object and the name of the cookie / session. It works very similar to
|
30
|
+
# ActiveRecord::Base#with_scopes. It accepts a hash with any of the
|
31
|
+
# following options:
|
32
32
|
#
|
33
33
|
# * <tt>find_options:</tt> any options you can pass into ActiveRecord::Base.find.
|
34
34
|
# This is used when trying to find the record.
|
@@ -37,21 +37,27 @@ module Authlogic
|
|
37
37
|
#
|
38
38
|
# Here is how you use it:
|
39
39
|
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
40
|
+
# ```
|
41
|
+
# UserSession.with_scope(find_options: {conditions: "account_id = 2"}, id: "account_2") do
|
42
|
+
# UserSession.find
|
43
|
+
# end
|
44
|
+
# ```
|
43
45
|
#
|
44
|
-
# Essentially what the above does is scope the searching of the object
|
45
|
-
# sql you provided. So instead of:
|
46
|
+
# Essentially what the above does is scope the searching of the object
|
47
|
+
# with the sql you provided. So instead of:
|
46
48
|
#
|
47
|
-
#
|
49
|
+
# ```
|
50
|
+
# User.where("login = 'ben'").first
|
51
|
+
# ```
|
48
52
|
#
|
49
53
|
# it would be:
|
50
54
|
#
|
51
|
-
#
|
55
|
+
# ```
|
56
|
+
# User.where("login = 'ben' and account_id = 2").first
|
57
|
+
# ```
|
52
58
|
#
|
53
|
-
# You will also notice the :id option. This works just like the id
|
54
|
-
# scopes your cookies. So the name of your cookie will be:
|
59
|
+
# You will also notice the :id option. This works just like the id
|
60
|
+
# method. It scopes your cookies. So the name of your cookie will be:
|
55
61
|
#
|
56
62
|
# account_2_user_credentials
|
57
63
|
#
|
@@ -59,9 +65,13 @@ module Authlogic
|
|
59
65
|
#
|
60
66
|
# user_credentials
|
61
67
|
#
|
62
|
-
# What is also nifty about scoping with an :id is that it merges your
|
68
|
+
# What is also nifty about scoping with an :id is that it merges your
|
69
|
+
# id's. So if you do:
|
63
70
|
#
|
64
|
-
# UserSession.with_scope(
|
71
|
+
# UserSession.with_scope(
|
72
|
+
# find_options: { conditions: "account_id = 2"},
|
73
|
+
# id: "account_2"
|
74
|
+
# ) do
|
65
75
|
# session = UserSession.new
|
66
76
|
# session.id = :secure
|
67
77
|
# end
|
@@ -69,7 +79,7 @@ module Authlogic
|
|
69
79
|
# The name of your cookies will be:
|
70
80
|
#
|
71
81
|
# secure_account_2_user_credentials
|
72
|
-
def with_scope(options = {}
|
82
|
+
def with_scope(options = {})
|
73
83
|
raise ArgumentError.new("You must provide a block") unless block_given?
|
74
84
|
self.scope = options
|
75
85
|
result = yield
|
@@ -79,9 +89,9 @@ module Authlogic
|
|
79
89
|
|
80
90
|
private
|
81
91
|
|
82
|
-
|
83
|
-
|
84
|
-
|
92
|
+
def scope=(value)
|
93
|
+
RequestStore.store[:authlogic_scope] = value
|
94
|
+
end
|
85
95
|
end
|
86
96
|
|
87
97
|
module InstanceMethods
|
@@ -98,21 +108,30 @@ module Authlogic
|
|
98
108
|
|
99
109
|
private
|
100
110
|
|
101
|
-
|
102
|
-
|
103
|
-
|
111
|
+
# Used for things like cookie_key, session_key, etc.
|
112
|
+
def build_key(last_part)
|
113
|
+
[scope[:id], super].compact.join("_")
|
114
|
+
end
|
115
|
+
|
116
|
+
# `args[0]` is the name of an AR method, like
|
117
|
+
# `find_by_single_access_token`.
|
118
|
+
def search_for_record(*args)
|
119
|
+
search_scope.scoping do
|
120
|
+
klass.send(*args)
|
104
121
|
end
|
122
|
+
end
|
105
123
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
124
|
+
# Returns an AR relation representing the scope of the search. The
|
125
|
+
# relation is either provided directly by, or defined by
|
126
|
+
# `find_options`.
|
127
|
+
def search_scope
|
128
|
+
if scope[:find_options].is_a?(ActiveRecord::Relation)
|
129
|
+
scope[:find_options]
|
130
|
+
else
|
131
|
+
conditions = scope[:find_options] && scope[:find_options][:conditions] || {}
|
132
|
+
klass.send(:where, conditions)
|
115
133
|
end
|
134
|
+
end
|
116
135
|
end
|
117
136
|
end
|
118
137
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Authlogic
|
2
2
|
module Session
|
3
|
-
# Handles all parts of authentication that deal with sessions. Such as persisting a
|
3
|
+
# Handles all parts of authentication that deal with sessions. Such as persisting a
|
4
|
+
# session and saving / destroy a session.
|
4
5
|
module Session
|
5
6
|
def self.included(klass)
|
6
7
|
klass.class_eval do
|
@@ -9,7 +10,7 @@ module Authlogic
|
|
9
10
|
persist :persist_by_session
|
10
11
|
after_save :update_session
|
11
12
|
after_destroy :update_session
|
12
|
-
after_persisting :update_session, :
|
13
|
+
after_persisting :update_session, unless: :single_access?
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
@@ -29,38 +30,47 @@ module Authlogic
|
|
29
30
|
module InstanceMethods
|
30
31
|
private
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
record = record_id.nil? ?
|
40
|
-
search_for_record("find_by_persistence_token", persistence_token.to_s) :
|
41
|
-
search_for_record("find_by_#{klass.primary_key}", record_id.to_s)
|
42
|
-
self.unauthorized_record = record if record && record.persistence_token == persistence_token
|
43
|
-
valid?
|
44
|
-
else
|
45
|
-
false
|
33
|
+
# Tries to validate the session from information in the session
|
34
|
+
def persist_by_session
|
35
|
+
persistence_token, record_id = session_credentials
|
36
|
+
if !persistence_token.nil?
|
37
|
+
record = persist_by_session_search(persistence_token, record_id)
|
38
|
+
if record && record.persistence_token == persistence_token
|
39
|
+
self.unauthorized_record = record
|
46
40
|
end
|
41
|
+
valid?
|
42
|
+
else
|
43
|
+
false
|
47
44
|
end
|
45
|
+
end
|
48
46
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
47
|
+
# Allow finding by persistence token, because when records are created
|
48
|
+
# the session is maintained in a before_save, when there is no id.
|
49
|
+
# This is done for performance reasons and to save on queries.
|
50
|
+
def persist_by_session_search(persistence_token, record_id)
|
51
|
+
if record_id.nil?
|
52
|
+
search_for_record("find_by_persistence_token", persistence_token.to_s)
|
53
|
+
else
|
54
|
+
search_for_record("find_by_#{klass.primary_key}", record_id.to_s)
|
54
55
|
end
|
56
|
+
end
|
55
57
|
|
56
|
-
|
57
|
-
|
58
|
-
|
58
|
+
def session_credentials
|
59
|
+
[
|
60
|
+
controller.session[session_key],
|
61
|
+
controller.session["#{session_key}_#{klass.primary_key}"]
|
62
|
+
].collect { |i| i.nil? ? i : i.to_s }.compact
|
63
|
+
end
|
59
64
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
65
|
+
def session_key
|
66
|
+
build_key(self.class.session_key)
|
67
|
+
end
|
68
|
+
|
69
|
+
def update_session
|
70
|
+
controller.session[session_key] = record && record.persistence_token
|
71
|
+
compound_key = "#{session_key}_#{klass.primary_key}"
|
72
|
+
controller.session[compound_key] = record && record.send(record.class.primary_key)
|
73
|
+
end
|
64
74
|
end
|
65
75
|
end
|
66
76
|
end
|