devise-argon2 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +47 -0
  3. data/.gitignore +3 -0
  4. data/CHANGELOG.md +20 -0
  5. data/Gemfile +7 -4
  6. data/README.md +119 -32
  7. data/devise-argon2.gemspec +15 -12
  8. data/lib/devise-argon2/model.rb +89 -0
  9. data/lib/devise-argon2/version.rb +5 -0
  10. data/lib/devise-argon2.rb +8 -3
  11. data/spec/devise-argon2_spec.rb +224 -27
  12. data/spec/orm/active_record.rb +1 -0
  13. data/spec/orm/mongoid.rb +5 -0
  14. data/spec/rails_app/Rakefile +6 -0
  15. data/spec/rails_app/app/active_record/old_user.rb +3 -0
  16. data/spec/rails_app/app/active_record/user.rb +3 -0
  17. data/spec/rails_app/app/controllers/application_controller.rb +3 -0
  18. data/spec/rails_app/app/mongoid/old_user.rb +12 -0
  19. data/spec/rails_app/app/mongoid/user.rb +10 -0
  20. data/spec/rails_app/bin/bundle +109 -0
  21. data/spec/rails_app/bin/rails +4 -0
  22. data/spec/rails_app/bin/rake +4 -0
  23. data/spec/rails_app/bin/setup +33 -0
  24. data/spec/rails_app/config/application.rb +24 -0
  25. data/spec/rails_app/config/boot.rb +5 -0
  26. data/spec/rails_app/config/database.yml +14 -0
  27. data/spec/rails_app/config/environment.rb +7 -0
  28. data/spec/rails_app/config/initializers/devise.rb +6 -0
  29. data/spec/rails_app/config/mongoid.yml +10 -0
  30. data/spec/rails_app/config.ru +6 -0
  31. data/spec/rails_app/db/migrate/20230617201921_devise_create_users.rb +15 -0
  32. data/spec/rails_app/db/migrate/20231004084147_devise_create_old_users.rb +17 -0
  33. data/spec/rails_app/db/schema.rb +31 -0
  34. data/spec/spec_helper.rb +7 -3
  35. metadata +57 -25
  36. data/.travis.yml +0 -13
  37. data/lib/devise/encryptable/encryptors/argon2/version.rb +0 -7
  38. data/lib/devise/encryptable/encryptors/argon2.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6710d39a7495e895f798cb907b4d4e4d67fa0f39fd3141d2a4d76d6da0c0b7a4
4
- data.tar.gz: 48086d611acd10f4d91b2ccfa229bac0abe6f587abdae1a3b3df154c6d2d6408
3
+ metadata.gz: 6d20fef20b04b01fd33fd4e8b7104f50ef57ce75e6a4406df46a3ea10fe693a1
4
+ data.tar.gz: 7b66c6a1e646eea75a16c43ae37a95b557e6342488885a73ab60a80d3d5903a5
5
5
  SHA512:
6
- metadata.gz: 9e1be2f0b26ca41bb61ea3f42d1bfae79e59b1a670fe23574d1453138173caa8e4267266a169d9bfcc15bead58be9fb8009d70d183bf18a22967682ba58c095a
7
- data.tar.gz: 22671a23d33b2de4c6c5ee5e50d1e5eb6a46009c338612256138b8ad72ff05cef442854c9f6ceab4f7a42934fbbafad0d213ce898c9c6ce6916c58625bed0335
6
+ metadata.gz: 668de52215782a24691ec1d44ced5c85376d99099da8352c583a3452fb78d60e928fef311efe5bdfadffc5dfac130b1affabf9ce26b0197ea807d5602ed257d8
7
+ data.tar.gz: 12669cb6bbd94d3cc6e0427a6bf805152417f47858145daf6fba20907d32c10f9c8bedf4535a7ede8af0c572723886537ee782967e7b9b7e5566ec01c6f6ec27
@@ -0,0 +1,47 @@
1
+ name: Test suite
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ ruby-version: ['2.7', '3.0', '3.1', '3.2', 'ruby-head']
11
+ rails-version: ['~> 7.0', '~> 6.1']
12
+ orm:
13
+ - adapter: active_record
14
+ - adapter: mongoid
15
+ mongoid-version: 8.1.2
16
+ - adapter: mongoid
17
+ mongoid-version: 8.0.6
18
+ - adapter: mongoid
19
+ mongoid-version: 7.5.4
20
+ include:
21
+ - rails-version: '~> 7.1'
22
+ ruby-version: '3.1'
23
+ orm:
24
+ adapter: active_record
25
+ - rails-version: '~> 7.1'
26
+ ruby-version: '3.2'
27
+ orm:
28
+ adapter: active_record
29
+ env:
30
+ RAILS_VERSION: ${{ matrix.rails-version || '~> 7.0'}}
31
+ MONGOID_VERSION: ${{ matrix.orm.mongoid-version || '8.1.2'}}
32
+ ORM: ${{ matrix.orm.adapter }}
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+ - name: Set up Ruby ${{ matrix.ruby-version }}
36
+ uses: ruby/setup-ruby@v1
37
+ with:
38
+ ruby-version: ${{ matrix.ruby-version }}
39
+ bundler-cache: true
40
+ - uses: supercharge/mongodb-github-action@1.10.0
41
+ if: ${{ matrix.orm.adapter == 'mongoid' }}
42
+ - name: Setup rails test environment
43
+ run: |
44
+ cd spec/rails_app
45
+ RAILS_ENV=test bin/rails db:setup
46
+ - name: Run tests
47
+ run: bundle exec rspec
data/.gitignore CHANGED
@@ -12,6 +12,9 @@ lib/bundler/man
12
12
  pkg
13
13
  rdoc
14
14
  spec/reports
15
+ spec/rails_app/log/*
16
+ spec/rails_app/tmp/*
17
+ spec/rails_app/db/test.sqlite3
15
18
  test/tmp
16
19
  test/version_tmp
17
20
  tmp
data/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ### Added
6
+ - Expose Argon2 options for configuring hashing work factors
7
+ - Add support for migration v1 hashes
8
+ - Add support for migrating bcrypt hashes
9
+ - Add tests for Mongoid
10
+ - Add Changelog :)
11
+
12
+ ### Changed
13
+ - Change salting / peppering mechanism
14
+ - Change CI from Travis to GitHub Actions
15
+
16
+ ### Removed
17
+ - Remove `devise-encryptable` dependency
18
+ - Remove superflous dependency on devise `password_salt` column
19
+
20
+
data/Gemfile CHANGED
@@ -1,10 +1,13 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in gvoe_auth-client.gemspec
4
3
  gemspec
5
4
 
6
5
  gem 'rspec'
7
6
  gem 'simplecov'
8
- gem 'activesupport'
9
- gem 'activemodel'
10
- gem 'pry'
7
+ gem 'activerecord'
8
+ gem 'sqlite3'
9
+ gem 'rails', ENV['RAILS_VERSION'] || '~> 7.0'
10
+
11
+ if ENV['ORM'] == 'mongoid'
12
+ gem 'mongoid', ENV['MONGOID_VERSION'] || '~> 7.5'
13
+ end
data/README.md CHANGED
@@ -1,41 +1,124 @@
1
- devise-argon2 [![Build Status](https://secure.travis-ci.org/erdostom/devise-argon2.png)][Continuous Integration] [![Gem Version](https://badge.fury.io/rb/devise-argon2.svg)](https://badge.fury.io/rb/devise-argon2)
2
- =============
1
+ # devise-argon2
2
+ [![Gem Version](https://badge.fury.io/rb/devise-argon2.svg)](https://badge.fury.io/rb/devise-argon2)
3
3
 
4
- **A [devise-encryptable] password encryptor that uses [argon2]**
4
+ A ruby gem that gives Devise models which use `database_authenticatable` the ability to hash
5
+ passwords with Argon2id.
5
6
 
6
- * [Continuous Integration]
7
+ ## Installation
7
8
 
8
- [Continuous Integration]: http://travis-ci.org/erdostom/devise-argon2 "Continuous integration @ travis-ci.org"
9
-
10
- [argon2]: https://github.com/technion/ruby-argon2
11
- [devise]: https://github.com/plataformatec/devise
12
- [devise-encryptable]: https://github.com/plataformatec/devise-encryptable
13
-
14
- ## Why use Argon2?
15
-
16
- Argon2 won Password Hashing Competition and offers additional security compared to the default `bcrypt` by adding a memory cost. More info:
17
-
18
- - https://github.com/P-H-C/phc-winner-argon2
19
- - https://hynek.me/articles/storing-passwords/
9
+ ```
10
+ bundle add devise-argon2
11
+ ```
20
12
 
21
13
  ## Usage
22
14
 
23
- Assuming you have [devise] (>= 2.1) and the [devise-encryptable] plugin
24
- set up in your application, add `devise-argon2` to your `Gemfile` and `bundle`:
25
-
26
- gem 'devise-argon2'
15
+ Add `devise :argon2` to your Devise model. For example:
16
+
17
+ ```
18
+ class User < ApplicationRecord
19
+ devise :database_authenticatable, :argon2
20
+ end
21
+ ```
22
+
23
+ Now the password of a newly created user will be hashed with Argon2id. Existing BCrypt hashes will
24
+ continue to work; if the password of a user is hashed with BCrypt, the Argon2id hash will replace
25
+ the existing hash as soon as a user signs in (more specifically: as soon as `valid_password?`
26
+ is called with a valid password).
27
+
28
+ ## Configuration
29
+
30
+ ### Argon2 options
31
+
32
+ For Argon2 hashing the gem [ruby-argon2](https://github.com/technion/ruby-argon2) is used, which
33
+ provides FFI bindings to the
34
+ [Argon 2 reference implementation](https://github.com/P-H-C/phc-winner-argon2).
35
+ `ruby-argon2` can be configured by passing parameters like `profile`, `t_cost`, `m_cost`, `p_cost`,
36
+ or `secret` to `Argon2::Password.new`. These parameters can be set like this:
37
+
38
+ ```
39
+ class User < ApplicationRecord
40
+ devise :database_authenticatable,
41
+ :argon2,
42
+ argon2_options: { t_cost: 3, p_cost: 2 }
43
+ end
44
+ ```
45
+
46
+ If the the configured work factors differ from the work factors of the hash in the database, the
47
+ password will be re-hashed as soon as `valid_password?` is called with a valid password.
48
+
49
+ ### Pepper/secret key
50
+
51
+ The [Argon 2 reference implementation](https://github.com/P-H-C/phc-winner-argon2#library) has a
52
+ built-in pepper which is called `secret`. This Argon2 secret key can be set like this:
53
+
54
+ ```
55
+ class User < ApplicationRecord
56
+ devise :database_authenticatable,
57
+ :argon2,
58
+ argon2_options: { secret: ENV['ARGON2_SECRET_KEY'] }
59
+ end
60
+ ```
61
+
62
+ Traditionally, peppers in Devise are configured by setting `config.pepper` in `devise.rb`. This
63
+ option in honored but `argon2_options[:secret]` takes precedence over `config.pepper`. Specifically:
64
+ - `config.pepper` is used as secret key for new hashes if and only if `argon2_options[:secret]` is
65
+ not set.
66
+ - The verification of existing BCrypt hashes is not touched, so it continues to use `config.pepper`
67
+ as pepper.
68
+
69
+ ## Updating from version 1
70
+
71
+ With version 2 come two major changes: First, `devise-encryptable` is no longer needed. Second, the mechanism for salting
72
+ and peppering has changed: Salts are now managed by Argon2 and the pepper is passed as secret key
73
+ parameter. If you have existing hashes in your database that have been generated by
74
+ devise-argon2 v1, you'll need to set `:migrate_from_devise_argon2_v1` in `argon2_options`.
75
+
76
+ With this option your existing hashes will continue to work as the old mechanism for salting and
77
+ peppering is used if and only if `password_salt` is truthy. The first time you pass a valid
78
+ password to `valid_password?`, the hash will be updated and `password_salt` will be set to `nil`.
79
+ The next time you call `valid_password?` the new salting and peppering mechanism will be used
80
+ because `password_salt` is not truthy anymore.
81
+
82
+ As soon as all `password_salt` fields are set to `nil`, you can delete the column from the database
83
+ and remove `:migrate_from_devise_argon2_v1` from `argon2_options`.
84
+
85
+ Please note that this works only if your database table has a field `password_salt`.
86
+
87
+ ### Upgrade Steps
88
+
89
+ 1. Update your `Gemfile` to use `devise-argon2` version 2: `gem 'devise-argon2', '~> 2.0'`
90
+ 1. Remove `devise-encryptable` from your `Gemfile`
91
+ 1. Run `bundle install`
92
+ 1. Remove the line `config.encryptor = :argon2` from `config/initializers/devise.rb`
93
+ 1. Change your Devise model by removing `:encryptable` and adding `:argon2, argon2_options: { migrate_from_devise_argon2_v1: true }`
94
+ 1. It should now look something like this
95
+
96
+ ```
97
+ class User < ApplicationRecord
98
+ devise :database_authenticatable,
99
+ :argon2,
100
+ argon2_options: { migrate_from_devise_argon2_v1: true }
101
+ end
102
+ ```
103
+
104
+ That's it, you're done! Your users will now be able to log in with their existing passwords and their passwords will be migrated to the V2 format the next time they log in.
105
+
106
+ ---
107
+
108
+ Once all of your users' passwords are migrated to the V2 format:
109
+ 1. Remove the `argon2_options { migrated_from_devise_argon2_v1: true }` line from your Devise model
110
+ 1. Delete the `password_salt` column from your database using a migration like this:
111
+ ```
112
+ class RemovePasswordSaltFromUsers < ActiveRecord::Migration[7.1]
113
+ def change
114
+ remove_column :users, :password_salt, :string
115
+ end
116
+ end
117
+ ```
118
+
119
+ Note: If you do this before all of your users' passwords are migrated to the V2 format, they will be unable to log
120
+ in with their current passwords.
27
121
 
28
- Then open up your [devise] configuration,`config/initializers/devise.rb` and configure your encryptor to be `argon2`:
29
-
30
- # config/initializers/devise.rb
31
- Devise.setup do |config|
32
- # ..
33
- config.encryptor = :argon2
34
- # ...
35
- end
36
-
37
- It is also recommended to uncomment (or add) `config.pepper` with a random
38
- string that will be used in addition to the per-user `password_salt` when hashing.
39
122
 
40
123
  ## Contributing
41
124
 
@@ -45,6 +128,10 @@ string that will be used in addition to the per-user `password_salt` when hashin
45
128
  4. Push to the branch (`git push origin my-new-feature`)
46
129
  5. Create new Pull Request
47
130
 
48
- ## Copyright
131
+ ## Contributors
132
+
133
+ Please see here for full list of contributors: https://github.com/erdostom/devise-argon2/graphs/contributors
134
+
135
+ ## License
49
136
 
50
137
  Released under MIT License.
@@ -1,23 +1,26 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "devise/encryptable/encryptors/argon2/version"
4
+ require "devise-argon2/version"
5
5
 
6
6
  Gem::Specification.new do |gem|
7
- gem.name = "devise-argon2"
8
- gem.version = Devise::Encryptable::Encryptors::ARGON2_VERSION
9
- gem.authors = ["Tamas Erdos"]
10
- gem.email = ["tamas at tamaserdos com"]
11
- gem.description = %q{A devise-encryptable password encryptor that uses Argon2}
12
- gem.summary = %q{A devise-encryptable password encryptor that uses Argon2}
13
- gem.homepage = "https://github.com/erdostom/devise-argon2"
7
+ gem.name = "devise-argon2"
8
+ gem.version = Devise::Argon2::ARGON2_VERSION
9
+ gem.authors = ["Tamas Erdos"]
10
+ gem.email = ["tamas at tamaserdos com"]
11
+ gem.description = %q{Enables Devise to hash passwords with Argon2id}
12
+ gem.summary = %q{Enables Devise to hash passwords with Argon2id}
13
+ gem.license = 'MIT'
14
+ gem.homepage = "https://github.com/erdostom/devise-argon2"
14
15
 
15
- gem.files = `git ls-files`.split($/)
16
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
19
  gem.require_paths = ["lib"]
19
20
 
20
21
  gem.add_dependency 'devise', '>= 2.1.0'
21
- gem.add_dependency 'devise-encryptable', '>= 0.2.0'
22
22
  gem.add_dependency 'argon2', '~> 2.0'
23
+
24
+
25
+ gem.post_install_message = "Version 2 of devise-argon2 introduces breaking changes, please see README.md for details."
23
26
  end
@@ -0,0 +1,89 @@
1
+ require 'argon2'
2
+
3
+ module Devise
4
+ module Models
5
+ module Argon2
6
+ def valid_password?(password)
7
+ is_valid = hash_needs_update = false
8
+
9
+ if ::Argon2::Password.valid_hash?(encrypted_password)
10
+ if migrate_hash_from_devise_argon2_v1?
11
+ is_valid = ::Argon2::Password.verify_password(
12
+ "#{password}#{password_salt}#{self.class.pepper}",
13
+ encrypted_password
14
+ )
15
+ hash_needs_update = true
16
+ else
17
+ argon2_secret = (self.class.argon2_options[:secret] || self.class.pepper)
18
+ is_valid = ::Argon2::Password.verify_password(
19
+ password,
20
+ encrypted_password,
21
+ argon2_secret
22
+ )
23
+ hash_needs_update = outdated_work_factors?
24
+ end
25
+ else
26
+ # Devise models are included in a fixed order, see
27
+ # https://github.com/heartcombo/devise/blob/f6e73e5b5c8f519f4be29ac9069c6ed8a2343ce4/lib/devise/models.rb#L79.
28
+ # Since we don't specify where this model should be inserted when we call add_module,
29
+ # it will be inserted at the end, i.e. after DatabaseAuthenticatable (see
30
+ # https://github.com/heartcombo/devise/blob/f6e73e5b5c8f519f4be29ac9069c6ed8a2343ce4/lib/devise.rb#L393).
31
+ # So we can call DatabaseAuthenticable's valid_password? with super.
32
+ is_valid = super
33
+ hash_needs_update = true
34
+ end
35
+
36
+ update_hash(password) if is_valid && hash_needs_update
37
+
38
+ is_valid
39
+ end
40
+
41
+ protected
42
+
43
+ def password_digest(password)
44
+ hasher_options = self.class.argon2_options.except(:migrate_from_devise_argon2_v1)
45
+ hasher_options[:secret] ||= self.class.pepper
46
+ hasher = ::Argon2::Password.new(hasher_options)
47
+ hasher.create(password)
48
+ end
49
+
50
+ private
51
+
52
+ def update_hash(password)
53
+ attributes = { encrypted_password: password_digest(password) }
54
+ attributes[:password_salt] = nil if migrate_hash_from_devise_argon2_v1?
55
+
56
+ self.assign_attributes(attributes)
57
+ self.save if self.persisted?
58
+ end
59
+
60
+ def outdated_work_factors?
61
+ # Since version 2.3.0 the argon2 gem exposes the default work factors via constants, see
62
+ # 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
66
+
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)
72
+
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
76
+ end
77
+
78
+ def migrate_hash_from_devise_argon2_v1?
79
+ self.class.argon2_options[:migrate_from_devise_argon2_v1] &&
80
+ defined?(password_salt) &&
81
+ password_salt
82
+ end
83
+
84
+ module ClassMethods
85
+ Devise::Models.config(self, :argon2_options)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,5 @@
1
+ module Devise
2
+ module Argon2
3
+ ARGON2_VERSION = '2.0.0'
4
+ end
5
+ end
data/lib/devise-argon2.rb CHANGED
@@ -1,4 +1,9 @@
1
1
  require 'devise'
2
- require 'devise-encryptable'
3
- require "devise/encryptable/encryptors/argon2/version"
4
- require "devise/encryptable/encryptors/argon2"
2
+ require 'devise-argon2/version'
3
+
4
+ module Devise
5
+ add_module(:argon2, :model => 'devise-argon2/model')
6
+
7
+ mattr_accessor :argon2_options
8
+ @@argon2_options = {}
9
+ end
@@ -1,50 +1,247 @@
1
1
  # encoding: utf-8
2
2
  require 'spec_helper'
3
+ require 'bcrypt'
3
4
 
4
- describe Devise::Encryptable::Encryptors::Argon2 do
5
- let(:argon2) { Devise::Encryptable::Encryptors::Argon2 }
6
- let(:password) { 'Tr0ub4dor&3' }
7
- let(:stretches) { 10 }
5
+ describe Devise::Models::Argon2 do
6
+ CORRECT_PASSWORD = 'Tr0ub4dor&3'
7
+ INCORRECT_PASSWORD = 'wrong'
8
+ DEFAULT_M_COST = 3
9
+ DEFAULT_T_COST = 1
10
+ DEFAULT_P_COST = 1
8
11
 
9
- describe "used with salt + pepper" do
10
- let(:salt) { "You say you love me like salt! The simplest spice in my kingdom!" }
11
- let(:pepper) { "I don't really want to stop the show But I thought that you might like to know That the singer's going to sing a song And he wants you all to sing along" }
12
+ let(:user) { User.new(password: CORRECT_PASSWORD) }
12
13
 
13
- describe ".compare" do
14
- let(:encrypted) { Argon2::Password.create("#{password}#{salt}#{pepper}").to_s }
14
+ before do
15
+ Devise.pepper = nil
16
+ Devise.argon2_options = {
17
+ m_cost: DEFAULT_M_COST,
18
+ t_cost: DEFAULT_T_COST,
19
+ p_cost: DEFAULT_P_COST
20
+ }
21
+ User.destroy_all
22
+ end
23
+
24
+ def work_factors(hash)
25
+ hash_format = Argon2::HashFormat.new(hash)
26
+ {
27
+ m_cost: hash_format.m_cost,
28
+ t_cost: hash_format.t_cost,
29
+ p_cost: hash_format.p_cost
30
+ }
31
+ end
15
32
 
16
- it "is true when the encrypted password contains the argon2id format" do
17
- expect(encrypted).to match /argon2id/
18
- end
33
+ describe 'valid_password?' do
34
+ shared_examples 'a password is validated if and only if it is correct' do
35
+ it 'validates the correct password' do
36
+ expect(user.valid_password?(CORRECT_PASSWORD)).to be true
37
+ end
19
38
 
20
- it "is true when comparing an encrypted password against given plaintext" do
21
- expect(argon2.compare(encrypted, password, stretches, salt, pepper)).to be true
39
+ it 'does not validate an incorrect password' do
40
+ expect(user.valid_password?(INCORRECT_PASSWORD)).to be false
22
41
  end
42
+ end
43
+
44
+ context 'no pepper' do
45
+ include_examples 'a password is validated if and only if it is correct'
46
+ end
47
+
48
+ context 'Devise.pepper is set' do
49
+ before do
50
+ Devise.pepper = 'pepper'
51
+ end
52
+
53
+ include_examples 'a password is validated if and only if it is correct'
54
+ end
55
+
56
+ context 'argon2_options[:secret] is set' do
57
+ before do
58
+ Devise.argon2_options[:secret] = 'pepper'
59
+ end
60
+
61
+ include_examples 'a password is validated if and only if it is correct'
62
+ end
23
63
 
24
- it "is false when comparing with wrong password" do
25
- expect(argon2.compare(encrypted, 'hunter2', stretches, salt, pepper)).to be false
64
+ context 'encrypted_password is a BCrypt hash' do
65
+ before do
66
+ Devise.pepper = 'devise pepper'
67
+ Devise.argon2_options.merge!({ secret: 'argon2 secret' })
68
+
69
+ # Devise peppers by concatenating password and pepper:
70
+ # https://github.com/heartcombo/devise/blob/main/lib/devise/encryptor.rb
71
+ bcrypt_hash = BCrypt::Password.create("#{CORRECT_PASSWORD}#{Devise.pepper}", cost: 4)
72
+
73
+ user.encrypted_password = bcrypt_hash
26
74
  end
27
75
 
28
- it "is false when comparing with correct password but wrong salt" do
29
- expect(argon2.compare(encrypted, password, stretches, 'nacl', pepper)).to be false
76
+ include_examples 'a password is validated if and only if it is correct'
77
+
78
+ it 'updates hash if valid password is given' do
79
+ expect{ user.valid_password?(CORRECT_PASSWORD) }.to(change(user, :encrypted_password))
80
+ expect(
81
+ ::Argon2::Password.verify_password(
82
+ CORRECT_PASSWORD,
83
+ user.encrypted_password,
84
+ 'argon2 secret'
85
+ )
86
+ ).to be true
30
87
  end
31
88
 
32
- it "is false when comparing with correct password but wrong pepper" do
33
- expect(argon2.compare(encrypted, password, stretches, salt, 'beatles')).to be false
89
+ it 'does not update the hash if an invalid password is given' do
90
+ expect{ user.valid_password?(INCORRECT_PASSWORD) }.not_to(change(user, :encrypted_password))
34
91
  end
35
92
  end
36
93
 
37
- describe "without any salt or pepper" do
38
- let(:encrypted) { Argon2::Password.create(password).to_s }
39
- let(:salt) { nil }
40
- let(:pepper) { nil }
41
- let(:encrypted) { Argon2::Password.create(password).to_s }
94
+ context 'encrypted_password is hashed with version 1 of devise-argon2' do
95
+ let(:user) { OldUser.new(password: CORRECT_PASSWORD) }
96
+
97
+ before do
98
+ Devise.pepper = 'devise pepper'
99
+ Devise.argon2_options.merge!({
100
+ secret: 'argon2 secret',
101
+ migrate_from_devise_argon2_v1: true
102
+ })
42
103
 
43
- it "is still works" do
44
- expect(argon2.compare(encrypted, password, stretches, salt, pepper)).to be true
104
+ user.password_salt = 'devise-argon2 v1 salt'
105
+ user.encrypted_password = ::Argon2::Password.create(
106
+ "#{CORRECT_PASSWORD}#{user.password_salt}#{Devise.pepper}"
107
+ )
108
+ end
109
+
110
+ include_examples 'a password is validated if and only if it is correct'
111
+
112
+ it 'updates hash once if valid password is given' do
113
+ expect{ user.valid_password?(CORRECT_PASSWORD) }.to(
114
+ change(user, :encrypted_password)
115
+ .and(change(user, :password_salt).to(nil))
116
+ )
117
+ expect(
118
+ ::Argon2::Password.verify_password(
119
+ CORRECT_PASSWORD,
120
+ user.encrypted_password,
121
+ 'argon2 secret'
122
+ )
123
+ ).to be true
124
+ expect{ user.valid_password?(CORRECT_PASSWORD) }.not_to(change(user, :encrypted_password))
125
+ end
126
+
127
+ it 'does not update the hash if an invalid password is given' do
128
+ expect{ user.valid_password?(INCORRECT_PASSWORD) }.not_to(change(user, :encrypted_password))
45
129
  end
46
130
  end
47
131
 
132
+ describe 'updating outdated work factors' do
133
+ it 'updates work factors if a valid password is given' do
134
+ user # build user
135
+
136
+ Devise.argon2_options.merge!({
137
+ m_cost: 4,
138
+ t_cost: 3,
139
+ p_cost: 2
140
+ })
141
+
142
+ expect{ user.valid_password?(CORRECT_PASSWORD) }.to(
143
+ change{ work_factors(user.encrypted_password) }
144
+ .from({ m_cost: 1 << DEFAULT_M_COST, t_cost: DEFAULT_T_COST, p_cost: DEFAULT_P_COST})
145
+ .to({ m_cost: 1 << 4, t_cost: 3, p_cost: 2 })
146
+ )
147
+ end
148
+
149
+ it 'does not update work factors if an invalid password is given' do
150
+ user # build user
151
+
152
+ Devise.argon2_options.merge!({
153
+ m_cost: 4,
154
+ t_cost: 3,
155
+ p_cost: 2
156
+ })
157
+
158
+ expect{ user.valid_password?(INCORRECT_PASSWORD) }
159
+ .not_to change{ work_factors(user.encrypted_password) }
160
+ end
161
+
162
+ it 'updates work factors for a persisted user' do
163
+ user.save!
164
+
165
+ Devise.argon2_options.merge!({
166
+ m_cost: 4,
167
+ t_cost: 3,
168
+ p_cost: 2
169
+ })
170
+
171
+ expect{ user.valid_password?(CORRECT_PASSWORD) }.to(
172
+ change{ work_factors(user.encrypted_password) }
173
+ .from({ m_cost: 1 << DEFAULT_M_COST, t_cost: DEFAULT_T_COST, p_cost: DEFAULT_P_COST})
174
+ .to({ m_cost: 1 << 4, t_cost: 3, p_cost: 2 })
175
+ )
176
+ end
177
+ end
178
+
179
+ it 'ignores migrate_from_devise_argon2_v1 if password_salt is not present' do
180
+ Devise.argon2_options.merge!({ migrate_from_devise_argon2_v1: true })
181
+ expect{ user.valid_password?(CORRECT_PASSWORD) }.not_to(change(user, :encrypted_password))
182
+ end
48
183
  end
49
184
 
185
+ describe 'password_digest' do
186
+ context 'no pepper' do
187
+ it 'hashes the given password with Argon2' do
188
+ expect(
189
+ Argon2::Password.verify_password(CORRECT_PASSWORD, user.encrypted_password)
190
+ ).to be true
191
+ end
192
+ end
193
+
194
+ context 'Devise.pepper is set' do
195
+ before do
196
+ Devise.pepper = 'pepper'
197
+ end
198
+
199
+ it 'uses Devise.pepper as secret key for Argon2' do
200
+ expect(
201
+ Argon2::Password.verify_password(CORRECT_PASSWORD, user.encrypted_password, 'pepper')
202
+ ).to be true
203
+ end
204
+ end
205
+
206
+ context 'argon2_options[:secret] is set' do
207
+ before do
208
+ Devise.argon2_options[:secret] = 'pepper'
209
+ end
210
+
211
+ it 'uses argon2_options[:secret] as secret key for Argon2' do
212
+ expect(
213
+ Argon2::Password.verify_password(CORRECT_PASSWORD, user.encrypted_password, 'pepper')
214
+ ).to be true
215
+ end
216
+ end
217
+
218
+ context 'both Devise.pepper and argon2_options[:secret] are set' do
219
+ before do
220
+ Devise.pepper = 'devise pepper'
221
+ Devise.argon2_options[:secret] = 'argon2_options pepper'
222
+ end
223
+
224
+ it 'uses argon2_options[:secret] as secret key for Argon2' do
225
+ expect(
226
+ Argon2::Password.verify_password(
227
+ CORRECT_PASSWORD,
228
+ user.encrypted_password,
229
+ 'argon2_options pepper'
230
+ )
231
+ ).to be true
232
+ end
233
+ end
234
+
235
+ it 'uses work factors given in argon2_options' do
236
+ Devise.argon2_options.merge!({
237
+ m_cost: 4,
238
+ t_cost: 3,
239
+ p_cost: 2
240
+ })
241
+
242
+ expect(work_factors(user.encrypted_password)).to eq(
243
+ { m_cost: 1 << 4, t_cost: 3, p_cost: 2 }
244
+ )
245
+ end
246
+ end
50
247
  end
@@ -0,0 +1 @@
1
+ ActiveRecord::Base.logger = Logger.new(nil)
@@ -0,0 +1,5 @@
1
+ require 'mongoid/version'
2
+
3
+ Mongoid.configure do |config|
4
+ config.load!('spec/rails_app/config/mongoid.yml')
5
+ end
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative "config/application"
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+ class OldUser < ActiveRecord::Base
2
+ devise :database_authenticatable, :argon2
3
+ end
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ devise :database_authenticatable, :argon2
3
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationController < ActionController::Base
2
+
3
+ end
@@ -0,0 +1,12 @@
1
+ class OldUser
2
+ include Mongoid::Document
3
+
4
+ devise :database_authenticatable, :argon2
5
+
6
+ field :email, type: String, default: ""
7
+ field :encrypted_password, type: String, default: ""
8
+
9
+ field :password_salt, type: String, default: ""
10
+
11
+ include Mongoid::Timestamps
12
+ end
@@ -0,0 +1,10 @@
1
+ class User
2
+ include Mongoid::Document
3
+
4
+ devise :database_authenticatable, :argon2
5
+
6
+ field :email, type: String, default: ""
7
+ field :encrypted_password, type: String, default: ""
8
+
9
+ include Mongoid::Timestamps
10
+ end
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'bundle' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "rubygems"
12
+
13
+ m = Module.new do
14
+ module_function
15
+
16
+ def invoked_as_script?
17
+ File.expand_path($0) == File.expand_path(__FILE__)
18
+ end
19
+
20
+ def env_var_version
21
+ ENV["BUNDLER_VERSION"]
22
+ end
23
+
24
+ def cli_arg_version
25
+ return unless invoked_as_script? # don't want to hijack other binstubs
26
+ return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
27
+ bundler_version = nil
28
+ update_index = nil
29
+ ARGV.each_with_index do |a, i|
30
+ if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
31
+ bundler_version = a
32
+ end
33
+ next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
34
+ bundler_version = $1
35
+ update_index = i
36
+ end
37
+ bundler_version
38
+ end
39
+
40
+ def gemfile
41
+ gemfile = ENV["BUNDLE_GEMFILE"]
42
+ return gemfile if gemfile && !gemfile.empty?
43
+
44
+ File.expand_path("../Gemfile", __dir__)
45
+ end
46
+
47
+ def lockfile
48
+ lockfile =
49
+ case File.basename(gemfile)
50
+ when "gems.rb" then gemfile.sub(/\.rb$/, ".locked")
51
+ else "#{gemfile}.lock"
52
+ end
53
+ File.expand_path(lockfile)
54
+ end
55
+
56
+ def lockfile_version
57
+ return unless File.file?(lockfile)
58
+ lockfile_contents = File.read(lockfile)
59
+ return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
60
+ Regexp.last_match(1)
61
+ end
62
+
63
+ def bundler_requirement
64
+ @bundler_requirement ||=
65
+ env_var_version ||
66
+ cli_arg_version ||
67
+ bundler_requirement_for(lockfile_version)
68
+ end
69
+
70
+ def bundler_requirement_for(version)
71
+ return "#{Gem::Requirement.default}.a" unless version
72
+
73
+ bundler_gem_version = Gem::Version.new(version)
74
+
75
+ bundler_gem_version.approximate_recommendation
76
+ end
77
+
78
+ def load_bundler!
79
+ ENV["BUNDLE_GEMFILE"] ||= gemfile
80
+
81
+ activate_bundler
82
+ end
83
+
84
+ def activate_bundler
85
+ gem_error = activation_error_handling do
86
+ gem "bundler", bundler_requirement
87
+ end
88
+ return if gem_error.nil?
89
+ require_error = activation_error_handling do
90
+ require "bundler/version"
91
+ end
92
+ return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
93
+ warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
94
+ exit 42
95
+ end
96
+
97
+ def activation_error_handling
98
+ yield
99
+ nil
100
+ rescue StandardError, LoadError => e
101
+ e
102
+ end
103
+ end
104
+
105
+ m.load_bundler!
106
+
107
+ if m.invoked_as_script?
108
+ load Gem.bin_path("bundler", "bundle")
109
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path("../config/application", __dir__)
3
+ require_relative "../config/boot"
4
+ require "rails/commands"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../config/boot"
3
+ require "rake"
4
+ Rake.application.run
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+ require "fileutils"
3
+
4
+ # path to your application root.
5
+ APP_ROOT = File.expand_path("..", __dir__)
6
+
7
+ def system!(*args)
8
+ system(*args) || abort("\n== Command #{args} failed ==")
9
+ end
10
+
11
+ FileUtils.chdir APP_ROOT do
12
+ # This script is a way to set up or update your development environment automatically.
13
+ # This script is idempotent, so that you can run it at any time and get an expectable outcome.
14
+ # Add necessary setup steps to this file.
15
+
16
+ puts "== Installing dependencies =="
17
+ system! "gem install bundler --conservative"
18
+ system("bundle check") || system!("bundle install")
19
+
20
+ # puts "\n== Copying sample files =="
21
+ # unless File.exist?("config/database.yml")
22
+ # FileUtils.cp "config/database.yml.sample", "config/database.yml"
23
+ # end
24
+
25
+ puts "\n== Preparing database =="
26
+ system! "bin/rails db:prepare"
27
+
28
+ puts "\n== Removing old logs and tempfiles =="
29
+ system! "bin/rails log:clear tmp:clear"
30
+
31
+ puts "\n== Restarting application server =="
32
+ system! "bin/rails restart"
33
+ end
@@ -0,0 +1,24 @@
1
+ ORM = (ENV['ORM'] || 'active_record')
2
+
3
+ require "rails"
4
+
5
+ Bundler.require :default, ORM
6
+
7
+ require "action_controller/railtie"
8
+
9
+ require "#{ORM}/railtie"
10
+ require "action_mailer/railtie"
11
+ require 'devise-argon2'
12
+
13
+ # Require the gems listed in Gemfile, including any gems
14
+ # you've limited to :test, :development, or :production.
15
+ Bundler.require(*Rails.groups)
16
+
17
+ module DummyRailsApp
18
+ class Application < Rails::Application
19
+ config.load_defaults Rails.version.match(/^\d.\d/)[0]
20
+ config.eager_load = false
21
+ config.autoload_paths.reject!{ |p| p =~ /\/app\/(\w+)$/ && !%w(controllers helpers mailers views).include?($1) }
22
+ config.autoload_paths += ["#{config.root}/app/#{ORM}"]
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__)
2
+
3
+ require "bundler/setup" # Set up gems listed in the Gemfile.
4
+
5
+ $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__)
@@ -0,0 +1,14 @@
1
+ # SQLite. Versions 3.8.0 and up are supported.
2
+ # gem install sqlite3
3
+ #
4
+ # Ensure the SQLite 3 gem is defined in your Gemfile
5
+ # gem "sqlite3"
6
+ #
7
+ default: &default
8
+ adapter: sqlite3
9
+ pool: 5
10
+ timeout: 5000
11
+
12
+ test:
13
+ <<: *default
14
+ database: db/test.sqlite3
@@ -0,0 +1,7 @@
1
+ ENV['RAILS_ENV'] = 'test'
2
+
3
+ # Load the Rails application.
4
+ require_relative "application"
5
+
6
+ # Initialize the Rails application.
7
+ Rails.application.initialize!
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ Devise.setup do |config|
4
+ require "devise/orm/#{ENV['ORM'] || 'active_record'}"
5
+ config.stretches = 1
6
+ end
@@ -0,0 +1,10 @@
1
+ test:
2
+ clients:
3
+ default:
4
+ database: dummy_rails_app_test
5
+ hosts:
6
+ - localhost:27017
7
+ options:
8
+ read:
9
+ mode: :primary
10
+ max_pool_size: 1
@@ -0,0 +1,6 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require_relative "config/environment"
4
+
5
+ run Rails.application
6
+ Rails.application.load_server
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DeviseCreateUsers < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :users do |t|
6
+ ## Database authenticatable
7
+ t.string :email, null: false, default: ""
8
+ t.string :encrypted_password, null: false, default: ""
9
+
10
+ t.timestamps null: false
11
+ end
12
+
13
+ add_index :users, :email, unique: true
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DeviseCreateOldUsers < ActiveRecord::Migration[7.0]
4
+ def change
5
+ create_table :old_users do |t|
6
+ ## Database authenticatable
7
+ t.string :email, null: false, default: ""
8
+ t.string :encrypted_password, null: false, default: ""
9
+
10
+ t.string :password_salt
11
+
12
+ t.timestamps null: false
13
+ end
14
+
15
+ add_index :old_users, :email, unique: true
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ # This file is auto-generated from the current state of the database. Instead
2
+ # of editing this file, please use the migrations feature of Active Record to
3
+ # incrementally modify your database, and then regenerate this schema definition.
4
+ #
5
+ # This file is the source Rails uses to define your schema when running `bin/rails
6
+ # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
7
+ # be faster and is potentially less error prone than running all of your
8
+ # migrations from scratch. Old migrations may fail to apply correctly if those
9
+ # migrations use external dependencies or application code.
10
+ #
11
+ # It's strongly recommended that you check this file into your version control system.
12
+
13
+ ActiveRecord::Schema.define(version: 2023_10_04_084147) do
14
+ create_table "old_users", force: :cascade do |t|
15
+ t.string "email", default: "", null: false
16
+ t.string "encrypted_password", default: "", null: false
17
+ t.string "password_salt"
18
+ t.datetime "created_at", null: false
19
+ t.datetime "updated_at", null: false
20
+ t.index ["email"], name: "index_old_users_on_email", unique: true
21
+ end
22
+
23
+ create_table "users", force: :cascade do |t|
24
+ t.string "email", default: "", null: false
25
+ t.string "encrypted_password", default: "", null: false
26
+ t.datetime "created_at", null: false
27
+ t.datetime "updated_at", null: false
28
+ t.index ["email"], name: "index_users_on_email", unique: true
29
+ end
30
+
31
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,13 @@
1
1
  require 'rubygems'
2
2
  require 'simplecov'
3
3
  SimpleCov.start
4
- require 'pry'
5
4
  require 'bundler/setup'
6
- require 'devise-argon2'
5
+
6
+ require 'rails_app/config/environment'
7
+ ORM = (ENV['ORM'] || 'active_record')
8
+ require "orm/#{ORM}"
9
+
10
+
7
11
 
8
12
  RSpec.configure do |config|
9
- end
13
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise-argon2
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tamas Erdos
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-30 00:00:00.000000000 Z
11
+ date: 2023-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: devise
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 2.1.0
27
- - !ruby/object:Gem::Dependency
28
- name: devise-encryptable
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 0.2.0
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: 0.2.0
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: argon2
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,30 +38,55 @@ dependencies:
52
38
  - - "~>"
53
39
  - !ruby/object:Gem::Version
54
40
  version: '2.0'
55
- description: A devise-encryptable password encryptor that uses Argon2
41
+ description: Enables Devise to hash passwords with Argon2id
56
42
  email:
57
43
  - tamas at tamaserdos com
58
44
  executables: []
59
45
  extensions: []
60
46
  extra_rdoc_files: []
61
47
  files:
48
+ - ".github/workflows/test.yml"
62
49
  - ".gitignore"
63
50
  - ".rspec"
64
- - ".travis.yml"
51
+ - CHANGELOG.md
65
52
  - Gemfile
66
53
  - LICENSE.txt
67
54
  - README.md
68
55
  - Rakefile
69
56
  - devise-argon2.gemspec
70
57
  - lib/devise-argon2.rb
71
- - lib/devise/encryptable/encryptors/argon2.rb
72
- - lib/devise/encryptable/encryptors/argon2/version.rb
58
+ - lib/devise-argon2/model.rb
59
+ - lib/devise-argon2/version.rb
73
60
  - spec/devise-argon2_spec.rb
61
+ - spec/orm/active_record.rb
62
+ - spec/orm/mongoid.rb
63
+ - spec/rails_app/Rakefile
64
+ - spec/rails_app/app/active_record/old_user.rb
65
+ - spec/rails_app/app/active_record/user.rb
66
+ - spec/rails_app/app/controllers/application_controller.rb
67
+ - spec/rails_app/app/mongoid/old_user.rb
68
+ - spec/rails_app/app/mongoid/user.rb
69
+ - spec/rails_app/bin/bundle
70
+ - spec/rails_app/bin/rails
71
+ - spec/rails_app/bin/rake
72
+ - spec/rails_app/bin/setup
73
+ - spec/rails_app/config.ru
74
+ - spec/rails_app/config/application.rb
75
+ - spec/rails_app/config/boot.rb
76
+ - spec/rails_app/config/database.yml
77
+ - spec/rails_app/config/environment.rb
78
+ - spec/rails_app/config/initializers/devise.rb
79
+ - spec/rails_app/config/mongoid.yml
80
+ - spec/rails_app/db/migrate/20230617201921_devise_create_users.rb
81
+ - spec/rails_app/db/migrate/20231004084147_devise_create_old_users.rb
82
+ - spec/rails_app/db/schema.rb
74
83
  - spec/spec_helper.rb
75
84
  homepage: https://github.com/erdostom/devise-argon2
76
- licenses: []
85
+ licenses:
86
+ - MIT
77
87
  metadata: {}
78
- post_install_message:
88
+ post_install_message: Version 2 of devise-argon2 introduces breaking changes, please
89
+ see README.md for details.
79
90
  rdoc_options: []
80
91
  require_paths:
81
92
  - lib
@@ -90,11 +101,32 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
101
  - !ruby/object:Gem::Version
91
102
  version: '0'
92
103
  requirements: []
93
- rubyforge_project:
94
- rubygems_version: 2.7.8
104
+ rubygems_version: 3.3.3
95
105
  signing_key:
96
106
  specification_version: 4
97
- summary: A devise-encryptable password encryptor that uses Argon2
107
+ summary: Enables Devise to hash passwords with Argon2id
98
108
  test_files:
99
109
  - spec/devise-argon2_spec.rb
110
+ - spec/orm/active_record.rb
111
+ - spec/orm/mongoid.rb
112
+ - spec/rails_app/Rakefile
113
+ - spec/rails_app/app/active_record/old_user.rb
114
+ - spec/rails_app/app/active_record/user.rb
115
+ - spec/rails_app/app/controllers/application_controller.rb
116
+ - spec/rails_app/app/mongoid/old_user.rb
117
+ - spec/rails_app/app/mongoid/user.rb
118
+ - spec/rails_app/bin/bundle
119
+ - spec/rails_app/bin/rails
120
+ - spec/rails_app/bin/rake
121
+ - spec/rails_app/bin/setup
122
+ - spec/rails_app/config.ru
123
+ - spec/rails_app/config/application.rb
124
+ - spec/rails_app/config/boot.rb
125
+ - spec/rails_app/config/database.yml
126
+ - spec/rails_app/config/environment.rb
127
+ - spec/rails_app/config/initializers/devise.rb
128
+ - spec/rails_app/config/mongoid.yml
129
+ - spec/rails_app/db/migrate/20230617201921_devise_create_users.rb
130
+ - spec/rails_app/db/migrate/20231004084147_devise_create_old_users.rb
131
+ - spec/rails_app/db/schema.rb
100
132
  - spec/spec_helper.rb
data/.travis.yml DELETED
@@ -1,13 +0,0 @@
1
- script:
2
- - bundle
3
- - bundle exec rspec
4
- rvm:
5
- - 1.9.3
6
- - 2.2.3
7
- - ruby-head
8
- - rbx-18mode
9
- - rbx-19mode
10
- notifications:
11
- email:
12
- on_success: always
13
- on_failure: always
@@ -1,7 +0,0 @@
1
- module Devise
2
- module Encryptable
3
- module Encryptors
4
- ARGON2_VERSION = '1.1.0'
5
- end
6
- end
7
- end
@@ -1,17 +0,0 @@
1
- require 'argon2'
2
-
3
- module Devise
4
- module Encryptable
5
- module Encryptors
6
- class Argon2 < Base
7
- def self.digest(password, stretches, salt, pepper)
8
- ::Argon2::Password.create("#{password}#{salt}#{pepper}")
9
- end
10
-
11
- def self.compare(encrypted_password, password, stretches, salt, pepper)
12
- ::Argon2::Password.verify_password("#{password}#{salt}#{pepper}", encrypted_password)
13
- end
14
- end
15
- end
16
- end
17
- end