devise-argon2 2.0.0 → 2.0.2

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: 6d20fef20b04b01fd33fd4e8b7104f50ef57ce75e6a4406df46a3ea10fe693a1
4
- data.tar.gz: 7b66c6a1e646eea75a16c43ae37a95b557e6342488885a73ab60a80d3d5903a5
3
+ metadata.gz: 7542aed226ac27c831a5f6acdbb6011fe8b6632e83a60902341c4f44bad38b27
4
+ data.tar.gz: 63891613bb7343641df64221d56533f646543c8eb0c8b3f9fe32186a4f4c46df
5
5
  SHA512:
6
- metadata.gz: 668de52215782a24691ec1d44ced5c85376d99099da8352c583a3452fb78d60e928fef311efe5bdfadffc5dfac130b1affabf9ce26b0197ea807d5602ed257d8
7
- data.tar.gz: 12669cb6bbd94d3cc6e0427a6bf805152417f47858145daf6fba20907d32c10f9c8bedf4535a7ede8af0c572723886537ee782967e7b9b7e5566ec01c6f6ec27
6
+ metadata.gz: 958d4df9feceff3bb4b28c85eed86f0072cd9914ba7f00cfdc26379def7af570647812fb72e14303b02ad435d4e8450d6cbc63b76a7b23380de1218828fac365
7
+ data.tar.gz: f639d484fe68ff7d39df88511a06c15b4518ddce58cf02057d39f99524aa5942af83427bb2268f66fe2facd36c30cb72477782f903d2d17e1dfd8f7e644a4fc7
@@ -1,35 +1,73 @@
1
1
  name: Test suite
2
2
 
3
- on: [push, pull_request]
3
+ on: [push, pull_request, workflow_dispatch]
4
4
 
5
5
  jobs:
6
6
  test:
7
7
  runs-on: ubuntu-latest
8
8
  strategy:
9
9
  matrix:
10
- ruby-version: ['2.7', '3.0', '3.1', '3.2', 'ruby-head']
11
- rails-version: ['~> 7.0', '~> 6.1']
10
+ ruby-version: ['2.7', '3.0', '3.1', '3.2', '3.3']
11
+ rails-version: ['~> 6.1', '~> 7.0', '~> 7.1', '~> 7.2', '8.0.0.beta1']
12
+ argon2-version: ['2.2', '2.3']
12
13
  orm:
13
14
  - adapter: active_record
14
15
  - adapter: mongoid
15
- mongoid-version: 8.1.2
16
+ mongoid-version: 9.0.2
16
17
  - adapter: mongoid
17
- mongoid-version: 8.0.6
18
+ mongoid-version: 8.1.6
19
+ - adapter: mongoid
20
+ mongoid-version: 8.0.8
18
21
  - adapter: mongoid
19
22
  mongoid-version: 7.5.4
20
- include:
21
- - rails-version: '~> 7.1'
23
+ exclude:
24
+ - rails-version: '~> 7.2'
25
+ ruby-version: '2.7'
26
+ - rails-version: '~> 7.2'
27
+ ruby-version: '3.0'
28
+ - rails-version: '8.0.0.beta1'
29
+ ruby-version: '2.7'
30
+ - rails-version: '8.0.0.beta1'
31
+ ruby-version: '3.0'
32
+ - rails-version: '8.0.0.beta1'
22
33
  ruby-version: '3.1'
23
- orm:
24
- adapter: active_record
25
- - rails-version: '~> 7.1'
34
+ - orm:
35
+ adapter: mongoid
36
+ rails-version: '8.0.0.beta1'
37
+ - orm:
38
+ adapter: mongoid
39
+ mongoid-version: 8.0.8
40
+ ruby-version: '3.3'
41
+ - orm:
42
+ adapter: mongoid
43
+ mongoid-version: 8.0.8
44
+ ruby-version: '3.2'
45
+ - orm:
46
+ adapter: mongoid
47
+ mongoid-version: 7.5.4
48
+ ruby-version: '3.3'
49
+ - orm:
50
+ adapter: mongoid
51
+ mongoid-version: 7.5.4
26
52
  ruby-version: '3.2'
27
- orm:
28
- adapter: active_record
53
+ - orm:
54
+ adapter: mongoid
55
+ mongoid-version: 8.0.8
56
+ rails-version: '~> 7.2'
57
+ - orm:
58
+ adapter: mongoid
59
+ mongoid-version: 7.5.4
60
+ rails-version: '~> 7.2'
61
+ - orm:
62
+ adapter: mongoid
63
+ mongoid-version: 7.5.4
64
+ rails-version: '~> 7.1'
29
65
  env:
30
66
  RAILS_VERSION: ${{ matrix.rails-version || '~> 7.0'}}
31
- MONGOID_VERSION: ${{ matrix.orm.mongoid-version || '8.1.2'}}
67
+ MONGOID_VERSION: ${{ matrix.orm.mongoid-version || '8.1.6'}}
32
68
  ORM: ${{ matrix.orm.adapter }}
69
+ ARGON2_VERSION: ${{ matrix.argon2-version }}
70
+ DEVISE_VERSION: ${{ matrix.devise-version || '~> 4.9' }}
33
71
  steps:
34
72
  - uses: actions/checkout@v4
35
73
  - name: Set up Ruby ${{ matrix.ruby-version }}
@@ -37,6 +75,7 @@ jobs:
37
75
  with:
38
76
  ruby-version: ${{ matrix.ruby-version }}
39
77
  bundler-cache: true
78
+ cache-version: 1
40
79
  - uses: supercharge/mongodb-github-action@1.10.0
41
80
  if: ${{ matrix.orm.adapter == 'mongoid' }}
42
81
  - name: Setup rails test environment
data/.gitignore CHANGED
@@ -14,7 +14,7 @@ rdoc
14
14
  spec/reports
15
15
  spec/rails_app/log/*
16
16
  spec/rails_app/tmp/*
17
- spec/rails_app/db/test.sqlite3
17
+ spec/rails_app/db/test.sqlite3*
18
18
  test/tmp
19
19
  test/version_tmp
20
20
  tmp
data/CHANGELOG.md CHANGED
@@ -1,20 +1,40 @@
1
- # Changelog
1
+ # Changelog
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## [2.0.2] - 2024-09-30
6
+
7
+ ### Changed
8
+ - When migrating users from v1 to v2, the `encrypted_password` update will no longer trigger callbacks (ie send email to users)
9
+
10
+ ### Added
11
+ - Tests for newer dependency versions
12
+
13
+ ## [2.0.1] - 2023-10-18
14
+
15
+ ### Added
16
+ - Add Argon2 and devise to the test suite
17
+ - Add @moritzhoeppner as an author
18
+
19
+ ### Fixed
20
+ - Fix work factors implementation
21
+
22
+ ## [2.0.0] - 2023-10-16
23
+
5
24
  ### Added
6
25
  - Expose Argon2 options for configuring hashing work factors
7
26
  - Add support for migration v1 hashes
8
27
  - Add support for migrating bcrypt hashes
9
28
  - Add tests for Mongoid
10
29
  - Add Changelog :)
11
-
30
+
12
31
  ### Changed
13
32
  - Change salting / peppering mechanism
14
33
  - Change CI from Travis to GitHub Actions
15
-
16
- ### Removed
34
+
35
+ ### Removed
17
36
  - Remove `devise-encryptable` dependency
18
37
  - Remove superflous dependency on devise `password_salt` column
19
38
 
39
+ Thank you to @moritzhoeppner for the significant contributions to this release!
20
40
 
data/Gemfile CHANGED
@@ -5,9 +5,16 @@ gemspec
5
5
  gem 'rspec'
6
6
  gem 'simplecov'
7
7
  gem 'activerecord'
8
- gem 'sqlite3'
9
8
  gem 'rails', ENV['RAILS_VERSION'] || '~> 7.0'
9
+ gem 'argon2', ENV['ARGON2_VERSION'] || '~> 2.3'
10
+ gem 'devise', ENV['DEVISE_VERSION'] || '~> 4.9'
10
11
 
11
12
  if ENV['ORM'] == 'mongoid'
12
13
  gem 'mongoid', ENV['MONGOID_VERSION'] || '~> 7.5'
13
14
  end
15
+
16
+ if ENV['RAILS_VERSION'] == '8.0.0.beta1'
17
+ gem 'sqlite3', '~> 2.1'
18
+ else
19
+ gem 'sqlite3', '~> 1.6', '>= 1.6.6'
20
+ end
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # devise-argon2
2
2
  [![Gem Version](https://badge.fury.io/rb/devise-argon2.svg)](https://badge.fury.io/rb/devise-argon2)
3
+ ![](https://github.com/erdostom/devise-argon2/actions/workflows/test.yml/badge.svg)
3
4
 
4
5
  A ruby gem that gives Devise models which use `database_authenticatable` the ability to hash
5
6
  passwords with Argon2id.
@@ -39,7 +40,7 @@ or `secret` to `Argon2::Password.new`. These parameters can be set like this:
39
40
  class User < ApplicationRecord
40
41
  devise :database_authenticatable,
41
42
  :argon2,
42
- argon2_options: { t_cost: 3, p_cost: 2 }
43
+ argon2_options: { t_cost: 3, p_cost: 2 }
43
44
  end
44
45
  ```
45
46
 
@@ -6,7 +6,7 @@ require "devise-argon2/version"
6
6
  Gem::Specification.new do |gem|
7
7
  gem.name = "devise-argon2"
8
8
  gem.version = Devise::Argon2::ARGON2_VERSION
9
- gem.authors = ["Tamas Erdos"]
9
+ gem.authors = ["Tamas Erdos", "Moritz Höppner"]
10
10
  gem.email = ["tamas at tamaserdos com"]
11
11
  gem.description = %q{Enables Devise to hash passwords with Argon2id}
12
12
  gem.summary = %q{Enables Devise to hash passwords with Argon2id}
@@ -18,8 +18,8 @@ Gem::Specification.new do |gem|
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
19
  gem.require_paths = ["lib"]
20
20
 
21
- gem.add_dependency 'devise', '>= 2.1.0'
22
- gem.add_dependency 'argon2', '~> 2.0'
21
+ gem.add_dependency 'devise', '~> 4.0'
22
+ gem.add_dependency 'argon2', '~> 2.1'
23
23
 
24
24
 
25
25
  gem.post_install_message = "Version 2 of devise-argon2 introduces breaking changes, please see README.md for details."
@@ -53,26 +53,49 @@ module Devise
53
53
  attributes = { encrypted_password: password_digest(password) }
54
54
  attributes[:password_salt] = nil if migrate_hash_from_devise_argon2_v1?
55
55
 
56
- self.assign_attributes(attributes)
57
- self.save if self.persisted?
56
+ if self.persisted?
57
+ update_without_callbacks(attributes)
58
+ else
59
+ self.assign_attributes(attributes)
60
+ end
61
+ end
62
+
63
+ def update_without_callbacks(attributes)
64
+ if defined?(Mongoid) && Mongoid.models.include?(self.class)
65
+ self.set(attributes)
66
+ else
67
+ self.update_columns(attributes)
68
+ end
58
69
  end
59
70
 
60
71
  def outdated_work_factors?
72
+ hash_format = ::Argon2::HashFormat.new(encrypted_password)
73
+ current_work_factors = {
74
+ t_cost: hash_format.t_cost,
75
+ m_cost: hash_format.m_cost,
76
+ p_cost: hash_format.p_cost
77
+ }
78
+ current_work_factors != configured_work_factors
79
+ end
80
+
81
+ def configured_work_factors
61
82
  # Since version 2.3.0 the argon2 gem exposes the default work factors via constants, see
62
83
  # https://github.com/technion/ruby-argon2/commit/d62ecf8b4ec6b8c1651fade5a5ebdc856e8aef42
63
- default_t_cost = defined?(::Argon2::Password::DEFAULT_T_COST) ? ::Argon2::Password::DEFAULT_T_COST : 2
64
- default_m_cost = defined?(::Argon2::Password::DEFAULT_M_COST) ? ::Argon2::Password::DEFAULT_M_COST : 16
65
- default_p_cost = defined?(::Argon2::Password::DEFAULT_P_COST) ? ::Argon2::Password::DEFAULT_P_COST : 1
84
+ work_factors = {
85
+ t_cost: defined?(::Argon2::Password::DEFAULT_T_COST) ? ::Argon2::Password::DEFAULT_T_COST : 2,
86
+ m_cost: defined?(::Argon2::Password::DEFAULT_M_COST) ? ::Argon2::Password::DEFAULT_M_COST : 16,
87
+ p_cost: defined?(::Argon2::Password::DEFAULT_P_COST) ? ::Argon2::Password::DEFAULT_P_COST : 1
88
+ }.merge(self.class.argon2_options.slice(:t_cost, :m_cost, :p_cost))
66
89
 
67
- current_t_cost = self.class.argon2_options[:t_cost] || default_t_cost
68
- current_m_cost = self.class.argon2_options[:m_cost] || default_m_cost
69
- current_p_cost = self.class.argon2_options[:p_cost] || default_p_cost
70
-
71
- hash_format = ::Argon2::HashFormat.new(encrypted_password)
90
+ # Since version 2.3.0 the argon2 gem supports defining work factors with named profiles, see
91
+ # https://github.com/technion/ruby-argon2/commit/6312a8fb3a6c6c5e771a736572e63d47485e8613
92
+ if self.class.argon2_options[:profile] && defined?(::Argon2::Profiles)
93
+ work_factors.merge!(::Argon2::Profiles[self.class.argon2_options[:profile]])
94
+ end
95
+
96
+ work_factors[:m_cost] = (1 << work_factors[:m_cost])
72
97
 
73
- hash_format.t_cost != current_t_cost ||
74
- hash_format.m_cost != (1 << current_m_cost) ||
75
- hash_format.p_cost != current_p_cost
98
+ work_factors
76
99
  end
77
100
 
78
101
  def migrate_hash_from_devise_argon2_v1?
@@ -1,5 +1,5 @@
1
1
  module Devise
2
2
  module Argon2
3
- ARGON2_VERSION = '2.0.0'
3
+ ARGON2_VERSION = '2.0.2'
4
4
  end
5
5
  end
@@ -19,6 +19,7 @@ describe Devise::Models::Argon2 do
19
19
  p_cost: DEFAULT_P_COST
20
20
  }
21
21
  User.destroy_all
22
+ OldUser.destroy_all
22
23
  end
23
24
 
24
25
  def work_factors(hash)
@@ -127,6 +128,14 @@ describe Devise::Models::Argon2 do
127
128
  it 'does not update the hash if an invalid password is given' do
128
129
  expect{ user.valid_password?(INCORRECT_PASSWORD) }.not_to(change(user, :encrypted_password))
129
130
  end
131
+
132
+ it 'does not send password change notification emails on hash updates' do
133
+ user.email = 'test@example.com'
134
+ user.save!
135
+ Devise.send_password_change_notification = true
136
+ expect{ user.valid_password?(CORRECT_PASSWORD) }
137
+ .not_to(change { ActionMailer::Base.deliveries.count })
138
+ end
130
139
  end
131
140
 
132
141
  describe 'updating outdated work factors' do
@@ -174,6 +183,55 @@ describe Devise::Models::Argon2 do
174
183
  .to({ m_cost: 1 << 4, t_cost: 3, p_cost: 2 })
175
184
  )
176
185
  end
186
+
187
+ if Argon2::VERSION >= '2.3.0'
188
+ it 'updates work factors if they changed via profile option' do
189
+ # Build user with argon2 default work factors (which match the RFC_9106_LOW_MEMORY
190
+ # profile.)
191
+ Devise.argon2_options = {}
192
+ user
193
+
194
+ Devise.argon2_options = { profile: :pre_rfc_9106 }
195
+
196
+ expect{ user.valid_password?(CORRECT_PASSWORD) }.to(
197
+ change{ work_factors(user.encrypted_password) }
198
+ .to(
199
+ {
200
+ m_cost: 1 << Argon2::Profiles[:pre_rfc_9106][:m_cost],
201
+ t_cost: Argon2::Profiles[:pre_rfc_9106][:t_cost],
202
+ p_cost: Argon2::Profiles[:pre_rfc_9106][:p_cost]
203
+ }
204
+ )
205
+ )
206
+ end
207
+
208
+ it 'gives precendence to the profile option over explicit configuration of work factors' do
209
+ Devise.argon2_options = {
210
+ m_cost: Argon2::Profiles[:pre_rfc_9106][:m_cost] + 1,
211
+ t_cost: Argon2::Profiles[:pre_rfc_9106][:t_cost] + 1,
212
+ p_cost: Argon2::Profiles[:pre_rfc_9106][:p_cost] + 1
213
+ }
214
+ user # build user
215
+
216
+ Devise.argon2_options = {
217
+ profile: :pre_rfc_9106,
218
+ m_cost: Argon2::Profiles[:pre_rfc_9106][:m_cost] + 1,
219
+ t_cost: Argon2::Profiles[:pre_rfc_9106][:t_cost] + 1,
220
+ p_cost: Argon2::Profiles[:pre_rfc_9106][:p_cost] + 1
221
+ }
222
+
223
+ expect{ user.valid_password?(CORRECT_PASSWORD) }.to(
224
+ change{ work_factors(user.encrypted_password) }
225
+ .to(
226
+ {
227
+ m_cost: 1 << Argon2::Profiles[:pre_rfc_9106][:m_cost],
228
+ t_cost: Argon2::Profiles[:pre_rfc_9106][:t_cost],
229
+ p_cost: Argon2::Profiles[:pre_rfc_9106][:p_cost]
230
+ }
231
+ )
232
+ )
233
+ end
234
+ end
177
235
  end
178
236
 
179
237
  it 'ignores migrate_from_devise_argon2_v1 if password_salt is not present' do
@@ -20,5 +20,7 @@ module DummyRailsApp
20
20
  config.eager_load = false
21
21
  config.autoload_paths.reject!{ |p| p =~ /\/app\/(\w+)$/ && !%w(controllers helpers mailers views).include?($1) }
22
22
  config.autoload_paths += ["#{config.root}/app/#{ORM}"]
23
+ config.action_mailer.delivery_method = :test
24
+ config.action_mailer.default_options = { from: 'test@example.com' }
23
25
  end
24
26
  end
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ devise_for :old_users
3
+ end
metadata CHANGED
@@ -1,43 +1,44 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise-argon2
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tamas Erdos
8
+ - Moritz Höppner
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2023-10-16 00:00:00.000000000 Z
12
+ date: 2024-09-30 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: devise
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
- - - ">="
18
+ - - "~>"
18
19
  - !ruby/object:Gem::Version
19
- version: 2.1.0
20
+ version: '4.0'
20
21
  type: :runtime
21
22
  prerelease: false
22
23
  version_requirements: !ruby/object:Gem::Requirement
23
24
  requirements:
24
- - - ">="
25
+ - - "~>"
25
26
  - !ruby/object:Gem::Version
26
- version: 2.1.0
27
+ version: '4.0'
27
28
  - !ruby/object:Gem::Dependency
28
29
  name: argon2
29
30
  requirement: !ruby/object:Gem::Requirement
30
31
  requirements:
31
32
  - - "~>"
32
33
  - !ruby/object:Gem::Version
33
- version: '2.0'
34
+ version: '2.1'
34
35
  type: :runtime
35
36
  prerelease: false
36
37
  version_requirements: !ruby/object:Gem::Requirement
37
38
  requirements:
38
39
  - - "~>"
39
40
  - !ruby/object:Gem::Version
40
- version: '2.0'
41
+ version: '2.1'
41
42
  description: Enables Devise to hash passwords with Argon2id
42
43
  email:
43
44
  - tamas at tamaserdos com
@@ -77,6 +78,7 @@ files:
77
78
  - spec/rails_app/config/environment.rb
78
79
  - spec/rails_app/config/initializers/devise.rb
79
80
  - spec/rails_app/config/mongoid.yml
81
+ - spec/rails_app/config/routes.rb
80
82
  - spec/rails_app/db/migrate/20230617201921_devise_create_users.rb
81
83
  - spec/rails_app/db/migrate/20231004084147_devise_create_old_users.rb
82
84
  - spec/rails_app/db/schema.rb
@@ -101,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
103
  - !ruby/object:Gem::Version
102
104
  version: '0'
103
105
  requirements: []
104
- rubygems_version: 3.3.3
106
+ rubygems_version: 3.4.22
105
107
  signing_key:
106
108
  specification_version: 4
107
109
  summary: Enables Devise to hash passwords with Argon2id
@@ -126,6 +128,7 @@ test_files:
126
128
  - spec/rails_app/config/environment.rb
127
129
  - spec/rails_app/config/initializers/devise.rb
128
130
  - spec/rails_app/config/mongoid.yml
131
+ - spec/rails_app/config/routes.rb
129
132
  - spec/rails_app/db/migrate/20230617201921_devise_create_users.rb
130
133
  - spec/rails_app/db/migrate/20231004084147_devise_create_old_users.rb
131
134
  - spec/rails_app/db/schema.rb