devise-two-factor 6.0.0 → 6.4.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: 131bab7308f2b9d46a41b5e11b85411e0cd097e97f16c82356eadb1cf87d5cc3
4
- data.tar.gz: b117115cfbb9ffe4f6dcec8127de0e0d51ca5fa835407a82cad8afc17f923f5f
3
+ metadata.gz: 48a006cc0a0b85e48b88b8c8521231b65691c513d1c7bf0c55fac2b24fe73d07
4
+ data.tar.gz: e3d988b573e1720e5f1532a8fb161e3399cfd601273cf971957b33a6ad82a489
5
5
  SHA512:
6
- metadata.gz: 187bd4ed05b0ad83da40cbe208c4bbe2ce91581d95f437cdaa27364ddb0be3696a2a03aad62e8a02f07adaeca24778202710f20f79422156b4aef63d13a03721
7
- data.tar.gz: 5635dccf010dd259404e9e03092eb9e107896b31f178d1ae3760046aa794fe449eb3bd2929bfb08c29df725376d8abc3dbb3f80e2dfb63405172c6130c24c687
6
+ metadata.gz: ee0fd9a8b7042adf26790545788508991ad06bab0191e2d75b3f7119f283f10fa922fa5a51353199539f8a220cb966706820538c22252e5f98a9f976077a8137
7
+ data.tar.gz: ad571291a241e5b7080f62c9ad410f91a0b1f140ff3d978370136212e659670cc8177328c1c306d6957ef925b7789dada8449e2aaa9e4f4096a78f7a97d7f777
@@ -12,14 +12,14 @@ jobs:
12
12
  fail-fast: false
13
13
  matrix:
14
14
  # Due to https://github.com/actions/runner/issues/849, we should quote versions
15
- ruby: ['3.1', '3.2', '3.3', 'truffleruby-head']
16
- rails: ['7.0', '7.1']
15
+ ruby: ['3.2', '3.3', '3.4', '4.0', 'truffleruby-head']
16
+ rails: ['7.2', '8.0', '8.1']
17
17
 
18
18
  name: Ruby ${{ matrix.ruby }}, Rails ${{ matrix.rails }}
19
19
  env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
20
20
  BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/rails_${{ matrix.rails }}.gemfile
21
21
  steps:
22
- - uses: actions/checkout@v4
22
+ - uses: actions/checkout@v6
23
23
  - name: Set up Ruby
24
24
  uses: ruby/setup-ruby@v1
25
25
  with:
@@ -0,0 +1,28 @@
1
+ name: Push Gem
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - v*
7
+
8
+ jobs:
9
+ deployment:
10
+ name: Push gem to RubyGems.org
11
+ environment: RubyGems
12
+ runs-on: ubuntu-latest
13
+
14
+ permissions:
15
+ id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
16
+ contents: write # IMPORTANT: this permission is required for `rake release` to push the release tag
17
+
18
+ steps:
19
+ # Set up
20
+ - uses: actions/checkout@v6
21
+ - name: Set up Ruby
22
+ uses: ruby/setup-ruby@v1
23
+ with:
24
+ bundler-cache: true
25
+ ruby-version: ruby
26
+
27
+ # Release
28
+ - uses: rubygems/release-gem@v1
data/Appraisals CHANGED
@@ -1,39 +1,14 @@
1
- appraise "rails-4.1" do
2
- gem 'railties', '~> 4.1'
3
- gem 'activesupport', '~> 4.1'
1
+ appraise "rails-7.2" do
2
+ gem 'railties', '~> 7.2.0'
3
+ gem 'activesupport', '~> 7.2.0'
4
4
  end
5
5
 
6
- appraise "rails-4.2" do
7
- gem 'railties', '~> 4.2'
8
- gem 'activesupport', '~> 4.2'
6
+ appraise "rails-8.0" do
7
+ gem 'railties', '~> 8.0.0'
8
+ gem 'activesupport', '~> 8.0.0'
9
9
  end
10
10
 
11
- appraise "rails-5.0" do
12
- gem 'railties', '~> 5.0'
13
- gem 'activesupport', '~> 5.0'
14
- end
15
-
16
- appraise "rails-5.1" do
17
- gem 'railties', '~> 5.1'
18
- gem 'activesupport', '~> 5.1'
19
- end
20
-
21
- appraise "rails-5.2" do
22
- gem 'railties', '~> 5.2'
23
- gem 'activesupport', '~> 5.2'
24
- end
25
-
26
- appraise "rails-6.0" do
27
- gem 'railties', '~> 6.0'
28
- gem 'activesupport', '~> 6.0'
29
- end
30
-
31
- appraise "rails-6.1" do
32
- gem 'railties', '~> 6.1'
33
- gem 'activesupport', '~> 6.1'
34
- end
35
-
36
- appraise "rails-7.0" do
37
- gem 'railties', '~> 7.0'
38
- gem 'activesupport', '~> 7.0'
11
+ appraise "rails-8.1" do
12
+ gem 'railties', '8.1.0'
13
+ gem 'activesupport', '8.1.0'
39
14
  end
data/CHANGELOG.md CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 6.4.0
6
+
7
+ - Remove upper limit on Devise version (allows v5) from gemspec
8
+
9
+ ## 6.3.1
10
+
11
+ - Fix DB-adapter-specific integration issue with backupable shared example
12
+ - Drop support for EOL Rails versions 7.0 and 7.1
13
+
14
+ ## 6.3.0
15
+
16
+ - Fixed timing to be consistent when Devise paranoid mode is active.
17
+
18
+ ## 6.2.0
19
+
20
+ - Rails 8.1 support
21
+
22
+ ## 6.1.0
23
+
24
+ - Rails 8 support
25
+
26
+ ## 6.0.0
27
+
28
+ **Breaking Changes**
29
+ - `otp_secret_length` and `otp_backup_code_length` options have changed to be the number of random bytes that are generated. See [UPGRADING.md](UPGRADING.md).
30
+ - `consume_otp!` and `invalidate_otp_backup_code!` now call `save!` instead of `save`. See [UPGRADING.md](UPGRADING.md).
31
+
5
32
  ## 5.1.0
6
33
 
7
34
  - Remove faker dev dependency
@@ -15,20 +42,24 @@
15
42
  - Rails 7 is now required.
16
43
 
17
44
  ## 4.1.0 / 4.1.1
45
+
18
46
  - Add support for attr_encrypted v4
19
47
 
20
48
  ## 4.0.2
49
+
21
50
  - Add Rails 7.0 support
22
51
  - Renew signing certificate
23
52
  - Use `after` option of TOTP#verify for additional timestamp verification
24
53
 
25
54
  ## 4.0.1
55
+
26
56
  - Convert CI from Travis CI to Github Actions ([#198](https://github.com/tinfoil/devise-two-factor/pull/198))
27
57
  - Fix ActiveSupport::Testing::TimeHelpers require in shared examples ([#191](https://github.com/tinfoil/devise-two-factor/pull/191))
28
58
  - Accept whitespace in provided codes ([#195](https://github.com/tinfoil/devise-two-factor/pull/195))
29
59
  - Add Truffleruby head to CI ([#200](https://github.com/tinfoil/devise-two-factor/pull/200))
30
60
 
31
61
  ## 4.0.0
62
+
32
63
  - [breaking] Drop support for Ruby <= 2.2
33
64
  - Update ROTP
34
65
  - Add Rails 6.1 support
@@ -37,20 +68,25 @@
37
68
  - Bugfixes & cleanup
38
69
 
39
70
  ## 3.1.0
71
+
40
72
  - Add Rails 6.0 support
41
73
  - New gem signing certificate
42
74
  - Fix paranoid-mode being ignored
43
75
 
44
76
  ## 3.0.3
77
+
45
78
  - Add Rails 5.2 support
46
79
 
47
80
  ## 3.0.2
81
+
48
82
  - Add Rails 5.1 support
49
83
 
50
84
  ## 3.0.1
85
+
51
86
  - Qualify call to rspec shared_examples
52
87
 
53
88
  ## 3.0.0
89
+
54
90
  See `UPGRADING.md` for specific help with breaking changes from 2.x to 3.0.0.
55
91
 
56
92
  - Adds support for Devise 4.
@@ -58,33 +94,41 @@ See `UPGRADING.md` for specific help with breaking changes from 2.x to 3.0.0.
58
94
  - Blocks the use of attr_encrypted 2.x. There was a significant vulnerability in the encryption implementation in attr_encrypted 2.x, and that version of the gem should not be used.
59
95
 
60
96
  ## 2.2.0
97
+
61
98
  - Use 192 bits, not 1024, as a secret key length. RFC 4226 recommends a minimum length of 128 bits and a recommended length of 160 bits. Google Authenticator doesn't accept 160 bit keys.
62
99
 
63
100
  ## 2.1.0
101
+
64
102
  - Return false if OTP value is nil, instead of an ROTP exception.
65
103
 
66
104
  ## 2.0.1
105
+
67
106
  No user-facing changes.
68
107
 
69
108
  ## 2.0.0
109
+
70
110
  See `UPGRADING.md` for specific help with breaking changes from 1.x to 2.0.0.
71
111
 
72
112
  - Replace `valid_otp?` method with `validate_and_consume_otp!`.
73
113
  - Disallow subsequent OTPs once validated via timesteps.
74
114
 
75
115
  ## 1.1.0
116
+
76
117
  - Removes runtimez activemodel dependency.
77
118
  - Uses `Devise::Encryptor` instead of `Devise.bcrypt`, which is deprecated.
78
119
  - Bump `rotp` dependency to 2.x.
79
120
 
80
121
  ## 1.0.2
122
+
81
123
  - Makes Railties the only requirement for Rails generators.
82
124
  - Explicitly check that the `otp_attempt` param is not nil in order to avoid 'ROTP only verifies strings' exceptions.
83
125
  - Adding warning about recoverable devise strategy and automatic `sign_in` after a password reset.
84
126
  - Loosen dependency version requirements for rotp, devise, and attr_encrypted.
85
127
 
86
128
  ## 1.0.1
129
+
87
130
  - Add version requirements for dependencies.
88
131
 
89
132
  ## 1.0.0
133
+
90
134
  - Initial release.
data/README.md CHANGED
@@ -82,7 +82,7 @@ This generator will:
82
82
 
83
83
  1. Edit `app/models/MODEL.rb` (where MODEL is your model name):
84
84
  * add the `:two_factor_authenticatable` devise module
85
- * remove the `:database_authenticatable` if present because it is incompatible with `:two_factor_authenticatable`
85
+ * remove the `:database_authenticatable` devise module, if present; having both modules enabled will lead to issues described below.
86
86
  1. Add a Warden config block to your Devise initializer, which enables the strategies required for two-factor authentication.
87
87
 
88
88
  Remember to apply the new migration after you run the generator:
@@ -107,9 +107,9 @@ Next you need to whitelist `:otp_attempt` as a permitted parameter in Devise `:s
107
107
  end
108
108
  ```
109
109
 
110
- Finally you should verify that `:database_authenticatable` is **not** being loaded by your model. The generator will try to remove it, but if you have a non-standard Devise setup, this step may fail.
110
+ Finally you should verify that `:database_authenticatable` is **not** being loaded by your model. The generator will try to remove it, but if you have a non-standard Devise setup, this step may fail. `:two_factor_authenticatable` includes all of `:database_authenticatable`'s functionality; it will still allow login without two-factor authentication until you enable it on your model's records with `otp_required_for_login`.
111
111
 
112
- **Loading both `:database_authenticatable` and `:two_factor_authenticatable` in a model is a security issue** It will allow users to bypass two-factor authenticatable due to the way Warden handles cascading strategies!
112
+ **Loading both `:database_authenticatable` and `:two_factor_authenticatable` in a model is a security issue.** It will allow users to bypass two-factor authentication regardless of how `otp_required_for_login` is set due to the way Warden handles cascading strategies!
113
113
 
114
114
  ## Designing Your Workflow
115
115
 
@@ -155,10 +155,7 @@ At Tinfoil Security, we opted to use the excellent [rqrcode-rails3](https://gith
155
155
  If you decide to do this you'll need to generate a URI to act as the source for the QR code. This can be done using the `User#otp_provisioning_uri` method.
156
156
 
157
157
  ```ruby
158
- issuer = 'Your App'
159
- label = "#{issuer}:#{current_user.email}"
160
-
161
- current_user.otp_provisioning_uri(label, issuer: issuer)
158
+ current_user.otp_provisioning_uri(current_user.email, issuer: 'Your App')
162
159
 
163
160
  # > "otpauth://totp/Your%20App:user@example.com?secret=[otp_secret]&issuer=Your+App"
164
161
  ```
@@ -236,7 +233,7 @@ class AddDeviseTwoFactorBackupableToUsers < ActiveRecord::Migration
236
233
  end
237
234
  ```
238
235
 
239
- #### MySQL
236
+ #### MySQL, SQL Server, other databases without an array string type
240
237
 
241
238
  ```ruby
242
239
  # migration
data/Rakefile CHANGED
@@ -11,6 +11,8 @@ rescue Bundler::BundlerError => e
11
11
  end
12
12
  require 'rake'
13
13
 
14
+ require 'bundler/gem_tasks'
15
+
14
16
  require 'rspec/core'
15
17
  require 'rspec/core/rake_task'
16
18
  RSpec::Core::RakeTask.new(:spec) do |spec|
data/UPGRADING.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ### save!
6
6
 
7
- `consume_otp!` and `invalidate_otp_backup_code!` now call `save!` instead of `save` (or nothing at all in the case of `invalide_otp_backup_code!`). If you manually called `save`/`save!` after calling `invalidate_otp_backup_code` you may be able to remove it.
7
+ `consume_otp!` and `invalidate_otp_backup_code!` now call `save!` instead of `save` (or nothing at all in the case of `invalidate_otp_backup_code!`). If you manually called `save`/`save!` after calling `invalidate_otp_backup_code!` you may be able to remove it.
8
8
 
9
9
  ### Secret Lengths
10
10
 
@@ -11,18 +11,13 @@ Gem::Specification.new do |s|
11
11
  s.description = 'Devise-Two-Factor is a minimalist extension to Devise which offers support for two-factor authentication through the TOTP scheme.'
12
12
  s.authors = ['Quinn Wilton']
13
13
 
14
- s.cert_chain = [
15
- 'certs/tinfoil-cacert.pem',
16
- 'certs/tinfoilsecurity-gems-cert.pem'
17
- ]
18
- s.signing_key = File.expand_path("~/.ssh/tinfoilsecurity-gems-key.pem") if $0 =~ /gem\z/
19
14
  s.files = `git ls-files`.split("\n").delete_if { |x| x.match('demo/*') }
20
15
  s.test_files = `git ls-files -- spec/*`.split("\n")
21
16
  s.require_paths = ['lib']
22
17
 
23
- s.add_runtime_dependency 'railties', '~> 7.0'
24
- s.add_runtime_dependency 'activesupport', '~> 7.0'
25
- s.add_runtime_dependency 'devise', '~> 4.0'
18
+ s.add_runtime_dependency 'railties', '>= 7.2', '< 8.2'
19
+ s.add_runtime_dependency 'activesupport', '>= 7.2', '< 8.2'
20
+ s.add_runtime_dependency 'devise', '>= 4.0', '< 6.0'
26
21
  s.add_runtime_dependency 'rotp', '~> 6.0'
27
22
 
28
23
  s.add_development_dependency 'activemodel'
@@ -30,4 +25,5 @@ Gem::Specification.new do |s|
30
25
  s.add_development_dependency 'bundler', '> 1.0'
31
26
  s.add_development_dependency 'rspec', '> 3'
32
27
  s.add_development_dependency 'simplecov'
28
+ s.add_development_dependency 'rake', '~> 13'
33
29
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "railties", "~> 7.0.0"
6
- gem "activesupport", "~> 7.0.0"
5
+ gem "railties", "~> 7.2.0"
6
+ gem "activesupport", "~> 7.2.0"
7
7
 
8
8
  gemspec path: "../"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "railties", "~> 7.1.0"
6
- gem "activesupport", "~> 7.1.0"
5
+ gem "railties", "~> 8.0.0"
6
+ gem "activesupport", "~> 8.0.0"
7
7
 
8
8
  gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "railties", "8.1.0"
6
+ gem "activesupport", "8.1.0"
7
+
8
+ gemspec path: "../"
@@ -1,3 +1,4 @@
1
+ require 'logger'
1
2
  require 'devise'
2
3
  require 'devise_two_factor/models'
3
4
  require 'devise_two_factor/strategies'
@@ -41,12 +41,11 @@ module Devise
41
41
 
42
42
  if self.consumed_timestep
43
43
  # reconstruct the timestamp of the last consumed timestep
44
- after_timestamp = self.consumed_timestep * otp.interval
44
+ after_timestamp = self.consumed_timestep * totp.interval
45
45
  end
46
46
 
47
- if totp.verify(code.gsub(/\s+/, ""), drift_behind: self.class.otp_allowed_drift, drift_ahead: self.class.otp_allowed_drift, after: after_timestamp)
48
- return consume_otp!
49
- end
47
+ timestamp = totp.verify(code.gsub(/\s+/, ""), drift_behind: self.class.otp_allowed_drift, drift_ahead: self.class.otp_allowed_drift, after: after_timestamp)
48
+ return consume_otp!(totp, timestamp) if timestamp
50
49
 
51
50
  false
52
51
  end
@@ -59,11 +58,6 @@ module Devise
59
58
  otp.at(Time.now)
60
59
  end
61
60
 
62
- # ROTP's TOTP#timecode is private, so we duplicate it here
63
- def current_otp_timestep
64
- Time.now.utc.to_i / otp.interval
65
- end
66
-
67
61
  def otp_provisioning_uri(account, options = {})
68
62
  otp_secret = options[:otp_secret] || self.otp_secret
69
63
  ROTP::TOTP.new(otp_secret, options).provisioning_uri(account)
@@ -78,10 +72,13 @@ module Devise
78
72
 
79
73
  # An OTP cannot be used more than once in a given timestep
80
74
  # Storing timestep of last valid OTP is sufficient to satisfy this requirement
81
- def consume_otp!
82
- if self.consumed_timestep != current_otp_timestep
83
- self.consumed_timestep = current_otp_timestep
75
+ def consume_otp!(otp, timestamp)
76
+ timestep = timestamp / otp.interval
77
+
78
+ if self.consumed_timestep != timestep
79
+ self.consumed_timestep = timestep
84
80
  save!(validate: false)
81
+
85
82
  return true
86
83
  end
87
84
 
@@ -94,7 +91,7 @@ module Devise
94
91
  :otp_encrypted_attribute_options,
95
92
  :otp_secret_encryption_key)
96
93
 
97
- # Geneartes an OTP secret of the specified length, returning it after Base32 encoding.
94
+ # Generates an OTP secret of the specified length, returning it after Base32 encoding.
98
95
  def generate_otp_secret(otp_secret_length = self.otp_secret_length)
99
96
  ROTP::Base32.random(otp_secret_length)
100
97
  end
@@ -34,6 +34,9 @@ module Devise
34
34
  def invalidate_otp_backup_code!(code)
35
35
  codes = self.otp_backup_codes || []
36
36
 
37
+ # Should we still have some other kind of non iterable result, terminate.
38
+ raise TypeError.new("`otp_backup_codes` is expected to be an Array, got #{codes.class.name}. Hint: If your database does not support arrays, does your model correctly `serialize :otp_backup_codes, Array`?") unless codes.is_a?(Array)
39
+
37
40
  codes.each do |backup_code|
38
41
  next unless Devise::Encryptor.compare(self.class, backup_code, code)
39
42
 
@@ -49,38 +49,67 @@ RSpec.shared_examples 'two_factor_backupable' do
49
49
  end
50
50
 
51
51
  describe '#invalidate_otp_backup_code!' do
52
- before do
53
- @plaintext_codes = subject.generate_otp_backup_codes!
54
- end
55
52
 
56
- context 'given an invalid recovery code' do
57
- it 'returns false' do
58
- expect(subject.invalidate_otp_backup_code!('password')).to be false
59
- end
60
- end
61
53
 
62
- context 'given a valid recovery code' do
63
- it 'returns true' do
64
- @plaintext_codes.each do |code|
65
- expect(subject.invalidate_otp_backup_code!(code)).to be true
54
+ describe "#invalidate_otp_backup_code!" do
55
+ context "with no backup codes" do
56
+ it "does nothing" do
57
+ expect(subject.invalidate_otp_backup_code!("foo")).to be false
66
58
  end
67
59
  end
68
60
 
69
- it 'invalidates that recovery code' do
70
- code = @plaintext_codes.sample
61
+ context "with an array of backup codes, newly generated" do
62
+ before do
63
+ @plaintext_codes = subject.generate_otp_backup_codes!
64
+ end
65
+
66
+ context 'given an invalid recovery code' do
67
+ it 'returns false' do
68
+ expect(subject.invalidate_otp_backup_code!('password')).to be false
69
+ end
70
+ end
71
+
72
+ context 'given a valid recovery code' do
73
+ it 'returns true' do
74
+ @plaintext_codes.each do |code|
75
+ expect(subject.invalidate_otp_backup_code!(code)).to be true
76
+ end
77
+ end
71
78
 
72
- subject.invalidate_otp_backup_code!(code)
73
- expect(subject.invalidate_otp_backup_code!(code)).to be false
79
+ it 'invalidates that recovery code' do
80
+ code = @plaintext_codes.sample
81
+
82
+ subject.invalidate_otp_backup_code!(code)
83
+ expect(subject.invalidate_otp_backup_code!(code)).to be false
84
+ end
85
+
86
+ it 'does not invalidate the other recovery codes' do
87
+ code = @plaintext_codes.sample
88
+ subject.invalidate_otp_backup_code!(code)
89
+
90
+ @plaintext_codes.delete(code)
91
+
92
+ @plaintext_codes.each do |code|
93
+ expect(subject.invalidate_otp_backup_code!(code)).to be true
94
+ end
95
+ end
96
+ end
74
97
  end
75
98
 
76
- it 'does not invalidate the other recovery codes' do
77
- code = @plaintext_codes.sample
78
- subject.invalidate_otp_backup_code!(code)
99
+ context "with backup codes as a string" do
100
+ before do
101
+ @plaintext_codes = subject.generate_otp_backup_codes!
79
102
 
80
- @plaintext_codes.delete(code)
103
+ # Simulates database adapters that don't understand `t.string :otp_backup_codes, type: array` properly
104
+ # such as SQL Server; and have just returned the serialized string still.
105
+ # and the user not having done:
106
+ # `serialize :otp_backup_codes, Array` in their model
107
+ subject.otp_backup_codes = subject.otp_backup_codes.to_json
108
+ end
81
109
 
82
- @plaintext_codes.each do |code|
83
- expect(subject.invalidate_otp_backup_code!(code)).to be true
110
+ # Do not run when DB adapter handles array assignment correctly
111
+ it "raises a meaningful error", unless: -> { subject.otp_backup_codes.is_a?(Array) } do
112
+ expect { subject.invalidate_otp_backup_code!("flork") }.to raise_error(TypeError)
84
113
  end
85
114
  end
86
115
  end
@@ -4,12 +4,18 @@ module Devise
4
4
 
5
5
  def authenticate!
6
6
  resource = mapping.to.find_for_database_authentication(authentication_hash)
7
+
8
+ hashed = false
7
9
  # We authenticate in two cases:
8
10
  # 1. The password and the OTP are correct
9
11
  # 2. The password is correct, and OTP is not required for login
10
12
  # We check the OTP, then defer to DatabaseAuthenticatable
11
- if validate(resource) { validate_otp(resource) }
13
+ if validate(resource) { hashed = true; validate_otp(resource) }
12
14
  super
15
+ else
16
+ # Paranoid mode: do the expensive hash even when resource is nil,
17
+ # to avoid timing-based user enumeration.
18
+ mapping.to.new.password = password if !hashed && Devise.paranoid
13
19
  end
14
20
 
15
21
  fail(Devise.paranoid ? :invalid : :not_found_in_database) unless resource
@@ -5,7 +5,7 @@ module Devise
5
5
  def authenticate!
6
6
  resource = mapping.to.find_for_database_authentication(authentication_hash)
7
7
 
8
- if validate(resource) { resource.invalidate_otp_backup_code!(params[scope]['otp_attempt']) }
8
+ if validate(resource) { validate_backup_code(resource) }
9
9
  super
10
10
  end
11
11
 
@@ -15,6 +15,11 @@ module Devise
15
15
  # but database authenticatable automatically halts on a bad password
16
16
  @halted = false if @result == :failure
17
17
  end
18
+
19
+ def validate_backup_code(resource)
20
+ return if params[scope].nil? || params[scope]['otp_attempt'].nil?
21
+ resource.invalidate_otp_backup_code!(params[scope]['otp_attempt'])
22
+ end
18
23
  end
19
24
  end
20
25
  end
@@ -1,3 +1,3 @@
1
1
  module DeviseTwoFactor
2
- VERSION = '6.0.0'.freeze
2
+ VERSION = '6.4.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,135 +1,74 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise-two-factor
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0
4
+ version: 6.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Quinn Wilton
8
- autorequire:
9
8
  bindir: bin
10
- cert_chain:
11
- - |
12
- -----BEGIN CERTIFICATE-----
13
- MIIHSjCCBTKgAwIBAgIJAK2u0LojMCNgMA0GCSqGSIb3DQEBBQUAMIGcMQswCQYD
14
- VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVBhbG8gQWx0bzEfMB0GA1UE
15
- ChMWVGluZm9pbCBTZWN1cml0eSwgSW5jLjEfMB0GA1UEAxMWVGluZm9pbCBTZWN1
16
- cml0eSwgSW5jLjEqMCgGCSqGSIb3DQEJARYbc3VwcG9ydEB0aW5mb2lsc2VjdXJp
17
- dHkuY29tMB4XDTIxMDkwOTE4MjIwMFoXDTI1MDkwOTE4MjIwMFowgZwxCzAJBgNV
18
- BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQK
19
- ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR8wHQYDVQQDExZUaW5mb2lsIFNlY3Vy
20
- aXR5LCBJbmMuMSowKAYJKoZIhvcNAQkBFhtzdXBwb3J0QHRpbmZvaWxzZWN1cml0
21
- eS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqbHvsSj0H0FB1
22
- 0gLYoDK1BKugkSB2DZeZZHP6B1UdWRahJXJP9oT1lhfQxx8iX4cgEi7JU3NqA6NR
23
- cIRFQ50eH/qlmgs7909gaf8pDaeC0vR3wd0GeRg6qr1eDEnkzIyr/D1AMiX6H1eP
24
- Y7J3SfrdaL3gft2iPRKGkgqsXR7oBNLA3n/ShiNgPXqRDl1CCj6aMY0cn5ROFScz
25
- vT2FUB4DEwPD2l18m1p99OnXqsOLL2J65qA2+cI8FtgFmlwIi5oSf+URvIdNx+cH
26
- lInlAtVHCvAKYLY0dlQ7czMQBcRpYjp2rwPt9f2ksq9b/voMTBABYHFV+IVn8svv
27
- GZ5e1+icjtr/R7dCGmCdEdFLXVxafmZhukymG9USv9DKuv1qh7r4q8KaPIE8n7nQ
28
- m97jENFfsgnwv+nUmIJ3tzuW5ZxO7A0tIIYdwzt0UjrO3ya4R5bTFXr4bnzZ/g/s
29
- CLknWqg1BCRlPd6LnpVGPT0gNDV1pEO25wE3A3Yy0Ujxudcgay/CgUhnlU11qOAc
30
- xmar2fhNZsviUhndd/220Ad5QMV2XzcAiopJIeu0juIVGRQM7x2h19Hsp0m6sOEF
31
- jfhvbdUa4nvmIFeYFY+hr/YkTmG9ZjyBa8YaZXhwjhSmKCQ374J7mn5e0Cryuvi5
32
- tYhwJn8rdwYZF/h2qqfEu8vaLoD09QIDAQABo4IBizCCAYcwHQYDVR0OBBYEFMmT
33
- /x412UH+5OHqgleeTjLOv6iHMIHRBgNVHSMEgckwgcaAFMmT/x412UH+5OHqglee
34
- TjLOv6iHoYGipIGfMIGcMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNV
35
- BAcTCVBhbG8gQWx0bzEfMB0GA1UEChMWVGluZm9pbCBTZWN1cml0eSwgSW5jLjEf
36
- MB0GA1UEAxMWVGluZm9pbCBTZWN1cml0eSwgSW5jLjEqMCgGCSqGSIb3DQEJARYb
37
- c3VwcG9ydEB0aW5mb2lsc2VjdXJpdHkuY29tggkAra7QuiMwI2AwDwYDVR0TAQH/
38
- BAUwAwEB/zARBglghkgBhvhCAQEEBAMCAQYwCQYDVR0SBAIwADArBglghkgBhvhC
39
- AQ0EHhYcVGlueUNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAmBgNVHREEHzAdgRtz
40
- dXBwb3J0QHRpbmZvaWxzZWN1cml0eS5jb20wDgYDVR0PAQH/BAQDAgEGMA0GCSqG
41
- SIb3DQEBBQUAA4ICAQBZy4JJSmwLuO0nZbdr4tJeVS2P8bcGi6PzAcdzVfwzjp6n
42
- 5qf8m4O8my4lnJieom0GrWSHQoPY1Yur4hEoZbugKO9DWZL3dTiGcrgw0TbQ6Gtq
43
- TTPatW3LA21qFJwvohSvLqPdmZuM+H9g49sdl2kNTDVI6iUyMYuNpL14aPKPGBFo
44
- o7UjciT1h7JtJl9b/fXrbPeRHBwpZXWeipiPGv/OZW5KnOsNlUkTquS7Zj4ETkIC
45
- 6mVtmsLvq+YwFthFyMU37pXwYxcmqRmH6lX+XC6AVW5oO4GBmG+Zr/Z+h5Cih5ca
46
- /mX88RkO+dGTjw1IdxKmxOqKL62OBATKrTDJ/scsmRptynA4TunYW+7ikOpDbPfL
47
- l18aleLISlcgWJg/Czf2nmBqAClPLnhV8qxWsvt58MQQ/Jpoggvpl8EG1PylWiBS
48
- Kc/4Ad/FKQFpTzXUgDg2kV07npVjYbBzA5p4ZSWSlflFu93jb9gg2+qtnRSImVCZ
49
- nQjZdsv8hebElPAIbtJjSnoH1Kz2ucYLakdF1UMKnpp1PVREtuKPz/foU9KUHs0z
50
- dWRALx8cWG4uKK9AIEUlVdGKfX0Wj0qFK0KGxl3f3jObud5Agwue2EPKWwUzEGUh
51
- Iqp60gNw3vBdKHw4dh1bfcbXWnRDL+OQPuOFZeMWgu1QmeHeuggYtYtRg7V5Kg==
52
- -----END CERTIFICATE-----
53
- - |
54
- -----BEGIN CERTIFICATE-----
55
- MIIGADCCA+igAwIBAgIIHIF9ta6cW3YwDQYJKoZIhvcNAQENBQAwgZwxCzAJBgNV
56
- BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQK
57
- ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR8wHQYDVQQDExZUaW5mb2lsIFNlY3Vy
58
- aXR5LCBJbmMuMSowKAYJKoZIhvcNAQkBFhtzdXBwb3J0QHRpbmZvaWxzZWN1cml0
59
- eS5jb20wHhcNMjIwMzIyMjI1MzAwWhcNMjUwOTA5MTgyMjAwWjCBiDELMAkGA1UE
60
- BhMCVVMxCzAJBgNVBAgTAkNBMR8wHQYDVQQKExZUaW5mb2lsIFNlY3VyaXR5LCBJ
61
- bmMuMR0wGwYDVQQDExR0aW5mb2lsc2VjdXJpdHktZ2VtczEsMCoGCSqGSIb3DQEJ
62
- ARYdZW5naW5lZXJzQHRpbmZvaWxzZWN1cml0eS5jb20wggIiMA0GCSqGSIb3DQEB
63
- AQUAA4ICDwAwggIKAoICAQDNJYNH8D+8lACLt3KzjEIPs3XVBCPaMm2eD/Xk9OOT
64
- uDV/NqgMK0icD9MRxMUtS3SCrC9QcPocXT76f2LQ3yVJuK+rBUasymEES47PIx2c
65
- zC4n4Hga0xPPuBpioO26oaRFsobyzh9RPOIbnYfpjyqtdrbm+YyM3sPR4XzFirv9
66
- xomT4E9T4RCLgOQHTcLKL9K9m+EN7PeVdVUXV0Pa7cVs2vJUKedsd7vnr6Lzbn8T
67
- oPk/7J/4W931PbaeI5yg9ZuaRa9K2IaY1TkPI67NW4qKitBVepRlXw6Sb7TYcUnc
68
- WEQ/eC5CpnOmqUrG5tfGD8cc5aGZOkitW/VXZgVj81xgCv1hk4HjErrqq4FBNAaC
69
- SNyBfwR0TUYqg1lN1nbNjOKwfb6YRn06R2ovcFJG0tmGhsQULCr6fW8u2TfSM+U9
70
- WFSIJx2griureY7EZPwg/MgsUiWUWMFemz3GVYXWJR3dN2pW9Uqr3rkjKZbA0bst
71
- GWahJO9HuFdDakQxoaTPYPtTQDC+kskkO6lKG1KLIoZ1iLZzB1Ks1vEeyE7lp1im
72
- WgpUq+q23PFkt1gIBi/4tGvzsLZye25QU2Y+XLzldCNm+DyRFXZ+Q+bK33IveUeU
73
- WEOv4T1qTXHAOypyzmgodVRG/PrlsSMOBfE515kG1mDMGjRcCpEtlskgxUbf7qM7
74
- hQIDAQABo1gwVjAJBgNVHRMEAjAAMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHBzOi8v
75
- d3d3LnRpbmZvaWxzZWN1cml0eS5jb20vc2VjdXJpdHkvcmV2b2NhdGlvbl9saXN0
76
- MA0GCSqGSIb3DQEBDQUAA4ICAQAiYF/m2ny/mxFvBVxHfdYuzybhCvsEUd+TSnoe
77
- mqOWntY3sxCOaY0aGOMB4vyg9G+oP/kT4m63sD4uQxeuU7WOjaG2smCSS5q+PSWS
78
- v63gILqPamjSyP/Om864EA6YlvVQ7nPXhVDEaiBt3iliefJGmb0wWSbbDCmq3aMb
79
- WTLuax/IeY6MjJi20LutIcuz+VX8OxlA1hSpgAToMz3xrhA8fPt5UkKhkDkPFYBF
80
- 5htKVipyijChWsXyt33YM2qGaavTEXzxza1I99PGNRKxUMvbSMas4YaLqkBpQSc+
81
- mcrLWYPiXWsePGu+j08AypE2Ubp4AOSZJN9rBBGotC3gofipo+K/sBiOM9xXI76Q
82
- 0HYOxXPa7D7UQQG1R9i0rcxmf9qepIVYCldmqVkKKDizcDo5UI9lRiLFjDyQhn6l
83
- YFY9bPQ4lKTK5Jr3M6+dV7fHxLhqXyMGs1905IUb7qvB7Bq/f0qJfC0JZuY/qdn2
84
- lL0SeFKOVsjErtobh3u8p8j2USkc8uJgIANHpXEMEExdp899CV/eVjh3TpAR7E6T
85
- mg7Q9Hi6Hh8z+Le9iR4I49vPEWDQEvj35IT6VfwU79UfIOlX+DkW8fFkPbaut3Se
86
- vqIDv6JBG9I16h/HhchntKfM58MI1bNZFBSdZqYOJiL8JIjP8HNIk76Y366ppG29
87
- EhBYYg==
88
- -----END CERTIFICATE-----
89
- date: 2024-09-17 00:00:00.000000000 Z
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
90
11
  dependencies:
91
12
  - !ruby/object:Gem::Dependency
92
13
  name: railties
93
14
  requirement: !ruby/object:Gem::Requirement
94
15
  requirements:
95
- - - "~>"
16
+ - - ">="
96
17
  - !ruby/object:Gem::Version
97
- version: '7.0'
18
+ version: '7.2'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '8.2'
98
22
  type: :runtime
99
23
  prerelease: false
100
24
  version_requirements: !ruby/object:Gem::Requirement
101
25
  requirements:
102
- - - "~>"
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '7.2'
29
+ - - "<"
103
30
  - !ruby/object:Gem::Version
104
- version: '7.0'
31
+ version: '8.2'
105
32
  - !ruby/object:Gem::Dependency
106
33
  name: activesupport
107
34
  requirement: !ruby/object:Gem::Requirement
108
35
  requirements:
109
- - - "~>"
36
+ - - ">="
110
37
  - !ruby/object:Gem::Version
111
- version: '7.0'
38
+ version: '7.2'
39
+ - - "<"
40
+ - !ruby/object:Gem::Version
41
+ version: '8.2'
112
42
  type: :runtime
113
43
  prerelease: false
114
44
  version_requirements: !ruby/object:Gem::Requirement
115
45
  requirements:
116
- - - "~>"
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '7.2'
49
+ - - "<"
117
50
  - !ruby/object:Gem::Version
118
- version: '7.0'
51
+ version: '8.2'
119
52
  - !ruby/object:Gem::Dependency
120
53
  name: devise
121
54
  requirement: !ruby/object:Gem::Requirement
122
55
  requirements:
123
- - - "~>"
56
+ - - ">="
124
57
  - !ruby/object:Gem::Version
125
58
  version: '4.0'
59
+ - - "<"
60
+ - !ruby/object:Gem::Version
61
+ version: '6.0'
126
62
  type: :runtime
127
63
  prerelease: false
128
64
  version_requirements: !ruby/object:Gem::Requirement
129
65
  requirements:
130
- - - "~>"
66
+ - - ">="
131
67
  - !ruby/object:Gem::Version
132
68
  version: '4.0'
69
+ - - "<"
70
+ - !ruby/object:Gem::Version
71
+ version: '6.0'
133
72
  - !ruby/object:Gem::Dependency
134
73
  name: rotp
135
74
  requirement: !ruby/object:Gem::Requirement
@@ -214,15 +153,29 @@ dependencies:
214
153
  - - ">="
215
154
  - !ruby/object:Gem::Version
216
155
  version: '0'
156
+ - !ruby/object:Gem::Dependency
157
+ name: rake
158
+ requirement: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - "~>"
161
+ - !ruby/object:Gem::Version
162
+ version: '13'
163
+ type: :development
164
+ prerelease: false
165
+ version_requirements: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: '13'
217
170
  description: Devise-Two-Factor is a minimalist extension to Devise which offers support
218
171
  for two-factor authentication through the TOTP scheme.
219
- email:
220
172
  executables: []
221
173
  extensions: []
222
174
  extra_rdoc_files: []
223
175
  files:
224
176
  - ".github/dependabot.yml"
225
177
  - ".github/workflows/ci.yml"
178
+ - ".github/workflows/push.yml"
226
179
  - ".gitignore"
227
180
  - ".markdownlint.json"
228
181
  - ".rspec"
@@ -235,11 +188,10 @@ files:
235
188
  - Rakefile
236
189
  - SECURITY.md
237
190
  - UPGRADING.md
238
- - certs/tinfoil-cacert.pem
239
- - certs/tinfoilsecurity-gems-cert.pem
240
191
  - devise-two-factor.gemspec
241
- - gemfiles/rails_7.0.gemfile
242
- - gemfiles/rails_7.1.gemfile
192
+ - gemfiles/rails_7.2.gemfile
193
+ - gemfiles/rails_8.0.gemfile
194
+ - gemfiles/rails_8.1.gemfile
243
195
  - lib/devise-two-factor.rb
244
196
  - lib/devise_two_factor/models.rb
245
197
  - lib/devise_two_factor/models/two_factor_authenticatable.rb
@@ -259,7 +211,6 @@ homepage: https://github.com/devise-two-factor/devise-two-factor
259
211
  licenses:
260
212
  - MIT
261
213
  metadata: {}
262
- post_install_message:
263
214
  rdoc_options: []
264
215
  require_paths:
265
216
  - lib
@@ -274,8 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
274
225
  - !ruby/object:Gem::Version
275
226
  version: '0'
276
227
  requirements: []
277
- rubygems_version: 3.5.11
278
- signing_key:
228
+ rubygems_version: 4.0.3
279
229
  specification_version: 4
280
230
  summary: Barebones two-factor authentication with Devise
281
231
  test_files:
checksums.yaml.gz.sig DELETED
Binary file
@@ -1,41 +0,0 @@
1
- -----BEGIN CERTIFICATE-----
2
- MIIHSjCCBTKgAwIBAgIJAK2u0LojMCNgMA0GCSqGSIb3DQEBBQUAMIGcMQswCQYD
3
- VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVBhbG8gQWx0bzEfMB0GA1UE
4
- ChMWVGluZm9pbCBTZWN1cml0eSwgSW5jLjEfMB0GA1UEAxMWVGluZm9pbCBTZWN1
5
- cml0eSwgSW5jLjEqMCgGCSqGSIb3DQEJARYbc3VwcG9ydEB0aW5mb2lsc2VjdXJp
6
- dHkuY29tMB4XDTIxMDkwOTE4MjIwMFoXDTI1MDkwOTE4MjIwMFowgZwxCzAJBgNV
7
- BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQK
8
- ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR8wHQYDVQQDExZUaW5mb2lsIFNlY3Vy
9
- aXR5LCBJbmMuMSowKAYJKoZIhvcNAQkBFhtzdXBwb3J0QHRpbmZvaWxzZWN1cml0
10
- eS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqbHvsSj0H0FB1
11
- 0gLYoDK1BKugkSB2DZeZZHP6B1UdWRahJXJP9oT1lhfQxx8iX4cgEi7JU3NqA6NR
12
- cIRFQ50eH/qlmgs7909gaf8pDaeC0vR3wd0GeRg6qr1eDEnkzIyr/D1AMiX6H1eP
13
- Y7J3SfrdaL3gft2iPRKGkgqsXR7oBNLA3n/ShiNgPXqRDl1CCj6aMY0cn5ROFScz
14
- vT2FUB4DEwPD2l18m1p99OnXqsOLL2J65qA2+cI8FtgFmlwIi5oSf+URvIdNx+cH
15
- lInlAtVHCvAKYLY0dlQ7czMQBcRpYjp2rwPt9f2ksq9b/voMTBABYHFV+IVn8svv
16
- GZ5e1+icjtr/R7dCGmCdEdFLXVxafmZhukymG9USv9DKuv1qh7r4q8KaPIE8n7nQ
17
- m97jENFfsgnwv+nUmIJ3tzuW5ZxO7A0tIIYdwzt0UjrO3ya4R5bTFXr4bnzZ/g/s
18
- CLknWqg1BCRlPd6LnpVGPT0gNDV1pEO25wE3A3Yy0Ujxudcgay/CgUhnlU11qOAc
19
- xmar2fhNZsviUhndd/220Ad5QMV2XzcAiopJIeu0juIVGRQM7x2h19Hsp0m6sOEF
20
- jfhvbdUa4nvmIFeYFY+hr/YkTmG9ZjyBa8YaZXhwjhSmKCQ374J7mn5e0Cryuvi5
21
- tYhwJn8rdwYZF/h2qqfEu8vaLoD09QIDAQABo4IBizCCAYcwHQYDVR0OBBYEFMmT
22
- /x412UH+5OHqgleeTjLOv6iHMIHRBgNVHSMEgckwgcaAFMmT/x412UH+5OHqglee
23
- TjLOv6iHoYGipIGfMIGcMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNV
24
- BAcTCVBhbG8gQWx0bzEfMB0GA1UEChMWVGluZm9pbCBTZWN1cml0eSwgSW5jLjEf
25
- MB0GA1UEAxMWVGluZm9pbCBTZWN1cml0eSwgSW5jLjEqMCgGCSqGSIb3DQEJARYb
26
- c3VwcG9ydEB0aW5mb2lsc2VjdXJpdHkuY29tggkAra7QuiMwI2AwDwYDVR0TAQH/
27
- BAUwAwEB/zARBglghkgBhvhCAQEEBAMCAQYwCQYDVR0SBAIwADArBglghkgBhvhC
28
- AQ0EHhYcVGlueUNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAmBgNVHREEHzAdgRtz
29
- dXBwb3J0QHRpbmZvaWxzZWN1cml0eS5jb20wDgYDVR0PAQH/BAQDAgEGMA0GCSqG
30
- SIb3DQEBBQUAA4ICAQBZy4JJSmwLuO0nZbdr4tJeVS2P8bcGi6PzAcdzVfwzjp6n
31
- 5qf8m4O8my4lnJieom0GrWSHQoPY1Yur4hEoZbugKO9DWZL3dTiGcrgw0TbQ6Gtq
32
- TTPatW3LA21qFJwvohSvLqPdmZuM+H9g49sdl2kNTDVI6iUyMYuNpL14aPKPGBFo
33
- o7UjciT1h7JtJl9b/fXrbPeRHBwpZXWeipiPGv/OZW5KnOsNlUkTquS7Zj4ETkIC
34
- 6mVtmsLvq+YwFthFyMU37pXwYxcmqRmH6lX+XC6AVW5oO4GBmG+Zr/Z+h5Cih5ca
35
- /mX88RkO+dGTjw1IdxKmxOqKL62OBATKrTDJ/scsmRptynA4TunYW+7ikOpDbPfL
36
- l18aleLISlcgWJg/Czf2nmBqAClPLnhV8qxWsvt58MQQ/Jpoggvpl8EG1PylWiBS
37
- Kc/4Ad/FKQFpTzXUgDg2kV07npVjYbBzA5p4ZSWSlflFu93jb9gg2+qtnRSImVCZ
38
- nQjZdsv8hebElPAIbtJjSnoH1Kz2ucYLakdF1UMKnpp1PVREtuKPz/foU9KUHs0z
39
- dWRALx8cWG4uKK9AIEUlVdGKfX0Wj0qFK0KGxl3f3jObud5Agwue2EPKWwUzEGUh
40
- Iqp60gNw3vBdKHw4dh1bfcbXWnRDL+OQPuOFZeMWgu1QmeHeuggYtYtRg7V5Kg==
41
- -----END CERTIFICATE-----
@@ -1,35 +0,0 @@
1
- -----BEGIN CERTIFICATE-----
2
- MIIGADCCA+igAwIBAgIIHIF9ta6cW3YwDQYJKoZIhvcNAQENBQAwgZwxCzAJBgNV
3
- BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQK
4
- ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR8wHQYDVQQDExZUaW5mb2lsIFNlY3Vy
5
- aXR5LCBJbmMuMSowKAYJKoZIhvcNAQkBFhtzdXBwb3J0QHRpbmZvaWxzZWN1cml0
6
- eS5jb20wHhcNMjIwMzIyMjI1MzAwWhcNMjUwOTA5MTgyMjAwWjCBiDELMAkGA1UE
7
- BhMCVVMxCzAJBgNVBAgTAkNBMR8wHQYDVQQKExZUaW5mb2lsIFNlY3VyaXR5LCBJ
8
- bmMuMR0wGwYDVQQDExR0aW5mb2lsc2VjdXJpdHktZ2VtczEsMCoGCSqGSIb3DQEJ
9
- ARYdZW5naW5lZXJzQHRpbmZvaWxzZWN1cml0eS5jb20wggIiMA0GCSqGSIb3DQEB
10
- AQUAA4ICDwAwggIKAoICAQDNJYNH8D+8lACLt3KzjEIPs3XVBCPaMm2eD/Xk9OOT
11
- uDV/NqgMK0icD9MRxMUtS3SCrC9QcPocXT76f2LQ3yVJuK+rBUasymEES47PIx2c
12
- zC4n4Hga0xPPuBpioO26oaRFsobyzh9RPOIbnYfpjyqtdrbm+YyM3sPR4XzFirv9
13
- xomT4E9T4RCLgOQHTcLKL9K9m+EN7PeVdVUXV0Pa7cVs2vJUKedsd7vnr6Lzbn8T
14
- oPk/7J/4W931PbaeI5yg9ZuaRa9K2IaY1TkPI67NW4qKitBVepRlXw6Sb7TYcUnc
15
- WEQ/eC5CpnOmqUrG5tfGD8cc5aGZOkitW/VXZgVj81xgCv1hk4HjErrqq4FBNAaC
16
- SNyBfwR0TUYqg1lN1nbNjOKwfb6YRn06R2ovcFJG0tmGhsQULCr6fW8u2TfSM+U9
17
- WFSIJx2griureY7EZPwg/MgsUiWUWMFemz3GVYXWJR3dN2pW9Uqr3rkjKZbA0bst
18
- GWahJO9HuFdDakQxoaTPYPtTQDC+kskkO6lKG1KLIoZ1iLZzB1Ks1vEeyE7lp1im
19
- WgpUq+q23PFkt1gIBi/4tGvzsLZye25QU2Y+XLzldCNm+DyRFXZ+Q+bK33IveUeU
20
- WEOv4T1qTXHAOypyzmgodVRG/PrlsSMOBfE515kG1mDMGjRcCpEtlskgxUbf7qM7
21
- hQIDAQABo1gwVjAJBgNVHRMEAjAAMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHBzOi8v
22
- d3d3LnRpbmZvaWxzZWN1cml0eS5jb20vc2VjdXJpdHkvcmV2b2NhdGlvbl9saXN0
23
- MA0GCSqGSIb3DQEBDQUAA4ICAQAiYF/m2ny/mxFvBVxHfdYuzybhCvsEUd+TSnoe
24
- mqOWntY3sxCOaY0aGOMB4vyg9G+oP/kT4m63sD4uQxeuU7WOjaG2smCSS5q+PSWS
25
- v63gILqPamjSyP/Om864EA6YlvVQ7nPXhVDEaiBt3iliefJGmb0wWSbbDCmq3aMb
26
- WTLuax/IeY6MjJi20LutIcuz+VX8OxlA1hSpgAToMz3xrhA8fPt5UkKhkDkPFYBF
27
- 5htKVipyijChWsXyt33YM2qGaavTEXzxza1I99PGNRKxUMvbSMas4YaLqkBpQSc+
28
- mcrLWYPiXWsePGu+j08AypE2Ubp4AOSZJN9rBBGotC3gofipo+K/sBiOM9xXI76Q
29
- 0HYOxXPa7D7UQQG1R9i0rcxmf9qepIVYCldmqVkKKDizcDo5UI9lRiLFjDyQhn6l
30
- YFY9bPQ4lKTK5Jr3M6+dV7fHxLhqXyMGs1905IUb7qvB7Bq/f0qJfC0JZuY/qdn2
31
- lL0SeFKOVsjErtobh3u8p8j2USkc8uJgIANHpXEMEExdp899CV/eVjh3TpAR7E6T
32
- mg7Q9Hi6Hh8z+Le9iR4I49vPEWDQEvj35IT6VfwU79UfIOlX+DkW8fFkPbaut3Se
33
- vqIDv6JBG9I16h/HhchntKfM58MI1bNZFBSdZqYOJiL8JIjP8HNIk76Y366ppG29
34
- EhBYYg==
35
- -----END CERTIFICATE-----
data.tar.gz.sig DELETED
Binary file
metadata.gz.sig DELETED
Binary file