authlogic 6.0.0 → 6.1.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: 755961398552a88cf3761088e4521e71e243249b3a131632e281679d723c82fe
4
- data.tar.gz: 2b739ad482ecdaad8218065a4c03882e730c86825bc7140691e451fc26032815
3
+ metadata.gz: 88e14eb91ceaf33fca0867ad9816d8ee719215bdb9f5ff26c2c4754d84c82dd8
4
+ data.tar.gz: 1dfbc1800a0fd0766bde87dfaa4a351b4b377e4240ad81aaa7d163c547d4d2a4
5
5
  SHA512:
6
- metadata.gz: 52e998e1210ac287f2bc91d01d2afba9416f5b0eee54cff13d89c6c9affdd2ff2a88ac1a80e78ce100c2a43dcbcf777a5f22dd3d72ff3571bc69c5242a89d97c
7
- data.tar.gz: 6152232cf873d2c9be4fa24584b3d8bf8013f95ae58a8117f4494c3a6632df814e36b85d02c67efc0e2b73849a43333c0b9bcf9cb1d379f10587147aa69f808e
6
+ metadata.gz: a9e01562988e0b0a1660b7fa51009d71a76c01bca17711d278fd9d5e7271c3ef0e165da9622a7049b15f45d994d108e51c6fff0689ad4afe063df62525ddc572
7
+ data.tar.gz: b0930fa9bc9d370cb71b1e502ece7885319429aff8f49039a8150621f9eb79bd9301c6e028933e156eb28f50a571a0586c23c918280a23f681bc7f45e440aa4f
@@ -102,7 +102,7 @@ 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
@@ -20,7 +20,7 @@ module Authlogic
20
20
  def encrypt(*tokens)
21
21
  digest = tokens.flatten.join(join_token)
22
22
  stretches.times { digest = Digest::MD5.digest(digest) }
23
- digest.unpack("H*")[0]
23
+ digest.unpack1("H*")
24
24
  end
25
25
 
26
26
  # Does the crypted password match the tokens? Uses the same tokens that
@@ -26,7 +26,7 @@ module Authlogic
26
26
  stretches.times do
27
27
  digest = Digest::SHA1.digest([digest, *tokens].join(join_token))
28
28
  end
29
- digest.unpack("H*")[0]
29
+ digest.unpack1("H*")
30
30
  end
31
31
 
32
32
  # Does the crypted password match the tokens? Uses the same tokens that
@@ -43,7 +43,7 @@ module Authlogic
43
43
  def encrypt(*tokens)
44
44
  digest = tokens.flatten.join(join_token)
45
45
  stretches.times { digest = Digest::SHA256.digest(digest) }
46
- digest.unpack("H*")[0]
46
+ digest.unpack1("H*")
47
47
  end
48
48
 
49
49
  # Does the crypted password match the tokens? Uses the same tokens that
@@ -24,7 +24,7 @@ module Authlogic
24
24
  stretches.times do
25
25
  digest = Digest::SHA512.digest(digest)
26
26
  end
27
- digest.unpack("H*")[0]
27
+ digest.unpack1("H*")
28
28
  end
29
29
 
30
30
  # Does the crypted password match the tokens? Uses the same tokens that
@@ -18,7 +18,7 @@ module Authlogic
18
18
  this default, then, in your User model (or equivalent), please set the
19
19
  following:
20
20
 
21
- acts_as_authentic do |config|
21
+ acts_as_authentic do |c|
22
22
  c.crypto_provider = ::Authlogic::CryptoProviders::SCrypt
23
23
  end
24
24
 
@@ -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(
@@ -956,6 +955,20 @@ module Authlogic
956
955
  end
957
956
  alias sign_cookie= sign_cookie
958
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.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_method :encrypt_cookie=, :encrypt_cookie
971
+
959
972
  # Works exactly like cookie_key, but for sessions. See cookie_key for more info.
960
973
  #
961
974
  # * <tt>Default:</tt> cookie_key
@@ -1060,24 +1073,10 @@ module Authlogic
1060
1073
  # Constructor
1061
1074
  # ===========
1062
1075
 
1063
- # rubocop:disable Metrics/AbcSize
1064
1076
  def initialize(*args)
1065
1077
  @id = nil
1066
1078
  self.scope = self.class.scope
1067
-
1068
- # Creating an alias method for the "record" method based on the klass
1069
- # name, so that we can do:
1070
- #
1071
- # session.user
1072
- #
1073
- # instead of:
1074
- #
1075
- # session.record
1076
- unless self.class.configured_klass_methods
1077
- self.class.send(:alias_method, klass_name.demodulize.underscore.to_sym, :record)
1078
- self.class.configured_klass_methods = true
1079
- end
1080
-
1079
+ define_record_alias_method
1081
1080
  raise Activation::NotActivatedError unless self.class.activated?
1082
1081
  unless self.class.configured_password_methods
1083
1082
  configure_password_methods
@@ -1086,7 +1085,6 @@ module Authlogic
1086
1085
  instance_variable_set("@#{password_field}", nil)
1087
1086
  self.credentials = args
1088
1087
  end
1089
- # rubocop:enable Metrics/AbcSize
1090
1088
 
1091
1089
  # Public instance methods
1092
1090
  # =======================
@@ -1475,6 +1473,23 @@ module Authlogic
1475
1473
  sign_cookie == true || sign_cookie == "true" || sign_cookie == "1"
1476
1474
  end
1477
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
+
1478
1493
  # The scope of the current object
1479
1494
  def scope
1480
1495
  @scope ||= {}
@@ -1492,24 +1507,21 @@ module Authlogic
1492
1507
  # Determines if the information you provided for authentication is valid
1493
1508
  # or not. If there is a problem with the information provided errors will
1494
1509
  # be added to the errors object and this method will return false.
1510
+ #
1511
+ # @api public
1495
1512
  def valid?
1496
1513
  errors.clear
1497
1514
  self.attempted_record = nil
1498
-
1499
- run_callbacks(:before_validation)
1500
- run_callbacks(new_session? ? :before_validation_on_create : :before_validation_on_update)
1515
+ run_the_before_validation_callbacks
1501
1516
 
1502
1517
  # Run the `validate` callbacks, eg. `validate_by_password`.
1503
1518
  # This is when `attempted_record` is set.
1504
1519
  run_callbacks(:validate)
1505
1520
 
1506
1521
  ensure_authentication_attempted
1507
-
1508
1522
  if errors.empty?
1509
- run_callbacks(new_session? ? :after_validation_on_create : :after_validation_on_update)
1510
- run_callbacks(:after_validation)
1523
+ run_the_after_validation_callbacks
1511
1524
  end
1512
-
1513
1525
  save_record(attempted_record)
1514
1526
  errors.empty?
1515
1527
  end
@@ -1618,7 +1630,9 @@ module Authlogic
1618
1630
  end
1619
1631
 
1620
1632
  def cookie_jar
1621
- if self.class.sign_cookie
1633
+ if self.class.encrypt_cookie
1634
+ controller.cookies.encrypted
1635
+ elsif self.class.sign_cookie
1622
1636
  controller.cookies.signed
1623
1637
  else
1624
1638
  controller.cookies
@@ -1636,15 +1650,23 @@ module Authlogic
1636
1650
  self.class.send(:attr_reader, login_field) unless respond_to?(login_field)
1637
1651
  end
1638
1652
 
1653
+ # @api private
1639
1654
  def define_password_field_methods
1640
1655
  return unless password_field
1641
- self.class.send(:attr_writer, password_field) unless respond_to?("#{password_field}=")
1642
- self.class.send(:define_method, password_field) {} unless respond_to?(password_field)
1656
+ define_password_field_writer_method
1657
+ define_password_field_reader_methods
1658
+ end
1643
1659
 
1644
- # The password should not be accessible publicly. This way forms
1645
- # using form_for don't fill the password with the attempted
1646
- # password. To prevent this we just create this method that is
1647
- # private.
1660
+ # The password should not be accessible publicly. This way forms using
1661
+ # form_for don't fill the password with the attempted password. To prevent
1662
+ # this we just create this method that is private.
1663
+ #
1664
+ # @api private
1665
+ def define_password_field_reader_methods
1666
+ unless respond_to?(password_field)
1667
+ # Deliberate no-op method, see rationale above.
1668
+ self.class.send(:define_method, password_field) {}
1669
+ end
1648
1670
  self.class.class_eval(
1649
1671
  <<-EOS, __FILE__, __LINE__ + 1
1650
1672
  private
@@ -1655,6 +1677,28 @@ module Authlogic
1655
1677
  )
1656
1678
  end
1657
1679
 
1680
+ def define_password_field_writer_method
1681
+ unless respond_to?("#{password_field}=")
1682
+ self.class.send(:attr_writer, password_field)
1683
+ end
1684
+ end
1685
+
1686
+ # Creating an alias method for the "record" method based on the klass
1687
+ # name, so that we can do:
1688
+ #
1689
+ # session.user
1690
+ #
1691
+ # instead of:
1692
+ #
1693
+ # session.record
1694
+ #
1695
+ # @api private
1696
+ def define_record_alias_method
1697
+ noun = klass_name.demodulize.underscore.to_sym
1698
+ return if respond_to?(noun)
1699
+ self.class.send(:alias_method, noun, :record)
1700
+ end
1701
+
1658
1702
  def destroy_cookie
1659
1703
  controller.cookies.delete cookie_key, domain: controller.cookie_domain
1660
1704
  end
@@ -1700,13 +1744,8 @@ module Authlogic
1700
1744
 
1701
1745
  # @api private
1702
1746
  def generate_cookie_for_saving
1703
- creds = ::Authlogic::CookieCredentials.new(
1704
- record.persistence_token,
1705
- record.send(record.class.primary_key),
1706
- remember_me? ? remember_me_until : nil
1707
- )
1708
1747
  {
1709
- value: creds.to_s,
1748
+ value: generate_cookie_value.to_s,
1710
1749
  expires: remember_me_until,
1711
1750
  secure: secure,
1712
1751
  httponly: httponly,
@@ -1715,6 +1754,14 @@ module Authlogic
1715
1754
  }
1716
1755
  end
1717
1756
 
1757
+ def generate_cookie_value
1758
+ ::Authlogic::CookieCredentials.new(
1759
+ record.persistence_token,
1760
+ record.send(record.class.primary_key),
1761
+ remember_me? ? remember_me_until : nil
1762
+ )
1763
+ end
1764
+
1718
1765
  # Returns a Proc to be executed by
1719
1766
  # `ActionController::HttpAuthentication::Basic` when credentials are
1720
1767
  # present in the HTTP request.
@@ -1893,6 +1940,18 @@ module Authlogic
1893
1940
  attempted_record.failed_login_count = 0
1894
1941
  end
1895
1942
 
1943
+ # @api private
1944
+ def run_the_after_validation_callbacks
1945
+ run_callbacks(new_session? ? :after_validation_on_create : :after_validation_on_update)
1946
+ run_callbacks(:after_validation)
1947
+ end
1948
+
1949
+ # @api private
1950
+ def run_the_before_validation_callbacks
1951
+ run_callbacks(:before_validation)
1952
+ run_callbacks(new_session? ? :before_validation_on_create : :before_validation_on_update)
1953
+ end
1954
+
1896
1955
  # `args[0]` is the name of a model method, like
1897
1956
  # `find_by_single_access_token` or `find_by_smart_case_login_field`.
1898
1957
  def search_for_record(*args)
@@ -1930,11 +1989,7 @@ module Authlogic
1930
1989
  end
1931
1990
 
1932
1991
  def save_cookie
1933
- if sign_cookie?
1934
- controller.cookies.signed[cookie_key] = generate_cookie_for_saving
1935
- else
1936
- controller.cookies[cookie_key] = generate_cookie_for_saving
1937
- end
1992
+ cookie_jar[cookie_key] = generate_cookie_for_saving
1938
1993
  end
1939
1994
 
1940
1995
  # @api private
@@ -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,35 @@ module Authlogic
51
56
  @parent_jar[key] = options
52
57
  end
53
58
  end
59
+
60
+ class MockEncryptedCookieJar < MockCookieJar
61
+ attr_reader :parent_jar # helper for testing
62
+
63
+ def initialize(parent_jar)
64
+ @parent_jar = parent_jar
65
+ parent_jar.each { |k, v| self[k] = v }
66
+ end
67
+
68
+ def [](val)
69
+ encrypted_message = @parent_jar[val]
70
+ if encrypted_message
71
+ self.class.decrypt(encrypted_message)
72
+ end
73
+ end
74
+
75
+ def []=(key, options)
76
+ options[:value] = self.class.encrypt(options[:value])
77
+ @parent_jar[key] = options
78
+ end
79
+
80
+ # simple caesar cipher for testing
81
+ def self.encrypt(str)
82
+ str.unpack("U*").map(&:succ).pack("U*")
83
+ end
84
+
85
+ def self.decrypt(str)
86
+ str.unpack("U*").map(&:pred).pack("U*")
87
+ end
88
+ end
54
89
  end
55
90
  end
@@ -17,6 +17,6 @@ module Authlogic
17
17
  #
18
18
  # @api public
19
19
  def self.gem_version
20
- ::Gem::Version.new("6.0.0")
20
+ ::Gem::Version.new("6.1.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: 6.0.0
4
+ version: 6.1.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: 2020-03-24 00:00:00.000000000 Z
13
+ date: 2020-05-08 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activemodel
@@ -176,14 +176,14 @@ dependencies:
176
176
  requirements:
177
177
  - - "~>"
178
178
  - !ruby/object:Gem::Version
179
- version: 0.67.2
179
+ version: 0.80.1
180
180
  type: :development
181
181
  prerelease: false
182
182
  version_requirements: !ruby/object:Gem::Requirement
183
183
  requirements:
184
184
  - - "~>"
185
185
  - !ruby/object:Gem::Version
186
- version: 0.67.2
186
+ version: 0.80.1
187
187
  - !ruby/object:Gem::Dependency
188
188
  name: rubocop-performance
189
189
  requirement: !ruby/object:Gem::Requirement