devise-otp 0.4.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/Rakefile +17 -18
- data/app/controllers/devise_otp/devise/otp_credentials_controller.rb +15 -21
- data/app/controllers/devise_otp/devise/otp_tokens_controller.rb +7 -11
- data/app/views/devise/otp_credentials/show.html.erb +1 -1
- data/app/views/devise/otp_tokens/_token_secret.html.erb +4 -4
- data/app/views/devise/otp_tokens/recovery.html.erb +4 -4
- data/app/views/devise/otp_tokens/show.html.erb +5 -5
- data/config/locales/en.yml +4 -12
- data/devise-otp.gemspec +13 -13
- data/lib/devise-otp/version.rb +1 -1
- data/lib/devise-otp.rb +15 -29
- data/lib/devise_otp_authenticatable/controllers/helpers.rb +21 -23
- data/lib/devise_otp_authenticatable/controllers/url_helpers.rb +0 -2
- data/lib/devise_otp_authenticatable/engine.rb +6 -4
- data/lib/devise_otp_authenticatable/hooks/sessions.rb +6 -6
- data/lib/devise_otp_authenticatable/hooks.rb +1 -3
- data/lib/devise_otp_authenticatable/models/otp_authenticatable.rb +19 -25
- data/lib/devise_otp_authenticatable/routes.rb +11 -14
- data/lib/generators/active_record/devise_otp_generator.rb +1 -1
- data/lib/generators/devise_otp/devise_otp_generator.rb +12 -13
- data/lib/generators/devise_otp/install_generator.rb +3 -4
- data/lib/generators/devise_otp/views_generator.rb +5 -5
- data/test/dummy/Rakefile +1 -1
- data/test/dummy/app/controllers/posts_controller.rb +2 -2
- data/test/dummy/app/models/user.rb +8 -8
- data/test/dummy/config/application.rb +4 -4
- data/test/dummy/config/boot.rb +5 -5
- data/test/dummy/config/environment.rb +1 -1
- data/test/dummy/config/environments/development.rb +1 -1
- data/test/dummy/config/environments/production.rb +1 -1
- data/test/dummy/config/environments/test.rb +2 -2
- data/test/dummy/config/initializers/devise.rb +6 -8
- data/test/dummy/config/initializers/secret_token.rb +2 -2
- data/test/dummy/config/initializers/session_store.rb +1 -1
- data/test/dummy/config/initializers/wrap_parameters.rb +1 -1
- data/test/dummy/config/routes.rb +1 -1
- data/test/dummy/config.ru +1 -1
- data/test/dummy/db/migrate/20130131092406_add_devise_to_users.rb +12 -13
- data/test/dummy/db/migrate/20130131160351_devise_otp_add_to_users.rb +13 -13
- data/test/dummy/script/rails +3 -3
- data/test/integration/persistence_test.rb +11 -12
- data/test/integration/refresh_test.rb +13 -14
- data/test/integration/sign_in_test.rb +24 -26
- data/test/integration/token_test.rb +5 -6
- data/test/integration_tests_helper.rb +16 -17
- data/test/model_tests_helper.rb +5 -7
- data/test/models/otp_authenticatable_test.rb +18 -19
- data/test/test_helper.rb +10 -10
- metadata +24 -64
data/test/dummy/script/rails
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
|
3
3
|
|
4
|
-
APP_PATH = File.expand_path(
|
5
|
-
require File.expand_path(
|
6
|
-
require
|
4
|
+
APP_PATH = File.expand_path("../../config/application", __FILE__)
|
5
|
+
require File.expand_path("../../config/boot", __FILE__)
|
6
|
+
require "rails/commands"
|
@@ -1,8 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "test_helper"
|
2
|
+
require "integration_tests_helper"
|
3
3
|
|
4
4
|
class PersistenceTest < ActionDispatch::IntegrationTest
|
5
|
-
|
6
5
|
def setup
|
7
6
|
@old_persistence = User.otp_trust_persistence
|
8
7
|
User.otp_trust_persistence = 3.seconds
|
@@ -13,7 +12,7 @@ class PersistenceTest < ActionDispatch::IntegrationTest
|
|
13
12
|
Capybara.reset_sessions!
|
14
13
|
end
|
15
14
|
|
16
|
-
test
|
15
|
+
test "a user should be requested the otp challenge every log in" do
|
17
16
|
# log in 1fa
|
18
17
|
user = enable_otp_and_sign_in
|
19
18
|
otp_challenge_for user
|
@@ -27,7 +26,7 @@ class PersistenceTest < ActionDispatch::IntegrationTest
|
|
27
26
|
assert_equal user_otp_credential_path, current_path
|
28
27
|
end
|
29
28
|
|
30
|
-
test
|
29
|
+
test "a user should be able to set their browser as trusted" do
|
31
30
|
# log in 1fa
|
32
31
|
user = enable_otp_and_sign_in
|
33
32
|
otp_challenge_for user
|
@@ -35,8 +34,8 @@ class PersistenceTest < ActionDispatch::IntegrationTest
|
|
35
34
|
visit user_otp_token_path
|
36
35
|
assert_equal user_otp_token_path, current_path
|
37
36
|
|
38
|
-
click_link(
|
39
|
-
assert_text
|
37
|
+
click_link("Trust this browser")
|
38
|
+
assert_text "Your browser is trusted."
|
40
39
|
sign_out
|
41
40
|
|
42
41
|
sign_user_in
|
@@ -44,7 +43,7 @@ class PersistenceTest < ActionDispatch::IntegrationTest
|
|
44
43
|
assert_equal root_path, current_path
|
45
44
|
end
|
46
45
|
|
47
|
-
test
|
46
|
+
test "a user should be able to download its recovery codes" do
|
48
47
|
# log in 1fa
|
49
48
|
user = enable_otp_and_sign_in
|
50
49
|
otp_challenge_for user
|
@@ -55,13 +54,13 @@ class PersistenceTest < ActionDispatch::IntegrationTest
|
|
55
54
|
enable_chrome_headless_downloads(page, "/tmp/devise-otp")
|
56
55
|
|
57
56
|
DownloadHelper.wait_for_download(count: 1) do
|
58
|
-
click_link(
|
57
|
+
click_link("Download recovery codes")
|
59
58
|
end
|
60
59
|
|
61
60
|
assert_equal 1, DownloadHelper.downloads.size
|
62
61
|
end
|
63
62
|
|
64
|
-
test
|
63
|
+
test "trusted status should expire" do
|
65
64
|
# log in 1fa
|
66
65
|
user = enable_otp_and_sign_in
|
67
66
|
otp_challenge_for user
|
@@ -69,8 +68,8 @@ class PersistenceTest < ActionDispatch::IntegrationTest
|
|
69
68
|
visit user_otp_token_path
|
70
69
|
assert_equal user_otp_token_path, current_path
|
71
70
|
|
72
|
-
click_link(
|
73
|
-
assert_text
|
71
|
+
click_link("Trust this browser")
|
72
|
+
assert_text "Your browser is trusted."
|
74
73
|
sign_out
|
75
74
|
|
76
75
|
sleep User.otp_trust_persistence.to_i + 1
|
@@ -1,8 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "test_helper"
|
2
|
+
require "integration_tests_helper"
|
3
3
|
|
4
4
|
class RefreshTest < ActionDispatch::IntegrationTest
|
5
|
-
|
6
5
|
def setup
|
7
6
|
@old_refresh = User.otp_credentials_refresh
|
8
7
|
User.otp_credentials_refresh = 1.second
|
@@ -13,14 +12,14 @@ class RefreshTest < ActionDispatch::IntegrationTest
|
|
13
12
|
Capybara.reset_sessions!
|
14
13
|
end
|
15
14
|
|
16
|
-
test
|
15
|
+
test "a user that just signed in should be able to access their OTP settings without refreshing" do
|
17
16
|
sign_user_in
|
18
17
|
|
19
18
|
visit user_otp_token_path
|
20
19
|
assert_equal user_otp_token_path, current_path
|
21
20
|
end
|
22
21
|
|
23
|
-
test
|
22
|
+
test "a user should be prompted for credentials when the credentials_refresh time is expired" do
|
24
23
|
sign_user_in
|
25
24
|
visit user_otp_token_path
|
26
25
|
assert_equal user_otp_token_path, current_path
|
@@ -31,7 +30,7 @@ class RefreshTest < ActionDispatch::IntegrationTest
|
|
31
30
|
assert_equal refresh_user_otp_credential_path, current_path
|
32
31
|
end
|
33
32
|
|
34
|
-
test
|
33
|
+
test "a user should be able to access their OTP settings after refreshing" do
|
35
34
|
sign_user_in
|
36
35
|
visit user_otp_token_path
|
37
36
|
assert_equal user_otp_token_path, current_path
|
@@ -41,12 +40,12 @@ class RefreshTest < ActionDispatch::IntegrationTest
|
|
41
40
|
visit user_otp_token_path
|
42
41
|
assert_equal refresh_user_otp_credential_path, current_path
|
43
42
|
|
44
|
-
fill_in
|
45
|
-
click_button
|
43
|
+
fill_in "user_refresh_password", with: "12345678"
|
44
|
+
click_button "Continue..."
|
46
45
|
assert_equal user_otp_token_path, current_path
|
47
46
|
end
|
48
47
|
|
49
|
-
test
|
48
|
+
test "a user should NOT be able to access their OTP settings unless refreshing" do
|
50
49
|
sign_user_in
|
51
50
|
visit user_otp_token_path
|
52
51
|
assert_equal user_otp_token_path, current_path
|
@@ -56,20 +55,20 @@ class RefreshTest < ActionDispatch::IntegrationTest
|
|
56
55
|
visit user_otp_token_path
|
57
56
|
assert_equal refresh_user_otp_credential_path, current_path
|
58
57
|
|
59
|
-
fill_in
|
60
|
-
click_button
|
58
|
+
fill_in "user_refresh_password", with: "12345670"
|
59
|
+
click_button "Continue..."
|
61
60
|
assert_equal refresh_user_otp_credential_path, current_path
|
62
61
|
end
|
63
62
|
|
64
|
-
test
|
63
|
+
test "user should be finally be able to access their settings, and just password is enough" do
|
65
64
|
user = enable_otp_and_sign_in_with_otp
|
66
65
|
|
67
66
|
sleep(2)
|
68
67
|
visit user_otp_token_path
|
69
68
|
assert_equal refresh_user_otp_credential_path, current_path
|
70
69
|
|
71
|
-
fill_in
|
72
|
-
click_button
|
70
|
+
fill_in "user_refresh_password", with: "12345678"
|
71
|
+
click_button "Continue..."
|
73
72
|
|
74
73
|
assert_equal user_otp_token_path, current_path
|
75
74
|
end
|
@@ -1,76 +1,74 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "test_helper"
|
2
|
+
require "integration_tests_helper"
|
3
3
|
|
4
4
|
class SignInTest < ActionDispatch::IntegrationTest
|
5
|
-
|
6
5
|
def teardown
|
7
6
|
Capybara.reset_sessions!
|
8
7
|
end
|
9
8
|
|
10
|
-
test
|
9
|
+
test "a new user should be able to sign in without using their token" do
|
11
10
|
create_full_user
|
12
11
|
|
13
12
|
visit posts_path
|
14
|
-
fill_in
|
15
|
-
fill_in
|
16
|
-
page.has_content?(
|
13
|
+
fill_in "user_email", with: "user@email.invalid"
|
14
|
+
fill_in "user_password", with: "12345678"
|
15
|
+
page.has_content?("Log in") ? click_button("Log in") : click_button("Sign in")
|
17
16
|
|
18
17
|
assert_equal posts_path, current_path
|
19
18
|
end
|
20
19
|
|
21
|
-
test
|
20
|
+
test "a new user, just signed in, should be able to sign in and enable their OTP authentication" do
|
22
21
|
user = sign_user_in
|
23
22
|
|
24
23
|
visit user_otp_token_path
|
25
|
-
assert !page.has_content?(
|
24
|
+
assert !page.has_content?("Your token secret")
|
26
25
|
|
27
|
-
check
|
28
|
-
click_button
|
26
|
+
check "user_otp_enabled"
|
27
|
+
click_button "Continue..."
|
29
28
|
|
30
29
|
assert_equal user_otp_token_path, current_path
|
31
30
|
|
32
|
-
assert page.has_content?(
|
31
|
+
assert page.has_content?("Your token secret")
|
33
32
|
assert !user.otp_auth_secret.nil?
|
34
33
|
assert !user.otp_persistence_seed.nil?
|
35
34
|
end
|
36
35
|
|
37
|
-
test
|
36
|
+
test "a new user should be able to sign in enable OTP and be prompted for their token" do
|
38
37
|
enable_otp_and_sign_in
|
39
38
|
|
40
39
|
assert_equal user_otp_credential_path, current_path
|
41
40
|
end
|
42
41
|
|
43
|
-
test
|
42
|
+
test "fail token authentication" do
|
44
43
|
enable_otp_and_sign_in
|
45
44
|
assert_equal user_otp_credential_path, current_path
|
46
45
|
|
47
|
-
fill_in
|
48
|
-
click_button
|
46
|
+
fill_in "user_token", with: "123456"
|
47
|
+
click_button "Submit Token"
|
49
48
|
|
50
49
|
assert_equal new_user_session_path, current_path
|
51
50
|
end
|
52
51
|
|
53
|
-
test
|
52
|
+
test "fail blank token authentication" do
|
54
53
|
enable_otp_and_sign_in
|
55
54
|
assert_equal user_otp_credential_path, current_path
|
56
55
|
|
57
|
-
fill_in
|
58
|
-
click_button
|
56
|
+
fill_in "user_token", with: ""
|
57
|
+
click_button "Submit Token"
|
59
58
|
|
60
59
|
assert_equal user_otp_credential_path, current_path
|
61
60
|
end
|
62
61
|
|
63
|
-
test
|
62
|
+
test "successful token authentication" do
|
64
63
|
user = enable_otp_and_sign_in
|
65
64
|
|
66
|
-
fill_in
|
67
|
-
click_button
|
65
|
+
fill_in "user_token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
|
66
|
+
click_button "Submit Token"
|
68
67
|
|
69
68
|
assert_equal root_path, current_path
|
70
69
|
end
|
71
70
|
|
72
|
-
|
73
|
-
test 'should fail if the the challenge times out' do
|
71
|
+
test "should fail if the the challenge times out" do
|
74
72
|
old_timeout = User.otp_authentication_timeout
|
75
73
|
User.otp_authentication_timeout = 1.second
|
76
74
|
|
@@ -78,8 +76,8 @@ class SignInTest < ActionDispatch::IntegrationTest
|
|
78
76
|
|
79
77
|
sleep(2)
|
80
78
|
|
81
|
-
fill_in
|
82
|
-
click_button
|
79
|
+
fill_in "user_token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
|
80
|
+
click_button "Submit Token"
|
83
81
|
|
84
82
|
User.otp_authentication_timeout = old_timeout
|
85
83
|
assert_equal new_user_session_path, current_path
|
@@ -1,20 +1,19 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "test_helper"
|
2
|
+
require "integration_tests_helper"
|
3
3
|
|
4
4
|
class TokenTest < ActionDispatch::IntegrationTest
|
5
|
-
|
6
5
|
def teardown
|
7
6
|
Capybara.reset_sessions!
|
8
7
|
end
|
9
8
|
|
10
|
-
test
|
9
|
+
test "disabling OTP after successfully enabling" do
|
11
10
|
# log in 1fa
|
12
11
|
user = enable_otp_and_sign_in
|
13
12
|
assert_equal user_otp_credential_path, current_path
|
14
13
|
|
15
14
|
# otp 2fa
|
16
|
-
fill_in
|
17
|
-
click_button
|
15
|
+
fill_in "user_token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
|
16
|
+
click_button "Submit Token"
|
18
17
|
assert_equal root_path, current_path
|
19
18
|
|
20
19
|
# disable OTP
|
@@ -2,15 +2,15 @@ class ActionDispatch::IntegrationTest
|
|
2
2
|
include Warden::Test::Helpers
|
3
3
|
|
4
4
|
def warden
|
5
|
-
request.env[
|
5
|
+
request.env["warden"]
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
def create_full_user
|
9
9
|
@user ||= begin
|
10
10
|
user = User.create!(
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
11
|
+
email: "user@email.invalid",
|
12
|
+
password: "12345678",
|
13
|
+
password_confirmation: "12345678"
|
14
14
|
)
|
15
15
|
user
|
16
16
|
end
|
@@ -18,8 +18,8 @@ class ActionDispatch::IntegrationTest
|
|
18
18
|
|
19
19
|
def enable_otp_and_sign_in_with_otp
|
20
20
|
enable_otp_and_sign_in.tap do |user|
|
21
|
-
fill_in
|
22
|
-
click_button
|
21
|
+
fill_in "user_token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
|
22
|
+
click_button "Submit Token"
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -27,8 +27,8 @@ class ActionDispatch::IntegrationTest
|
|
27
27
|
user = create_full_user
|
28
28
|
sign_user_in(user)
|
29
29
|
visit user_otp_token_path
|
30
|
-
check
|
31
|
-
click_button
|
30
|
+
check "user_otp_enabled"
|
31
|
+
click_button "Continue..."
|
32
32
|
|
33
33
|
Capybara.reset_sessions!
|
34
34
|
|
@@ -37,14 +37,14 @@ class ActionDispatch::IntegrationTest
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def otp_challenge_for(user)
|
40
|
-
fill_in
|
41
|
-
click_button
|
40
|
+
fill_in "user_token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
|
41
|
+
click_button "Submit Token"
|
42
42
|
end
|
43
43
|
|
44
44
|
def disable_otp
|
45
45
|
visit user_otp_token_path
|
46
|
-
uncheck
|
47
|
-
click_button
|
46
|
+
uncheck "user_otp_enabled"
|
47
|
+
click_button "Continue..."
|
48
48
|
end
|
49
49
|
|
50
50
|
def sign_out
|
@@ -55,11 +55,10 @@ class ActionDispatch::IntegrationTest
|
|
55
55
|
user ||= create_full_user
|
56
56
|
resource_name = user.class.name.underscore
|
57
57
|
visit send("new_#{resource_name}_session_path")
|
58
|
-
fill_in "#{resource_name}_email", :
|
59
|
-
fill_in "#{resource_name}_password", :
|
58
|
+
fill_in "#{resource_name}_email", with: user.email
|
59
|
+
fill_in "#{resource_name}_password", with: user.password
|
60
60
|
|
61
|
-
page.has_content?(
|
61
|
+
page.has_content?("Log in") ? click_button("Log in") : click_button("Sign in")
|
62
62
|
user
|
63
63
|
end
|
64
|
-
|
65
64
|
end
|
data/test/model_tests_helper.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
class ActiveSupport::TestCase
|
2
|
-
|
3
2
|
#
|
4
3
|
# Helpers for creating new users
|
5
4
|
#
|
@@ -9,14 +8,13 @@ class ActiveSupport::TestCase
|
|
9
8
|
"user-#{@@unique_identity_count}@mail.invalid"
|
10
9
|
end
|
11
10
|
|
12
|
-
def valid_attributes(attributes={})
|
13
|
-
{
|
14
|
-
|
15
|
-
|
11
|
+
def valid_attributes(attributes = {})
|
12
|
+
{email: unique_identity,
|
13
|
+
password: "12345678",
|
14
|
+
password_confirmation: "12345678"}.update(attributes)
|
16
15
|
end
|
17
16
|
|
18
|
-
def new_user(attributes={})
|
17
|
+
def new_user(attributes = {})
|
19
18
|
User.new(valid_attributes(attributes)).save
|
20
19
|
end
|
21
|
-
|
22
20
|
end
|
@@ -1,32 +1,31 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "test_helper"
|
2
|
+
require "model_tests_helper"
|
3
3
|
|
4
4
|
class OtpAuthenticatableTest < ActiveSupport::TestCase
|
5
|
-
|
6
5
|
def setup
|
7
6
|
new_user
|
8
7
|
end
|
9
8
|
|
10
|
-
test
|
9
|
+
test "new users have a non-nil secret set" do
|
11
10
|
assert_not_nil User.first.otp_auth_secret
|
12
11
|
end
|
13
12
|
|
14
|
-
test
|
13
|
+
test "new users have OTP disabled by default" do
|
15
14
|
assert !User.first.otp_enabled
|
16
15
|
end
|
17
16
|
|
18
|
-
test
|
17
|
+
test "users should have an instance of TOTP/ROTP objects" do
|
19
18
|
u = User.first
|
20
19
|
assert u.time_based_otp.is_a? ROTP::TOTP
|
21
20
|
assert u.recovery_otp.is_a? ROTP::HOTP
|
22
21
|
end
|
23
22
|
|
24
|
-
test
|
23
|
+
test "users should have their otp_auth_secret/persistence_seed set on creation" do
|
25
24
|
assert User.first.otp_auth_secret
|
26
25
|
assert User.first.otp_persistence_seed
|
27
26
|
end
|
28
27
|
|
29
|
-
test
|
28
|
+
test "reset_otp_credentials should generate new secrets and disable OTP" do
|
30
29
|
u = User.first
|
31
30
|
u.update_attribute(:otp_enabled, true)
|
32
31
|
assert u.otp_enabled
|
@@ -39,7 +38,7 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
|
|
39
38
|
assert !u.otp_enabled
|
40
39
|
end
|
41
40
|
|
42
|
-
test
|
41
|
+
test "reset_otp_persistence should generate new persistence_seed but NOT change the otp_auth_secret" do
|
43
42
|
u = User.first
|
44
43
|
u.update_attribute(:otp_enabled, true)
|
45
44
|
assert u.otp_enabled
|
@@ -47,22 +46,22 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
|
|
47
46
|
otp_persistence_seed = u.otp_persistence_seed
|
48
47
|
|
49
48
|
u.reset_otp_persistence!
|
50
|
-
assert
|
49
|
+
assert(otp_auth_secret == u.otp_auth_secret)
|
51
50
|
assert !(otp_persistence_seed == u.otp_persistence_seed)
|
52
51
|
assert u.otp_enabled
|
53
52
|
end
|
54
53
|
|
55
|
-
test
|
54
|
+
test "generating a challenge, should retrieve the user later" do
|
56
55
|
u = User.first
|
57
56
|
u.update_attribute(:otp_enabled, true)
|
58
57
|
challenge = u.generate_otp_challenge!
|
59
58
|
|
60
59
|
w = User.find_valid_otp_challenge(challenge)
|
61
60
|
assert w.is_a? User
|
62
|
-
assert_equal w,u
|
61
|
+
assert_equal w, u
|
63
62
|
end
|
64
63
|
|
65
|
-
test
|
64
|
+
test "expiring the challenge, should retrieve nothing" do
|
66
65
|
u = User.first
|
67
66
|
u.update_attribute(:otp_enabled, true)
|
68
67
|
challenge = u.generate_otp_challenge!(1.second)
|
@@ -72,7 +71,7 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
|
|
72
71
|
assert_nil w
|
73
72
|
end
|
74
73
|
|
75
|
-
test
|
74
|
+
test "expired challenges should not be valid" do
|
76
75
|
u = User.first
|
77
76
|
u.update_attribute(:otp_enabled, true)
|
78
77
|
challenge = u.generate_otp_challenge!(1.second)
|
@@ -80,14 +79,14 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
|
|
80
79
|
assert_equal false, u.otp_challenge_valid?
|
81
80
|
end
|
82
81
|
|
83
|
-
test
|
82
|
+
test "null otp challenge" do
|
84
83
|
u = User.first
|
85
84
|
u.update_attribute(:otp_enabled, true)
|
86
|
-
assert_equal false, u.validate_otp_token(
|
85
|
+
assert_equal false, u.validate_otp_token("")
|
87
86
|
assert_equal false, u.validate_otp_token(nil)
|
88
87
|
end
|
89
88
|
|
90
|
-
test
|
89
|
+
test "generated otp token should be valid for the user" do
|
91
90
|
u = User.first
|
92
91
|
u.update_attribute(:otp_enabled, true)
|
93
92
|
|
@@ -97,7 +96,7 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
|
|
97
96
|
assert_equal true, u.validate_otp_token(token)
|
98
97
|
end
|
99
98
|
|
100
|
-
test
|
99
|
+
test "generated otp token, out of drift window, should be NOT valid for the user" do
|
101
100
|
u = User.first
|
102
101
|
u.update_attribute(:otp_enabled, true)
|
103
102
|
|
@@ -109,7 +108,7 @@ class OtpAuthenticatableTest < ActiveSupport::TestCase
|
|
109
108
|
end
|
110
109
|
end
|
111
110
|
|
112
|
-
test
|
111
|
+
test "recovery secrets should be valid, and valid only once" do
|
113
112
|
u = User.first
|
114
113
|
u.update_attribute(:otp_enabled, true)
|
115
114
|
recovery = u.next_otp_recovery_tokens
|
data/test/test_helper.rb
CHANGED
@@ -4,21 +4,21 @@ DEVISE_ORM = (ENV["DEVISE_ORM"] || :active_record).to_sym
|
|
4
4
|
puts "\n==> Devise.orm = #{DEVISE_ORM.inspect}"
|
5
5
|
require "dummy/config/environment"
|
6
6
|
require "orm/#{DEVISE_ORM}"
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
7
|
+
require "rails/test_help"
|
8
|
+
require "capybara/rails"
|
9
|
+
require "capybara/cuprite"
|
10
|
+
require "minitest/reporters"
|
11
11
|
|
12
12
|
MiniTest::Reporters.use!
|
13
13
|
|
14
|
-
#I18n.load_path << File.expand_path("../support/locale/en.yml", __FILE__) if DEVISE_ORM == :mongoid
|
14
|
+
# I18n.load_path << File.expand_path("../support/locale/en.yml", __FILE__) if DEVISE_ORM == :mongoid
|
15
15
|
|
16
|
-
#ActiveSupport::Deprecation.silenced = true
|
16
|
+
# ActiveSupport::Deprecation.silenced = true
|
17
17
|
|
18
18
|
# Use a module to not pollute the global namespace
|
19
19
|
module CapybaraHelper
|
20
20
|
def self.register_driver(driver_name, args = [])
|
21
|
-
opts = {
|
21
|
+
opts = {headless: true, js_errors: true, window_size: [1920, 1200], browser_options: {}}
|
22
22
|
args.each do |arg|
|
23
23
|
opts[:browser_options][arg] = nil
|
24
24
|
end
|
@@ -33,12 +33,12 @@ end
|
|
33
33
|
CapybaraHelper.register_driver(:headless_chrome, %w[disable-gpu no-sandbox disable-dev-shm-usage])
|
34
34
|
|
35
35
|
# Configure Capybara JS driver
|
36
|
-
Capybara.current_driver
|
36
|
+
Capybara.current_driver = :headless_chrome
|
37
37
|
Capybara.javascript_driver = :headless_chrome
|
38
38
|
|
39
39
|
# Configure Capybara server
|
40
40
|
Capybara.run_server = true
|
41
|
-
Capybara.server
|
41
|
+
Capybara.server = :puma, {Silent: true}
|
42
42
|
|
43
43
|
class ActionDispatch::IntegrationTest
|
44
44
|
include Capybara::DSL
|
@@ -53,7 +53,7 @@ class ActionDispatch::IntegrationTest
|
|
53
53
|
# See: https://github.com/rubycdp/ferrum/blob/master/lib/ferrum/page.rb
|
54
54
|
def enable_chrome_headless_downloads(session, directory)
|
55
55
|
page = session.driver.browser.page
|
56
|
-
page.command(
|
56
|
+
page.command("Page.setDownloadBehavior", behavior: "allow", downloadPath: directory)
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|