authlogic 5.0.4 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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
  - - ">="