authlogic 6.2.0 → 6.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3db4f35b09d1723bab91b36afb8fbd79c1583896b19186846f8b1b25cb7793e
4
- data.tar.gz: a517af1c9f5341e9bd58722711f7046fb51dfd2c1440e072f81170be196d2518
3
+ metadata.gz: b9fad0a99beb89fdac894ff1979b59459073fbf597d375b6749b263e86be930d
4
+ data.tar.gz: 4ea061519a7a881f78cfaddf1a86e312d082dba28ce22c51e52c38ac51c62581
5
5
  SHA512:
6
- metadata.gz: dd2fa0ad62c54eb721a8d3fb1d85ca1aa59b122bed688eca908a4cde2487fce1a5c084ffa365fd3b975d576f99a6a86bd243f950a1f2d07ddc1b6f171afed345
7
- data.tar.gz: 519fcf4568fee21a0a43c9f7ec5ea740edcb84cf5cb95f48bf5a1819a1c091ba882f2f54f169497c20cf3821eb556a537687330950643b1b7d4f0d2a138961f0
6
+ metadata.gz: db0557f858087739e3a292ab6ba63becbca9a0204874f2794e934e442c58fc6d7cf814450d9df4adc76c8d1e41d18f625fc7fa942df2810248aa7b9176aa60ee
7
+ data.tar.gz: 8c349c4e3a8579384c2e1b7f3fc2f85b2974e7871d02a9e9a93117f83777110e21ad57fa4f04493f8116aaba113ebd9f735cea7ff9ae8b02e73897a51b044593
@@ -8,6 +8,7 @@ module Authlogic
8
8
  class AbstractAdapter
9
9
  E_COOKIE_DOMAIN_ADAPTER = "The cookie_domain method has not been " \
10
10
  "implemented by the controller adapter"
11
+ ENV_SESSION_OPTIONS = "rack.session.options"
11
12
 
12
13
  attr_accessor :controller
13
14
 
@@ -44,6 +45,26 @@ module Authlogic
44
45
  request.content_type
45
46
  end
46
47
 
48
+ # Inform Rack that we would like a new session ID to be assigned. Changes
49
+ # the ID, but not the contents of the session.
50
+ #
51
+ # The `:renew` option is read by `rack/session/abstract/id.rb`.
52
+ #
53
+ # This is how Devise (via warden) implements defense against Session
54
+ # Fixation. Our implementation is copied directly from the warden gem
55
+ # (set_user in warden/proxy.rb)
56
+ def renew_session_id
57
+ env = request.env
58
+ options = env[ENV_SESSION_OPTIONS]
59
+ if options
60
+ if options.frozen?
61
+ env[ENV_SESSION_OPTIONS] = options.merge(renew: true).freeze
62
+ else
63
+ options[:renew] = true
64
+ end
65
+ end
66
+ end
67
+
47
68
  def session
48
69
  controller.session
49
70
  end
@@ -351,6 +351,13 @@ module Authlogic
351
351
  - https://github.com/binarylogic/authlogic/pull/558
352
352
  - https://github.com/binarylogic/authlogic/pull/577
353
353
  EOS
354
+ E_DPR_FIND_BY_LOGIN_METHOD = <<~EOS.squish.freeze
355
+ find_by_login_method is deprecated in favor of record_selection_method,
356
+ to avoid confusion with ActiveRecord's "Dynamic Finders".
357
+ (https://guides.rubyonrails.org/v6.0/active_record_querying.html#dynamic-finders)
358
+ For example, rubocop-rails is confused by the deprecated method.
359
+ (https://github.com/rubocop-hq/rubocop-rails/blob/master/lib/rubocop/cop/rails/dynamic_find_by.rb)
360
+ EOS
354
361
  VALID_SAME_SITE_VALUES = [nil, "Lax", "Strict", "None"].freeze
355
362
 
356
363
  # Callbacks
@@ -417,6 +424,7 @@ module Authlogic
417
424
  after_save :reset_perishable_token!
418
425
  after_save :save_cookie, if: :cookie_enabled?
419
426
  after_save :update_session
427
+ after_create :renew_session_id
420
428
 
421
429
  after_destroy :destroy_cookie, if: :cookie_enabled?
422
430
  after_destroy :update_session
@@ -663,35 +671,10 @@ module Authlogic
663
671
  end
664
672
  end
665
673
 
666
- # Authlogic tries to validate the credentials passed to it. One part of
667
- # validation is actually finding the user and making sure it exists.
668
- # What method it uses the do this is up to you.
669
- #
670
- # Let's say you have a UserSession that is authenticating a User. By
671
- # default UserSession will call User.find_by_login(login). You can
672
- # change what method UserSession calls by specifying it here. Then in
673
- # your User model you can make that method do anything you want, giving
674
- # you complete control of how users are found by the UserSession.
675
- #
676
- # Let's take an example: You want to allow users to login by username or
677
- # email. Set this to the name of the class method that does this in the
678
- # User model. Let's call it "find_by_username_or_email"
679
- #
680
- # class User < ActiveRecord::Base
681
- # def self.find_by_username_or_email(login)
682
- # find_by_username(login) || find_by_email(login)
683
- # end
684
- # end
685
- #
686
- # Now just specify the name of this method for this configuration option
687
- # and you are all set. You can do anything you want here. Maybe you
688
- # allow users to have multiple logins and you want to search a has_many
689
- # relationship, etc. The sky is the limit.
690
- #
691
- # * <tt>Default:</tt> "find_by_smart_case_login_field"
692
- # * <tt>Accepts:</tt> Symbol or String
674
+ # @deprecated in favor of record_selection_method
693
675
  def find_by_login_method(value = nil)
694
- rw_config(:find_by_login_method, value, "find_by_smart_case_login_field")
676
+ ::ActiveSupport::Deprecation.warn(E_DPR_FIND_BY_LOGIN_METHOD)
677
+ record_selection_method(value)
695
678
  end
696
679
  alias find_by_login_method= find_by_login_method
697
680
 
@@ -776,15 +759,23 @@ module Authlogic
776
759
  # example, the UserSession class will authenticate with the User class
777
760
  # unless you specify otherwise in your configuration. See
778
761
  # authenticate_with for information on how to change this value.
762
+ #
763
+ # @api public
779
764
  def klass
780
765
  @klass ||= klass_name ? klass_name.constantize : nil
781
766
  end
782
767
 
783
- # The string of the model name class guessed from the actual session class name.
768
+ # The model name, guessed from the session class name, e.g. "User",
769
+ # from "UserSession".
770
+ #
771
+ # TODO: This method can return nil. We should explore this. It seems
772
+ # likely to cause a NoMethodError later, so perhaps we should raise an
773
+ # error instead.
774
+ #
775
+ # @api private
784
776
  def klass_name
785
- return @klass_name if defined?(@klass_name)
786
- @klass_name = name.scan(/(.*)Session/)[0]
787
- @klass_name = klass_name ? klass_name[0] : nil
777
+ return @klass_name if instance_variable_defined?(:@klass_name)
778
+ @klass_name = name.scan(/(.*)Session/)[0]&.first
788
779
  end
789
780
 
790
781
  # The name of the method you want Authlogic to create for storing the
@@ -792,8 +783,8 @@ module Authlogic
792
783
  # Authlogic::Session, if you want it can be something completely
793
784
  # different than the field in your model. So if you wanted people to
794
785
  # login with a field called "login" and then find users by email this is
795
- # completely doable. See the find_by_login_method configuration option
796
- # for more details.
786
+ # completely doable. See the `record_selection_method` configuration
787
+ # option for details.
797
788
  #
798
789
  # * <tt>Default:</tt> klass.login_field || klass.email_field
799
790
  # * <tt>Accepts:</tt> Symbol or String
@@ -876,6 +867,47 @@ module Authlogic
876
867
  end
877
868
  alias password_field= password_field
878
869
 
870
+ # Authlogic tries to validate the credentials passed to it. One part of
871
+ # validation is actually finding the user and making sure it exists.
872
+ # What method it uses the do this is up to you.
873
+ #
874
+ # ```
875
+ # # user_session.rb
876
+ # record_selection_method :find_by_email
877
+ # ```
878
+ #
879
+ # This is the recommended way to find the user by email address.
880
+ # The resulting query will be `User.find_by_email(send(login_field))`.
881
+ # (`login_field` will fall back to `email_field` if there's no `login`
882
+ # or `username` column).
883
+ #
884
+ # In your User model you can make that method do anything you want,
885
+ # giving you complete control of how users are found by the UserSession.
886
+ #
887
+ # Let's take an example: You want to allow users to login by username or
888
+ # email. Set this to the name of the class method that does this in the
889
+ # User model. Let's call it "find_by_username_or_email"
890
+ #
891
+ # ```
892
+ # class User < ActiveRecord::Base
893
+ # def self.find_by_username_or_email(login)
894
+ # find_by_username(login) || find_by_email(login)
895
+ # end
896
+ # end
897
+ # ```
898
+ #
899
+ # Now just specify the name of this method for this configuration option
900
+ # and you are all set. You can do anything you want here. Maybe you
901
+ # allow users to have multiple logins and you want to search a has_many
902
+ # relationship, etc. The sky is the limit.
903
+ #
904
+ # * <tt>Default:</tt> "find_by_smart_case_login_field"
905
+ # * <tt>Accepts:</tt> Symbol or String
906
+ def record_selection_method(value = nil)
907
+ rw_config(:record_selection_method, value, "find_by_smart_case_login_field")
908
+ end
909
+ alias record_selection_method= record_selection_method
910
+
879
911
  # Whether or not to request HTTP authentication
880
912
  #
881
913
  # If set to true and no HTTP authentication credentials are sent with
@@ -945,6 +977,16 @@ module Authlogic
945
977
  end
946
978
  alias secure= secure
947
979
 
980
+ # Should the Rack session ID be reset after authentication, to protect
981
+ # against Session Fixation attacks?
982
+ #
983
+ # * <tt>Default:</tt> true
984
+ # * <tt>Accepts:</tt> Boolean
985
+ def session_fixation_defense(value = nil)
986
+ rw_config(:session_fixation_defense, value, true)
987
+ end
988
+ alias session_fixation_defense= session_fixation_defense
989
+
948
990
  # Should the cookie be signed? If the controller adapter supports it, this is a
949
991
  # measure against cookie tampering.
950
992
  def sign_cookie(value = nil)
@@ -1650,6 +1692,13 @@ module Authlogic
1650
1692
  define_password_field_methods
1651
1693
  end
1652
1694
 
1695
+ # Assign a new controller-session ID, to defend against Session Fixation.
1696
+ # https://guides.rubyonrails.org/v6.0/security.html#session-fixation
1697
+ def renew_session_id
1698
+ return unless self.class.session_fixation_defense
1699
+ controller.renew_session_id
1700
+ end
1701
+
1653
1702
  def define_login_field_methods
1654
1703
  return unless login_field
1655
1704
  self.class.send(:attr_writer, login_field) unless respond_to?("#{login_field}=")
@@ -1740,8 +1789,10 @@ module Authlogic
1740
1789
  attempted_record.failed_login_count >= consecutive_failed_logins_limit
1741
1790
  end
1742
1791
 
1792
+ # @deprecated in favor of `self.class.record_selection_method`
1743
1793
  def find_by_login_method
1744
- self.class.find_by_login_method
1794
+ ::ActiveSupport::Deprecation.warn(E_DPR_FIND_BY_LOGIN_METHOD)
1795
+ self.class.record_selection_method
1745
1796
  end
1746
1797
 
1747
1798
  def generalize_credentials_error_messages?
@@ -1795,7 +1846,7 @@ module Authlogic
1795
1846
  end
1796
1847
  end
1797
1848
 
1798
- def increment_login_cout
1849
+ def increment_login_count
1799
1850
  if record.respond_to?(:login_count)
1800
1851
  record.login_count = (record.login_count.blank? ? 1 : record.login_count + 1)
1801
1852
  end
@@ -1980,7 +2031,7 @@ module Authlogic
1980
2031
 
1981
2032
  # @api private
1982
2033
  def set_last_request_at
1983
- current_time = klass.default_timezone == :utc ? Time.now.utc : Time.now
2034
+ current_time = Time.current
1984
2035
  MagicColumn::AssignsLastRequestAt
1985
2036
  .new(current_time, record, controller, last_request_at_threshold)
1986
2037
  .assign
@@ -2025,7 +2076,7 @@ module Authlogic
2025
2076
  end
2026
2077
 
2027
2078
  def update_info
2028
- increment_login_cout
2079
+ increment_login_count
2029
2080
  clear_failed_login_count
2030
2081
  update_login_timestamps
2031
2082
  update_login_ip_addresses
@@ -2041,7 +2092,7 @@ module Authlogic
2041
2092
  def update_login_timestamps
2042
2093
  if record.respond_to?(:current_login_at)
2043
2094
  record.last_login_at = record.current_login_at if record.respond_to?(:last_login_at)
2044
- record.current_login_at = klass.default_timezone == :utc ? Time.now.utc : Time.now
2095
+ record.current_login_at = Time.current
2045
2096
  end
2046
2097
  end
2047
2098
 
@@ -2072,7 +2123,10 @@ module Authlogic
2072
2123
  self.invalid_password = false
2073
2124
  validate_by_password__blank_fields
2074
2125
  return if errors.count > 0
2075
- self.attempted_record = search_for_record(find_by_login_method, send(login_field))
2126
+ self.attempted_record = search_for_record(
2127
+ self.class.record_selection_method,
2128
+ send(login_field)
2129
+ )
2076
2130
  if attempted_record.blank?
2077
2131
  add_login_not_found_error
2078
2132
  return
@@ -3,6 +3,7 @@
3
3
  module Authlogic
4
4
  module TestCase
5
5
  # A mock of `ActionDispatch::Cookies::CookieJar`.
6
+ # See action_dispatch/middleware/cookies.rb
6
7
  class MockCookieJar < Hash # :nodoc:
7
8
  attr_accessor :set_cookies
8
9
 
@@ -11,9 +12,12 @@ module Authlogic
11
12
  hash && hash[:value]
12
13
  end
13
14
 
15
+ # @param options - "the cookie's value [usually a string] or a hash of
16
+ # options as documented above [in action_dispatch/middleware/cookies.rb]"
14
17
  def []=(key, options)
15
- (@set_cookies ||= {})[key.to_s] = options
16
- super
18
+ opt = cookie_options_to_hash(options)
19
+ (@set_cookies ||= {})[key.to_s] = opt
20
+ super(key, opt)
17
21
  end
18
22
 
19
23
  def delete(key, _options = {})
@@ -27,6 +31,17 @@ module Authlogic
27
31
  def encrypted
28
32
  @encrypted ||= MockEncryptedCookieJar.new(self)
29
33
  end
34
+
35
+ private
36
+
37
+ # @api private
38
+ def cookie_options_to_hash(options)
39
+ if options.is_a?(Hash)
40
+ options
41
+ else
42
+ { value: options }
43
+ end
44
+ end
30
45
  end
31
46
 
32
47
  # A mock of `ActionDispatch::Cookies::SignedKeyRotatingCookieJar`
@@ -52,8 +67,9 @@ module Authlogic
52
67
  end
53
68
 
54
69
  def []=(key, options)
55
- options[:value] = "#{options[:value]}--#{Digest::SHA1.hexdigest options[:value]}"
56
- @parent_jar[key] = options
70
+ opt = cookie_options_to_hash(options)
71
+ opt[:value] = "#{opt[:value]}--#{Digest::SHA1.hexdigest opt[:value]}"
72
+ @parent_jar[key] = opt
57
73
  end
58
74
  end
59
75
 
@@ -75,8 +91,9 @@ module Authlogic
75
91
  end
76
92
 
77
93
  def []=(key, options)
78
- options[:value] = self.class.encrypt(options[:value])
79
- @parent_jar[key] = options
94
+ opt = cookie_options_to_hash(options)
95
+ opt[:value] = self.class.encrypt(opt[:value])
96
+ @parent_jar[key] = opt
80
97
  end
81
98
 
82
99
  # simple caesar cipher for testing
@@ -9,6 +9,12 @@ module Authlogic
9
9
  self.controller = controller
10
10
  end
11
11
 
12
+ def env
13
+ @env ||= {
14
+ ControllerAdapters::AbstractAdapter::ENV_SESSION_OPTIONS => {}
15
+ }
16
+ end
17
+
12
18
  def format
13
19
  controller.request_content_type if controller.respond_to? :request_content_type
14
20
  end
@@ -12,7 +12,7 @@ module Authlogic
12
12
  def cookies
13
13
  new_cookies = MockCookieJar.new
14
14
  super.each do |key, value|
15
- new_cookies[key] = value[:value]
15
+ new_cookies[key] = cookie_value(value)
16
16
  end
17
17
  new_cookies
18
18
  end
@@ -28,6 +28,12 @@ module Authlogic
28
28
  def request_content_type
29
29
  request.format.to_s
30
30
  end
31
+
32
+ private
33
+
34
+ def cookie_value(value)
35
+ value.is_a?(Hash) ? value[:value] : value
36
+ end
31
37
  end
32
38
  end
33
39
  end
@@ -17,6 +17,6 @@ module Authlogic
17
17
  #
18
18
  # @api public
19
19
  def self.gem_version
20
- ::Gem::Version.new("6.2.0")
20
+ ::Gem::Version.new("6.4.2")
21
21
  end
22
22
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: authlogic
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.2.0
4
+ version: 6.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Johnson
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-09-10 00:00:00.000000000 Z
13
+ date: 2021-12-21 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activemodel
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '5.2'
22
22
  - - "<"
23
23
  - !ruby/object:Gem::Version
24
- version: '6.1'
24
+ version: '7.1'
25
25
  type: :runtime
26
26
  prerelease: false
27
27
  version_requirements: !ruby/object:Gem::Requirement
@@ -31,7 +31,7 @@ dependencies:
31
31
  version: '5.2'
32
32
  - - "<"
33
33
  - !ruby/object:Gem::Version
34
- version: '6.1'
34
+ version: '7.1'
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: activerecord
37
37
  requirement: !ruby/object:Gem::Requirement
@@ -41,7 +41,7 @@ dependencies:
41
41
  version: '5.2'
42
42
  - - "<"
43
43
  - !ruby/object:Gem::Version
44
- version: '6.1'
44
+ version: '7.1'
45
45
  type: :runtime
46
46
  prerelease: false
47
47
  version_requirements: !ruby/object:Gem::Requirement
@@ -51,7 +51,7 @@ dependencies:
51
51
  version: '5.2'
52
52
  - - "<"
53
53
  - !ruby/object:Gem::Version
54
- version: '6.1'
54
+ version: '7.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: activesupport
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -61,7 +61,7 @@ dependencies:
61
61
  version: '5.2'
62
62
  - - "<"
63
63
  - !ruby/object:Gem::Version
64
- version: '6.1'
64
+ version: '7.1'
65
65
  type: :runtime
66
66
  prerelease: false
67
67
  version_requirements: !ruby/object:Gem::Requirement
@@ -71,7 +71,7 @@ dependencies:
71
71
  version: '5.2'
72
72
  - - "<"
73
73
  - !ruby/object:Gem::Version
74
- version: '6.1'
74
+ version: '7.1'
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: request_store
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -170,6 +170,20 @@ dependencies:
170
170
  - - "~>"
171
171
  - !ruby/object:Gem::Version
172
172
  version: 1.1.4
173
+ - !ruby/object:Gem::Dependency
174
+ name: rake
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: '13.0'
180
+ type: :development
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '13.0'
173
187
  - !ruby/object:Gem::Dependency
174
188
  name: rubocop
175
189
  requirement: !ruby/object:Gem::Requirement
@@ -327,7 +341,7 @@ files:
327
341
  - lib/authlogic/test_case/mock_request.rb
328
342
  - lib/authlogic/test_case/rails_request_adapter.rb
329
343
  - lib/authlogic/version.rb
330
- homepage: http://github.com/binarylogic/authlogic
344
+ homepage: https://github.com/binarylogic/authlogic
331
345
  licenses:
332
346
  - MIT
333
347
  metadata: {}
@@ -339,7 +353,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
339
353
  requirements:
340
354
  - - ">="
341
355
  - !ruby/object:Gem::Version
342
- version: 2.4.0
356
+ version: 2.6.0
343
357
  required_rubygems_version: !ruby/object:Gem::Requirement
344
358
  requirements:
345
359
  - - ">="