authlogic 3.8.0 → 4.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|