devise-otp 0.2.0 → 0.3.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 (99) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +36 -0
  3. data/.gitignore +0 -0
  4. data/Gemfile +1 -22
  5. data/LICENSE.txt +0 -0
  6. data/README.md +43 -66
  7. data/Rakefile +0 -0
  8. data/app/assets/javascripts/devise-otp.js +1 -0
  9. data/app/assets/javascripts/qrcode.js +609 -0
  10. data/app/controllers/devise_otp/devise/credentials_controller.rb +102 -0
  11. data/app/controllers/devise_otp/devise/tokens_controller.rb +112 -0
  12. data/app/views/devise/credentials/refresh.html.erb +19 -0
  13. data/app/views/devise/credentials/show.html.erb +31 -0
  14. data/app/views/devise/tokens/_token_secret.html.erb +23 -0
  15. data/app/views/devise/tokens/_trusted_devices.html.erb +12 -0
  16. data/app/views/devise/tokens/recovery.html.erb +21 -0
  17. data/app/views/devise/tokens/recovery_codes.text.erb +3 -0
  18. data/app/views/devise/tokens/show.html.erb +21 -0
  19. data/config/locales/en.yml +10 -10
  20. data/devise-otp.gemspec +14 -9
  21. data/docs/QR_CODES.md +48 -0
  22. data/lib/devise-otp/version.rb +1 -1
  23. data/lib/devise-otp.rb +22 -14
  24. data/lib/devise_otp_authenticatable/controllers/helpers.rb +29 -16
  25. data/lib/devise_otp_authenticatable/controllers/url_helpers.rb +6 -9
  26. data/lib/devise_otp_authenticatable/engine.rb +22 -13
  27. data/lib/devise_otp_authenticatable/hooks/sessions.rb +8 -7
  28. data/lib/devise_otp_authenticatable/hooks.rb +1 -1
  29. data/lib/devise_otp_authenticatable/models/otp_authenticatable.rb +28 -28
  30. data/lib/devise_otp_authenticatable/routes.rb +9 -10
  31. data/lib/generators/active_record/devise_otp_generator.rb +1 -1
  32. data/lib/generators/active_record/templates/migration.rb +1 -2
  33. data/lib/generators/devise_otp/devise_otp_generator.rb +0 -0
  34. data/lib/generators/devise_otp/install_generator.rb +30 -5
  35. data/lib/generators/devise_otp/views_generator.rb +2 -3
  36. data/test/dummy/README.rdoc +0 -0
  37. data/test/dummy/Rakefile +0 -0
  38. data/test/dummy/app/assets/config/manifest.js +2 -0
  39. data/test/dummy/app/assets/javascripts/application.js +1 -0
  40. data/test/dummy/app/assets/stylesheets/application.css +0 -0
  41. data/test/dummy/app/controllers/application_controller.rb +1 -1
  42. data/test/dummy/app/controllers/posts_controller.rb +2 -0
  43. data/test/dummy/app/helpers/application_helper.rb +0 -0
  44. data/test/dummy/app/helpers/posts_helper.rb +0 -0
  45. data/test/dummy/app/mailers/.gitkeep +0 -0
  46. data/test/dummy/app/models/post.rb +0 -0
  47. data/test/dummy/app/models/user.rb +1 -1
  48. data/test/dummy/app/views/layouts/application.html.erb +0 -0
  49. data/test/dummy/app/views/posts/_form.html.erb +0 -0
  50. data/test/dummy/app/views/posts/edit.html.erb +0 -0
  51. data/test/dummy/app/views/posts/index.html.erb +0 -0
  52. data/test/dummy/app/views/posts/new.html.erb +0 -0
  53. data/test/dummy/app/views/posts/show.html.erb +0 -0
  54. data/test/dummy/config/application.rb +2 -1
  55. data/test/dummy/config/boot.rb +0 -0
  56. data/test/dummy/config/database.yml +1 -1
  57. data/test/dummy/config/environment.rb +0 -0
  58. data/test/dummy/config/environments/development.rb +0 -7
  59. data/test/dummy/config/environments/production.rb +0 -4
  60. data/test/dummy/config/environments/test.rb +0 -0
  61. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -0
  62. data/test/dummy/config/initializers/devise.rb +0 -0
  63. data/test/dummy/config/initializers/inflections.rb +0 -0
  64. data/test/dummy/config/initializers/mime_types.rb +0 -0
  65. data/test/dummy/config/initializers/secret_token.rb +0 -0
  66. data/test/dummy/config/initializers/session_store.rb +0 -0
  67. data/test/dummy/config/initializers/wrap_parameters.rb +0 -0
  68. data/test/dummy/config/locales/en.yml +0 -0
  69. data/test/dummy/config/routes.rb +0 -0
  70. data/test/dummy/config.ru +0 -0
  71. data/test/dummy/db/migrate/20130125101430_create_users.rb +1 -1
  72. data/test/dummy/db/migrate/20130131092406_add_devise_to_users.rb +1 -1
  73. data/test/dummy/db/migrate/20130131142320_create_posts.rb +1 -1
  74. data/test/dummy/db/migrate/20130131160351_devise_otp_add_to_users.rb +2 -2
  75. data/test/dummy/db/test.sqlite3-journal +0 -0
  76. data/test/dummy/lib/assets/.gitkeep +0 -0
  77. data/test/dummy/public/404.html +0 -0
  78. data/test/dummy/public/422.html +0 -0
  79. data/test/dummy/public/500.html +0 -0
  80. data/test/dummy/public/favicon.ico +0 -0
  81. data/test/integration/persistence_test.rb +81 -0
  82. data/test/integration/refresh_test.rb +2 -18
  83. data/test/integration/sign_in_test.rb +14 -4
  84. data/test/integration/token_test.rb +31 -0
  85. data/test/integration_tests_helper.rb +19 -2
  86. data/test/model_tests_helper.rb +0 -0
  87. data/test/models/otp_authenticatable_test.rb +14 -9
  88. data/test/orm/active_record.rb +3 -1
  89. data/test/test_helper.rb +71 -2
  90. metadata +135 -24
  91. data/.travis.yml +0 -11
  92. data/app/controllers/devise_otp/credentials_controller.rb +0 -106
  93. data/app/controllers/devise_otp/tokens_controller.rb +0 -105
  94. data/app/views/devise_otp/credentials/refresh.html.erb +0 -20
  95. data/app/views/devise_otp/credentials/show.html.erb +0 -23
  96. data/app/views/devise_otp/tokens/_token_secret.html.erb +0 -17
  97. data/app/views/devise_otp/tokens/recovery.html.erb +0 -21
  98. data/app/views/devise_otp/tokens/show.html.erb +0 -31
  99. data/lib/devise_otp_authenticatable/mapping.rb +0 -19
@@ -1,9 +1,7 @@
1
1
  module DeviseOtpAuthenticatable
2
-
3
2
  module Controllers
4
3
  module Helpers
5
4
 
6
-
7
5
  def authenticate_scope!
8
6
  send(:"authenticate_#{resource_name}!", :force => true)
9
7
  self.resource = send("current_#{resource_name}")
@@ -18,17 +16,19 @@ module DeviseOtpAuthenticatable
18
16
  options[:default] = Array(options[:default]).unshift(kind.to_sym)
19
17
  options[:resource_name] = resource_name
20
18
  options = devise_i18n_options(options) if respond_to?(:devise_i18n_options, true)
21
- message = I18n.t("#{options[:resource_name]}.#{kind}", options)
19
+ message = I18n.t("#{options[:resource_name]}.#{kind}", **options)
22
20
  flash[key] = message if message.present?
23
21
  end
24
22
 
25
23
  def otp_t()
26
-
27
24
  end
28
25
 
26
+ def trusted_devices_enabled?
27
+ resource.class.otp_trust_persistence && (resource.class.otp_trust_persistence > 0)
28
+ end
29
29
 
30
30
  def recovery_enabled?
31
- resource_class.recovery_tokens && (resource_class.recovery_tokens > 0)
31
+ resource_class.otp_recovery_tokens && (resource_class.otp_recovery_tokens > 0)
32
32
  end
33
33
 
34
34
  #
@@ -40,9 +40,7 @@ module DeviseOtpAuthenticatable
40
40
  end
41
41
  end
42
42
 
43
-
44
43
  # fixme do cookies and persistence need to be scoped? probably
45
-
46
44
  #
47
45
  # check if the resource needs a credentials refresh. IE, they need to be asked a password again to access
48
46
  # this resource.
@@ -62,15 +60,14 @@ module DeviseOtpAuthenticatable
62
60
  session[otp_scoped_refresh_property] = (Time.now + resource.class.otp_credentials_refresh)
63
61
  end
64
62
 
65
-
66
63
  #
67
64
  # is the current browser trusted?
68
65
  #
69
- def is_otp_trusted_device_for?(resource)
66
+ def is_otp_trusted_browser_for?(resource)
67
+ return false unless resource.class.otp_trust_persistence
70
68
  if cookies[otp_scoped_persistence_cookie].present?
71
69
  cookies.signed[otp_scoped_persistence_cookie] ==
72
- [resource.class.serialize_into_cookie(resource), resource.otp_persistence_seed].tap do
73
- end
70
+ [resource.to_key, resource.authenticatable_salt, resource.otp_persistence_seed]
74
71
  else
75
72
  false
76
73
  end
@@ -80,10 +77,11 @@ module DeviseOtpAuthenticatable
80
77
  # make the current browser trusted
81
78
  #
82
79
  def otp_set_trusted_device_for(resource)
80
+ return unless resource.class.otp_trust_persistence
83
81
  cookies.signed[otp_scoped_persistence_cookie] = {
84
82
  :httponly => true,
85
- :expires => 30.days.from_now,
86
- :value => [resource.class.serialize_into_cookie(resource), resource.otp_persistence_seed]
83
+ :expires => Time.now + resource.class.otp_trust_persistence,
84
+ :value => [resource.to_key, resource.authenticatable_salt, resource.otp_persistence_seed]
87
85
  }
88
86
  end
89
87
 
@@ -115,7 +113,6 @@ module DeviseOtpAuthenticatable
115
113
  cookies.delete(otp_scoped_persistence_cookie)
116
114
  end
117
115
 
118
-
119
116
  #
120
117
  # clears the persistence list for this kind of resource
121
118
  #
@@ -128,11 +125,27 @@ module DeviseOtpAuthenticatable
128
125
  # returns the URL for the QR Code to initialize the Authenticator device
129
126
  #
130
127
  def otp_authenticator_token_image(resource)
131
- otp_authenticator_token_image_google(resource.otp_provisioning_uri)
128
+ otp_authenticator_token_image_js(resource.otp_provisioning_uri)
132
129
  end
133
130
 
134
131
  private
135
132
 
133
+ def otp_authenticator_token_image_js(otp_url)
134
+ content_tag(:div, :class => 'qrcode-container') do
135
+ tag(:div, :id => 'qrcode', :class => 'qrcode') +
136
+ javascript_tag(%Q[
137
+ new QRCode("qrcode", {
138
+ text: "#{otp_url}",
139
+ width: 256,
140
+ height: 256,
141
+ colorDark : "#000000",
142
+ colorLight : "#ffffff",
143
+ correctLevel : QRCode.CorrectLevel.H
144
+ });
145
+ ]) + tag("/div")
146
+ end
147
+ end
148
+
136
149
  def otp_authenticator_token_image_google(otp_url)
137
150
  otp_url = Rack::Utils.escape(otp_url)
138
151
  url = "https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=#{otp_url}"
@@ -141,4 +154,4 @@ module DeviseOtpAuthenticatable
141
154
 
142
155
  end
143
156
  end
144
- end
157
+ end
@@ -1,35 +1,32 @@
1
1
  module DeviseOtpAuthenticatable
2
2
  module Controllers
3
-
4
3
  module UrlHelpers
5
4
 
6
-
7
5
  def recovery_otp_token_for(resource_or_scope, opts = {})
8
- scope = Devise::Mapping.find_scope!(resource_or_scope)
6
+ scope = ::Devise::Mapping.find_scope!(resource_or_scope)
9
7
  send("recovery_#{scope}_otp_token_path", opts)
10
8
  end
11
9
 
12
-
13
10
  def refresh_otp_credential_path_for(resource_or_scope, opts = {})
14
- scope = Devise::Mapping.find_scope!(resource_or_scope)
11
+ scope = ::Devise::Mapping.find_scope!(resource_or_scope)
15
12
  send("refresh_#{scope}_otp_credential_path", opts)
16
13
  end
17
14
 
18
15
  def persistence_otp_token_path_for(resource_or_scope, opts = {})
19
- scope = Devise::Mapping.find_scope!(resource_or_scope)
16
+ scope = ::Devise::Mapping.find_scope!(resource_or_scope)
20
17
  send("persistence_#{scope}_otp_token_path", opts)
21
18
  end
22
19
 
23
20
  def otp_token_path_for(resource_or_scope, opts = {})
24
- scope = Devise::Mapping.find_scope!(resource_or_scope)
21
+ scope = ::Devise::Mapping.find_scope!(resource_or_scope)
25
22
  send("#{scope}_otp_token_path", opts)
26
23
  end
27
24
 
28
25
  def otp_credential_path_for(resource_or_scope, opts = {})
29
- scope = Devise::Mapping.find_scope!(resource_or_scope)
26
+ scope = ::Devise::Mapping.find_scope!(resource_or_scope)
30
27
  send("#{scope}_otp_credential_path", opts)
31
28
  end
32
29
 
33
30
  end
34
31
  end
35
- end
32
+ end
@@ -1,23 +1,32 @@
1
1
  module DeviseOtpAuthenticatable
2
2
  class Engine < ::Rails::Engine
3
3
 
4
- ActiveSupport.on_load(:action_controller) do
5
- include DeviseOtpAuthenticatable::Controllers::UrlHelpers
6
- include DeviseOtpAuthenticatable::Controllers::Helpers
7
- end
8
- ActiveSupport.on_load(:action_view) do
9
- include DeviseOtpAuthenticatable::Controllers::UrlHelpers
10
- include DeviseOtpAuthenticatable::Controllers::Helpers
11
- end
12
-
13
4
  # We use to_prepare instead of after_initialize here because Devise is a Rails engine;
14
5
  config.to_prepare do
15
6
  DeviseOtpAuthenticatable::Hooks.apply
16
7
  end
17
8
 
18
- # extend mapping with after_initialize because is not reloaded
19
- config.after_initialize do
20
- Devise::Mapping.send :include, DeviseOtpAuthenticatable::Mapping
9
+ initializer "devise-otp", group: :all do |app|
10
+ ActiveSupport.on_load(:devise_controller) do
11
+ include DeviseOtpAuthenticatable::Controllers::UrlHelpers
12
+ include DeviseOtpAuthenticatable::Controllers::Helpers
13
+ end
14
+
15
+ ActiveSupport.on_load(:action_view) do
16
+ include DeviseOtpAuthenticatable::Controllers::UrlHelpers
17
+ include DeviseOtpAuthenticatable::Controllers::Helpers
18
+ end
19
+
20
+ # See: https://guides.rubyonrails.org/engines.html#separate-assets-and-precompiling
21
+ # check if Rails api mode
22
+ if app.config.respond_to?(:assets)
23
+ if defined?(Sprockets) && Sprockets::VERSION >= "4"
24
+ app.config.assets.precompile << "devise-otp.js"
25
+ else
26
+ # use a proc instead of a string
27
+ app.config.assets.precompile << proc { |path| path == "devise-otp.js" }
28
+ end
29
+ end
21
30
  end
22
31
  end
23
- end
32
+ end
@@ -4,36 +4,36 @@ module DeviseOtpAuthenticatable::Hooks
4
4
  include DeviseOtpAuthenticatable::Controllers::UrlHelpers
5
5
 
6
6
  included do
7
- alias_method_chain :create, :otp
7
+ alias_method :create, :create_with_otp
8
8
  end
9
9
 
10
10
  #
11
11
  # replaces Devise::SessionsController#create
12
12
  #
13
13
  def create_with_otp
14
-
15
14
  resource = warden.authenticate!(auth_options)
16
15
 
16
+ devise_stored_location = stored_location_for(resource) # Grab the current stored location before it gets lost by warden.logout
17
+ store_location_for(resource, devise_stored_location) # Restore it since #stored_location_for removes it
18
+
17
19
  otp_refresh_credentials_for(resource)
18
20
 
19
21
  if otp_challenge_required_on?(resource)
20
22
  challenge = resource.generate_otp_challenge!
21
23
  warden.logout
24
+ store_location_for(resource, devise_stored_location) # restore the stored location
22
25
  respond_with resource, :location => otp_credential_path_for(resource, {:challenge => challenge})
23
-
24
26
  elsif otp_mandatory_on?(resource) # if mandatory, log in user but send him to the must activate otp
25
27
  set_flash_message(:notice, :signed_in_but_otp) if is_navigational_format?
26
28
  sign_in(resource_name, resource)
27
29
  respond_with resource, :location => otp_token_path_for(resource)
28
30
  else
29
-
30
31
  set_flash_message(:notice, :signed_in) if is_navigational_format?
31
32
  sign_in(resource_name, resource)
32
33
  respond_with resource, :location => after_sign_in_path_for(resource)
33
34
  end
34
35
  end
35
36
 
36
-
37
37
  private
38
38
 
39
39
  #
@@ -41,7 +41,8 @@ module DeviseOtpAuthenticatable::Hooks
41
41
  #
42
42
  def otp_challenge_required_on?(resource)
43
43
  return false unless resource.respond_to?(:otp_enabled) && resource.respond_to?(:otp_auth_secret)
44
- resource.otp_enabled && !is_otp_trusted_device_for?(resource)
44
+
45
+ resource.otp_enabled && !is_otp_trusted_browser_for?(resource)
45
46
  end
46
47
 
47
48
  #
@@ -54,4 +55,4 @@ module DeviseOtpAuthenticatable::Hooks
54
55
  resource.otp_mandatory && !resource.otp_enabled
55
56
  end
56
57
  end
57
- end
58
+ end
@@ -5,7 +5,7 @@ module DeviseOtpAuthenticatable
5
5
 
6
6
  class << self
7
7
  def apply
8
- Devise::SessionsController.send(:include, Hooks::Sessions)
8
+ ::Devise::SessionsController.send(:include, Hooks::Sessions)
9
9
  end
10
10
  end
11
11
 
@@ -11,8 +11,9 @@ module Devise::Models
11
11
  end
12
12
 
13
13
  module ClassMethods
14
- ::Devise::Models.config(self, :otp_authentication_timeout, :otp_drift_window,
15
- :otp_mandatory, :otp_credentials_refresh, :otp_uri_application, :recovery_tokens)
14
+ ::Devise::Models.config(self, :otp_authentication_timeout, :otp_drift_window, :otp_trust_persistence,
15
+ :otp_mandatory, :otp_credentials_refresh, :otp_issuer, :otp_recovery_tokens,
16
+ :otp_controller_path)
16
17
 
17
18
  def find_valid_otp_challenge(challenge)
18
19
  with_valid_otp_challenge(Time.now).where(:otp_session_challenge => challenge).first
@@ -20,7 +21,7 @@ module Devise::Models
20
21
  end
21
22
 
22
23
  def time_based_otp
23
- @time_based_otp ||= ROTP::TOTP.new(otp_auth_secret)
24
+ @time_based_otp ||= ROTP::TOTP.new(otp_auth_secret, issuer: "#{self.class.otp_issuer || Rails.application.class.module_parent_name}")
24
25
  end
25
26
 
26
27
  def recovery_otp
@@ -32,7 +33,7 @@ module Devise::Models
32
33
  end
33
34
 
34
35
  def otp_provisioning_identifier
35
- "#{email}/#{self.class.otp_uri_application || Rails.application.class.parent_name}"
36
+ email
36
37
  end
37
38
 
38
39
 
@@ -41,9 +42,9 @@ module Devise::Models
41
42
  @recovery_otp = nil
42
43
  generate_otp_auth_secret
43
44
  reset_otp_persistence
44
- update_columns(:otp_enabled => false, :otp_time_drift => 0,
45
- :otp_session_challenge => nil, :otp_challenge_expires => nil,
46
- :otp_recovery_counter => 0)
45
+ update!(:otp_enabled => false,
46
+ :otp_session_challenge => nil, :otp_challenge_expires => nil,
47
+ :otp_recovery_counter => 0)
47
48
  end
48
49
 
49
50
  def reset_otp_credentials!
@@ -51,7 +52,6 @@ module Devise::Models
51
52
  save!
52
53
  end
53
54
 
54
-
55
55
  def reset_otp_persistence
56
56
  generate_otp_persistence_seed
57
57
  end
@@ -61,9 +61,21 @@ module Devise::Models
61
61
  save!
62
62
  end
63
63
 
64
+ def enable_otp!
65
+ if otp_persistence_seed.nil?
66
+ reset_otp_credentials!
67
+ end
68
+
69
+ update!(:otp_enabled => true, :otp_enabled_on => Time.now)
70
+ end
71
+
72
+ def disable_otp!
73
+ update!(:otp_enabled => false, :otp_enabled_on => nil)
74
+ end
75
+
64
76
  def generate_otp_challenge!(expires = nil)
65
- update_columns(:otp_session_challenge => SecureRandom.hex,
66
- :otp_challenge_expires => DateTime.now + (expires || self.class.otp_authentication_timeout))
77
+ update!(:otp_session_challenge => SecureRandom.hex,
78
+ :otp_challenge_expires => DateTime.now + (expires || self.class.otp_authentication_timeout))
67
79
  otp_session_challenge
68
80
  end
69
81
 
@@ -82,16 +94,12 @@ module Devise::Models
82
94
  alias_method :valid_otp_token?, :validate_otp_token
83
95
 
84
96
  def validate_otp_time_token(token)
85
- if drift = validate_otp_token_with_drift(token)
86
- update_column(:otp_time_drift, drift)
87
- true
88
- else
89
- false
90
- end
97
+ return false if token.blank?
98
+ validate_otp_token_with_drift(token)
91
99
  end
92
100
  alias_method :valid_otp_time_token?, :validate_otp_time_token
93
101
 
94
- def next_otp_recovery_tokens(number = 5)
102
+ def next_otp_recovery_tokens(number = self.class.otp_recovery_tokens)
95
103
  (otp_recovery_counter..otp_recovery_counter + number).inject({}) do |h, index|
96
104
  h[index] = recovery_otp.at(index)
97
105
  h
@@ -108,21 +116,13 @@ module Devise::Models
108
116
 
109
117
 
110
118
 
111
-
112
-
113
119
  private
114
120
 
115
- #
116
- # refactor me, I suck
117
- #
118
121
  def validate_otp_token_with_drift(token)
119
- # valid_vals << ROTP::TOTP.new(otp_auth_secret).at(Time.now)
120
122
 
121
123
  # should be centered around saved drift
122
- (-self.class.otp_drift_window..self.class.otp_drift_window).each do |drift|
123
- return drift if(time_based_otp.verify(token, Time.now.ago(30 * drift)))
124
- end
125
- false
124
+ (-self.class.otp_drift_window..self.class.otp_drift_window).any? {|drift|
125
+ (time_based_otp.verify(token, at: Time.now.ago(30 * drift))) }
126
126
  end
127
127
 
128
128
  def generate_otp_persistence_seed
@@ -135,4 +135,4 @@ module Devise::Models
135
135
  end
136
136
 
137
137
  end
138
- end
138
+ end
@@ -1,30 +1,29 @@
1
1
  module ActionDispatch::Routing
2
2
  class Mapper
3
3
 
4
-
5
4
  protected
6
- #########
7
5
 
8
6
  def devise_otp(mapping, controllers)
9
-
10
7
  namespace :otp, :module => :devise_otp do
11
8
  resource :token, :only => [:show, :update, :destroy],
12
- :path => mapping.path_names[:token], :controller => controllers[:otp_tokens] do
9
+ :path => mapping.path_names[:token], :controller => controllers[:tokens] do
13
10
 
14
- get :persistence, :action => 'get_persistence'
15
- post :persistence, :action => 'clear_persistence'
16
- delete :persistence, :action => 'delete_persistence'
11
+ if Devise.otp_trust_persistence
12
+ get :persistence, :action => 'get_persistence'
13
+ post :persistence, :action => 'clear_persistence'
14
+ delete :persistence, :action => 'delete_persistence'
15
+ end
17
16
 
18
17
  get :recovery
19
18
  end
20
19
 
21
20
  resource :credential, :only => [:show, :update],
22
- :path => mapping.path_names[:credentials], :controller => controllers[:otp_credentials] do
21
+ :path => mapping.path_names[:credentials], :controller => controllers[:credentials] do
23
22
 
24
- get :refresh, :action => 'get_refresh'
23
+ get :refresh, :action => 'get_refresh'
25
24
  put :refresh, :action => 'set_refresh'
26
25
  end
27
26
  end
28
27
  end
29
28
  end
30
- end
29
+ end
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  source_root File.expand_path("../templates", __FILE__)
7
7
 
8
8
  def copy_devise_migration
9
- migration_template "migration.rb", "db/migrate/devise_otp_add_to_#{table_name}"
9
+ migration_template "migration.rb", "db/migrate/devise_otp_add_to_#{table_name}.rb"
10
10
  end
11
11
  end
12
12
  end
@@ -6,7 +6,6 @@ class DeviseOtpAddTo<%= table_name.camelize %> < ActiveRecord::Migration
6
6
  t.boolean :otp_enabled, :default => false, :null => false
7
7
  t.boolean :otp_mandatory, :default => false, :null => false
8
8
  t.datetime :otp_enabled_on
9
- t.integer :otp_time_drift, :default => 0, :null => false
10
9
  t.integer :otp_failed_attempts, :default => 0, :null => false
11
10
  t.integer :otp_recovery_counter, :default => 0, :null => false
12
11
  t.string :otp_persistence_seed
@@ -21,7 +20,7 @@ class DeviseOtpAddTo<%= table_name.camelize %> < ActiveRecord::Migration
21
20
  def self.down
22
21
  change_table :<%= table_name %> do |t|
23
22
  t.remove :otp_auth_secret, :otp_recovery_secret, :otp_enabled, :otp_mandatory, :otp_enabled_on, :otp_session_challenge,
24
- :otp_challenge_expires, :otp_time_drift, :otp_failed_attempts, :otp_recovery_counter, :otp_persistence_seed
23
+ :otp_challenge_expires, :otp_failed_attempts, :otp_recovery_counter, :otp_persistence_seed
25
24
 
26
25
  end
27
26
  end
File without changes
@@ -13,11 +13,36 @@ content = <<-CONTENT
13
13
  # ==> Devise OTP Extension
14
14
  # Configure OTP extension for devise
15
15
 
16
- # How long should the user have to enter their token. To change the default, uncomment and change the below:
17
- #config.otp_authentication_timeout = 3.minutes
16
+ # OTP is mandatory, users are going to be asked to
17
+ # enroll OTP the next time they sign in, before they can successfully complete the session establishment.
18
+ # This is the global value, can also be set on each user.
19
+ #config.otp_mandatory = false
20
+
21
+ # Drift: a window which provides allowance for drift between a user's token device clock
22
+ # (and therefore their OTP tokens) and the authentication server's clock.
23
+ # Expressed in minutes centered at the current time. (Note: it's a number, *NOT* 3.minutes )
24
+ #config.otp_drift_window = 3
25
+
26
+ # Users that have logged in longer than this time ago, are going to be asked their password
27
+ # (and an OTP challenge, if enabled) before they can see or change their otp informations.
28
+ #config.otp_credentials_refresh = 15.minutes
29
+
30
+ # Users are given a list of one-time recovery tokens, for emergency access
31
+ # set to false to disable giving recovery tokens.
32
+ #config.otp_recovery_tokens = 10
33
+
34
+ # The user is allowed to set his browser as "trusted", no more OTP challenges will be
35
+ # asked for that browser, for a limited time.
36
+ # set to false to disable setting the browser as trusted
37
+ #config.otp_trust_persistence = 1.month
38
+
39
+ # The name of the token issuer, to be added to the provisioning
40
+ # url. Display will vary based on token application. (defaults to the Rails application class)
41
+ #config.otp_issuer = 'my_application'
42
+
43
+ # Custom view path for Devise OTP controllers
44
+ #config.otp_controller_path = 'devise'
18
45
 
19
- # Change time drift settings for valid token values. To change the default, uncomment and change the below:
20
- #config.otp_authentication_time_drift = 3
21
46
  CONTENT
22
47
 
23
48
  inject_into_file "config/initializers/devise.rb", content, :before => /end[ |\n|]+\Z/
@@ -28,4 +53,4 @@ CONTENT
28
53
  end
29
54
  end
30
55
  end
31
- end
56
+ end
@@ -9,10 +9,9 @@ module DeviseOtp
9
9
  :desc => "The scope to copy views to"
10
10
 
11
11
  include ::Devise::Generators::ViewPathTemplates
12
- source_root File.expand_path("../../../../app/views/devise_otp", __FILE__)
12
+ source_root File.expand_path("../../../../app/views", __FILE__)
13
13
  def copy_views
14
- view_directory :credentials, 'app/views/devise_otp/credentials'
15
- view_directory :tokens, 'app/views/devise_otp/tokens'
14
+ view_directory :devise, 'app/views/devise'
16
15
  end
17
16
  end
18
17
  end
File without changes
data/test/dummy/Rakefile CHANGED
File without changes
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts .js
2
+ //= link_directory ../stylesheets .css
@@ -11,3 +11,4 @@
11
11
  // GO AFTER THE REQUIRES BELOW.
12
12
  //
13
13
  //= require_tree .
14
+ //= require devise-otp
File without changes
@@ -1,4 +1,4 @@
1
1
  class ApplicationController < ActionController::Base
2
2
  protect_from_forgery
3
- before_filter :authenticate_user!
3
+ before_action :authenticate_user!
4
4
  end
@@ -1,4 +1,6 @@
1
1
  class PostsController < ApplicationController
2
+ before_action :authenticate_user!
3
+
2
4
  # GET /posts
3
5
  # GET /posts.json
4
6
  def index
File without changes
File without changes
File without changes
File without changes
@@ -12,7 +12,7 @@ class User < PARENT_MODEL_CLASS
12
12
  end
13
13
 
14
14
  devise :otp_authenticatable, :database_authenticatable, :registerable,
15
- :recoverable, :rememberable, :trackable, :validatable
15
+ :trackable, :validatable
16
16
 
17
17
  # Setup accessible (or protected) attributes for your model
18
18
  #attr_accessible :otp_enabled, :otp_mandatory, :as => :otp_privileged
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -53,6 +53,8 @@ module Dummy
53
53
  # Enable escaping HTML in JSON.
54
54
  config.active_support.escape_html_entities_in_json = true
55
55
 
56
+ config.active_record.legacy_connection_handling = false
57
+
56
58
  # Use SQL instead of Active Record's schema dumper when creating the database.
57
59
  # This is necessary if your schema can't be completely dumped by the schema dumper,
58
60
  # like if you have constraints or database-specific column types
@@ -65,4 +67,3 @@ module Dummy
65
67
  config.assets.version = '1.0'
66
68
  end
67
69
  end
68
-
File without changes
@@ -14,7 +14,7 @@ development:
14
14
  # Do not set this db to the same as development or production.
15
15
  test:
16
16
  adapter: sqlite3
17
- database: ":memory:"
17
+ database: db/test.sqlite3
18
18
  pool: 5
19
19
  timeout: 5000
20
20
 
File without changes
@@ -22,13 +22,6 @@ Dummy::Application.configure do
22
22
  # Only use best-standards-support built into browsers
23
23
  config.action_dispatch.best_standards_support = :builtin
24
24
 
25
- # Raise exception on mass assignment protection for Active Record models
26
- config.active_record.mass_assignment_sanitizer = :strict
27
-
28
- # Log the query plan for queries taking more than this (works
29
- # with SQLite, MySQL, and PostgreSQL)
30
- config.active_record.auto_explain_threshold_in_seconds = 0.5
31
-
32
25
  # Do not compress assets
33
26
  config.assets.compress = false
34
27
 
@@ -66,8 +66,4 @@ Dummy::Application.configure do
66
66
 
67
67
  # Send deprecation notices to registered listeners
68
68
  config.active_support.deprecation = :notify
69
-
70
- # Log the query plan for queries taking more than this (works
71
- # with SQLite, MySQL, and PostgreSQL)
72
- # config.active_record.auto_explain_threshold_in_seconds = 0.5
73
69
  end
File without changes
File without changes
File without changes