authlogic 6.2.0 → 6.4.2

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 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
  - - ">="