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,64 +0,0 @@
1
- class ActionDispatch::IntegrationTest
2
- include Warden::Test::Helpers
3
-
4
- def warden
5
- request.env['warden']
6
- end
7
-
8
- def create_full_user
9
- @user ||= begin
10
- user = User.create!(
11
- email: 'user@email.invalid',
12
- password: '12345678',
13
- password_confirmation: '12345678'
14
- )
15
- user
16
- end
17
- end
18
-
19
- def enable_otp_and_sign_in_with_otp
20
- enable_otp_and_sign_in.tap do |user|
21
- fill_in 'user_token', with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
22
- click_button 'Submit Token'
23
- end
24
- end
25
-
26
- def enable_otp_and_sign_in
27
- user = create_full_user
28
- sign_user_in(user)
29
- visit user_token_path
30
- check 'user_otp_enabled'
31
- click_button 'Continue...'
32
-
33
- Capybara.reset_sessions!
34
-
35
- sign_user_in(user)
36
- user
37
- end
38
-
39
- def otp_challenge_for(user)
40
- fill_in 'user_token', with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
41
- click_button 'Submit Token'
42
- end
43
-
44
- def disable_otp
45
- visit user_token_path
46
- uncheck 'user_otp_enabled'
47
- click_button 'Continue...'
48
- end
49
-
50
- def sign_out
51
- logout :user
52
- end
53
-
54
- def sign_user_in(user = nil)
55
- user ||= create_full_user
56
- resource_name = user.class.name.underscore
57
- visit send("new_#{resource_name}_session_path")
58
- fill_in "#{resource_name}_email", with: user.email
59
- fill_in "#{resource_name}_password", with: user.password
60
-
61
- page.has_content?('Log in') ? click_button('Log in') : click_button('Sign in')
62
- user
63
- end
64
- end
@@ -1,20 +0,0 @@
1
- class ActiveSupport::TestCase
2
- #
3
- # Helpers for creating new users
4
- #
5
- def unique_identity
6
- @@unique_identity_count ||= 0
7
- @@unique_identity_count += 1
8
- "user-#{@@unique_identity_count}@mail.invalid"
9
- end
10
-
11
- def valid_attributes(attributes = {})
12
- { email: unique_identity,
13
- password: '12345678',
14
- password_confirmation: '12345678' }.update(attributes)
15
- end
16
-
17
- def new_user(attributes = {})
18
- User.new(valid_attributes(attributes)).save
19
- end
20
- end
@@ -1,120 +0,0 @@
1
- require 'test_helper'
2
- require 'model_tests_helper'
3
-
4
- class TwoFactorableTest < ActiveSupport::TestCase
5
- def setup
6
- new_user
7
- end
8
-
9
- test 'new users have a non-nil secret set' do
10
- assert_not_nil User.first.otp_auth_secret
11
- end
12
-
13
- test 'new users have OTP disabled by default' do
14
- assert !User.first.otp_enabled
15
- end
16
-
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
- end
22
-
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
26
- end
27
-
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
34
-
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
39
- end
40
-
41
- test 'reset_otp_persistence should generate new persistence_seed but NOT change the otp_auth_secret' do
42
- u = User.first
43
- u.update_attribute(:otp_enabled, true)
44
- assert u.otp_enabled
45
- otp_auth_secret = u.otp_auth_secret
46
- otp_persistence_seed = u.otp_persistence_seed
47
-
48
- u.reset_otp_persistence!
49
- assert (otp_auth_secret == u.otp_auth_secret)
50
- assert !(otp_persistence_seed == u.otp_persistence_seed)
51
- assert u.otp_enabled
52
- end
53
-
54
- test 'generating a challenge, should retrieve the user later' do
55
- u = User.first
56
- u.update_attribute(:otp_enabled, true)
57
- challenge = u.generate_otp_challenge!
58
-
59
- w = User.find_valid_otp_challenge(challenge)
60
- assert w.is_a? User
61
- assert_equal w, u
62
- end
63
-
64
- test 'expiring the challenge, should retrieve nothing' do
65
- u = User.first
66
- u.update_attribute(:otp_enabled, true)
67
- challenge = u.generate_otp_challenge!(1.second)
68
- sleep(2)
69
-
70
- w = User.find_valid_otp_challenge(challenge)
71
- assert_nil w
72
- end
73
-
74
- test 'expired challenges should not be valid' do
75
- u = User.first
76
- u.update_attribute(:otp_enabled, true)
77
- challenge = u.generate_otp_challenge!(1.second)
78
- sleep(2)
79
- assert_equal false, u.otp_challenge_valid?
80
- end
81
-
82
- test 'null otp challenge' do
83
- u = User.first
84
- u.update_attribute(:otp_enabled, true)
85
- assert_equal false, u.validate_otp_token('')
86
- assert_equal false, u.validate_otp_token(nil)
87
- end
88
-
89
- test 'generated otp token should be valid for the user' do
90
- u = User.first
91
- u.update_attribute(:otp_enabled, true)
92
-
93
- secret = u.otp_auth_secret
94
- token = ROTP::TOTP.new(secret).now
95
-
96
- assert_equal true, u.validate_otp_token(token)
97
- end
98
-
99
- test 'generated otp token, out of drift window, should be NOT valid for the user' do
100
- u = User.first
101
- u.update_attribute(:otp_enabled, true)
102
-
103
- secret = u.otp_auth_secret
104
-
105
- [3.minutes.from_now, 3.minutes.ago].each do |time|
106
- token = ROTP::TOTP.new(secret).at(time)
107
- assert_equal false, u.valid_otp_token?(token)
108
- end
109
- end
110
-
111
- test 'recovery secrets should be valid, and valid only once' do
112
- u = User.first
113
- u.update_attribute(:otp_enabled, true)
114
- recovery = u.next_otp_recovery_tokens
115
-
116
- assert u.valid_otp_recovery_token? recovery.fetch(0)
117
- assert_equal false, u.valid_otp_recovery_token?(recovery.fetch(0))
118
- assert u.valid_otp_recovery_token? recovery.fetch(2)
119
- end
120
- end
@@ -1,4 +0,0 @@
1
- ActiveRecord::Migration.verbose = false
2
- ActiveRecord::Base.logger = Logger.new(nil)
3
-
4
- ActiveRecord::Migrator.migrate(File.expand_path('../../dummy/db/migrate/', __FILE__))
@@ -1,13 +0,0 @@
1
- require 'mongoid/version'
2
-
3
- Mongoid.configure do |config|
4
- config.load!('test/support/mongoid.yml')
5
- config.use_utc = true
6
- config.include_root_in_json = true
7
- end
8
-
9
- class ActiveSupport::TestCase
10
- setup do
11
- Mongoid.purge!
12
- end
13
- end
@@ -1,6 +0,0 @@
1
- test:
2
- <%= Mongoid::VERSION.to_i > 4 ? 'clients' : 'sessions' %>:
3
- default:
4
- database: devise-2fa-test-suite
5
- hosts:
6
- - localhost:<%= ENV['MONGODB_PORT'] || '27017' %>
@@ -1,70 +0,0 @@
1
- #
2
- # Symmetric Encryption for Ruby
3
- #
4
- ---
5
- # For the development and test environments the test symmetric encryption keys
6
- # can be placed directly in the source code.
7
- # And therefore no RSA private key is required
8
- development: &development_defaults
9
- key: 1234567890ABCDEF1234567890ABCDEF
10
- iv: 1234567890ABCDEF
11
- cipher_name: aes-128-cbc
12
-
13
- test:
14
- <<: *development_defaults
15
-
16
- production:
17
- # Since the key to encrypt and decrypt with must NOT be stored along with the
18
- # source code, we only hold a RSA key that is used to unlock the file
19
- # containing the actual symmetric encryption key
20
- #
21
- # Sample RSA Key, DO NOT use this RSA key, generate a new one using
22
- # openssl genrsa 2048
23
- private_rsa_key: |
24
- -----BEGIN RSA PRIVATE KEY-----
25
- MIIEpAIBAAKCAQEAxIL9H/jYUGpA38v6PowRSRJEo3aNVXULNM/QNRpx2DTf++KH
26
- 6DcuFTFcNSSSxG9n4y7tKi755be8N0uwCCuOzvXqfWmXYjbLwK3Ib2vm0btpHyvA
27
- qxgqeJOOCxKdW/cUFLWn0tACUcEjVCNfWEGaFyvkOUuR7Ub9KfhbW9cZO3BxZMUf
28
- IPGlHl/gWyf484sXygd+S7cpDTRRzo9RjG74DwfE0MFGf9a1fTkxnSgeOJ6asTOy
29
- fp9tEToUlbglKaYGpOGHYQ9TV5ZsyJ9jRUyb4SP5wK2eK6dHTxTcHvT03kD90Hv4
30
- WeKIXv3WOjkwNEyMdpnJJfSDb5oquQvCNi7ZSQIDAQABAoIBAQCbzR7TUoBugU+e
31
- ICLvpC2wOYOh9kRoFLwlyv3QnH7WZFWRZzFJszYeJ1xr5etXQtyjCnmOkGAg+WOI
32
- k8GlOKOpAuA/PpB/leJFiYL4lBwU/PmDdTT0cdx6bMKZlNCeMW8CXGQKiFDOcMqJ
33
- 0uGtH5YD+RChPIEeFsJxnC8SyZ9/t2ra7XnMGiCZvRXIUDSEIIsRx/mOymJ7bL+h
34
- Lbp46IfXf6ZuIzwzoIk0JReV/r+wdmkAVDkrrMkCmVS4/X1wN/Tiik9/yvbsh/CL
35
- ztC55eSIEjATkWxnXfPASZN6oUfQPEveGH3HzNjdncjH/Ho8FaNMIAfFpBhhLPi9
36
- nG5sbH+BAoGBAOdoUyVoAA/QUa3/FkQaa7Ajjehe5MR5k6VtaGtcxrLiBjrNR7x+
37
- nqlZlGvWDMiCz49dgj+G1Qk1bbYrZLRX/Hjeqy5dZOGLMfgf9eKUmS1rDwAzBMcj
38
- M9jnnJEBx8HIlNzaR6wzp3GMd0rrccs660A8URvzkgo9qNbvMLq9vyUtAoGBANll
39
- SY1Iv9uaIz8klTXU9YzYtsfUmgXzw7K8StPdbEbo8F1J3JPJB4D7QHF0ObIaSWuf
40
- suZqLsvWlYGuJeyX2ntlBN82ORfvUdOrdrbDlmPyj4PfFVl0AK3U3Ai374DNrjKR
41
- hF6YFm4TLDaJhUjeV5C43kbE1N2FAMS9LYtPJ44NAoGAFDGHZ/E+aCLerddfwwun
42
- MBS6MnftcLPHTZ1RimTrNfsBXipBw1ItWEvn5s0kCm9X24PmdNK4TnhqHYaF4DL5
43
- ZjbQK1idEA2Mi8GGPIKJJ2x7P6I0HYiV4qy7fe/w1ZlCXE90B7PuPbtrQY9wO7Ll
44
- ipJ45X6I1PnyfOcckn8yafUCgYACtPAlgjJhWZn2v03cTbqA9nHQKyV/zXkyUIXd
45
- /XPLrjrP7ouAi5A8WuSChR/yx8ECRgrEM65Be3qBEtoGCB4AS1G0NcigM6qhKBFi
46
- VS0aMXr3+V8argcUIwJaWW/x+p2go48yXlJpLHPweeXe8mXEt4iM+QZte6p2yKQ4
47
- h9PGQQKBgQCqSydmXBnXGIVTp2sH/2GnpxLYnDBpcJE0tM8bJ42HEQQgRThIChsn
48
- PnGA91G9MVikYapgI0VYBHQOTsz8rTIUzsKwXG+TIaK+W84nxH5y6jUkjqwxZmAz
49
- r1URaMAun2PfAB4g2N/kEZTExgeOGqXjFhvvjdzl97ux2cTyZhaTXg==
50
- -----END RSA PRIVATE KEY-----
51
-
52
- # List Symmetric Key files in the order of current / latest first
53
- ciphers:
54
- -
55
- # Filename containing Symmetric Encryption Key encrypted using the
56
- # RSA public key derived from the private key above
57
- key_filename: /etc/rails/.rails.key
58
- iv_filename: /etc/rails/.rails.iv
59
-
60
- # Encryption cipher_name
61
- # Recommended values:
62
- # aes-256-cbc
63
- # 256 AES CBC Algorithm. Very strong
64
- # Ruby 1.8.7 MRI Approximately 100,000 encryptions or decryptions per second
65
- # JRuby 1.6.7 with Ruby 1.8.7 Approximately 22,000 encryptions or decryptions per second
66
- # aes-128-cbc
67
- # 128 AES CBC Algorithm. Less strong.
68
- # Ruby 1.8.7 MRI Approximately 100,000 encryptions or decryptions per second
69
- # JRuby 1.6.7 with Ruby 1.8.7 Approximately 22,000 encryptions or decryptions per second
70
- cipher_name: aes-256-cbc
@@ -1,18 +0,0 @@
1
- ENV['RAILS_ENV'] = 'test'
2
- DEVISE_ORM = (ENV['DEVISE_ORM'] || :active_record).to_sym
3
-
4
- $LOAD_PATH.unshift File.dirname(__FILE__)
5
- puts "\n==> Devise.orm = #{DEVISE_ORM.inspect}"
6
- require 'dummy/config/environment'
7
- require "orm/#{DEVISE_ORM}"
8
- require 'rails/test_help'
9
- require 'capybara/rails'
10
- require 'minitest/reporters'
11
-
12
- I18n.load_path << File.expand_path("../support/locale/en.yml", __FILE__) if DEVISE_ORM == :mongoid
13
-
14
- MiniTest::Reporters.use!
15
-
16
- class ActionDispatch::IntegrationTest
17
- include Capybara::DSL
18
- end