devise-otp 0.5.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +0 -2
  3. data/.gitignore +3 -1
  4. data/CHANGELOG.md +51 -8
  5. data/README.md +12 -2
  6. data/app/controllers/devise_otp/devise/otp_credentials_controller.rb +46 -29
  7. data/app/controllers/devise_otp/devise/otp_tokens_controller.rb +24 -6
  8. data/app/views/devise/otp_credentials/show.html.erb +6 -6
  9. data/app/views/devise/otp_tokens/_token_secret.html.erb +3 -4
  10. data/app/views/devise/otp_tokens/edit.html.erb +26 -0
  11. data/app/views/devise/otp_tokens/show.html.erb +7 -14
  12. data/config/locales/en.yml +23 -14
  13. data/devise-otp.gemspec +2 -3
  14. data/lib/devise/strategies/database_authenticatable.rb +64 -0
  15. data/lib/devise-otp/version.rb +1 -1
  16. data/lib/devise-otp.rb +31 -11
  17. data/lib/devise_otp_authenticatable/controllers/helpers.rb +9 -10
  18. data/lib/devise_otp_authenticatable/controllers/public_helpers.rb +39 -0
  19. data/lib/devise_otp_authenticatable/controllers/url_helpers.rb +10 -0
  20. data/lib/devise_otp_authenticatable/engine.rb +2 -5
  21. data/lib/devise_otp_authenticatable/hooks/refreshable.rb +5 -0
  22. data/lib/devise_otp_authenticatable/models/otp_authenticatable.rb +22 -20
  23. data/lib/devise_otp_authenticatable/routes.rb +3 -1
  24. data/test/dummy/app/controllers/admin_posts_controller.rb +85 -0
  25. data/test/dummy/app/controllers/application_controller.rb +0 -1
  26. data/test/dummy/app/controllers/base_controller.rb +6 -0
  27. data/test/dummy/app/models/admin.rb +25 -0
  28. data/test/dummy/app/views/admin_posts/_form.html.erb +25 -0
  29. data/test/dummy/app/views/admin_posts/edit.html.erb +6 -0
  30. data/test/dummy/app/views/admin_posts/index.html.erb +25 -0
  31. data/test/dummy/app/views/admin_posts/new.html.erb +5 -0
  32. data/test/dummy/app/views/admin_posts/show.html.erb +15 -0
  33. data/test/dummy/app/views/base/home.html.erb +1 -0
  34. data/test/dummy/config/application.rb +0 -2
  35. data/test/dummy/config/routes.rb +4 -1
  36. data/test/dummy/db/migrate/20240604000001_create_admins.rb +9 -0
  37. data/test/dummy/db/migrate/20240604000002_add_devise_to_admins.rb +52 -0
  38. data/test/dummy/db/migrate/20240604000003_devise_otp_add_to_admins.rb +28 -0
  39. data/test/integration/disable_token_test.rb +53 -0
  40. data/test/integration/enable_otp_form_test.rb +57 -0
  41. data/test/integration/persistence_test.rb +3 -6
  42. data/test/integration/refresh_test.rb +32 -0
  43. data/test/integration/reset_token_test.rb +45 -0
  44. data/test/integration/sign_in_test.rb +10 -14
  45. data/test/integration/trackable_test.rb +50 -0
  46. data/test/integration_tests_helper.rb +24 -6
  47. data/test/models/otp_authenticatable_test.rb +62 -27
  48. data/test/test_helper.rb +1 -71
  49. metadata +28 -25
  50. data/lib/devise_otp_authenticatable/hooks/sessions.rb +0 -57
  51. data/lib/devise_otp_authenticatable/hooks.rb +0 -11
  52. data/test/integration/token_test.rb +0 -30
@@ -51,13 +51,10 @@ class PersistenceTest < ActionDispatch::IntegrationTest
51
51
  visit user_otp_token_path
52
52
  assert_equal user_otp_token_path, current_path
53
53
 
54
- enable_chrome_headless_downloads(page, "/tmp/devise-otp")
54
+ click_link("Download recovery codes")
55
55
 
56
- DownloadHelper.wait_for_download(count: 1) do
57
- click_link("Download recovery codes")
58
- end
59
-
60
- assert_equal 1, DownloadHelper.downloads.size
56
+ assert current_path.match?(/recovery\.text/)
57
+ assert page.body.match?(user.next_otp_recovery_tokens.values.join("\n"))
61
58
  end
62
59
 
63
60
  test "trusted status should expire" do
@@ -5,10 +5,12 @@ class RefreshTest < ActionDispatch::IntegrationTest
5
5
  def setup
6
6
  @old_refresh = User.otp_credentials_refresh
7
7
  User.otp_credentials_refresh = 1.second
8
+ Admin.otp_credentials_refresh = 1.second
8
9
  end
9
10
 
10
11
  def teardown
11
12
  User.otp_credentials_refresh = @old_refresh
13
+ Admin.otp_credentials_refresh = @old_refresh
12
14
  Capybara.reset_sessions!
13
15
  end
14
16
 
@@ -72,4 +74,34 @@ class RefreshTest < ActionDispatch::IntegrationTest
72
74
 
73
75
  assert_equal user_otp_token_path, current_path
74
76
  end
77
+
78
+ test "works for non-default warden scopes" do
79
+ admin = create_full_admin
80
+
81
+ admin.populate_otp_secrets!
82
+ admin.enable_otp!
83
+
84
+ visit new_admin_session_path
85
+ fill_in "admin_email", with: admin.email
86
+ fill_in "admin_password", with: admin.password
87
+
88
+ page.has_content?("Log in") ? click_button("Log in") : click_button("Sign in")
89
+
90
+ assert_equal admin_otp_credential_path, current_path
91
+
92
+ fill_in "token", with: ROTP::TOTP.new(admin.otp_auth_secret).at(Time.now)
93
+ click_button "Submit Token"
94
+ assert_equal "/", current_path
95
+
96
+ sleep(2)
97
+
98
+ visit admin_otp_token_path
99
+ assert_equal refresh_admin_otp_credential_path, current_path
100
+
101
+ fill_in "admin_refresh_password", with: "12345678"
102
+ click_button "Continue..."
103
+
104
+ assert_equal admin_otp_token_path, current_path
105
+ end
106
+
75
107
  end
@@ -0,0 +1,45 @@
1
+ require "test_helper"
2
+ require "integration_tests_helper"
3
+
4
+ class ResetTokenTest < ActionDispatch::IntegrationTest
5
+
6
+ def setup
7
+ # log in 1fa
8
+ @user = enable_otp_and_sign_in
9
+ assert_equal user_otp_credential_path, current_path
10
+
11
+ # otp 2fa
12
+ fill_in "token", with: ROTP::TOTP.new(@user.otp_auth_secret).at(Time.now)
13
+ click_button "Submit Token"
14
+ assert_equal root_path, current_path
15
+ end
16
+
17
+
18
+ def teardown
19
+ Capybara.reset_sessions!
20
+ end
21
+
22
+ test "redirects to otp_tokens#edit page" do
23
+ reset_otp
24
+
25
+ assert_equal "/users/otp/token/edit", current_path
26
+ end
27
+
28
+ test "generates new token secrets" do
29
+ # get auth secrets
30
+ auth_secret = @user.otp_auth_secret
31
+ recovery_secret = @user.otp_recovery_secret
32
+
33
+ # reset otp
34
+ reset_otp
35
+
36
+ # compare auth secrets
37
+ @user.reload
38
+ assert_not_nil @user.otp_auth_secret
39
+ assert_not_equal @user.otp_auth_secret, auth_secret
40
+
41
+ assert_not_nil @user.otp_recovery_secret
42
+ assert_not_equal @user.otp_recovery_secret, recovery_secret
43
+ end
44
+
45
+ end
@@ -17,20 +17,16 @@ class SignInTest < ActionDispatch::IntegrationTest
17
17
  assert_equal posts_path, current_path
18
18
  end
19
19
 
20
- test "a new user, just signed in, should be able to sign in and enable their OTP authentication" do
20
+ test "a new user, just signed in, should be able to see and click the 'Enable Two-Factor Authentication' link" do
21
21
  user = sign_user_in
22
22
 
23
23
  visit user_otp_token_path
24
- assert !page.has_content?("Your token secret")
24
+ assert page.has_content?("Disabled")
25
25
 
26
- check "user_otp_enabled"
27
- click_button "Continue..."
26
+ click_link "Enable Two-Factor Authentication"
28
27
 
29
- assert_equal user_otp_token_path, current_path
30
-
31
- assert page.has_content?("Your token secret")
32
- assert !user.otp_auth_secret.nil?
33
- assert !user.otp_persistence_seed.nil?
28
+ assert page.has_content?("Enable Two-Factor Authentication")
29
+ assert_equal edit_user_otp_token_path, current_path
34
30
  end
35
31
 
36
32
  test "a new user should be able to sign in enable OTP and be prompted for their token" do
@@ -43,17 +39,17 @@ class SignInTest < ActionDispatch::IntegrationTest
43
39
  enable_otp_and_sign_in
44
40
  assert_equal user_otp_credential_path, current_path
45
41
 
46
- fill_in "user_token", with: "123456"
42
+ fill_in "token", with: "123456"
47
43
  click_button "Submit Token"
48
44
 
49
- assert_equal new_user_session_path, current_path
45
+ assert_equal user_otp_credential_path, current_path
50
46
  end
51
47
 
52
48
  test "fail blank token authentication" do
53
49
  enable_otp_and_sign_in
54
50
  assert_equal user_otp_credential_path, current_path
55
51
 
56
- fill_in "user_token", with: ""
52
+ fill_in "token", with: ""
57
53
  click_button "Submit Token"
58
54
 
59
55
  assert_equal user_otp_credential_path, current_path
@@ -62,7 +58,7 @@ class SignInTest < ActionDispatch::IntegrationTest
62
58
  test "successful token authentication" do
63
59
  user = enable_otp_and_sign_in
64
60
 
65
- fill_in "user_token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
61
+ fill_in "token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
66
62
  click_button "Submit Token"
67
63
 
68
64
  assert_equal root_path, current_path
@@ -76,7 +72,7 @@ class SignInTest < ActionDispatch::IntegrationTest
76
72
 
77
73
  sleep(2)
78
74
 
79
- fill_in "user_token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
75
+ fill_in "token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
80
76
  click_button "Submit Token"
81
77
 
82
78
  User.otp_authentication_timeout = old_timeout
@@ -0,0 +1,50 @@
1
+ require "test_helper"
2
+ require "integration_tests_helper"
3
+
4
+ class TrackableTest < ActionDispatch::IntegrationTest
5
+
6
+ def setup
7
+ @user = sign_user_in
8
+
9
+ @user.reload
10
+
11
+ @sign_in_count = @user.sign_in_count
12
+ @current_sign_in_at = @user.current_sign_in_at
13
+
14
+ sign_out
15
+ end
16
+
17
+ def teardown
18
+ Capybara.reset_sessions!
19
+ end
20
+
21
+ test "if otp is disabled, it should update devise trackable fields as usual when the user signs in" do
22
+ sign_user_in(@user)
23
+
24
+ @user.reload
25
+
26
+ assert_not_equal @sign_in_count, @user.sign_in_count
27
+ assert_not_equal @current_sign_in_at, @user.current_sign_in_at
28
+ end
29
+
30
+ test "if otp is enabled, it should not update devise trackable fields until user enters their user token to complete their sign in" do
31
+ @user.populate_otp_secrets!
32
+ @user.enable_otp!
33
+
34
+ sign_user_in(@user)
35
+
36
+ @user.reload
37
+
38
+ assert_equal @sign_in_count, @user.sign_in_count
39
+ assert_equal @current_sign_in_at, @user.current_sign_in_at
40
+
41
+ fill_in "token", with: ROTP::TOTP.new(@user.otp_auth_secret).at(Time.now)
42
+ click_button "Submit Token"
43
+
44
+ @user.reload
45
+
46
+ assert_not_equal @sign_in_count, @user.sign_in_count
47
+ assert_not_equal @current_sign_in_at, @user.current_sign_in_at
48
+ end
49
+
50
+ end
@@ -16,18 +16,31 @@ class ActionDispatch::IntegrationTest
16
16
  end
17
17
  end
18
18
 
19
+ def create_full_admin
20
+ @admin ||= begin
21
+ admin = Admin.create!(
22
+ email: "admin@email.invalid",
23
+ password: "12345678",
24
+ password_confirmation: "12345678"
25
+ )
26
+ admin
27
+ end
28
+ end
29
+
19
30
  def enable_otp_and_sign_in_with_otp
20
31
  enable_otp_and_sign_in.tap do |user|
21
- fill_in "user_token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
32
+ fill_in "token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
22
33
  click_button "Submit Token"
23
34
  end
24
35
  end
25
36
 
26
37
  def enable_otp_and_sign_in
27
38
  user = create_full_user
39
+ user.populate_otp_secrets!
40
+
28
41
  sign_user_in(user)
29
- visit user_otp_token_path
30
- check "user_otp_enabled"
42
+ visit edit_user_otp_token_path
43
+ fill_in "confirmation_code", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
31
44
  click_button "Continue..."
32
45
 
33
46
  Capybara.reset_sessions!
@@ -37,14 +50,18 @@ class ActionDispatch::IntegrationTest
37
50
  end
38
51
 
39
52
  def otp_challenge_for(user)
40
- fill_in "user_token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
53
+ fill_in "token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
41
54
  click_button "Submit Token"
42
55
  end
43
56
 
44
57
  def disable_otp
45
58
  visit user_otp_token_path
46
- uncheck "user_otp_enabled"
47
- click_button "Continue..."
59
+ click_button "Disable Two-Factor Authentication"
60
+ end
61
+
62
+ def reset_otp
63
+ visit user_otp_token_path
64
+ click_button "Reset Token Secret"
48
65
  end
49
66
 
50
67
  def sign_out
@@ -61,4 +78,5 @@ class ActionDispatch::IntegrationTest
61
78
  page.has_content?("Log in") ? click_button("Log in") : click_button("Sign in")
62
79
  user
63
80
  end
81
+
64
82
  end
@@ -6,42 +6,70 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
6
6
  new_user
7
7
  end
8
8
 
9
- test "new users have a non-nil secret set" do
10
- assert_not_nil User.first.otp_auth_secret
9
+ test "new users do not have a secret set" do
10
+ user = User.first
11
+
12
+ [:otp_auth_secret, :otp_recovery_secret, :otp_persistence_seed].each do |field|
13
+ assert_nil user.send(field)
14
+ end
11
15
  end
12
16
 
13
17
  test "new users have OTP disabled by default" do
14
18
  assert !User.first.otp_enabled
15
19
  end
16
20
 
17
- test "users should have an instance of TOTP/ROTP objects" do
18
- u = User.first
19
- assert u.time_based_otp.is_a? ROTP::TOTP
20
- assert u.recovery_otp.is_a? ROTP::HOTP
21
+ test "populating otp secrets should populate all required fields" do
22
+ user = User.first
23
+ user.populate_otp_secrets!
24
+
25
+ [:otp_auth_secret, :otp_recovery_secret, :otp_persistence_seed].each do |field|
26
+ assert_not_nil user.send(field)
27
+ end
21
28
  end
22
29
 
23
- test "users should have their otp_auth_secret/persistence_seed set on creation" do
24
- assert User.first.otp_auth_secret
25
- assert User.first.otp_persistence_seed
30
+ test "time_based_otp and recover_otp fields should be an instance of TOTP/ROTP objects" do
31
+ user = User.first
32
+ user.populate_otp_secrets!
33
+
34
+ assert user.time_based_otp.is_a? ROTP::TOTP
35
+ assert user.recovery_otp.is_a? ROTP::HOTP
26
36
  end
27
37
 
28
- test "reset_otp_credentials should generate new secrets and disable OTP" do
29
- u = User.first
30
- u.update_attribute(:otp_enabled, true)
31
- assert u.otp_enabled
32
- otp_auth_secret = u.otp_auth_secret
33
- otp_persistence_seed = u.otp_persistence_seed
38
+ test "clear_otp_fields should clear all otp fields" do
39
+ user = User.first
40
+ user.populate_otp_secrets!
34
41
 
35
- u.reset_otp_credentials!
36
- assert !(otp_auth_secret == u.otp_auth_secret)
37
- assert !(otp_persistence_seed == u.otp_persistence_seed)
38
- assert !u.otp_enabled
42
+ user.enable_otp!
43
+ user.generate_otp_challenge!
44
+ user.update(
45
+ :otp_failed_attempts => 1,
46
+ :otp_recovery_counter => 1
47
+ )
48
+
49
+
50
+ assert user.otp_enabled
51
+ [:otp_auth_secret, :otp_recovery_secret, :otp_persistence_seed].each do |field|
52
+ assert_not_nil user.send(field)
53
+ end
54
+ [:otp_failed_attempts, :otp_recovery_counter].each do |field|
55
+ assert_not user.send(field) == 0
56
+ end
57
+
58
+ user.clear_otp_fields!
59
+ [:otp_auth_secret, :otp_recovery_secret, :otp_persistence_seed].each do |field|
60
+ assert_nil user.send(field)
61
+ end
62
+ [:otp_failed_attempts, :otp_recovery_counter].each do |field|
63
+ assert user.send(field) == 0
64
+ end
39
65
  end
40
66
 
41
67
  test "reset_otp_persistence should generate new persistence_seed but NOT change the otp_auth_secret" do
42
68
  u = User.first
43
- u.update_attribute(:otp_enabled, true)
69
+ u.populate_otp_secrets!
70
+ u.enable_otp!
44
71
  assert u.otp_enabled
72
+
45
73
  otp_auth_secret = u.otp_auth_secret
46
74
  otp_persistence_seed = u.otp_persistence_seed
47
75
 
@@ -53,7 +81,8 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
53
81
 
54
82
  test "generating a challenge, should retrieve the user later" do
55
83
  u = User.first
56
- u.update_attribute(:otp_enabled, true)
84
+ u.populate_otp_secrets!
85
+ u.enable_otp!
57
86
  challenge = u.generate_otp_challenge!
58
87
 
59
88
  w = User.find_valid_otp_challenge(challenge)
@@ -63,7 +92,8 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
63
92
 
64
93
  test "expiring the challenge, should retrieve nothing" do
65
94
  u = User.first
66
- u.update_attribute(:otp_enabled, true)
95
+ u.populate_otp_secrets!
96
+ u.enable_otp!
67
97
  challenge = u.generate_otp_challenge!(1.second)
68
98
  sleep(2)
69
99
 
@@ -73,7 +103,8 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
73
103
 
74
104
  test "expired challenges should not be valid" do
75
105
  u = User.first
76
- u.update_attribute(:otp_enabled, true)
106
+ u.populate_otp_secrets!
107
+ u.enable_otp!
77
108
  challenge = u.generate_otp_challenge!(1.second)
78
109
  sleep(2)
79
110
  assert_equal false, u.otp_challenge_valid?
@@ -81,14 +112,16 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
81
112
 
82
113
  test "null otp challenge" do
83
114
  u = User.first
84
- u.update_attribute(:otp_enabled, true)
115
+ u.populate_otp_secrets!
116
+ u.enable_otp!
85
117
  assert_equal false, u.validate_otp_token("")
86
118
  assert_equal false, u.validate_otp_token(nil)
87
119
  end
88
120
 
89
121
  test "generated otp token should be valid for the user" do
90
122
  u = User.first
91
- u.update_attribute(:otp_enabled, true)
123
+ u.populate_otp_secrets!
124
+ u.enable_otp!
92
125
 
93
126
  secret = u.otp_auth_secret
94
127
  token = ROTP::TOTP.new(secret).now
@@ -98,7 +131,8 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
98
131
 
99
132
  test "generated otp token, out of drift window, should be NOT valid for the user" do
100
133
  u = User.first
101
- u.update_attribute(:otp_enabled, true)
134
+ u.populate_otp_secrets!
135
+ u.enable_otp!
102
136
 
103
137
  secret = u.otp_auth_secret
104
138
 
@@ -110,7 +144,8 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
110
144
 
111
145
  test "recovery secrets should be valid, and valid only once" do
112
146
  u = User.first
113
- u.update_attribute(:otp_enabled, true)
147
+ u.populate_otp_secrets!
148
+ u.enable_otp!
114
149
  recovery = u.next_otp_recovery_tokens
115
150
 
116
151
  assert u.valid_otp_recovery_token? recovery.fetch(0)
data/test/test_helper.rb CHANGED
@@ -6,86 +6,16 @@ require "dummy/config/environment"
6
6
  require "orm/#{DEVISE_ORM}"
7
7
  require "rails/test_help"
8
8
  require "capybara/rails"
9
- require "capybara/cuprite"
10
9
  require "minitest/reporters"
11
10
 
12
- MiniTest::Reporters.use!
11
+ Minitest::Reporters.use!
13
12
 
14
13
  # I18n.load_path << File.expand_path("../support/locale/en.yml", __FILE__) if DEVISE_ORM == :mongoid
15
14
 
16
15
  # ActiveSupport::Deprecation.silenced = true
17
16
 
18
- # Use a module to not pollute the global namespace
19
- module CapybaraHelper
20
- def self.register_driver(driver_name, args = [])
21
- opts = {headless: true, js_errors: true, window_size: [1920, 1200], browser_options: {}}
22
- args.each do |arg|
23
- opts[:browser_options][arg] = nil
24
- end
25
-
26
- Capybara.register_driver(driver_name) do |app|
27
- Capybara::Cuprite::Driver.new(app, opts)
28
- end
29
- end
30
- end
31
-
32
- # Register our own custom drivers
33
- CapybaraHelper.register_driver(:headless_chrome, %w[disable-gpu no-sandbox disable-dev-shm-usage])
34
-
35
- # Configure Capybara JS driver
36
- Capybara.current_driver = :headless_chrome
37
- Capybara.javascript_driver = :headless_chrome
38
-
39
- # Configure Capybara server
40
- Capybara.run_server = true
41
- Capybara.server = :puma, {Silent: true}
42
-
43
17
  class ActionDispatch::IntegrationTest
44
18
  include Capybara::DSL
45
-
46
- # What capybara calls a "page" in its DSL is actually a Capybara::Session
47
- # and doesn't know about the *command* method that allows us to play with
48
- # the Chrome API.
49
- # See: https://rubydoc.info/github/jnicklas/capybara/master/Capybara/Session
50
- #
51
- # To enable downloads we need to do it on the browser's page object, so fetch it
52
- # from this long method chain.
53
- # See: https://github.com/rubycdp/ferrum/blob/master/lib/ferrum/page.rb
54
- def enable_chrome_headless_downloads(session, directory)
55
- page = session.driver.browser.page
56
- page.command("Page.setDownloadBehavior", behavior: "allow", downloadPath: directory)
57
- end
58
- end
59
-
60
- # From https://collectiveidea.com/blog/archives/2012/01/27/testing-file-downloads-with-capybara-and-chromedriver
61
- module DownloadHelper
62
- extend self
63
-
64
- TIMEOUT = 10
65
-
66
- def downloads
67
- Dir["/tmp/devise-otp/*"]
68
- end
69
-
70
- def wait_for_download(count: 1)
71
- yield if block_given?
72
-
73
- Timeout.timeout(TIMEOUT) do
74
- sleep 0.2 until downloaded?(count)
75
- end
76
- end
77
-
78
- def downloaded?(count)
79
- !downloading? && downloads.size == count
80
- end
81
-
82
- def downloading?
83
- downloads.grep(/\.crdownload$/).any?
84
- end
85
-
86
- def clear_downloads
87
- FileUtils.rm_f(downloads)
88
- end
89
19
  end
90
20
 
91
21
  require "devise-otp"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise-otp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lele Forzani
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-02-18 00:00:00.000000000 Z
12
+ date: 2024-06-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -17,7 +17,7 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '7.0'
20
+ version: '6.1'
21
21
  - - "<"
22
22
  - !ruby/object:Gem::Version
23
23
  version: '7.2'
@@ -27,7 +27,7 @@ dependencies:
27
27
  requirements:
28
28
  - - ">="
29
29
  - !ruby/object:Gem::Version
30
- version: '7.0'
30
+ version: '6.1'
31
31
  - - "<"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '7.2'
@@ -79,20 +79,6 @@ dependencies:
79
79
  - - ">="
80
80
  - !ruby/object:Gem::Version
81
81
  version: '0'
82
- - !ruby/object:Gem::Dependency
83
- name: cuprite
84
- requirement: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - ">="
87
- - !ruby/object:Gem::Version
88
- version: '0'
89
- type: :development
90
- prerelease: false
91
- version_requirements: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - ">="
94
- - !ruby/object:Gem::Version
95
- version: '0'
96
82
  - !ruby/object:Gem::Dependency
97
83
  name: minitest-reporters
98
84
  requirement: !ruby/object:Gem::Requirement
@@ -167,16 +153,16 @@ dependencies:
167
153
  name: sqlite3
168
154
  requirement: !ruby/object:Gem::Requirement
169
155
  requirements:
170
- - - ">="
156
+ - - "~>"
171
157
  - !ruby/object:Gem::Version
172
- version: '0'
158
+ version: '1.4'
173
159
  type: :development
174
160
  prerelease: false
175
161
  version_requirements: !ruby/object:Gem::Requirement
176
162
  requirements:
177
- - - ">="
163
+ - - "~>"
178
164
  - !ruby/object:Gem::Version
179
- version: '0'
165
+ version: '1.4'
180
166
  - !ruby/object:Gem::Dependency
181
167
  name: standardrb
182
168
  requirement: !ruby/object:Gem::Requirement
@@ -214,6 +200,7 @@ files:
214
200
  - app/views/devise/otp_credentials/show.html.erb
215
201
  - app/views/devise/otp_tokens/_token_secret.html.erb
216
202
  - app/views/devise/otp_tokens/_trusted_devices.html.erb
203
+ - app/views/devise/otp_tokens/edit.html.erb
217
204
  - app/views/devise/otp_tokens/recovery.html.erb
218
205
  - app/views/devise/otp_tokens/recovery_codes.text.erb
219
206
  - app/views/devise/otp_tokens/show.html.erb
@@ -222,11 +209,12 @@ files:
222
209
  - docs/QR_CODES.md
223
210
  - lib/devise-otp.rb
224
211
  - lib/devise-otp/version.rb
212
+ - lib/devise/strategies/database_authenticatable.rb
225
213
  - lib/devise_otp_authenticatable/controllers/helpers.rb
214
+ - lib/devise_otp_authenticatable/controllers/public_helpers.rb
226
215
  - lib/devise_otp_authenticatable/controllers/url_helpers.rb
227
216
  - lib/devise_otp_authenticatable/engine.rb
228
- - lib/devise_otp_authenticatable/hooks.rb
229
- - lib/devise_otp_authenticatable/hooks/sessions.rb
217
+ - lib/devise_otp_authenticatable/hooks/refreshable.rb
230
218
  - lib/devise_otp_authenticatable/models/otp_authenticatable.rb
231
219
  - lib/devise_otp_authenticatable/routes.rb
232
220
  - lib/generators/active_record/devise_otp_generator.rb
@@ -239,13 +227,22 @@ files:
239
227
  - test/dummy/app/assets/config/manifest.js
240
228
  - test/dummy/app/assets/javascripts/application.js
241
229
  - test/dummy/app/assets/stylesheets/application.css
230
+ - test/dummy/app/controllers/admin_posts_controller.rb
242
231
  - test/dummy/app/controllers/application_controller.rb
232
+ - test/dummy/app/controllers/base_controller.rb
243
233
  - test/dummy/app/controllers/posts_controller.rb
244
234
  - test/dummy/app/helpers/application_helper.rb
245
235
  - test/dummy/app/helpers/posts_helper.rb
246
236
  - test/dummy/app/mailers/.gitkeep
237
+ - test/dummy/app/models/admin.rb
247
238
  - test/dummy/app/models/post.rb
248
239
  - test/dummy/app/models/user.rb
240
+ - test/dummy/app/views/admin_posts/_form.html.erb
241
+ - test/dummy/app/views/admin_posts/edit.html.erb
242
+ - test/dummy/app/views/admin_posts/index.html.erb
243
+ - test/dummy/app/views/admin_posts/new.html.erb
244
+ - test/dummy/app/views/admin_posts/show.html.erb
245
+ - test/dummy/app/views/base/home.html.erb
249
246
  - test/dummy/app/views/layouts/application.html.erb
250
247
  - test/dummy/app/views/posts/_form.html.erb
251
248
  - test/dummy/app/views/posts/edit.html.erb
@@ -273,16 +270,22 @@ files:
273
270
  - test/dummy/db/migrate/20130131092406_add_devise_to_users.rb
274
271
  - test/dummy/db/migrate/20130131142320_create_posts.rb
275
272
  - test/dummy/db/migrate/20130131160351_devise_otp_add_to_users.rb
273
+ - test/dummy/db/migrate/20240604000001_create_admins.rb
274
+ - test/dummy/db/migrate/20240604000002_add_devise_to_admins.rb
275
+ - test/dummy/db/migrate/20240604000003_devise_otp_add_to_admins.rb
276
276
  - test/dummy/lib/assets/.gitkeep
277
277
  - test/dummy/public/404.html
278
278
  - test/dummy/public/422.html
279
279
  - test/dummy/public/500.html
280
280
  - test/dummy/public/favicon.ico
281
281
  - test/dummy/script/rails
282
+ - test/integration/disable_token_test.rb
283
+ - test/integration/enable_otp_form_test.rb
282
284
  - test/integration/persistence_test.rb
283
285
  - test/integration/refresh_test.rb
286
+ - test/integration/reset_token_test.rb
284
287
  - test/integration/sign_in_test.rb
285
- - test/integration/token_test.rb
288
+ - test/integration/trackable_test.rb
286
289
  - test/integration_tests_helper.rb
287
290
  - test/model_tests_helper.rb
288
291
  - test/models/otp_authenticatable_test.rb