authlogic 5.0.4 → 6.2.0

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: f33a408b68b3809a5f68ee4a2c42aa60f472fd139f9e0a10fc1e20a6e32eb9a7
4
- data.tar.gz: 75bfa342ae455b28fa5c12b46c6f03f3a872b3a65654a3bd911e4891b8b8e611
3
+ metadata.gz: f3db4f35b09d1723bab91b36afb8fbd79c1583896b19186846f8b1b25cb7793e
4
+ data.tar.gz: a517af1c9f5341e9bd58722711f7046fb51dfd2c1440e072f81170be196d2518
5
5
  SHA512:
6
- metadata.gz: dbdfd4e20f62d723e1171326aae2b48d195b2ffcb8e31ed0b4ec451a118abcd6f5b8c9bbf3a2b0a7206383f73f81e7fb9983c81d02f6c76cef732deda40f2736
7
- data.tar.gz: dac40f1a0049cb1e63d699f6dda6a1df5bdedcc4e1a5f412d3908e515edd877ce3c9ba1d4955f154da18c515381e9bbcb551ba84afbc4a8c8bbe53ae02e10b17
6
+ metadata.gz: dd2fa0ad62c54eb721a8d3fb1d85ca1aa59b122bed688eca908a4cde2487fce1a5c084ffa365fd3b975d576f99a6a86bd243f950a1f2d07ddc1b6f171afed345
7
+ data.tar.gz: 519fcf4568fee21a0a43c9f7ec5ea740edcb84cf5cb95f48bf5a1819a1c091ba882f2f54f169497c20cf3821eb556a537687330950643b1b7d4f0d2a138961f0
@@ -13,6 +13,7 @@ require "active_record"
13
13
  path = File.dirname(__FILE__) + "/authlogic/"
14
14
 
15
15
  [
16
+ "errors",
16
17
  "i18n",
17
18
  "random",
18
19
  "config",
@@ -31,8 +31,8 @@ module Authlogic
31
31
  #
32
32
  # See the various sub modules for the configuration they provide.
33
33
  def acts_as_authentic
34
- return unless db_setup?
35
34
  yield self if block_given?
35
+ return unless db_setup?
36
36
  acts_as_authentic_modules.each { |mod| include mod }
37
37
  end
38
38
 
@@ -65,12 +65,27 @@ module Authlogic
65
65
  self.acts_as_authentic_modules = modules
66
66
  end
67
67
 
68
+ # Some Authlogic modules requires a database connection with a existing
69
+ # users table by the moment when you call the `acts_as_authentic`
70
+ # method. If you try to call `acts_as_authentic` without a database
71
+ # connection, it will raise a `Authlogic::ModelSetupError`.
72
+ #
73
+ # If you rely on the User model before the database is setup correctly,
74
+ # set this field to false.
75
+ # * <tt>Default:</tt> false
76
+ # * <tt>Accepts:</tt> Boolean
77
+ def raise_on_model_setup_error(value = nil)
78
+ rw_config(:raise_on_model_setup_error, value, false)
79
+ end
80
+ alias raise_on_model_setup_error= raise_on_model_setup_error
81
+
68
82
  private
69
83
 
70
84
  def db_setup?
71
85
  column_names
72
86
  true
73
87
  rescue StandardError
88
+ raise ModelSetupError if raise_on_model_setup_error
74
89
  false
75
90
  end
76
91
 
@@ -102,20 +102,30 @@ module Authlogic
102
102
  # The family of adaptive hash functions (BCrypt, SCrypt, PBKDF2) is the
103
103
  # best choice for password storage today. We recommend SCrypt. Other
104
104
  # one-way functions like SHA512 are inferior, but widely used.
105
- # Reverisbile functions like AES256 are the worst choice, and we no
105
+ # Reversible functions like AES256 are the worst choice, and we no
106
106
  # longer support them.
107
107
  #
108
108
  # You can use the `transition_from_crypto_providers` option to gradually
109
109
  # transition to a better crypto provider without causing your users any
110
110
  # pain.
111
111
  #
112
- # * <tt>Default:</tt> CryptoProviders::SCrypt
112
+ # * <tt>Default:</tt> There is no longer a default value. Prior to
113
+ # Authlogic 6, the default was `CryptoProviders::SCrypt`. If you try
114
+ # to read this config option before setting it, it will raise a
115
+ # `NilCryptoProvider` error. See that error's message for further
116
+ # details, and rationale for this change.
113
117
  # * <tt>Accepts:</tt> Class
114
- def crypto_provider(value = nil)
118
+ def crypto_provider
119
+ acts_as_authentic_config[:crypto_provider].tap { |provider|
120
+ raise NilCryptoProvider if provider.nil?
121
+ }
122
+ end
123
+
124
+ def crypto_provider=(value)
125
+ raise NilCryptoProvider if value.nil?
115
126
  CryptoProviders::Guidance.new(value).impart_wisdom
116
- rw_config(:crypto_provider, value, CryptoProviders::SCrypt)
127
+ rw_config(:crypto_provider, value)
117
128
  end
118
- alias crypto_provider= crypto_provider
119
129
 
120
130
  # Let's say you originally encrypted your passwords with Sha1. Sha1 is
121
131
  # starting to join the party with MD5 and you want to switch to
@@ -93,9 +93,9 @@ module Authlogic
93
93
  end
94
94
 
95
95
  # Save the record and skip session maintenance all together.
96
- def save_without_session_maintenance(*args)
96
+ def save_without_session_maintenance(**options)
97
97
  self.skip_session_maintenance = true
98
- result = save(*args)
98
+ result = save(**options)
99
99
  self.skip_session_maintenance = false
100
100
  result
101
101
  end
@@ -176,7 +176,9 @@ module Authlogic
176
176
  end
177
177
 
178
178
  def log_in_after_password_change?
179
- will_save_change_to_persistence_token? && self.class.log_in_after_password_change
179
+ persisted? &&
180
+ will_save_change_to_persistence_token? &&
181
+ self.class.log_in_after_password_change
180
182
  end
181
183
  end
182
184
  end
@@ -14,7 +14,7 @@ module Authlogic
14
14
  # Returns a `ActionDispatch::Cookies::CookieJar`. See the AC guide
15
15
  # http://guides.rubyonrails.org/action_controller_overview.html#cookies
16
16
  def cookies
17
- controller.send(:cookies)
17
+ controller.respond_to?(:cookies, true) ? controller.send(:cookies) : nil
18
18
  end
19
19
 
20
20
  def cookie_domain
@@ -6,6 +6,9 @@ module Authlogic
6
6
  module CryptoProviders
7
7
  # A poor choice. There are known attacks against this algorithm.
8
8
  class MD5
9
+ # V2 hashes the digest bytes in repeated stretches instead of hex characters.
10
+ autoload :V2, File.join(__dir__, "md5", "v2")
11
+
9
12
  class << self
10
13
  attr_accessor :join_token
11
14
 
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest/md5"
4
+
5
+ module Authlogic
6
+ module CryptoProviders
7
+ class MD5
8
+ # A poor choice. There are known attacks against this algorithm.
9
+ class V2
10
+ class << self
11
+ attr_accessor :join_token
12
+
13
+ # The number of times to loop through the encryption.
14
+ def stretches
15
+ @stretches ||= 1
16
+ end
17
+ attr_writer :stretches
18
+
19
+ # Turns your raw password into a MD5 hash.
20
+ def encrypt(*tokens)
21
+ digest = tokens.flatten.join(join_token)
22
+ stretches.times { digest = Digest::MD5.digest(digest) }
23
+ digest.unpack1("H*")
24
+ end
25
+
26
+ # Does the crypted password match the tokens? Uses the same tokens that
27
+ # were used to encrypt.
28
+ def matches?(crypted, *tokens)
29
+ encrypt(*tokens) == crypted
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -6,6 +6,9 @@ module Authlogic
6
6
  module CryptoProviders
7
7
  # A poor choice. There are known attacks against this algorithm.
8
8
  class Sha1
9
+ # V2 hashes the digest bytes in repeated stretches instead of hex characters.
10
+ autoload :V2, File.join(__dir__, "sha1", "v2")
11
+
9
12
  class << self
10
13
  def join_token
11
14
  @join_token ||= "--"
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest/sha1"
4
+
5
+ module Authlogic
6
+ module CryptoProviders
7
+ class Sha1
8
+ # A poor choice. There are known attacks against this algorithm.
9
+ class V2
10
+ class << self
11
+ def join_token
12
+ @join_token ||= "--"
13
+ end
14
+ attr_writer :join_token
15
+
16
+ # The number of times to loop through the encryption.
17
+ def stretches
18
+ @stretches ||= 10
19
+ end
20
+ attr_writer :stretches
21
+
22
+ # Turns your raw password into a Sha1 hash.
23
+ def encrypt(*tokens)
24
+ tokens = tokens.flatten
25
+ digest = tokens.shift
26
+ stretches.times do
27
+ digest = Digest::SHA1.digest([digest, *tokens].join(join_token))
28
+ end
29
+ digest.unpack1("H*")
30
+ end
31
+
32
+ # Does the crypted password match the tokens? Uses the same tokens that
33
+ # were used to encrypt.
34
+ def matches?(crypted, *tokens)
35
+ encrypt(*tokens) == crypted
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -29,6 +29,9 @@ module Authlogic
29
29
  #
30
30
  # Uses the Sha256 hash algorithm to encrypt passwords.
31
31
  class Sha256
32
+ # V2 hashes the digest bytes in repeated stretches instead of hex characters.
33
+ autoload :V2, File.join(__dir__, "sha256", "v2")
34
+
32
35
  class << self
33
36
  attr_accessor :join_token
34
37
 
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest/sha2"
4
+
5
+ module Authlogic
6
+ # The acts_as_authentic method has a crypto_provider option. This allows you
7
+ # to use any type of encryption you like. Just create a class with a class
8
+ # level encrypt and matches? method. See example below.
9
+ #
10
+ # === Example
11
+ #
12
+ # class MyAwesomeEncryptionMethod
13
+ # def self.encrypt(*tokens)
14
+ # # the tokens passed will be an array of objects, what type of object
15
+ # # is irrelevant, just do what you need to do with them and return a
16
+ # # single encrypted string. for example, you will most likely join all
17
+ # # of the objects into a single string and then encrypt that string
18
+ # end
19
+ #
20
+ # def self.matches?(crypted, *tokens)
21
+ # # return true if the crypted string matches the tokens. Depending on
22
+ # # your algorithm you might decrypt the string then compare it to the
23
+ # # token, or you might encrypt the tokens and make sure it matches the
24
+ # # crypted string, its up to you.
25
+ # end
26
+ # end
27
+ module CryptoProviders
28
+ class Sha256
29
+ # = Sha256
30
+ #
31
+ # Uses the Sha256 hash algorithm to encrypt passwords.
32
+ class V2
33
+ class << self
34
+ attr_accessor :join_token
35
+
36
+ # The number of times to loop through the encryption.
37
+ def stretches
38
+ @stretches ||= 20
39
+ end
40
+ attr_writer :stretches
41
+
42
+ # Turns your raw password into a Sha256 hash.
43
+ def encrypt(*tokens)
44
+ digest = tokens.flatten.join(join_token)
45
+ stretches.times { digest = Digest::SHA256.digest(digest) }
46
+ digest.unpack1("H*")
47
+ end
48
+
49
+ # Does the crypted password match the tokens? Uses the same tokens that
50
+ # were used to encrypt.
51
+ def matches?(crypted, *tokens)
52
+ encrypt(*tokens) == crypted
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -8,6 +8,9 @@ module Authlogic
8
8
  # there are better choices. We recommend transitioning to a more secure,
9
9
  # adaptive hashing algorithm, like scrypt.
10
10
  class Sha512
11
+ # V2 hashes the digest bytes in repeated stretches instead of hex characters.
12
+ autoload :V2, File.join(__dir__, "sha512", "v2")
13
+
11
14
  class << self
12
15
  attr_accessor :join_token
13
16
 
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest/sha2"
4
+
5
+ module Authlogic
6
+ module CryptoProviders
7
+ class Sha512
8
+ # SHA-512 does not have any practical known attacks against it. However,
9
+ # there are better choices. We recommend transitioning to a more secure,
10
+ # adaptive hashing algorithm, like scrypt.
11
+ class V2
12
+ class << self
13
+ attr_accessor :join_token
14
+
15
+ # The number of times to loop through the encryption.
16
+ def stretches
17
+ @stretches ||= 20
18
+ end
19
+ attr_writer :stretches
20
+
21
+ # Turns your raw password into a Sha512 hash.
22
+ def encrypt(*tokens)
23
+ digest = tokens.flatten.join(join_token)
24
+ stretches.times do
25
+ digest = Digest::SHA512.digest(digest)
26
+ end
27
+ digest.unpack1("H*")
28
+ end
29
+
30
+ # Does the crypted password match the tokens? Uses the same tokens that
31
+ # were used to encrypt.
32
+ def matches?(crypted, *tokens)
33
+ encrypt(*tokens) == crypted
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Authlogic
4
+ # Parent class of all Authlogic errors.
5
+ class Error < StandardError
6
+ end
7
+
8
+ # :nodoc:
9
+ class InvalidCryptoProvider < Error
10
+ end
11
+
12
+ # :nodoc:
13
+ class NilCryptoProvider < InvalidCryptoProvider
14
+ def message
15
+ <<~EOS
16
+ In version 5, Authlogic used SCrypt by default. As of version 6, there
17
+ is no default. We still recommend SCrypt. If you previously relied on
18
+ this default, then, in your User model (or equivalent), please set the
19
+ following:
20
+
21
+ acts_as_authentic do |c|
22
+ c.crypto_provider = ::Authlogic::CryptoProviders::SCrypt
23
+ end
24
+
25
+ Furthermore, the authlogic gem no longer depends on the scrypt gem. In
26
+ your Gemfile, please add scrypt.
27
+
28
+ gem "scrypt", "~> 3.0"
29
+
30
+ We have made this change in Authlogic 6 so that users of other crypto
31
+ providers no longer need to install the scrypt gem.
32
+ EOS
33
+ end
34
+ end
35
+
36
+ # :nodoc:
37
+ class ModelSetupError < Error
38
+ def message
39
+ <<-EOS
40
+ You must establish a database connection and run the migrations before
41
+ using acts_as_authentic. If you need to load the User model before the
42
+ database is set up correctly, please set the following:
43
+
44
+ acts_as_authentic do |c|
45
+ c.raise_on_model_setup_error = false
46
+ end
47
+ EOS
48
+ end
49
+ end
50
+ end
@@ -8,7 +8,7 @@ module Authlogic
8
8
  # arguments, else returns +options[:default]+.
9
9
  def translate(key, options = {})
10
10
  if defined?(::I18n)
11
- ::I18n.translate key, options
11
+ ::I18n.translate key, **options
12
12
  else
13
13
  options[:default]
14
14
  end
@@ -198,7 +198,7 @@ module Authlogic
198
198
  # 2. Enable logging out on timeouts
199
199
  #
200
200
  # class UserSession < Authlogic::Session::Base
201
- # logout_on_timeout true # default if false
201
+ # logout_on_timeout true # default is false
202
202
  # end
203
203
  #
204
204
  # This will require a user to log back in if they are inactive for more than
@@ -351,7 +351,7 @@ module Authlogic
351
351
  - https://github.com/binarylogic/authlogic/pull/558
352
352
  - https://github.com/binarylogic/authlogic/pull/577
353
353
  EOS
354
- VALID_SAME_SITE_VALUES = [nil, "Lax", "Strict"].freeze
354
+ VALID_SAME_SITE_VALUES = [nil, "Lax", "Strict", "None"].freeze
355
355
 
356
356
  # Callbacks
357
357
  # =========
@@ -415,10 +415,10 @@ module Authlogic
415
415
  before_save :set_last_request_at
416
416
 
417
417
  after_save :reset_perishable_token!
418
- after_save :save_cookie
418
+ after_save :save_cookie, if: :cookie_enabled?
419
419
  after_save :update_session
420
420
 
421
- after_destroy :destroy_cookie
421
+ after_destroy :destroy_cookie, if: :cookie_enabled?
422
422
  after_destroy :update_session
423
423
 
424
424
  # `validate` callbacks, in deliberate order. For example,
@@ -438,8 +438,7 @@ module Authlogic
438
438
 
439
439
  class << self
440
440
  attr_accessor(
441
- :configured_password_methods,
442
- :configured_klass_methods
441
+ :configured_password_methods
443
442
  )
444
443
  end
445
444
  attr_accessor(
@@ -472,14 +471,9 @@ module Authlogic
472
471
  !controller.nil?
473
472
  end
474
473
 
475
- # Do you want to allow your users to log in via HTTP basic auth?
474
+ # Allow users to log in via HTTP basic authentication.
476
475
  #
477
- # I recommend keeping this enabled. The only time I feel this should be
478
- # disabled is if you are not comfortable having your users provide their
479
- # raw username and password. Whatever the reason, you can disable it
480
- # here.
481
- #
482
- # * <tt>Default:</tt> true
476
+ # * <tt>Default:</tt> false
483
477
  # * <tt>Accepts:</tt> Boolean
484
478
  def allow_http_basic_auth(value = nil)
485
479
  rw_config(:allow_http_basic_auth, value, false)
@@ -954,13 +948,27 @@ module Authlogic
954
948
  # Should the cookie be signed? If the controller adapter supports it, this is a
955
949
  # measure against cookie tampering.
956
950
  def sign_cookie(value = nil)
957
- if value && !controller.cookies.respond_to?(:signed)
951
+ if value && controller && !controller.cookies.respond_to?(:signed)
958
952
  raise "Signed cookies not supported with #{controller.class}!"
959
953
  end
960
954
  rw_config(:sign_cookie, value, false)
961
955
  end
962
956
  alias sign_cookie= sign_cookie
963
957
 
958
+ # Should the cookie be encrypted? If the controller adapter supports it, this is a
959
+ # measure to hide the contents of the cookie (e.g. persistence_token)
960
+ def encrypt_cookie(value = nil)
961
+ if value && controller && !controller.cookies.respond_to?(:encrypted)
962
+ raise "Encrypted cookies not supported with #{controller.class}!"
963
+ end
964
+ if value && sign_cookie
965
+ raise "It is recommended to use encrypt_cookie instead of sign_cookie. " \
966
+ "You may not enable both options."
967
+ end
968
+ rw_config(:encrypt_cookie, value, false)
969
+ end
970
+ alias encrypt_cookie= encrypt_cookie
971
+
964
972
  # Works exactly like cookie_key, but for sessions. See cookie_key for more info.
965
973
  #
966
974
  # * <tt>Default:</tt> cookie_key
@@ -1065,24 +1073,10 @@ module Authlogic
1065
1073
  # Constructor
1066
1074
  # ===========
1067
1075
 
1068
- # rubocop:disable Metrics/AbcSize
1069
1076
  def initialize(*args)
1070
1077
  @id = nil
1071
1078
  self.scope = self.class.scope
1072
-
1073
- # Creating an alias method for the "record" method based on the klass
1074
- # name, so that we can do:
1075
- #
1076
- # session.user
1077
- #
1078
- # instead of:
1079
- #
1080
- # session.record
1081
- unless self.class.configured_klass_methods
1082
- self.class.send(:alias_method, klass_name.demodulize.underscore.to_sym, :record)
1083
- self.class.configured_klass_methods = true
1084
- end
1085
-
1079
+ define_record_alias_method
1086
1080
  raise Activation::NotActivatedError unless self.class.activated?
1087
1081
  unless self.class.configured_password_methods
1088
1082
  configure_password_methods
@@ -1091,7 +1085,6 @@ module Authlogic
1091
1085
  instance_variable_set("@#{password_field}", nil)
1092
1086
  self.credentials = args
1093
1087
  end
1094
- # rubocop:enable Metrics/AbcSize
1095
1088
 
1096
1089
  # Public instance methods
1097
1090
  # =======================
@@ -1480,6 +1473,23 @@ module Authlogic
1480
1473
  sign_cookie == true || sign_cookie == "true" || sign_cookie == "1"
1481
1474
  end
1482
1475
 
1476
+ # If the cookie should be encrypted
1477
+ def encrypt_cookie
1478
+ return @encrypt_cookie if defined?(@encrypt_cookie)
1479
+ @encrypt_cookie = self.class.encrypt_cookie
1480
+ end
1481
+
1482
+ # Accepts a boolean as to whether the cookie should be encrypted. If true
1483
+ # the cookie will be saved in an encrypted state.
1484
+ def encrypt_cookie=(value)
1485
+ @encrypt_cookie = value
1486
+ end
1487
+
1488
+ # See encrypt_cookie
1489
+ def encrypt_cookie?
1490
+ encrypt_cookie == true || encrypt_cookie == "true" || encrypt_cookie == "1"
1491
+ end
1492
+
1483
1493
  # The scope of the current object
1484
1494
  def scope
1485
1495
  @scope ||= {}
@@ -1497,24 +1507,21 @@ module Authlogic
1497
1507
  # Determines if the information you provided for authentication is valid
1498
1508
  # or not. If there is a problem with the information provided errors will
1499
1509
  # be added to the errors object and this method will return false.
1510
+ #
1511
+ # @api public
1500
1512
  def valid?
1501
1513
  errors.clear
1502
1514
  self.attempted_record = nil
1503
-
1504
- run_callbacks(:before_validation)
1505
- run_callbacks(new_session? ? :before_validation_on_create : :before_validation_on_update)
1515
+ run_the_before_validation_callbacks
1506
1516
 
1507
1517
  # Run the `validate` callbacks, eg. `validate_by_password`.
1508
1518
  # This is when `attempted_record` is set.
1509
1519
  run_callbacks(:validate)
1510
1520
 
1511
1521
  ensure_authentication_attempted
1512
-
1513
1522
  if errors.empty?
1514
- run_callbacks(new_session? ? :after_validation_on_create : :after_validation_on_update)
1515
- run_callbacks(:after_validation)
1523
+ run_the_after_validation_callbacks
1516
1524
  end
1517
-
1518
1525
  save_record(attempted_record)
1519
1526
  errors.empty?
1520
1527
  end
@@ -1616,14 +1623,22 @@ module Authlogic
1616
1623
  # @api private
1617
1624
  # @return ::Authlogic::CookieCredentials or if no cookie is found, nil
1618
1625
  def cookie_credentials
1626
+ return unless cookie_enabled?
1627
+
1619
1628
  cookie_value = cookie_jar[cookie_key]
1620
1629
  unless cookie_value.nil?
1621
1630
  ::Authlogic::CookieCredentials.parse(cookie_value)
1622
1631
  end
1623
1632
  end
1624
1633
 
1634
+ def cookie_enabled?
1635
+ !controller.cookies.nil?
1636
+ end
1637
+
1625
1638
  def cookie_jar
1626
- if self.class.sign_cookie
1639
+ if self.class.encrypt_cookie
1640
+ controller.cookies.encrypted
1641
+ elsif self.class.sign_cookie
1627
1642
  controller.cookies.signed
1628
1643
  else
1629
1644
  controller.cookies
@@ -1641,15 +1656,23 @@ module Authlogic
1641
1656
  self.class.send(:attr_reader, login_field) unless respond_to?(login_field)
1642
1657
  end
1643
1658
 
1659
+ # @api private
1644
1660
  def define_password_field_methods
1645
1661
  return unless password_field
1646
- self.class.send(:attr_writer, password_field) unless respond_to?("#{password_field}=")
1647
- self.class.send(:define_method, password_field) {} unless respond_to?(password_field)
1662
+ define_password_field_writer_method
1663
+ define_password_field_reader_methods
1664
+ end
1648
1665
 
1649
- # The password should not be accessible publicly. This way forms
1650
- # using form_for don't fill the password with the attempted
1651
- # password. To prevent this we just create this method that is
1652
- # private.
1666
+ # The password should not be accessible publicly. This way forms using
1667
+ # form_for don't fill the password with the attempted password. To prevent
1668
+ # this we just create this method that is private.
1669
+ #
1670
+ # @api private
1671
+ def define_password_field_reader_methods
1672
+ unless respond_to?(password_field)
1673
+ # Deliberate no-op method, see rationale above.
1674
+ self.class.send(:define_method, password_field) {}
1675
+ end
1653
1676
  self.class.class_eval(
1654
1677
  <<-EOS, __FILE__, __LINE__ + 1
1655
1678
  private
@@ -1660,6 +1683,28 @@ module Authlogic
1660
1683
  )
1661
1684
  end
1662
1685
 
1686
+ def define_password_field_writer_method
1687
+ unless respond_to?("#{password_field}=")
1688
+ self.class.send(:attr_writer, password_field)
1689
+ end
1690
+ end
1691
+
1692
+ # Creating an alias method for the "record" method based on the klass
1693
+ # name, so that we can do:
1694
+ #
1695
+ # session.user
1696
+ #
1697
+ # instead of:
1698
+ #
1699
+ # session.record
1700
+ #
1701
+ # @api private
1702
+ def define_record_alias_method
1703
+ noun = klass_name.demodulize.underscore.to_sym
1704
+ return if respond_to?(noun)
1705
+ self.class.send(:alias_method, noun, :record)
1706
+ end
1707
+
1663
1708
  def destroy_cookie
1664
1709
  controller.cookies.delete cookie_key, domain: controller.cookie_domain
1665
1710
  end
@@ -1705,13 +1750,8 @@ module Authlogic
1705
1750
 
1706
1751
  # @api private
1707
1752
  def generate_cookie_for_saving
1708
- creds = ::Authlogic::CookieCredentials.new(
1709
- record.persistence_token,
1710
- record.send(record.class.primary_key),
1711
- remember_me? ? remember_me_until : nil
1712
- )
1713
1753
  {
1714
- value: creds.to_s,
1754
+ value: generate_cookie_value.to_s,
1715
1755
  expires: remember_me_until,
1716
1756
  secure: secure,
1717
1757
  httponly: httponly,
@@ -1720,6 +1760,14 @@ module Authlogic
1720
1760
  }
1721
1761
  end
1722
1762
 
1763
+ def generate_cookie_value
1764
+ ::Authlogic::CookieCredentials.new(
1765
+ record.persistence_token,
1766
+ record.send(record.class.primary_key),
1767
+ remember_me? ? remember_me_until : nil
1768
+ )
1769
+ end
1770
+
1723
1771
  # Returns a Proc to be executed by
1724
1772
  # `ActionController::HttpAuthentication::Basic` when credentials are
1725
1773
  # present in the HTTP request.
@@ -1898,6 +1946,18 @@ module Authlogic
1898
1946
  attempted_record.failed_login_count = 0
1899
1947
  end
1900
1948
 
1949
+ # @api private
1950
+ def run_the_after_validation_callbacks
1951
+ run_callbacks(new_session? ? :after_validation_on_create : :after_validation_on_update)
1952
+ run_callbacks(:after_validation)
1953
+ end
1954
+
1955
+ # @api private
1956
+ def run_the_before_validation_callbacks
1957
+ run_callbacks(:before_validation)
1958
+ run_callbacks(new_session? ? :before_validation_on_create : :before_validation_on_update)
1959
+ end
1960
+
1901
1961
  # `args[0]` is the name of a model method, like
1902
1962
  # `find_by_single_access_token` or `find_by_smart_case_login_field`.
1903
1963
  def search_for_record(*args)
@@ -1935,11 +1995,7 @@ module Authlogic
1935
1995
  end
1936
1996
 
1937
1997
  def save_cookie
1938
- if sign_cookie?
1939
- controller.cookies.signed[cookie_key] = generate_cookie_for_saving
1940
- else
1941
- controller.cookies[cookie_key] = generate_cookie_for_saving
1942
- end
1998
+ cookie_jar[cookie_key] = generate_cookie_for_saving
1943
1999
  end
1944
2000
 
1945
2001
  # @api private
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require File.dirname(__FILE__) + "/test_case/rails_request_adapter"
4
+ require File.dirname(__FILE__) + "/test_case/mock_api_controller"
4
5
  require File.dirname(__FILE__) + "/test_case/mock_cookie_jar"
5
6
  require File.dirname(__FILE__) + "/test_case/mock_controller"
6
7
  require File.dirname(__FILE__) + "/test_case/mock_logger"
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Authlogic
4
+ module TestCase
5
+ # Basically acts like an API controller but doesn't do anything.
6
+ # Authlogic can interact with this, do it's thing and then you can look at
7
+ # the controller object to see if anything changed.
8
+ class MockAPIController < ControllerAdapters::AbstractAdapter
9
+ attr_writer :request_content_type
10
+
11
+ def initialize
12
+ end
13
+
14
+ # Expected API controller has no cookies method.
15
+ undef :cookies
16
+
17
+ def cookie_domain
18
+ nil
19
+ end
20
+
21
+ def logger
22
+ @logger ||= MockLogger.new
23
+ end
24
+
25
+ def params
26
+ @params ||= {}
27
+ end
28
+
29
+ def request
30
+ @request ||= MockRequest.new(self)
31
+ end
32
+
33
+ def request_content_type
34
+ @request_content_type ||= "text/html"
35
+ end
36
+
37
+ def session
38
+ @session ||= {}
39
+ end
40
+
41
+ # If method is defined, it causes below behavior...
42
+ # controller = Authlogic::ControllerAdapters::RailsAdapter.new(
43
+ # Authlogic::TestCase::MockAPIController.new
44
+ # )
45
+ # controller.responds_to_single_access_allowed? #=> true
46
+ # controller.single_access_allowed?
47
+ # #=> NoMethodError: undefined method `single_access_allowed?' for nil:NilClass
48
+ #
49
+ undef :single_access_allowed?
50
+ end
51
+ end
52
+ end
@@ -39,7 +39,7 @@ module Authlogic
39
39
  end
40
40
 
41
41
  def request
42
- @request ||= MockRequest.new(controller)
42
+ @request ||= MockRequest.new(self)
43
43
  end
44
44
 
45
45
  def request_content_type
@@ -23,6 +23,10 @@ module Authlogic
23
23
  def signed
24
24
  @signed ||= MockSignedCookieJar.new(self)
25
25
  end
26
+
27
+ def encrypted
28
+ @encrypted ||= MockEncryptedCookieJar.new(self)
29
+ end
26
30
  end
27
31
 
28
32
  # A mock of `ActionDispatch::Cookies::SignedKeyRotatingCookieJar`
@@ -35,6 +39,7 @@ module Authlogic
35
39
 
36
40
  def initialize(parent_jar)
37
41
  @parent_jar = parent_jar
42
+ parent_jar.each { |k, v| self[k] = v }
38
43
  end
39
44
 
40
45
  def [](val)
@@ -51,5 +56,37 @@ module Authlogic
51
56
  @parent_jar[key] = options
52
57
  end
53
58
  end
59
+
60
+ # Which ActionDispatch class is this a mock of?
61
+ # TODO: Document as with other mocks above.
62
+ class MockEncryptedCookieJar < MockCookieJar
63
+ attr_reader :parent_jar # helper for testing
64
+
65
+ def initialize(parent_jar)
66
+ @parent_jar = parent_jar
67
+ parent_jar.each { |k, v| self[k] = v }
68
+ end
69
+
70
+ def [](val)
71
+ encrypted_message = @parent_jar[val]
72
+ if encrypted_message
73
+ self.class.decrypt(encrypted_message)
74
+ end
75
+ end
76
+
77
+ def []=(key, options)
78
+ options[:value] = self.class.encrypt(options[:value])
79
+ @parent_jar[key] = options
80
+ end
81
+
82
+ # simple caesar cipher for testing
83
+ def self.encrypt(str)
84
+ str.unpack("U*").map(&:succ).pack("U*")
85
+ end
86
+
87
+ def self.decrypt(str)
88
+ str.unpack("U*").map(&:pred).pack("U*")
89
+ end
90
+ end
54
91
  end
55
92
  end
@@ -9,6 +9,10 @@ module Authlogic
9
9
  self.controller = controller
10
10
  end
11
11
 
12
+ def format
13
+ controller.request_content_type if controller.respond_to? :request_content_type
14
+ end
15
+
12
16
  def ip
13
17
  controller&.respond_to?(:env) &&
14
18
  controller.env.is_a?(Hash) &&
@@ -17,6 +17,6 @@ module Authlogic
17
17
  #
18
18
  # @api public
19
19
  def self.gem_version
20
- ::Gem::Version.new("5.0.4")
20
+ ::Gem::Version.new("6.2.0")
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: 5.0.4
4
+ version: 6.2.0
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: 2019-09-11 00:00:00.000000000 Z
13
+ date: 2020-09-10 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activemodel
@@ -86,26 +86,6 @@ dependencies:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
88
  version: '1.0'
89
- - !ruby/object:Gem::Dependency
90
- name: scrypt
91
- requirement: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - ">="
94
- - !ruby/object:Gem::Version
95
- version: '1.2'
96
- - - "<"
97
- - !ruby/object:Gem::Version
98
- version: '4.0'
99
- type: :runtime
100
- prerelease: false
101
- version_requirements: !ruby/object:Gem::Requirement
102
- requirements:
103
- - - ">="
104
- - !ruby/object:Gem::Version
105
- version: '1.2'
106
- - - "<"
107
- - !ruby/object:Gem::Version
108
- version: '4.0'
109
89
  - !ruby/object:Gem::Dependency
110
90
  name: bcrypt
111
91
  requirement: !ruby/object:Gem::Requirement
@@ -196,14 +176,14 @@ dependencies:
196
176
  requirements:
197
177
  - - "~>"
198
178
  - !ruby/object:Gem::Version
199
- version: 0.67.2
179
+ version: 0.80.1
200
180
  type: :development
201
181
  prerelease: false
202
182
  version_requirements: !ruby/object:Gem::Requirement
203
183
  requirements:
204
184
  - - "~>"
205
185
  - !ruby/object:Gem::Version
206
- version: 0.67.2
186
+ version: 0.80.1
207
187
  - !ruby/object:Gem::Dependency
208
188
  name: rubocop-performance
209
189
  requirement: !ruby/object:Gem::Requirement
@@ -218,6 +198,26 @@ dependencies:
218
198
  - - "~>"
219
199
  - !ruby/object:Gem::Version
220
200
  version: '1.1'
201
+ - !ruby/object:Gem::Dependency
202
+ name: scrypt
203
+ requirement: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ version: '1.2'
208
+ - - "<"
209
+ - !ruby/object:Gem::Version
210
+ version: '4.0'
211
+ type: :development
212
+ prerelease: false
213
+ version_requirements: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ version: '1.2'
218
+ - - "<"
219
+ - !ruby/object:Gem::Version
220
+ version: '4.0'
221
221
  - !ruby/object:Gem::Dependency
222
222
  name: simplecov
223
223
  requirement: !ruby/object:Gem::Requirement
@@ -252,14 +252,14 @@ dependencies:
252
252
  requirements:
253
253
  - - "~>"
254
254
  - !ruby/object:Gem::Version
255
- version: 1.3.13
255
+ version: 1.4.0
256
256
  type: :development
257
257
  prerelease: false
258
258
  version_requirements: !ruby/object:Gem::Requirement
259
259
  requirements:
260
260
  - - "~>"
261
261
  - !ruby/object:Gem::Version
262
- version: 1.3.13
262
+ version: 1.4.0
263
263
  - !ruby/object:Gem::Dependency
264
264
  name: timecop
265
265
  requirement: !ruby/object:Gem::Requirement
@@ -305,16 +305,22 @@ files:
305
305
  - lib/authlogic/crypto_providers.rb
306
306
  - lib/authlogic/crypto_providers/bcrypt.rb
307
307
  - lib/authlogic/crypto_providers/md5.rb
308
+ - lib/authlogic/crypto_providers/md5/v2.rb
308
309
  - lib/authlogic/crypto_providers/scrypt.rb
309
310
  - lib/authlogic/crypto_providers/sha1.rb
311
+ - lib/authlogic/crypto_providers/sha1/v2.rb
310
312
  - lib/authlogic/crypto_providers/sha256.rb
313
+ - lib/authlogic/crypto_providers/sha256/v2.rb
311
314
  - lib/authlogic/crypto_providers/sha512.rb
315
+ - lib/authlogic/crypto_providers/sha512/v2.rb
316
+ - lib/authlogic/errors.rb
312
317
  - lib/authlogic/i18n.rb
313
318
  - lib/authlogic/i18n/translator.rb
314
319
  - lib/authlogic/random.rb
315
320
  - lib/authlogic/session/base.rb
316
321
  - lib/authlogic/session/magic_column/assigns_last_request_at.rb
317
322
  - lib/authlogic/test_case.rb
323
+ - lib/authlogic/test_case/mock_api_controller.rb
318
324
  - lib/authlogic/test_case/mock_controller.rb
319
325
  - lib/authlogic/test_case/mock_cookie_jar.rb
320
326
  - lib/authlogic/test_case/mock_logger.rb
@@ -333,7 +339,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
333
339
  requirements:
334
340
  - - ">="
335
341
  - !ruby/object:Gem::Version
336
- version: 2.3.0
342
+ version: 2.4.0
337
343
  required_rubygems_version: !ruby/object:Gem::Requirement
338
344
  requirements:
339
345
  - - ">="