devise_two_factor_authentication 3.0.0

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 (94) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/tests.yml +42 -0
  3. data/.gitignore +23 -0
  4. data/.rubocop.yml +293 -0
  5. data/CHANGELOG.md +119 -0
  6. data/Gemfile +35 -0
  7. data/LICENSE +19 -0
  8. data/README.md +401 -0
  9. data/Rakefile +16 -0
  10. data/app/controllers/devise/two_factor_authentication_controller.rb +88 -0
  11. data/app/views/devise/two_factor_authentication/max_login_attempts_reached.html.erb +3 -0
  12. data/app/views/devise/two_factor_authentication/show.html.erb +19 -0
  13. data/config/locales/de.yml +8 -0
  14. data/config/locales/en.yml +8 -0
  15. data/config/locales/es.yml +8 -0
  16. data/config/locales/fr.yml +8 -0
  17. data/config/locales/ru.yml +8 -0
  18. data/devise_two_factor_authentication.gemspec +40 -0
  19. data/lib/devise_two_factor_authentication/controllers/helpers.rb +54 -0
  20. data/lib/devise_two_factor_authentication/hooks/two_factor_authenticatable.rb +17 -0
  21. data/lib/devise_two_factor_authentication/models/two_factor_authenticatable.rb +206 -0
  22. data/lib/devise_two_factor_authentication/orm/active_record.rb +14 -0
  23. data/lib/devise_two_factor_authentication/rails.rb +7 -0
  24. data/lib/devise_two_factor_authentication/routes.rb +19 -0
  25. data/lib/devise_two_factor_authentication/schema.rb +31 -0
  26. data/lib/devise_two_factor_authentication/version.rb +3 -0
  27. data/lib/devise_two_factor_authentication.rb +52 -0
  28. data/lib/generators/active_record/templates/migration.rb +15 -0
  29. data/lib/generators/active_record/two_factor_authentication_generator.rb +14 -0
  30. data/lib/generators/two_factor_authentication/two_factor_authentication_generator.rb +17 -0
  31. data/spec/controllers/two_factor_authentication_controller_spec.rb +41 -0
  32. data/spec/features/two_factor_authenticatable_spec.rb +236 -0
  33. data/spec/generators/active_record/two_factor_authentication_generator_spec.rb +36 -0
  34. data/spec/lib/two_factor_authentication/models/two_factor_authenticatable_spec.rb +326 -0
  35. data/spec/rails_app/.gitignore +3 -0
  36. data/spec/rails_app/README.md +3 -0
  37. data/spec/rails_app/Rakefile +9 -0
  38. data/spec/rails_app/app/assets/config/manifest.js +2 -0
  39. data/spec/rails_app/app/assets/javascripts/application.js +1 -0
  40. data/spec/rails_app/app/assets/stylesheets/application.css +4 -0
  41. data/spec/rails_app/app/controllers/application_controller.rb +3 -0
  42. data/spec/rails_app/app/controllers/home_controller.rb +10 -0
  43. data/spec/rails_app/app/helpers/application_helper.rb +8 -0
  44. data/spec/rails_app/app/mailers/.gitkeep +0 -0
  45. data/spec/rails_app/app/models/.gitkeep +0 -0
  46. data/spec/rails_app/app/models/admin.rb +6 -0
  47. data/spec/rails_app/app/models/encrypted_user.rb +15 -0
  48. data/spec/rails_app/app/models/guest_user.rb +17 -0
  49. data/spec/rails_app/app/models/user.rb +14 -0
  50. data/spec/rails_app/app/views/home/dashboard.html.erb +11 -0
  51. data/spec/rails_app/app/views/home/index.html.erb +3 -0
  52. data/spec/rails_app/app/views/layouts/application.html.erb +20 -0
  53. data/spec/rails_app/config/application.rb +64 -0
  54. data/spec/rails_app/config/boot.rb +10 -0
  55. data/spec/rails_app/config/database.yml +19 -0
  56. data/spec/rails_app/config/environment.rb +5 -0
  57. data/spec/rails_app/config/environments/development.rb +28 -0
  58. data/spec/rails_app/config/environments/production.rb +68 -0
  59. data/spec/rails_app/config/environments/test.rb +41 -0
  60. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  61. data/spec/rails_app/config/initializers/cookies_serializer.rb +3 -0
  62. data/spec/rails_app/config/initializers/devise.rb +258 -0
  63. data/spec/rails_app/config/initializers/inflections.rb +15 -0
  64. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  65. data/spec/rails_app/config/initializers/secret_token.rb +7 -0
  66. data/spec/rails_app/config/initializers/session_store.rb +8 -0
  67. data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
  68. data/spec/rails_app/config/locales/devise.en.yml +59 -0
  69. data/spec/rails_app/config/locales/en.yml +5 -0
  70. data/spec/rails_app/config/routes.rb +65 -0
  71. data/spec/rails_app/config.ru +4 -0
  72. data/spec/rails_app/db/migrate/20140403184646_devise_create_users.rb +42 -0
  73. data/spec/rails_app/db/migrate/20140407172619_two_factor_authentication_add_to_users.rb +15 -0
  74. data/spec/rails_app/db/migrate/20140407215513_add_nickanme_to_users.rb +7 -0
  75. data/spec/rails_app/db/migrate/20151224171231_add_encrypted_columns_to_user.rb +9 -0
  76. data/spec/rails_app/db/migrate/20151224180310_populate_otp_column.rb +19 -0
  77. data/spec/rails_app/db/migrate/20151228230340_remove_otp_secret_key_from_user.rb +5 -0
  78. data/spec/rails_app/db/migrate/20160209032439_devise_create_admins.rb +42 -0
  79. data/spec/rails_app/db/schema.rb +54 -0
  80. data/spec/rails_app/lib/assets/.gitkeep +0 -0
  81. data/spec/rails_app/lib/sms_provider.rb +17 -0
  82. data/spec/rails_app/public/404.html +26 -0
  83. data/spec/rails_app/public/422.html +26 -0
  84. data/spec/rails_app/public/500.html +25 -0
  85. data/spec/rails_app/public/favicon.ico +0 -0
  86. data/spec/rails_app/script/rails +9 -0
  87. data/spec/spec_helper.rb +27 -0
  88. data/spec/support/authenticated_model_helper.rb +59 -0
  89. data/spec/support/capybara.rb +3 -0
  90. data/spec/support/controller_helper.rb +16 -0
  91. data/spec/support/features_spec_helper.rb +42 -0
  92. data/spec/support/sms_provider.rb +5 -0
  93. data/spec/support/totp_helper.rb +11 -0
  94. metadata +294 -0
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ require 'generators/active_record/two_factor_authentication_generator'
4
+
5
+ describe ActiveRecord::Generators::TwoFactorAuthenticationGenerator, type: :generator do
6
+ destination File.expand_path('../../../../../tmp', __FILE__)
7
+
8
+ before do
9
+ prepare_destination
10
+ end
11
+
12
+ it 'runs all methods in the generator' do
13
+ gen = generator %w(users)
14
+ expect(gen).to receive(:copy_two_factor_authentication_migration)
15
+ gen.invoke_all
16
+ end
17
+
18
+ describe 'the generated files' do
19
+ before do
20
+ run_generator %w(users)
21
+ end
22
+
23
+ describe 'the migration' do
24
+ subject { migration_file('db/migrate/two_factor_authentication_add_to_users.rb') }
25
+
26
+ it { is_expected.to exist }
27
+ it { is_expected.to be_a_migration }
28
+ it { is_expected.to contain /def change/ }
29
+ it { is_expected.to contain /add_column :users, :second_factor_attempts_count, :integer, default: 0/ }
30
+ it { is_expected.to contain /add_column :users, :encrypted_otp_secret_key, :string/ }
31
+ it { is_expected.to contain /add_column :users, :encrypted_otp_secret_key_iv, :string/ }
32
+ it { is_expected.to contain /add_column :users, :encrypted_otp_secret_key_salt, :string/ }
33
+ it { is_expected.to contain /add_index :users, :encrypted_otp_secret_key, unique: true/ }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,326 @@
1
+ require 'spec_helper'
2
+ include AuthenticatedModelHelper
3
+
4
+ describe Devise::Models::TwoFactorAuthenticatable do
5
+ describe '#create_direct_otp' do
6
+ let(:instance) { build_guest_user }
7
+
8
+ it 'set direct_otp field' do
9
+ expect(instance.direct_otp).to be_nil
10
+ instance.create_direct_otp
11
+ expect(instance.direct_otp).not_to be_nil
12
+ end
13
+
14
+ it 'set direct_otp_send_at field to current time' do
15
+ Timecop.freeze() do
16
+ instance.create_direct_otp
17
+ expect(instance.direct_otp_sent_at).to eq(Time.now)
18
+ end
19
+ end
20
+
21
+ it 'honors .direct_otp_length' do
22
+ expect(instance.class).to receive(:direct_otp_length).and_return(10)
23
+ instance.create_direct_otp
24
+ expect(instance.direct_otp.length).to equal(10)
25
+
26
+ expect(instance.class).to receive(:direct_otp_length).and_return(6)
27
+ instance.create_direct_otp
28
+ expect(instance.direct_otp.length).to equal(6)
29
+ end
30
+
31
+ it "honors 'direct_otp_length' in options paramater" do
32
+ instance.create_direct_otp(length: 8)
33
+ expect(instance.direct_otp.length).to equal(8)
34
+ instance.create_direct_otp(length: 10)
35
+ expect(instance.direct_otp.length).to equal(10)
36
+ end
37
+ end
38
+
39
+ describe '#authenticate_direct_otp' do
40
+ let(:instance) { build_guest_user }
41
+ it 'fails if no direct_otp has been set' do
42
+ expect(instance.authenticate_direct_otp('12345')).to eq(false)
43
+ end
44
+
45
+ context 'after generating an OTP' do
46
+ before :each do
47
+ instance.create_direct_otp
48
+ end
49
+
50
+ it 'accepts correct OTP' do
51
+ Timecop.freeze(Time.now + instance.class.direct_otp_valid_for - 1.second)
52
+ expect(instance.authenticate_direct_otp(instance.direct_otp)).to eq(true)
53
+ end
54
+
55
+ it 'rejects invalid OTP' do
56
+ Timecop.freeze(Time.now + instance.class.direct_otp_valid_for - 1.second)
57
+ expect(instance.authenticate_direct_otp('12340')).to eq(false)
58
+ end
59
+
60
+ it 'rejects expired OTP' do
61
+ Timecop.freeze(Time.now + instance.class.direct_otp_valid_for + 1.second)
62
+ expect(instance.authenticate_direct_otp(instance.direct_otp)).to eq(false)
63
+ end
64
+
65
+ it 'prevents code re-use' do
66
+ expect(instance.authenticate_direct_otp(instance.direct_otp)).to eq(true)
67
+ expect(instance.authenticate_direct_otp(instance.direct_otp)).to eq(false)
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '#authenticate_totp' do
73
+ shared_examples 'authenticate_totp' do |instance|
74
+ before :each do
75
+ instance.otp_secret_key = '2z6hxkdwi3uvrnpn'
76
+ instance.totp_timestamp = nil
77
+ @totp_helper = TotpHelper.new(instance.otp_secret_key, instance.class.otp_length)
78
+ end
79
+
80
+ def do_invoke(code, user)
81
+ user.authenticate_totp(code)
82
+ end
83
+
84
+ it 'authenticates a recently created code' do
85
+ code = @totp_helper.totp_code
86
+ expect(do_invoke(code, instance)).to eq(true)
87
+ end
88
+
89
+ it 'authenticates a code entered with a space' do
90
+ code = @totp_helper.totp_code.insert(3, ' ')
91
+ expect(do_invoke(code, instance)).to eq(true)
92
+ end
93
+
94
+ it 'does not authenticate an old code' do
95
+ code = @totp_helper.totp_code(1.minutes.ago.to_i)
96
+ expect(do_invoke(code, instance)).to eq(false)
97
+ end
98
+
99
+ it 'prevents code reuse' do
100
+ code = @totp_helper.totp_code
101
+ expect(do_invoke(code, instance)).to eq(true)
102
+ expect(do_invoke(code, instance)).to eq(false)
103
+ end
104
+ end
105
+
106
+ it_behaves_like 'authenticate_totp', GuestUser.new
107
+ it_behaves_like 'authenticate_totp', EncryptedUser.new
108
+ end
109
+
110
+ describe '#send_two_factor_authentication_code' do
111
+ let(:instance) { build_guest_user }
112
+
113
+ it 'raises an error by default' do
114
+ expect { instance.send_two_factor_authentication_code(123) }.
115
+ to raise_error(NotImplementedError)
116
+ end
117
+
118
+ it 'is overrideable' do
119
+ def instance.send_two_factor_authentication_code(code)
120
+ 'Code sent'
121
+ end
122
+ expect(instance.send_two_factor_authentication_code(123)).to eq('Code sent')
123
+ end
124
+ end
125
+
126
+ describe '#provisioning_uri' do
127
+
128
+ shared_examples 'provisioning_uri' do |instance|
129
+ it 'fails until generate_totp_secret is called' do
130
+ expect { instance.provisioning_uri }.to raise_error(Exception)
131
+ end
132
+
133
+ describe 'with secret set' do
134
+ before do
135
+ instance.email = 'houdini@example.com'
136
+ instance.otp_secret_key = instance.generate_totp_secret
137
+ end
138
+
139
+ it "returns uri with user's email" do
140
+ expect(instance.provisioning_uri).
141
+ to match(%r{otpauth://totp/houdini%40example.com\?secret=\w{32}})
142
+ end
143
+
144
+ it 'returns uri with issuer option' do
145
+ expect(instance.provisioning_uri('houdini')).
146
+ to match(%r{otpauth://totp/houdini\?secret=\w{32}$})
147
+ end
148
+
149
+ it 'returns uri with issuer option' do
150
+ require 'cgi'
151
+ uri = URI.parse(instance.provisioning_uri('houdini', issuer: 'Magic'))
152
+ params = CGI.parse(uri.query)
153
+
154
+ expect(uri.scheme).to eq('otpauth')
155
+ expect(uri.host).to eq('totp')
156
+ expect(uri.path).to eq('/Magic:houdini')
157
+ expect(params['issuer'].shift).to eq('Magic')
158
+ expect(params['secret'].shift).to match(/\w{32}/)
159
+ end
160
+ end
161
+ end
162
+
163
+ it_behaves_like 'provisioning_uri', GuestUser.new
164
+ it_behaves_like 'provisioning_uri', EncryptedUser.new
165
+ end
166
+
167
+ describe '#generate_totp_secret' do
168
+ shared_examples 'generate_totp_secret' do |klass|
169
+ let(:instance) { klass.new }
170
+
171
+ it 'returns a 32 character string' do
172
+ secret = instance.generate_totp_secret
173
+
174
+ expect(secret).to match(/\w{32}/)
175
+ end
176
+ end
177
+
178
+ it_behaves_like 'generate_totp_secret', GuestUser
179
+ it_behaves_like 'generate_totp_secret', EncryptedUser
180
+ end
181
+
182
+ describe '#confirm_totp_secret' do
183
+ shared_examples 'confirm_totp_secret' do |klass|
184
+ let(:instance) { klass.new }
185
+ let(:secret) { instance.generate_totp_secret }
186
+ let(:totp_helper) { TotpHelper.new(secret, instance.class.otp_length) }
187
+
188
+ it 'populates otp_secret_key column when given correct code' do
189
+ instance.confirm_totp_secret(secret, totp_helper.totp_code)
190
+
191
+ expect(instance.otp_secret_key).to match(secret)
192
+ end
193
+
194
+ it 'does not populate otp_secret_key when when given incorrect code' do
195
+ instance.confirm_totp_secret(secret, '123')
196
+ expect(instance.otp_secret_key).to be_nil
197
+ end
198
+
199
+ it 'returns true when given correct code' do
200
+ expect(instance.confirm_totp_secret(secret, totp_helper.totp_code)).to be true
201
+ end
202
+
203
+ it 'returns false when given incorrect code' do
204
+ expect(instance.confirm_totp_secret(secret, '123')).to be false
205
+ end
206
+
207
+ end
208
+
209
+ it_behaves_like 'confirm_totp_secret', GuestUser
210
+ it_behaves_like 'confirm_totp_secret', EncryptedUser
211
+ end
212
+
213
+ describe '#max_login_attempts' do
214
+ let(:instance) { build_guest_user }
215
+
216
+ before do
217
+ @original_max_login_attempts = GuestUser.max_login_attempts
218
+ GuestUser.max_login_attempts = 3
219
+ end
220
+
221
+ after { GuestUser.max_login_attempts = @original_max_login_attempts }
222
+
223
+ it 'returns class setting' do
224
+ expect(instance.max_login_attempts).to eq(3)
225
+ end
226
+
227
+ it 'returns false as boolean' do
228
+ instance.second_factor_attempts_count = nil
229
+ expect(instance.max_login_attempts?).to be_falsey
230
+ instance.second_factor_attempts_count = 0
231
+ expect(instance.max_login_attempts?).to be_falsey
232
+ instance.second_factor_attempts_count = 1
233
+ expect(instance.max_login_attempts?).to be_falsey
234
+ instance.second_factor_attempts_count = 2
235
+ expect(instance.max_login_attempts?).to be_falsey
236
+ end
237
+
238
+ it 'returns true as boolean after too many attempts' do
239
+ instance.second_factor_attempts_count = 3
240
+ expect(instance.max_login_attempts?).to be_truthy
241
+ instance.second_factor_attempts_count = 4
242
+ expect(instance.max_login_attempts?).to be_truthy
243
+ end
244
+ end
245
+
246
+ describe '.has_one_time_password' do
247
+ context 'when encrypted: true option is passed' do
248
+ let(:instance) { EncryptedUser.new }
249
+
250
+ it 'encrypts otp_secret_key with iv, salt, and encoding' do
251
+ instance.otp_secret_key = '2z6hxkdwi3uvrnpn'
252
+
253
+ expect(instance.encrypted_otp_secret_key).to match(/.{44}/)
254
+
255
+ expect(instance.encrypted_otp_secret_key_iv).to match(/.{24}/)
256
+
257
+ expect(instance.encrypted_otp_secret_key_salt).to match(/.{25}/)
258
+ end
259
+
260
+ it 'does not encrypt a nil otp_secret_key' do
261
+ instance.otp_secret_key = nil
262
+
263
+ expect(instance.encrypted_otp_secret_key).to be_nil
264
+
265
+ expect(instance.encrypted_otp_secret_key_iv).to be_nil
266
+
267
+ expect(instance.encrypted_otp_secret_key_salt).to be_nil
268
+ end
269
+
270
+ it 'does not encrypt an empty otp_secret_key' do
271
+ instance.otp_secret_key = ''
272
+
273
+ expect(instance.encrypted_otp_secret_key).to eq ''
274
+
275
+ expect(instance.encrypted_otp_secret_key_iv).to be_nil
276
+
277
+ expect(instance.encrypted_otp_secret_key_salt).to be_nil
278
+ end
279
+
280
+ it 'raises an error when Devise.otp_secret_encryption_key is not set' do
281
+ allow(Devise).to receive(:otp_secret_encryption_key).and_return nil
282
+
283
+ # This error is raised by the encryptor gem
284
+ expect { instance.otp_secret_key = '2z6hxkdwi3uvrnpn' }.
285
+ to raise_error ArgumentError
286
+ end
287
+
288
+ it 'passes in the correct options to Encryptor.
289
+ We test here output of
290
+ Devise::Models::TwoFactorAuthenticatable::EncryptionInstanceMethods.encryption_options_for' do
291
+ instance.otp_secret_key = 'testing'
292
+ iv = instance.encrypted_otp_secret_key_iv
293
+ salt = instance.encrypted_otp_secret_key_salt
294
+
295
+ # it's important here to put the same crypto algorithm from that method
296
+ encrypted = Encryptor.encrypt(
297
+ value: 'testing',
298
+ key: Devise.otp_secret_encryption_key,
299
+ iv: iv.unpack('m').first,
300
+ salt: salt.unpack('m').first,
301
+ algorithm: 'aes-256-cbc'
302
+ )
303
+
304
+ expect(instance.encrypted_otp_secret_key).to eq [encrypted].pack('m')
305
+ end
306
+
307
+ it 'varies the iv per instance' do
308
+ instance.otp_secret_key = 'testing'
309
+ user2 = EncryptedUser.new
310
+ user2.otp_secret_key = 'testing'
311
+
312
+ expect(user2.encrypted_otp_secret_key_iv).
313
+ to_not eq instance.encrypted_otp_secret_key_iv
314
+ end
315
+
316
+ it 'varies the salt per instance' do
317
+ instance.otp_secret_key = 'testing'
318
+ user2 = EncryptedUser.new
319
+ user2.otp_secret_key = 'testing'
320
+
321
+ expect(user2.encrypted_otp_secret_key_salt).
322
+ to_not eq instance.encrypted_otp_secret_key_salt
323
+ end
324
+ end
325
+ end
326
+ end
@@ -0,0 +1,3 @@
1
+ log/
2
+ tmp/
3
+ *.sqlite3
@@ -0,0 +1,3 @@
1
+ # Dummy
2
+
3
+ You have found the dummy rails app used for integration testing of the `two_factor_authentication` gem.
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ # frozen_string_literal: true
3
+
4
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
5
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
6
+
7
+ require File.expand_path('config/application', __dir__)
8
+
9
+ Dummy::Application.load_tasks
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts .js
2
+ //= link_directory ../stylesheets .css
@@ -0,0 +1 @@
1
+ //= require_tree .
@@ -0,0 +1,4 @@
1
+ /*
2
+ *= require_self
3
+ *= require_tree .
4
+ */
@@ -0,0 +1,3 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery
3
+ end
@@ -0,0 +1,10 @@
1
+ class HomeController < ApplicationController
2
+ before_action :authenticate_user!, only: :dashboard
3
+
4
+ def index
5
+ end
6
+
7
+ def dashboard
8
+ end
9
+
10
+ end
@@ -0,0 +1,8 @@
1
+ module ApplicationHelper
2
+
3
+ def render_flash
4
+ flash.map do |name, message|
5
+ content_tag(:p, message, class: "flash #{name}")
6
+ end.join.html_safe
7
+ end
8
+ end
File without changes
File without changes
@@ -0,0 +1,6 @@
1
+ class Admin < ActiveRecord::Base
2
+ # Include default devise modules. Others available are:
3
+ # :confirmable, :lockable, :timeoutable and :omniauthable
4
+ devise :database_authenticatable, :registerable,
5
+ :recoverable, :rememberable, :trackable, :validatable
6
+ end
@@ -0,0 +1,15 @@
1
+ class EncryptedUser
2
+ extend ActiveModel::Callbacks
3
+ include ActiveModel::Validations
4
+ include Devise::Models::TwoFactorAuthenticatable
5
+
6
+ define_model_callbacks :create
7
+ attr_accessor :encrypted_otp_secret_key,
8
+ :encrypted_otp_secret_key_iv,
9
+ :encrypted_otp_secret_key_salt,
10
+ :email,
11
+ :second_factor_attempts_count,
12
+ :totp_timestamp
13
+
14
+ has_one_time_password(encrypted: true)
15
+ end
@@ -0,0 +1,17 @@
1
+ class GuestUser
2
+ extend ActiveModel::Callbacks
3
+ include ActiveModel::Validations
4
+ include Devise::Models::TwoFactorAuthenticatable
5
+
6
+ define_model_callbacks :create
7
+ attr_accessor :direct_otp, :direct_otp_sent_at, :otp_secret_key, :email,
8
+ :second_factor_attempts_count, :totp_timestamp
9
+
10
+ def update(attrs)
11
+ attrs.each do |key, value|
12
+ send(key.to_s + '=', value)
13
+ end
14
+ end
15
+
16
+ has_one_time_password
17
+ end
@@ -0,0 +1,14 @@
1
+ class User < ActiveRecord::Base
2
+ devise :two_factor_authenticatable, :database_authenticatable, :registerable,
3
+ :recoverable, :rememberable, :trackable, :validatable
4
+
5
+ has_one_time_password
6
+
7
+ def send_two_factor_authentication_code(code)
8
+ SmsProvider.send_message(to: phone_number, body: code)
9
+ end
10
+
11
+ def phone_number
12
+ '14159341234'
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ <h1>Your Personal Dashboard</h1>
2
+
3
+ <p>Hi <%= current_user.nickname %></p>
4
+
5
+ <p>Your registered email address is <%= current_user.email %></p>
6
+
7
+ <p> Param A is <%= params[:A] %></p>
8
+
9
+ <p> Param B is <%= params[:B] %></p>
10
+
11
+ <p>You can only see this page after successfully completing two factor authentication</p>
@@ -0,0 +1,3 @@
1
+ <h1>Welcome Home</h1>
2
+
3
+ <p>Find me in app/views/home/index.html.erb</p>
@@ -0,0 +1,20 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Dummy</title>
5
+ <%= stylesheet_link_tag "application", :media => "all" %>
6
+ <%= javascript_include_tag "application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+ <nav>
11
+ <% if user_signed_in? %>
12
+ You are signed in as <%= current_user.nickname %>
13
+ <% else %>
14
+ You are signed out
15
+ <% end %>
16
+ </nav>
17
+ <%= render_flash %>
18
+ <%= yield %>
19
+ </body>
20
+ </html>
@@ -0,0 +1,64 @@
1
+ require File.expand_path('../boot', __FILE__)
2
+
3
+ require "active_record/railtie"
4
+ require "action_controller/railtie"
5
+ require "action_mailer/railtie"
6
+ require "sprockets/railtie"
7
+
8
+ Bundler.require(*Rails.groups)
9
+ require "devise_two_factor_authentication"
10
+
11
+ module Dummy
12
+ class Application < Rails::Application
13
+ # Settings in config/environments/* take precedence over those specified here.
14
+ # Application configuration should go into files in config/initializers
15
+ # -- all .rb files in that directory are automatically loaded.
16
+
17
+ # Custom directories with classes and modules you want to be autoloadable.
18
+ # config.autoload_paths += %W(#{config.root}/extras)
19
+ config.autoload_paths += %W(#{config.root}/lib)
20
+
21
+ # Only load the plugins named here, in the order given (default is alphabetical).
22
+ # :all can be used as a placeholder for all plugins not explicitly named.
23
+ # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
24
+
25
+ # Activate observers that should always be running.
26
+ # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
27
+
28
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
29
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
30
+ # config.time_zone = 'Central Time (US & Canada)'
31
+
32
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
33
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
34
+ # config.i18n.default_locale = :de
35
+
36
+ # Configure the default encoding used in templates for Ruby 1.9.
37
+ config.encoding = "utf-8"
38
+
39
+ # Configure sensitive parameters which will be filtered from the log file.
40
+ config.filter_parameters += [:password]
41
+
42
+ # Enable escaping HTML in JSON.
43
+ config.active_support.escape_html_entities_in_json = true
44
+
45
+ # Use SQL instead of Active Record's schema dumper when creating the database.
46
+ # This is necessary if your schema can't be completely dumped by the schema dumper,
47
+ # like if you have constraints or database-specific column types
48
+ # config.active_record.schema_format = :sql
49
+
50
+ config.active_record.legacy_connection_handling = false
51
+
52
+ # Enable the asset pipeline
53
+ config.assets.enabled = true
54
+
55
+ # Version of your assets, change this if you want to expire all your assets
56
+ config.assets.version = '1.0'
57
+
58
+ config.action_mailer.default_url_options = { host: 'localhost:3000' }
59
+
60
+ config.i18n.enforce_available_locales = false
61
+
62
+ config.secret_key_base = 'secretvalue'
63
+ end
64
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ gemfile = File.expand_path('../../../../Gemfile', __FILE__)
3
+
4
+ if File.exist?(gemfile)
5
+ ENV['BUNDLE_GEMFILE'] = gemfile
6
+ require 'bundler'
7
+ Bundler.setup
8
+ end
9
+
10
+ $:.unshift File.expand_path('../../../../lib', __FILE__)
@@ -0,0 +1,19 @@
1
+ # SQLite version 3.x
2
+ # gem install sqlite3
3
+ #
4
+ # Ensure the SQLite 3 gem is defined in your Gemfile
5
+ # gem 'sqlite3'
6
+ development:
7
+ adapter: sqlite3
8
+ database: db/development.sqlite3
9
+ pool: 5
10
+ timeout: 5000
11
+
12
+ # Warning: The database defined as "test" will be erased and
13
+ # re-generated from your development database when you run "rake".
14
+ # Do not set this db to the same as development or production.
15
+ test:
16
+ adapter: sqlite3
17
+ database: db/test.sqlite3
18
+ pool: 5
19
+ timeout: 5000
@@ -0,0 +1,5 @@
1
+ # Load the rails application
2
+ require File.expand_path('../application', __FILE__)
3
+
4
+ # Initialize the rails application
5
+ Dummy::Application.initialize!
@@ -0,0 +1,28 @@
1
+ Dummy::Application.configure do
2
+ # Settings specified here will take precedence over those in config/application.rb
3
+
4
+ # In the development environment your application's code is reloaded on
5
+ # every request. This slows down response time but is perfect for development
6
+ # since you don't have to restart the web server when you make code changes.
7
+ config.cache_classes = false
8
+ config.eager_load = false
9
+
10
+ # Show full error reports and disable caching
11
+ config.consider_all_requests_local = true
12
+ config.action_controller.perform_caching = false
13
+
14
+ # Don't care if the mailer can't send
15
+ config.action_mailer.raise_delivery_errors = false
16
+
17
+ # Print deprecation notices to the Rails logger
18
+ config.active_support.deprecation = :log
19
+
20
+ # Only use best-standards-support built into browsers
21
+ config.action_dispatch.best_standards_support = :builtin
22
+
23
+ # Do not compress assets
24
+ config.assets.compress = false
25
+
26
+ # Expands the lines which load the assets
27
+ config.assets.debug = true
28
+ end