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
@@ -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
|
4
|
-
# modify Authlogic's behavior. I will do everything I can to make sure
|
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
|
7
|
-
# importantly they use the same API that you would use to extend
|
8
|
-
#
|
9
|
-
#
|
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,
|
12
|
-
# You have similar callbacks with Authlogic, see the METHODS
|
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
|
42
|
-
# changes to the associated record, there is no need to save the
|
43
|
-
# This allows multiple modules to modify the
|
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,56 +54,100 @@ 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
|
54
|
-
# to extend properly with multiple extensions. Please ONLY use the
|
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
|
-
METHODS = [
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
61
|
+
METHODS = %w[
|
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
|
-
|
65
|
-
base
|
66
|
-
|
67
|
-
base
|
68
|
-
base
|
69
|
-
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 } }]
|
72
|
-
else
|
73
|
-
base.define_callbacks *METHODS + [{ :terminator => 'result == false' }]
|
74
|
-
base.define_callbacks *['persist', { :terminator => 'result == true' }]
|
82
|
+
class << self
|
83
|
+
def included(base) #:nodoc:
|
84
|
+
base.send :include, ActiveSupport::Callbacks
|
85
|
+
define_session_callbacks(base)
|
86
|
+
define_session_callback_installation_methods(base)
|
75
87
|
end
|
76
88
|
|
77
|
-
|
78
|
-
|
89
|
+
private
|
90
|
+
|
91
|
+
# Defines the "callback installation methods". Other modules will use
|
92
|
+
# these class methods to install their callbacks. Examples:
|
93
|
+
#
|
94
|
+
# ```
|
95
|
+
# # session/timeout.rb, in `included`
|
96
|
+
# before_persisting :reset_stale_state
|
97
|
+
#
|
98
|
+
# # session/password.rb, in `included`
|
99
|
+
# validate :validate_by_password, if: :authenticating_with_password?
|
100
|
+
# ```
|
101
|
+
def define_session_callback_installation_methods(base)
|
79
102
|
METHODS.each do |method|
|
80
|
-
base.class_eval <<-
|
81
|
-
def self.#{method}(*
|
82
|
-
set_callback
|
103
|
+
base.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
104
|
+
def self.#{method}(*filter_list, &block)
|
105
|
+
set_callback(:#{method}, *filter_list, &block)
|
83
106
|
end
|
84
|
-
|
107
|
+
EOS
|
85
108
|
end
|
86
109
|
end
|
87
|
-
end
|
88
110
|
|
89
|
-
|
111
|
+
# Defines session life cycle events that support callbacks.
|
112
|
+
def define_session_callbacks(base)
|
113
|
+
if Gem::Version.new(ActiveSupport::VERSION::STRING) >= Gem::Version.new("5")
|
114
|
+
base.define_callbacks(
|
115
|
+
*METHODS,
|
116
|
+
terminator: ->(_target, result_lambda) { result_lambda.call == false }
|
117
|
+
)
|
118
|
+
base.define_callbacks(
|
119
|
+
"persist",
|
120
|
+
terminator: ->(_target, result_lambda) { result_lambda.call == true }
|
121
|
+
)
|
122
|
+
else
|
123
|
+
base.define_callbacks(
|
124
|
+
*METHODS,
|
125
|
+
terminator: ->(_target, result) { result == false }
|
126
|
+
)
|
127
|
+
base.define_callbacks(
|
128
|
+
"persist",
|
129
|
+
terminator: ->(_target, result) { result == true }
|
130
|
+
)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
90
134
|
|
91
|
-
|
92
|
-
|
135
|
+
METHODS.each do |method|
|
136
|
+
class_eval(
|
137
|
+
<<-EOS, __FILE__, __LINE__ + 1
|
93
138
|
def #{method}
|
94
139
|
run_callbacks(:#{method})
|
95
140
|
end
|
96
|
-
|
97
|
-
|
141
|
+
EOS
|
142
|
+
)
|
143
|
+
end
|
98
144
|
|
99
|
-
|
100
|
-
|
101
|
-
|
145
|
+
def save_record(alternate_record = nil)
|
146
|
+
r = alternate_record || record
|
147
|
+
if r&.changed? && !r.readonly?
|
148
|
+
r.save_without_session_maintenance(validate: false)
|
102
149
|
end
|
150
|
+
end
|
103
151
|
end
|
104
152
|
end
|
105
153
|
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", "None"].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>
|
59
|
+
# * <tt>Default:</tt> true
|
58
60
|
# * <tt>Accepts:</tt> Boolean
|
59
61
|
def secure(value = nil)
|
60
|
-
rw_config(:secure, value,
|
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>
|
69
|
+
# * <tt>Default:</tt> true
|
68
70
|
# * <tt>Accepts:</tt> Boolean
|
69
71
|
def httponly(value = nil)
|
70
|
-
rw_config(:httponly, value,
|
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)
|
@@ -80,6 +96,20 @@ module Authlogic
|
|
80
96
|
rw_config(:sign_cookie, value, false)
|
81
97
|
end
|
82
98
|
alias_method :sign_cookie=, :sign_cookie
|
99
|
+
|
100
|
+
# Should the cookie be encrypted? If the controller adapter supports it, this is a
|
101
|
+
# measure to hide the contents of the cookie (e.g. persistence_token)"
|
102
|
+
def encrypt_cookie(value = nil)
|
103
|
+
if value && !controller.cookies.respond_to?(:encrypted)
|
104
|
+
raise "Encrypted cookies not supported with #{controller.class}!"
|
105
|
+
end
|
106
|
+
if value && sign_cookie
|
107
|
+
raise "It is recommended to use encrypt_cookie instead of sign_cookie. " \
|
108
|
+
"You may not enable both options."
|
109
|
+
end
|
110
|
+
rw_config(:encrypt_cookie, value, false)
|
111
|
+
end
|
112
|
+
alias_method :encrypt_cookie=, :encrypt_cookie
|
83
113
|
end
|
84
114
|
|
85
115
|
# The methods available for an Authlogic::Session::Base object that make up the
|
@@ -95,8 +125,8 @@ module Authlogic
|
|
95
125
|
self.remember_me = values.first.with_indifferent_access[:remember_me]
|
96
126
|
end
|
97
127
|
else
|
98
|
-
r = values.find { |
|
99
|
-
self.remember_me = r
|
128
|
+
r = values.find { |val| val.is_a?(TrueClass) || val.is_a?(FalseClass) }
|
129
|
+
self.remember_me = r unless r.nil?
|
100
130
|
end
|
101
131
|
end
|
102
132
|
|
@@ -172,6 +202,21 @@ module Authlogic
|
|
172
202
|
httponly == true || httponly == "true" || httponly == "1"
|
173
203
|
end
|
174
204
|
|
205
|
+
# If the cookie should be marked as SameSite with 'Lax' or 'Strict' flag.
|
206
|
+
def same_site
|
207
|
+
return @same_site if defined?(@same_site)
|
208
|
+
@same_site = self.class.same_site(nil)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Accepts nil, 'Lax' or 'Strict' as possible flags.
|
212
|
+
def same_site=(value)
|
213
|
+
unless VALID_SAME_SITE_VALUES.include?(value)
|
214
|
+
msg = "Invalid same_site value: #{value}. Valid: #{VALID_SAME_SITE_VALUES.inspect}"
|
215
|
+
raise ArgumentError.new(msg)
|
216
|
+
end
|
217
|
+
@same_site = value
|
218
|
+
end
|
219
|
+
|
175
220
|
# If the cookie should be signed
|
176
221
|
def sign_cookie
|
177
222
|
return @sign_cookie if defined?(@sign_cookie)
|
@@ -189,55 +234,95 @@ module Authlogic
|
|
189
234
|
sign_cookie == true || sign_cookie == "true" || sign_cookie == "1"
|
190
235
|
end
|
191
236
|
|
237
|
+
# If the cookie should be encrypted
|
238
|
+
def encrypt_cookie
|
239
|
+
return @encrypt_cookie if defined?(@encrypt_cookie)
|
240
|
+
@encrypt_cookie = self.class.encrypt_cookie
|
241
|
+
end
|
242
|
+
|
243
|
+
# Accepts a boolean as to whether the cookie should be encrypted. If true
|
244
|
+
# the cookie will be saved in an encrypted state.
|
245
|
+
def encrypt_cookie=(value)
|
246
|
+
@encrypt_cookie = value
|
247
|
+
end
|
248
|
+
|
249
|
+
# See encrypt_cookie
|
250
|
+
def encrypt_cookie?
|
251
|
+
encrypt_cookie == true || encrypt_cookie == "true" || encrypt_cookie == "1"
|
252
|
+
end
|
253
|
+
|
192
254
|
private
|
193
255
|
|
194
|
-
|
195
|
-
|
196
|
-
|
256
|
+
def cookie_key
|
257
|
+
build_key(self.class.cookie_key)
|
258
|
+
end
|
197
259
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
cookie && cookie.split("::")
|
205
|
-
end
|
260
|
+
# Returns an array of cookie elements. See cookie format in
|
261
|
+
# `generate_cookie_for_saving`. If no cookie is found, returns nil.
|
262
|
+
def cookie_credentials
|
263
|
+
cookie = cookie_jar[cookie_key]
|
264
|
+
cookie&.split("::")
|
265
|
+
end
|
206
266
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
267
|
+
# The third element of the cookie indicates whether the user wanted
|
268
|
+
# to be remembered (Actually, it's a timestamp, `remember_me_until`)
|
269
|
+
# See cookie format in `generate_cookie_for_saving`.
|
270
|
+
def cookie_credentials_remember_me?
|
271
|
+
!cookie_credentials.nil? && !cookie_credentials[2].nil?
|
272
|
+
end
|
273
|
+
|
274
|
+
def cookie_jar
|
275
|
+
if self.class.encrypt_cookie
|
276
|
+
controller.cookies.encrypted
|
277
|
+
elsif self.class.sign_cookie
|
278
|
+
controller.cookies.signed
|
279
|
+
else
|
280
|
+
controller.cookies
|
217
281
|
end
|
282
|
+
end
|
218
283
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
284
|
+
# Tries to validate the session from information in the cookie
|
285
|
+
def persist_by_cookie
|
286
|
+
persistence_token, record_id = cookie_credentials
|
287
|
+
if persistence_token.present?
|
288
|
+
record = search_for_record("find_by_#{klass.primary_key}", record_id)
|
289
|
+
if record && record.persistence_token == persistence_token
|
290
|
+
self.unauthorized_record = record
|
224
291
|
end
|
292
|
+
valid?
|
293
|
+
else
|
294
|
+
false
|
225
295
|
end
|
296
|
+
end
|
226
297
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
:expires => remember_me_until,
|
232
|
-
:secure => secure,
|
233
|
-
:httponly => httponly,
|
234
|
-
:domain => controller.cookie_domain
|
235
|
-
}
|
236
|
-
end
|
298
|
+
def save_cookie
|
299
|
+
cookie_jar[cookie_key] = generate_cookie_for_saving
|
300
|
+
true
|
301
|
+
end
|
237
302
|
|
238
|
-
|
239
|
-
|
240
|
-
|
303
|
+
def generate_cookie_for_saving
|
304
|
+
{
|
305
|
+
value: generate_cookie_value,
|
306
|
+
expires: remember_me_until,
|
307
|
+
secure: secure,
|
308
|
+
httponly: httponly,
|
309
|
+
same_site: same_site,
|
310
|
+
domain: controller.cookie_domain
|
311
|
+
}
|
312
|
+
end
|
313
|
+
|
314
|
+
def generate_cookie_value
|
315
|
+
format(
|
316
|
+
"%s::%s%s",
|
317
|
+
record.persistence_token.to_s,
|
318
|
+
record.send(record.class.primary_key).to_s,
|
319
|
+
remember_me? ? "::#{remember_me_until.iso8601}" : ""
|
320
|
+
)
|
321
|
+
end
|
322
|
+
|
323
|
+
def destroy_cookie
|
324
|
+
controller.cookies.delete cookie_key, domain: controller.cookie_domain
|
325
|
+
end
|
241
326
|
end
|
242
327
|
end
|
243
328
|
end
|
@@ -1,10 +1,16 @@
|
|
1
1
|
module Authlogic
|
2
2
|
module Session
|
3
|
-
# Provides methods to create and destroy objects. Basically controls their
|
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
|
-
|
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
|
44
|
-
# the user to authenticate again if
|
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
|
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
|
61
|
-
# run validation checks and find the
|
62
|
-
#
|
63
|
-
|
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
|
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.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,33 +53,52 @@ module Authlogic
|
|
22
53
|
[]
|
23
54
|
end
|
24
55
|
|
25
|
-
# Set your credentials before you save your session.
|
26
|
-
#
|
56
|
+
# Set your credentials before you save your session. There are many
|
57
|
+
# method signatures.
|
27
58
|
#
|
28
|
-
#
|
59
|
+
# ```
|
60
|
+
# # A hash of credentials is most common
|
61
|
+
# session.credentials = { login: "foo", password: "bar", remember_me: true }
|
29
62
|
#
|
30
|
-
#
|
63
|
+
# # You must pass an actual Hash, `ActionController::Parameters` is
|
64
|
+
# # specifically not allowed.
|
31
65
|
#
|
32
|
-
#
|
66
|
+
# # You can pass an array of objects:
|
67
|
+
# session.credentials = [my_user_object, true]
|
33
68
|
#
|
34
|
-
#
|
35
|
-
# item in the array you pass, since the id
|
36
|
-
# it should never be set from
|
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
|
-
#
|
39
|
-
#
|
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
|
44
|
-
|
90
|
+
format(
|
91
|
+
"#<%s: %s>",
|
92
|
+
self.class.name,
|
93
|
+
credentials.blank? ? "no credentials provided" : credentials.inspect
|
94
|
+
)
|
45
95
|
end
|
46
96
|
|
47
97
|
private
|
48
98
|
|
49
|
-
|
50
|
-
|
51
|
-
|
99
|
+
def build_key(last_part)
|
100
|
+
last_part
|
101
|
+
end
|
52
102
|
end
|
53
103
|
end
|
54
104
|
end
|