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.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/devise-argon2.svg)](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
|