devise-2fa 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +46 -0
  3. data/.gitignore +8 -0
  4. data/Gemfile +3 -22
  5. data/README.md +13 -14
  6. data/Rakefile +6 -28
  7. data/bin/rspec +10 -0
  8. data/bin/setup +12 -0
  9. data/{devise-2fa.gemspec → devise_2fa.gemspec} +15 -8
  10. data/lib/devise-2fa/version.rb +1 -1
  11. data/lib/devise_two_factorable/models/two_factorable.rb +5 -1
  12. data/{test → spec}/dummy/Rakefile +2 -3
  13. data/{test/dummy/app/mailers/.gitkeep → spec/dummy/app/assets/images/.keep} +0 -0
  14. data/spec/dummy/app/assets/javascripts/application.js +3 -0
  15. data/{test/dummy/lib/assets/.gitkeep → spec/dummy/app/assets/javascripts/channels/.keep} +0 -0
  16. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  17. data/{test → spec}/dummy/app/controllers/application_controller.rb +4 -1
  18. data/{test/dummy/public/favicon.ico → spec/dummy/app/controllers/concerns/.keep} +0 -0
  19. data/{test → spec}/dummy/app/helpers/application_helper.rb +0 -0
  20. data/spec/dummy/app/models/application_record.rb +3 -0
  21. data/spec/dummy/app/models/concerns/.keep +0 -0
  22. data/spec/dummy/app/models/user.rb +6 -0
  23. data/spec/dummy/app/views/layouts/application.html.erb +19 -0
  24. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  25. data/spec/dummy/bin/bundle +3 -0
  26. data/spec/dummy/bin/rails +4 -0
  27. data/spec/dummy/bin/rake +4 -0
  28. data/spec/dummy/bin/setup +25 -0
  29. data/spec/dummy/bin/update +25 -0
  30. data/spec/dummy/bin/yarn +11 -0
  31. data/spec/dummy/config.ru +5 -0
  32. data/spec/dummy/config/application.rb +14 -0
  33. data/spec/dummy/config/boot.rb +5 -0
  34. data/{test → spec}/dummy/config/database.yml +10 -10
  35. data/spec/dummy/config/environment.rb +5 -0
  36. data/spec/dummy/config/environments/development.rb +61 -0
  37. data/{test → spec}/dummy/config/environments/test.rb +15 -5
  38. data/spec/dummy/config/initializers/assets.rb +4 -0
  39. data/{test → spec}/dummy/config/initializers/backtrace_silencers.rb +0 -0
  40. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  41. data/{test → spec}/dummy/config/initializers/devise.rb +134 -56
  42. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  43. data/{test → spec}/dummy/config/initializers/inflections.rb +6 -5
  44. data/{test → spec}/dummy/config/initializers/mime_types.rb +0 -1
  45. data/{test → spec}/dummy/config/initializers/wrap_parameters.rb +5 -5
  46. data/spec/dummy/config/locales/devise.en.yml +68 -0
  47. data/spec/dummy/config/locales/devise.two_factor.en.yml +57 -0
  48. data/spec/dummy/config/locales/en.yml +2 -0
  49. data/spec/dummy/config/puma.rb +9 -0
  50. data/spec/dummy/config/routes.rb +4 -0
  51. data/spec/dummy/config/spring.rb +6 -0
  52. data/spec/dummy/config/storage.yml +8 -0
  53. data/spec/dummy/db/migrate/20190311184605_devise_create_users.rb +44 -0
  54. data/{test/dummy/db/migrate/20130131160351_devise_otp_add_to_users.rb → spec/dummy/db/migrate/20190312222952_devise_two_factor_add_to_users.rb} +4 -5
  55. data/spec/dummy/db/schema.rb +39 -0
  56. data/spec/dummy/lib/assets/.keep +0 -0
  57. data/spec/dummy/package.json +5 -0
  58. data/spec/dummy/public/404.html +1 -0
  59. data/spec/dummy/public/422.html +1 -0
  60. data/spec/dummy/public/500.html +19 -0
  61. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  62. data/spec/dummy/public/apple-touch-icon.png +0 -0
  63. data/spec/dummy/public/favicon.ico +0 -0
  64. data/spec/dummy/storage/.keep +0 -0
  65. data/spec/models/user_spec.rb +33 -0
  66. data/spec/spec_helper.rb +69 -0
  67. data/spec/system/persistence_spec.rb +59 -0
  68. data/spec/system/refresh_spec.rb +100 -0
  69. data/spec/system/token_spec.rb +41 -0
  70. data/spec/system/users_spec.rb +98 -0
  71. metadata +213 -123
  72. data/.travis.yml +0 -28
  73. data/lib/devise_two_factorable/two_factorable.rb +0 -131
  74. data/test/dummy/README.rdoc +0 -261
  75. data/test/dummy/app/assets/javascripts/application.js +0 -13
  76. data/test/dummy/app/assets/stylesheets/application.css +0 -13
  77. data/test/dummy/app/controllers/posts_controller.rb +0 -83
  78. data/test/dummy/app/helpers/posts_helper.rb +0 -2
  79. data/test/dummy/app/models/post.rb +0 -2
  80. data/test/dummy/app/models/user.rb +0 -20
  81. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  82. data/test/dummy/app/views/posts/_form.html.erb +0 -25
  83. data/test/dummy/app/views/posts/edit.html.erb +0 -6
  84. data/test/dummy/app/views/posts/index.html.erb +0 -25
  85. data/test/dummy/app/views/posts/new.html.erb +0 -5
  86. data/test/dummy/app/views/posts/show.html.erb +0 -15
  87. data/test/dummy/config.ru +0 -4
  88. data/test/dummy/config/application.rb +0 -67
  89. data/test/dummy/config/boot.rb +0 -10
  90. data/test/dummy/config/environment.rb +0 -5
  91. data/test/dummy/config/environments/development.rb +0 -37
  92. data/test/dummy/config/environments/production.rb +0 -73
  93. data/test/dummy/config/initializers/secret_token.rb +0 -8
  94. data/test/dummy/config/initializers/session_store.rb +0 -8
  95. data/test/dummy/config/locales/en.yml +0 -5
  96. data/test/dummy/config/routes.rb +0 -6
  97. data/test/dummy/db/migrate/20130125101430_create_users.rb +0 -9
  98. data/test/dummy/db/migrate/20130131092406_add_devise_to_users.rb +0 -52
  99. data/test/dummy/db/migrate/20130131142320_create_posts.rb +0 -10
  100. data/test/dummy/public/404.html +0 -26
  101. data/test/dummy/public/422.html +0 -26
  102. data/test/dummy/public/500.html +0 -25
  103. data/test/dummy/script/rails +0 -6
  104. data/test/integration/persistence_test.rb +0 -63
  105. data/test/integration/refresh_test.rb +0 -103
  106. data/test/integration/sign_in_test.rb +0 -85
  107. data/test/integration/token_test.rb +0 -30
  108. data/test/integration_tests_helper.rb +0 -64
  109. data/test/model_tests_helper.rb +0 -20
  110. data/test/models/two_factorable_test.rb +0 -120
  111. data/test/orm/active_record.rb +0 -4
  112. data/test/orm/mongoid.rb +0 -13
  113. data/test/support/mongoid.yml +0 -6
  114. data/test/support/symmetric_encryption.yml +0 -70
  115. data/test/test_helper.rb +0 -18
@@ -1,9 +0,0 @@
1
- class CreateUsers < ActiveRecord::Migration
2
- def change
3
- create_table :users do |t|
4
- t.string :name
5
-
6
- t.timestamps
7
- end
8
- end
9
- end
@@ -1,52 +0,0 @@
1
- class AddDeviseToUsers < ActiveRecord::Migration
2
- def self.up
3
- change_table(:users) do |t|
4
- ## Database authenticatable
5
- t.string :email, null: false, default: ''
6
- t.string :encrypted_password, null: false, default: ''
7
-
8
- ## Recoverable
9
- t.string :reset_password_token
10
- t.datetime :reset_password_sent_at
11
-
12
- ## Rememberable
13
- t.datetime :remember_created_at
14
-
15
- ## Trackable
16
- t.integer :sign_in_count, default: 0
17
- t.datetime :current_sign_in_at
18
- t.datetime :last_sign_in_at
19
- t.string :current_sign_in_ip
20
- t.string :last_sign_in_ip
21
-
22
- ## Confirmable
23
- # t.string :confirmation_token
24
- # t.datetime :confirmed_at
25
- # t.datetime :confirmation_sent_at
26
- # t.string :unconfirmed_email # Only if using reconfirmable
27
-
28
- ## Lockable
29
- t.integer :failed_attempts, default: 0 # Only if lock strategy is :failed_attempts
30
- t.string :unlock_token # Only if unlock strategy is :email or :both
31
- t.datetime :locked_at
32
-
33
- ## Token authenticatable
34
- t.string :authentication_token
35
-
36
- # Uncomment below if timestamps were not included in your original model.
37
- # t.timestamps
38
- end
39
-
40
- add_index :users, :email, unique: true
41
- add_index :users, :reset_password_token, unique: true
42
- # add_index :users, :confirmation_token, :unique => true
43
- add_index :users, :unlock_token, unique: true
44
- add_index :users, :authentication_token, unique: true
45
- end
46
-
47
- def self.down
48
- # By default, we don't want to make any assumption about how to roll back a migration when your
49
- # model already existed. Please edit below which fields you would like to remove in this migration.
50
- raise ActiveRecord::IrreversibleMigration
51
- end
52
- end
@@ -1,10 +0,0 @@
1
- class CreatePosts < ActiveRecord::Migration
2
- def change
3
- create_table :posts do |t|
4
- t.string :title
5
- t.text :body
6
-
7
- t.timestamps
8
- end
9
- end
10
- end
@@ -1,26 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>The page you were looking for doesn't exist (404)</title>
5
- <style type="text/css">
6
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
- div.dialog {
8
- width: 25em;
9
- padding: 0 4em;
10
- margin: 4em auto 0 auto;
11
- border: 1px solid #ccc;
12
- border-right-color: #999;
13
- border-bottom-color: #999;
14
- }
15
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
- </style>
17
- </head>
18
-
19
- <body>
20
- <!-- This file lives in public/404.html -->
21
- <div class="dialog">
22
- <h1>The page you were looking for doesn't exist.</h1>
23
- <p>You may have mistyped the address or the page may have moved.</p>
24
- </div>
25
- </body>
26
- </html>
@@ -1,26 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>The change you wanted was rejected (422)</title>
5
- <style type="text/css">
6
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
- div.dialog {
8
- width: 25em;
9
- padding: 0 4em;
10
- margin: 4em auto 0 auto;
11
- border: 1px solid #ccc;
12
- border-right-color: #999;
13
- border-bottom-color: #999;
14
- }
15
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
- </style>
17
- </head>
18
-
19
- <body>
20
- <!-- This file lives in public/422.html -->
21
- <div class="dialog">
22
- <h1>The change you wanted was rejected.</h1>
23
- <p>Maybe you tried to change something you didn't have access to.</p>
24
- </div>
25
- </body>
26
- </html>
@@ -1,25 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>We're sorry, but something went wrong (500)</title>
5
- <style type="text/css">
6
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
- div.dialog {
8
- width: 25em;
9
- padding: 0 4em;
10
- margin: 4em auto 0 auto;
11
- border: 1px solid #ccc;
12
- border-right-color: #999;
13
- border-bottom-color: #999;
14
- }
15
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
- </style>
17
- </head>
18
-
19
- <body>
20
- <!-- This file lives in public/500.html -->
21
- <div class="dialog">
22
- <h1>We're sorry, but something went wrong.</h1>
23
- </div>
24
- </body>
25
- </html>
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
-
4
- APP_PATH = File.expand_path('../../config/application', __FILE__)
5
- require File.expand_path('../../config/boot', __FILE__)
6
- require 'rails/commands'
@@ -1,63 +0,0 @@
1
- require 'test_helper'
2
- require 'integration_tests_helper'
3
-
4
- class PersistenceTest < ActionDispatch::IntegrationTest
5
- def setup
6
- @old_persistence = User.otp_trust_persistence
7
- User.otp_trust_persistence = 3.seconds
8
- end
9
-
10
- def teardown
11
- User.otp_trust_persistence = @old_persistence
12
- Capybara.reset_sessions!
13
- end
14
-
15
- test 'a user should be requested the otp challenge every log in' do
16
- # log in 1fa
17
- user = enable_otp_and_sign_in
18
- otp_challenge_for user
19
-
20
- visit user_token_path
21
- assert_equal user_token_path, current_path
22
-
23
- sign_out
24
- sign_user_in
25
-
26
- assert_equal user_credential_path, current_path
27
- end
28
-
29
- test 'a user should be able to set their browser as trusted' do
30
- # log in 1fa
31
- user = enable_otp_and_sign_in
32
- otp_challenge_for user
33
-
34
- visit user_token_path
35
- assert_equal user_token_path, current_path
36
-
37
- click_link('Trust this browser')
38
- assert_text 'Your browser is trusted.'
39
- sign_out
40
-
41
- sign_user_in
42
-
43
- assert_equal root_path, current_path
44
- end
45
-
46
- test 'trusted status should expire' do
47
- # log in 1fa
48
- user = enable_otp_and_sign_in
49
- otp_challenge_for user
50
-
51
- visit user_token_path
52
- assert_equal user_token_path, current_path
53
-
54
- click_link('Trust this browser')
55
- assert_text 'Your browser is trusted.'
56
- sign_out
57
-
58
- sleep User.otp_trust_persistence.to_i + 1
59
- sign_user_in
60
-
61
- assert_equal user_credential_path, current_path
62
- end
63
- end
@@ -1,103 +0,0 @@
1
- require 'test_helper'
2
- require 'integration_tests_helper'
3
-
4
- class RefreshTest < ActionDispatch::IntegrationTest
5
- def setup
6
- @old_refresh = User.otp_credentials_refresh
7
- User.otp_credentials_refresh = 1.second
8
- end
9
-
10
- def teardown
11
- User.otp_credentials_refresh = @old_refresh
12
- Capybara.reset_sessions!
13
- end
14
-
15
- test 'a user that just signed in should be able to access their OTP settings without refreshing' do
16
- sign_user_in
17
-
18
- visit user_token_path
19
- assert_equal user_token_path, current_path
20
- end
21
-
22
- test 'a user should be prompted for credentials when the credentials_refresh time is expired' do
23
- sign_user_in
24
- visit user_token_path
25
- assert_equal user_token_path, current_path
26
-
27
- sleep(2)
28
-
29
- visit user_token_path
30
- assert_equal refresh_user_credential_path, current_path
31
- end
32
-
33
- test 'a user should be able to access their OTP settings after refreshing' do
34
- sign_user_in
35
- visit user_token_path
36
- assert_equal user_token_path, current_path
37
-
38
- sleep(2)
39
-
40
- visit user_token_path
41
- assert_equal refresh_user_credential_path, current_path
42
-
43
- fill_in 'user_refresh_password', with: '12345678'
44
- click_button 'Continue...'
45
- assert_equal user_token_path, current_path
46
- end
47
-
48
- test 'a user should NOT be able to access their OTP settings unless refreshing' do
49
- sign_user_in
50
- visit user_token_path
51
- assert_equal user_token_path, current_path
52
-
53
- sleep(2)
54
-
55
- visit user_token_path
56
- assert_equal refresh_user_credential_path, current_path
57
-
58
- fill_in 'user_refresh_password', with: '12345670'
59
- click_button 'Continue...'
60
- assert_equal refresh_user_credential_path, current_path
61
- end
62
-
63
- test 'user should be asked their OTP challenge in order to refresh, if they have OTP' do
64
- enable_otp_and_sign_in_with_otp
65
-
66
- sleep(2)
67
- visit user_token_path
68
- assert_equal refresh_user_credential_path, current_path
69
-
70
- fill_in 'user_refresh_password', with: '12345678'
71
- click_button 'Continue...'
72
-
73
- assert_equal refresh_user_credential_path, current_path
74
- end
75
-
76
- test 'user should be finally be able to access their settings, if they provide both a password and a valid OTP token' do
77
- user = enable_otp_and_sign_in_with_otp
78
-
79
- sleep(2)
80
- visit user_token_path
81
- assert_equal refresh_user_credential_path, current_path
82
-
83
- fill_in 'user_refresh_password', with: '12345678'
84
- fill_in 'user_token', with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
85
- click_button 'Continue...'
86
-
87
- assert_equal user_token_path, current_path
88
- end
89
-
90
- test 'and rejected when the token is blank or null' do
91
- user = enable_otp_and_sign_in_with_otp
92
-
93
- sleep(2)
94
- visit user_token_path
95
- assert_equal refresh_user_credential_path, current_path
96
-
97
- fill_in 'user_refresh_password', with: '12345678'
98
- fill_in 'user_token', with: ''
99
- click_button 'Continue...'
100
-
101
- assert_equal refresh_user_credential_path, current_path
102
- end
103
- end
@@ -1,85 +0,0 @@
1
- require 'test_helper'
2
- require 'integration_tests_helper'
3
-
4
- class SignInTest < ActionDispatch::IntegrationTest
5
- def teardown
6
- Capybara.reset_sessions!
7
- end
8
-
9
- test 'a new user should be able to sign in without using their token' do
10
- create_full_user
11
-
12
- visit new_user_session_path
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')
16
-
17
- assert_equal root_path, current_path
18
- end
19
-
20
- test 'a new user, just signed in, should be able to sign in and enable their OTP authentication' do
21
- user = sign_user_in
22
-
23
- visit user_token_path
24
- assert !page.has_content?('Your token secret')
25
-
26
- check 'user_otp_enabled'
27
- click_button 'Continue...'
28
-
29
- assert_equal user_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?
34
- end
35
-
36
- test 'a new user should be able to sign in enable OTP and be prompted for their token' do
37
- enable_otp_and_sign_in
38
-
39
- assert_equal user_credential_path, current_path
40
- end
41
-
42
- test 'fail token authentication' do
43
- enable_otp_and_sign_in
44
- assert_equal user_credential_path, current_path
45
-
46
- fill_in 'user_token', with: '123456'
47
- click_button 'Submit Token'
48
-
49
- assert_equal new_user_session_path, current_path
50
- end
51
-
52
- test 'fail blank token authentication' do
53
- enable_otp_and_sign_in
54
- assert_equal user_credential_path, current_path
55
-
56
- fill_in 'user_token', with: ''
57
- click_button 'Submit Token'
58
-
59
- assert_equal user_credential_path, current_path
60
- end
61
-
62
- test 'successful token authentication' do
63
- user = enable_otp_and_sign_in
64
-
65
- fill_in 'user_token', with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
66
- click_button 'Submit Token'
67
-
68
- assert_equal root_path, current_path
69
- end
70
-
71
- test 'should fail if the the challenge times out' do
72
- old_timeout = User.otp_authentication_timeout
73
- User.otp_authentication_timeout = 1.second
74
-
75
- user = enable_otp_and_sign_in
76
-
77
- sleep(2)
78
-
79
- fill_in 'user_token', with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
80
- click_button 'Submit Token'
81
-
82
- User.otp_authentication_timeout = old_timeout
83
- assert_equal new_user_session_path, current_path
84
- end
85
- end
@@ -1,30 +0,0 @@
1
- require 'test_helper'
2
- require 'integration_tests_helper'
3
-
4
- class TokenTest < ActionDispatch::IntegrationTest
5
- def teardown
6
- Capybara.reset_sessions!
7
- end
8
-
9
- test 'disabling OTP after successfully enabling' do
10
- # log in 1fa
11
- user = enable_otp_and_sign_in
12
- assert_equal user_credential_path, current_path
13
-
14
- # otp two_factor
15
- fill_in 'user_token', with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
16
- click_button 'Submit Token'
17
- assert_equal root_path, current_path
18
-
19
- # disable OTP
20
- disable_otp
21
-
22
- # logout
23
- sign_out
24
-
25
- # log back in 1fa
26
- sign_user_in(user)
27
-
28
- assert_equal root_path, current_path
29
- end
30
- end