devise-argon2 1.1.0 → 2.0.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 +4 -4
- data/.github/workflows/test.yml +47 -0
- data/.gitignore +3 -0
- data/CHANGELOG.md +20 -0
- data/Gemfile +7 -4
- data/README.md +119 -32
- data/devise-argon2.gemspec +15 -12
- data/lib/devise-argon2/model.rb +89 -0
- data/lib/devise-argon2/version.rb +5 -0
- data/lib/devise-argon2.rb +8 -3
- data/spec/devise-argon2_spec.rb +224 -27
- data/spec/orm/active_record.rb +1 -0
- data/spec/orm/mongoid.rb +5 -0
- data/spec/rails_app/Rakefile +6 -0
- data/spec/rails_app/app/active_record/old_user.rb +3 -0
- data/spec/rails_app/app/active_record/user.rb +3 -0
- data/spec/rails_app/app/controllers/application_controller.rb +3 -0
- data/spec/rails_app/app/mongoid/old_user.rb +12 -0
- data/spec/rails_app/app/mongoid/user.rb +10 -0
- data/spec/rails_app/bin/bundle +109 -0
- data/spec/rails_app/bin/rails +4 -0
- data/spec/rails_app/bin/rake +4 -0
- data/spec/rails_app/bin/setup +33 -0
- data/spec/rails_app/config/application.rb +24 -0
- data/spec/rails_app/config/boot.rb +5 -0
- data/spec/rails_app/config/database.yml +14 -0
- data/spec/rails_app/config/environment.rb +7 -0
- data/spec/rails_app/config/initializers/devise.rb +6 -0
- data/spec/rails_app/config/mongoid.yml +10 -0
- data/spec/rails_app/config.ru +6 -0
- data/spec/rails_app/db/migrate/20230617201921_devise_create_users.rb +15 -0
- data/spec/rails_app/db/migrate/20231004084147_devise_create_old_users.rb +17 -0
- data/spec/rails_app/db/schema.rb +31 -0
- data/spec/spec_helper.rb +7 -3
- metadata +57 -25
- data/.travis.yml +0 -13
- data/lib/devise/encryptable/encryptors/argon2/version.rb +0 -7
- data/lib/devise/encryptable/encryptors/argon2.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d20fef20b04b01fd33fd4e8b7104f50ef57ce75e6a4406df46a3ea10fe693a1
|
4
|
+
data.tar.gz: 7b66c6a1e646eea75a16c43ae37a95b557e6342488885a73ab60a80d3d5903a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
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 '
|
9
|
-
gem '
|
10
|
-
gem '
|
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
|
-
|
2
|
-
|
1
|
+
# devise-argon2
|
2
|
+
[](https://badge.fury.io/rb/devise-argon2)
|
3
3
|
|
4
|
-
|
4
|
+
A ruby gem that gives Devise models which use `database_authenticatable` the ability to hash
|
5
|
+
passwords with Argon2id.
|
5
6
|
|
6
|
-
|
7
|
+
## Installation
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
##
|
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.
|
data/devise-argon2.gemspec
CHANGED
@@ -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
|
4
|
+
require "devise-argon2/version"
|
5
5
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
|
-
gem.name
|
8
|
-
gem.version
|
9
|
-
gem.authors
|
10
|
-
gem.email
|
11
|
-
gem.description
|
12
|
-
gem.summary
|
13
|
-
gem.
|
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
|
16
|
-
gem.executables
|
17
|
-
gem.test_files
|
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
|
data/lib/devise-argon2.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
1
|
require 'devise'
|
2
|
-
require 'devise-
|
3
|
-
|
4
|
-
|
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
|
data/spec/devise-argon2_spec.rb
CHANGED
@@ -1,50 +1,247 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'spec_helper'
|
3
|
+
require 'bcrypt'
|
3
4
|
|
4
|
-
describe Devise::
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
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
|
-
|
14
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
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
|
21
|
-
expect(
|
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
|
-
|
25
|
-
|
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
|
-
|
29
|
-
|
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
|
33
|
-
expect(
|
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
|
-
|
38
|
-
let(:
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
44
|
-
|
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)
|
data/spec/orm/mongoid.rb
ADDED
@@ -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,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,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,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,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
|
-
|
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:
|
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:
|
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:
|
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
|
-
-
|
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/
|
72
|
-
- lib/devise
|
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
|
-
|
94
|
-
rubygems_version: 2.7.8
|
104
|
+
rubygems_version: 3.3.3
|
95
105
|
signing_key:
|
96
106
|
specification_version: 4
|
97
|
-
summary:
|
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,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
|