devise-argon2 1.1.0 → 2.0.1

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +66 -0
  3. data/.gitignore +3 -0
  4. data/CHANGELOG.md +32 -0
  5. data/Gemfile +9 -4
  6. data/README.md +120 -32
  7. data/devise-argon2.gemspec +17 -14
  8. data/lib/devise-argon2/model.rb +101 -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 +273 -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 +64 -31
  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
@@ -1,50 +1,296 @@
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
32
+
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
38
+
39
+ it 'does not validate an incorrect password' do
40
+ expect(user.valid_password?(INCORRECT_PASSWORD)).to be false
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
15
60
 
16
- it "is true when the encrypted password contains the argon2id format" do
17
- expect(encrypted).to match /argon2id/
18
- end
61
+ include_examples 'a password is validated if and only if it is correct'
62
+ end
63
+
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
74
+ end
75
+
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
87
+ end
19
88
 
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
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))
22
91
  end
92
+ end
93
+
94
+ context 'encrypted_password is hashed with version 1 of devise-argon2' do
95
+ let(:user) { OldUser.new(password: CORRECT_PASSWORD) }
23
96
 
24
- it "is false when comparing with wrong password" do
25
- expect(argon2.compare(encrypted, 'hunter2', stretches, salt, pepper)).to be false
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
+ })
103
+
104
+ user.password_salt = 'devise-argon2 v1 salt'
105
+ user.encrypted_password = ::Argon2::Password.create(
106
+ "#{CORRECT_PASSWORD}#{user.password_salt}#{Devise.pepper}"
107
+ )
26
108
  end
27
109
 
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
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))
30
125
  end
31
126
 
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
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))
34
129
  end
35
130
  end
36
131
 
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 }
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
42
151
 
43
- it "is still works" do
44
- expect(argon2.compare(encrypted, password, stretches, salt, pepper)).to be true
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
+
178
+ if Argon2::VERSION >= '2.3.0'
179
+ it 'updates work factors if they changed via profile option' do
180
+ # Build user with argon2 default work factors (which match the RFC_9106_LOW_MEMORY
181
+ # profile.)
182
+ Devise.argon2_options = {}
183
+ user
184
+
185
+ Devise.argon2_options = { profile: :pre_rfc_9106 }
186
+
187
+ expect{ user.valid_password?(CORRECT_PASSWORD) }.to(
188
+ change{ work_factors(user.encrypted_password) }
189
+ .to(
190
+ {
191
+ m_cost: 1 << Argon2::Profiles[:pre_rfc_9106][:m_cost],
192
+ t_cost: Argon2::Profiles[:pre_rfc_9106][:t_cost],
193
+ p_cost: Argon2::Profiles[:pre_rfc_9106][:p_cost]
194
+ }
195
+ )
196
+ )
197
+ end
198
+
199
+ it 'gives precendence to the profile option over explicit configuration of work factors' do
200
+ Devise.argon2_options = {
201
+ m_cost: Argon2::Profiles[:pre_rfc_9106][:m_cost] + 1,
202
+ t_cost: Argon2::Profiles[:pre_rfc_9106][:t_cost] + 1,
203
+ p_cost: Argon2::Profiles[:pre_rfc_9106][:p_cost] + 1
204
+ }
205
+ user # build user
206
+
207
+ Devise.argon2_options = {
208
+ profile: :pre_rfc_9106,
209
+ m_cost: Argon2::Profiles[:pre_rfc_9106][:m_cost] + 1,
210
+ t_cost: Argon2::Profiles[:pre_rfc_9106][:t_cost] + 1,
211
+ p_cost: Argon2::Profiles[:pre_rfc_9106][:p_cost] + 1
212
+ }
213
+
214
+ expect{ user.valid_password?(CORRECT_PASSWORD) }.to(
215
+ change{ work_factors(user.encrypted_password) }
216
+ .to(
217
+ {
218
+ m_cost: 1 << Argon2::Profiles[:pre_rfc_9106][:m_cost],
219
+ t_cost: Argon2::Profiles[:pre_rfc_9106][:t_cost],
220
+ p_cost: Argon2::Profiles[:pre_rfc_9106][:p_cost]
221
+ }
222
+ )
223
+ )
224
+ end
45
225
  end
46
226
  end
47
227
 
228
+ it 'ignores migrate_from_devise_argon2_v1 if password_salt is not present' do
229
+ Devise.argon2_options.merge!({ migrate_from_devise_argon2_v1: true })
230
+ expect{ user.valid_password?(CORRECT_PASSWORD) }.not_to(change(user, :encrypted_password))
231
+ end
48
232
  end
49
233
 
234
+ describe 'password_digest' do
235
+ context 'no pepper' do
236
+ it 'hashes the given password with Argon2' do
237
+ expect(
238
+ Argon2::Password.verify_password(CORRECT_PASSWORD, user.encrypted_password)
239
+ ).to be true
240
+ end
241
+ end
242
+
243
+ context 'Devise.pepper is set' do
244
+ before do
245
+ Devise.pepper = 'pepper'
246
+ end
247
+
248
+ it 'uses Devise.pepper as secret key for Argon2' do
249
+ expect(
250
+ Argon2::Password.verify_password(CORRECT_PASSWORD, user.encrypted_password, 'pepper')
251
+ ).to be true
252
+ end
253
+ end
254
+
255
+ context 'argon2_options[:secret] is set' do
256
+ before do
257
+ Devise.argon2_options[:secret] = 'pepper'
258
+ end
259
+
260
+ it 'uses argon2_options[:secret] as secret key for Argon2' do
261
+ expect(
262
+ Argon2::Password.verify_password(CORRECT_PASSWORD, user.encrypted_password, 'pepper')
263
+ ).to be true
264
+ end
265
+ end
266
+
267
+ context 'both Devise.pepper and argon2_options[:secret] are set' do
268
+ before do
269
+ Devise.pepper = 'devise pepper'
270
+ Devise.argon2_options[:secret] = 'argon2_options pepper'
271
+ end
272
+
273
+ it 'uses argon2_options[:secret] as secret key for Argon2' do
274
+ expect(
275
+ Argon2::Password.verify_password(
276
+ CORRECT_PASSWORD,
277
+ user.encrypted_password,
278
+ 'argon2_options pepper'
279
+ )
280
+ ).to be true
281
+ end
282
+ end
283
+
284
+ it 'uses work factors given in argon2_options' do
285
+ Devise.argon2_options.merge!({
286
+ m_cost: 4,
287
+ t_cost: 3,
288
+ p_cost: 2
289
+ })
290
+
291
+ expect(work_factors(user.encrypted_password)).to eq(
292
+ { m_cost: 1 << 4, t_cost: 3, p_cost: 2 }
293
+ )
294
+ end
295
+ end
50
296
  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