devise_security_extension 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +39 -0
  3. data/.rubocop.yml +38 -0
  4. data/Gemfile +1 -5
  5. data/Gemfile.lock +144 -141
  6. data/README.md +37 -11
  7. data/Rakefile +13 -29
  8. data/app/controllers/devise/paranoid_verification_code_controller.rb +42 -0
  9. data/app/controllers/devise/password_expired_controller.rb +16 -7
  10. data/app/views/devise/paranoid_verification_code/show.html.erb +10 -0
  11. data/config/locales/de.yml +2 -0
  12. data/config/locales/en.yml +6 -4
  13. data/config/locales/it.yml +10 -0
  14. data/devise_security_extension.gemspec +24 -104
  15. data/lib/devise_security_extension.rb +18 -8
  16. data/lib/devise_security_extension/controllers/helpers.rb +39 -6
  17. data/lib/devise_security_extension/hooks/paranoid_verification.rb +5 -0
  18. data/lib/devise_security_extension/hooks/session_limitable.rb +1 -0
  19. data/lib/devise_security_extension/models/paranoid_verification.rb +35 -0
  20. data/lib/devise_security_extension/models/password_archivable.rb +3 -7
  21. data/lib/devise_security_extension/models/password_expirable.rb +9 -5
  22. data/lib/devise_security_extension/patches/confirmations_controller_captcha.rb +3 -1
  23. data/lib/devise_security_extension/patches/confirmations_controller_security_question.rb +3 -1
  24. data/lib/devise_security_extension/patches/passwords_controller_captcha.rb +3 -1
  25. data/lib/devise_security_extension/patches/passwords_controller_security_question.rb +3 -1
  26. data/lib/devise_security_extension/patches/registrations_controller_captcha.rb +5 -3
  27. data/lib/devise_security_extension/patches/sessions_controller_captcha.rb +5 -3
  28. data/lib/devise_security_extension/patches/unlocks_controller_captcha.rb +3 -1
  29. data/lib/devise_security_extension/patches/unlocks_controller_security_question.rb +3 -1
  30. data/lib/devise_security_extension/routes.rb +4 -0
  31. data/lib/devise_security_extension/version.rb +3 -0
  32. data/lib/generators/devise_security_extension/install_generator.rb +16 -33
  33. data/lib/generators/templates/devise_security_extension.rb +38 -0
  34. data/test/dummy/Rakefile +6 -0
  35. data/test/dummy/app/controllers/application_controller.rb +2 -0
  36. data/test/dummy/app/controllers/foos_controller.rb +0 -0
  37. data/test/dummy/app/models/user.rb +2 -1
  38. data/test/dummy/app/views/foos/index.html.erb +0 -0
  39. data/test/dummy/config/application.rb +4 -2
  40. data/test/dummy/config/boot.rb +1 -1
  41. data/test/dummy/config/environments/test.rb +4 -2
  42. data/test/dummy/config/initializers/devise.rb +4 -4
  43. data/test/dummy/config/routes.rb +6 -0
  44. data/test/dummy/config/secrets.yml +3 -0
  45. data/test/dummy/db/migrate/20120508165529_create_tables.rb +4 -4
  46. data/test/dummy/db/migrate/20150402165590_add_verification_columns.rb +11 -0
  47. data/test/dummy/db/migrate/20150407162345_add_verification_attempt_column.rb +9 -0
  48. data/test/test_helper.rb +10 -0
  49. data/test/test_install_generator.rb +16 -0
  50. data/test/test_paranoid_verification.rb +124 -0
  51. data/test/test_password_archivable.rb +35 -21
  52. data/test/test_password_expired_controller.rb +24 -0
  53. metadata +104 -34
  54. data/VERSION +0 -1
  55. data/lib/devise_security_extension/models/security_question.rb +0 -3
  56. data/test/helper.rb +0 -22
  57. data/test/test_devise_security_extension.rb +0 -6
data/Rakefile CHANGED
@@ -1,39 +1,23 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), 'lib')
1
2
  require 'rubygems'
2
3
  require 'bundler'
3
- begin
4
- Bundler.setup(:default, :development)
5
- rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
8
- exit e.status_code
9
- end
10
- require 'rake'
4
+ require 'rake/testtask'
5
+ require 'rdoc/task'
6
+ require 'devise_security_extension/version'
11
7
 
12
- require 'jeweler'
13
- Jeweler::Tasks.new do |gem|
14
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
- gem.name = "devise_security_extension"
16
- gem.homepage = "http://github.com/phatworx/devise_security_extension"
17
- gem.license = "MIT"
18
- gem.summary = %Q{Security extension for devise}
19
- gem.description = %Q{An enterprise security extension for devise, trying to meet industrial standard security demands for web applications.}
20
- gem.email = "team@phatworx.de"
21
- gem.authors = ["Marco Scholl", "Alexander Dreher"]
22
- end
23
- Jeweler::RubygemsDotOrgTasks.new
8
+ desc 'Default: Run DeviseSecurityExtension unit tests'
9
+ task default: :test
24
10
 
25
- require 'rake/testtask'
26
- Rake::TestTask.new(:test) do |test|
27
- test.libs << 'lib' << 'test'
28
- test.pattern = 'test/**/test_*.rb'
29
- test.verbose = true
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs << 'lib'
13
+ t.libs << 'test'
14
+ t.test_files = FileList['test/*test*.rb']
15
+ t.verbose = true
16
+ t.warning = false
30
17
  end
31
18
 
32
- task :default => :test
33
-
34
- require 'rdoc/task'
35
19
  Rake::RDocTask.new do |rdoc|
36
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
20
+ version = DeviseSecurityExtension::VERSION.dup
37
21
 
38
22
  rdoc.rdoc_dir = 'rdoc'
39
23
  rdoc.title = "devise_security_extension #{version}"
@@ -0,0 +1,42 @@
1
+ class Devise::ParanoidVerificationCodeController < DeviseController
2
+ skip_before_filter :handle_paranoid_verification
3
+ prepend_before_filter :authenticate_scope!, :only => [:show, :update]
4
+
5
+ def show
6
+ if !resource.nil? && resource.need_paranoid_verification?
7
+ respond_with(resource)
8
+ else
9
+ redirect_to :root
10
+ end
11
+ end
12
+
13
+ def update
14
+ if resource.verify_code(resource_params[:paranoid_verification_code])
15
+ warden.session(scope)['paranoid_verify'] = false
16
+ set_flash_message :notice, :updated
17
+ sign_in scope, resource, :bypass => true
18
+ redirect_to stored_location_for(scope) || :root
19
+ else
20
+ respond_with(resource, action: :show)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def resource_params
27
+ if params.respond_to?(:permit)
28
+ params.require(resource_name).permit(:paranoid_verification_code)
29
+ else
30
+ params[scope].slice(:paranoid_verification_code)
31
+ end
32
+ end
33
+
34
+ def scope
35
+ resource_name.to_sym
36
+ end
37
+
38
+ def authenticate_scope!
39
+ send(:"authenticate_#{resource_name}!")
40
+ self.resource = send("current_#{resource_name}")
41
+ end
42
+ end
@@ -1,13 +1,10 @@
1
1
  class Devise::PasswordExpiredController < DeviseController
2
2
  skip_before_filter :handle_password_change
3
+ before_filter :skip_password_change, only: [:show, :update]
3
4
  prepend_before_filter :authenticate_scope!, :only => [:show, :update]
4
5
 
5
6
  def show
6
- if not resource.nil? and resource.need_change_password?
7
- respond_with(resource)
8
- else
9
- redirect_to :root
10
- end
7
+ respond_with(resource)
11
8
  end
12
9
 
13
10
  def update
@@ -24,9 +21,21 @@ class Devise::PasswordExpiredController < DeviseController
24
21
  end
25
22
 
26
23
  private
27
- def resource_params
28
- params.require(resource_name).permit(:current_password, :password, :password_confirmation)
24
+
25
+ def skip_password_change
26
+ return if !resource.nil? && resource.need_change_password?
27
+ redirect_to :root
28
+ end
29
+
30
+ def resource_params
31
+ permitted_params = [:current_password, :password, :password_confirmation]
32
+
33
+ if params.respond_to?(:permit)
34
+ params.require(resource_name).permit(*permitted_params)
35
+ else
36
+ params[scope].slice(*permitted_params)
29
37
  end
38
+ end
30
39
 
31
40
  def scope
32
41
  resource_name.to_sym
@@ -0,0 +1,10 @@
1
+ <h2>Submit verification code</h2>
2
+
3
+ <%= form_for(resource, :as => resource_name, :url => [resource_name, :paranoid_verification_code], :html => { :method => :put }) do |f| %>
4
+ <%= devise_error_messages! %>
5
+
6
+ <p><%= f.label :paranoid_verification_code, 'Verification code' %><br />
7
+ <%= f.text_field :paranoid_verification_code, value: '' %></p>
8
+
9
+ <p><%= f.submit "Submit" %></p>
10
+ <% end %>
@@ -6,6 +6,8 @@ de:
6
6
  password_format: "müssen große, kleine Buchstaben und Ziffern enthalten"
7
7
  devise:
8
8
  invalid_captcha: "Die Captchaeingabe ist nicht gültig!"
9
+ paranoid_verify:
10
+ code_required: "Bitte geben Sie den Code unser Support-Team zur Verfügung gestellt"
9
11
  password_expired:
10
12
  updated: "Das neue Passwort wurde übernommen."
11
13
  change_required: "Ihr Passwort ist abgelaufen. Bitte vergeben sie ein neues Passwort!"
@@ -1,14 +1,16 @@
1
1
  en:
2
2
  errors:
3
3
  messages:
4
- taken_in_past: "was already taken in the past!"
5
- equal_to_current_password: "must be different to the current password!"
4
+ taken_in_past: "was used previously."
5
+ equal_to_current_password: "must be different than the current password."
6
6
  password_format: "must contain big, small letters and digits"
7
7
  devise:
8
- invalid_captcha: "The captcha input is not valid!"
8
+ invalid_captcha: "The captcha input was invalid."
9
+ paranoid_verify:
10
+ code_required: "Please enter the code our support team provided"
9
11
  password_expired:
10
12
  updated: "Your new password is saved."
11
- change_required: "Your password is expired. Please renew your password!"
13
+ change_required: "Your password is expired. Please renew your password."
12
14
  failure:
13
15
  session_limited: 'Your login credentials were used in another browser. Please sign in again to continue in this browser.'
14
16
  expired: 'Your account has expired due to inactivity. Please contact the site administrator.'
@@ -0,0 +1,10 @@
1
+ it:
2
+ errors:
3
+ messages:
4
+ taken_in_past: "e' stata gia' utilizzata in passato!"
5
+ equal_to_current_password: " deve essere differente dalla password corrente!"
6
+ devise:
7
+ invalid_captcha: "Il captcha inserito non e' valido!"
8
+ password_expired:
9
+ updated: "La tua nuova password e' stata salvata."
10
+ change_required: "La tua password e' scaduta. Si prega di rinnovarla!"
@@ -1,111 +1,31 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
1
  # -*- encoding: utf-8 -*-
5
- # stub: devise_security_extension 0.8.3 ruby lib
2
+ $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
3
+ require 'devise_security_extension/version'
6
4
 
7
5
  Gem::Specification.new do |s|
8
- s.name = "devise_security_extension"
9
- s.version = "0.9.2"
6
+ s.name = 'devise_security_extension'
7
+ s.version = DeviseSecurityExtension::VERSION.dup
8
+ s.platform = Gem::Platform::RUBY
9
+ s.licenses = ['MIT']
10
+ s.summary = 'Security extension for devise'
11
+ s.email = 'team@phatworx.de'
12
+ s.homepage = 'https://github.com/phatworx/devise_security_extension'
13
+ s.description = 'An enterprise security extension for devise, trying to meet industrial standard security demands for web applications.'
14
+ s.authors = ['Marco Scholl', 'Alexander Dreher']
10
15
 
11
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
- s.require_paths = ["lib"]
13
- s.authors = ["Marco Scholl", "Alexander Dreher"]
14
- s.date = "2015-04-02"
15
- s.description = "An enterprise security extension for devise, trying to meet industrial standard security demands for web applications."
16
- s.email = "team@phatworx.de"
17
- s.extra_rdoc_files = [
18
- "LICENSE.txt",
19
- "README.md"
20
- ]
21
- s.files = [
22
- ".document",
23
- "Gemfile",
24
- "Gemfile.lock",
25
- "LICENSE.txt",
26
- "README.md",
27
- "Rakefile",
28
- "VERSION",
29
- "app/controllers/devise/password_expired_controller.rb",
30
- "app/views/devise/password_expired/show.html.erb",
31
- "config/locales/de.yml",
32
- "config/locales/en.yml",
33
- "devise_security_extension.gemspec",
34
- "lib/devise_security_extension.rb",
35
- "lib/devise_security_extension/controllers/helpers.rb",
36
- "lib/devise_security_extension/hooks/expirable.rb",
37
- "lib/devise_security_extension/hooks/password_expirable.rb",
38
- "lib/devise_security_extension/hooks/session_limitable.rb",
39
- "lib/devise_security_extension/models/expirable.rb",
40
- "lib/devise_security_extension/models/old_password.rb",
41
- "lib/devise_security_extension/models/password_archivable.rb",
42
- "lib/devise_security_extension/models/password_expirable.rb",
43
- "lib/devise_security_extension/models/secure_validatable.rb",
44
- "lib/devise_security_extension/models/security_question.rb",
45
- "lib/devise_security_extension/models/security_questionable.rb",
46
- "lib/devise_security_extension/models/session_limitable.rb",
47
- "lib/devise_security_extension/orm/active_record.rb",
48
- "lib/devise_security_extension/models/database_authenticatable_patch.rb",
49
- "lib/devise_security_extension/patches.rb",
50
- "lib/devise_security_extension/patches/confirmations_controller_captcha.rb",
51
- "lib/devise_security_extension/patches/confirmations_controller_security_question.rb",
52
- "lib/devise_security_extension/patches/passwords_controller_captcha.rb",
53
- "lib/devise_security_extension/patches/passwords_controller_security_question.rb",
54
- "lib/devise_security_extension/patches/registrations_controller_captcha.rb",
55
- "lib/devise_security_extension/patches/sessions_controller_captcha.rb",
56
- "lib/devise_security_extension/patches/unlocks_controller_captcha.rb",
57
- "lib/devise_security_extension/patches/unlocks_controller_security_question.rb",
58
- "lib/devise_security_extension/rails.rb",
59
- "lib/devise_security_extension/routes.rb",
60
- "lib/devise_security_extension/schema.rb",
61
- "lib/generators/devise_security_extension/install_generator.rb",
62
- "test/dummy/app/models/.gitkeep",
63
- "test/dummy/app/models/user.rb",
64
- "test/dummy/config.ru",
65
- "test/dummy/config/application.rb",
66
- "test/dummy/config/boot.rb",
67
- "test/dummy/config/database.yml",
68
- "test/dummy/config/environment.rb",
69
- "test/dummy/config/environments/test.rb",
70
- "test/dummy/config/initializers/devise.rb",
71
- "test/dummy/db/migrate/20120508165529_create_tables.rb",
72
- "test/helper.rb",
73
- "test/test_devise_security_extension.rb",
74
- "test/test_password_archivable.rb"
75
- ]
76
- s.homepage = "http://github.com/phatworx/devise_security_extension"
77
- s.licenses = ["MIT"]
78
- s.rubygems_version = "2.2.2"
79
- s.summary = "Security extension for devise"
16
+ s.rubyforge_project = 'devise_security_extension'
80
17
 
81
- if s.respond_to? :specification_version then
82
- s.specification_version = 4
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- test/*`.split("\n")
20
+ s.require_paths = ['lib']
21
+ s.required_ruby_version = '>= 1.9.3'
83
22
 
84
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
85
- s.add_runtime_dependency(%q<rails>, [">= 3.1.1"])
86
- s.add_runtime_dependency(%q<devise>, [">= 2.0.0"])
87
- s.add_development_dependency(%q<rails_email_validator>, [">= 0"])
88
- s.add_development_dependency(%q<easy_captcha>, [">= 0"])
89
- s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
90
- s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
91
- s.add_development_dependency(%q<sqlite3>, [">= 0"])
92
- else
93
- s.add_dependency(%q<rails>, [">= 3.1.1"])
94
- s.add_dependency(%q<devise>, [">= 2.0.0"])
95
- s.add_dependency(%q<rails_email_validator>, [">= 0"])
96
- s.add_dependency(%q<easy_captcha>, [">= 0"])
97
- s.add_dependency(%q<bundler>, [">= 1.0.0"])
98
- s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
99
- s.add_dependency(%q<sqlite3>, [">= 0"])
100
- end
101
- else
102
- s.add_dependency(%q<rails>, [">= 3.1.1"])
103
- s.add_dependency(%q<devise>, [">= 2.0.0"])
104
- s.add_dependency(%q<rails_email_validator>, [">= 0"])
105
- s.add_dependency(%q<easy_captcha>, [">= 0"])
106
- s.add_dependency(%q<bundler>, [">= 1.0.0"])
107
- s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
108
- s.add_dependency(%q<sqlite3>, [">= 0"])
109
- end
23
+ s.add_runtime_dependency 'railties', '>= 3.2.6', '< 5.0'
24
+ s.add_runtime_dependency 'devise', '>= 3.0.0', '< 4.0'
25
+ s.add_development_dependency 'bundler', '>= 1.3.0', '< 2.0'
26
+ s.add_development_dependency 'sqlite3', '~> 1.3.10'
27
+ s.add_development_dependency 'rubocop', '~> 0'
28
+ s.add_development_dependency 'minitest'
29
+ s.add_development_dependency 'easy_captcha', '~> 0'
30
+ s.add_development_dependency 'rails_email_validator', '~> 0'
110
31
  end
111
-
@@ -14,7 +14,7 @@ module Devise
14
14
  mattr_accessor :password_regex
15
15
  @@password_regex = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/
16
16
 
17
- # How often save old passwords in archive
17
+ # Number of old passwords in archive
18
18
  mattr_accessor :password_archiving_count
19
19
  @@password_archiving_count = 5
20
20
 
@@ -62,11 +62,19 @@ module Devise
62
62
  mattr_accessor :captcha_for_confirmation
63
63
  @@captcha_for_confirmation = false
64
64
 
65
+ # captcha integration for confirmation form
66
+ mattr_accessor :verification_code_generator
67
+ @@verification_code_generator = -> { SecureRandom.hex[0..4] }
68
+
65
69
  # Time period for account expiry from last_activity_at
66
70
  mattr_accessor :expire_after
67
71
  @@expire_after = 90.days
68
72
  mattr_accessor :delete_expired_after
69
73
  @@delete_expired_after = 90.days
74
+
75
+ # paranoid_verification will regenerate verifacation code after faild attempt
76
+ mattr_accessor :paranoid_code_regenerate_after_attempt
77
+ @@paranoid_code_regenerate_after_attempt = 10
70
78
  end
71
79
 
72
80
  # an security extension for devise
@@ -80,17 +88,19 @@ module DeviseSecurityExtension
80
88
  end
81
89
 
82
90
  # modules
83
- Devise.add_module :password_expirable, :controller => :password_expirable, :model => 'devise_security_extension/models/password_expirable', :route => :password_expired
84
- Devise.add_module :secure_validatable, :model => 'devise_security_extension/models/secure_validatable'
85
- Devise.add_module :password_archivable, :model => 'devise_security_extension/models/password_archivable'
86
- Devise.add_module :session_limitable, :model => 'devise_security_extension/models/session_limitable'
87
- Devise.add_module :expirable, :model => 'devise_security_extension/models/expirable'
88
- Devise.add_module :security_questionable, :model => 'devise_security_extension/models/security_questionable'
91
+ Devise.add_module :password_expirable, controller: :password_expirable, model: 'devise_security_extension/models/password_expirable', route: :password_expired
92
+ Devise.add_module :secure_validatable, model: 'devise_security_extension/models/secure_validatable'
93
+ Devise.add_module :password_archivable, model: 'devise_security_extension/models/password_archivable'
94
+ Devise.add_module :session_limitable, model: 'devise_security_extension/models/session_limitable'
95
+ Devise.add_module :session_non_transferable, model: 'devise_security_extension/models/session_non_transferable'
96
+ Devise.add_module :expirable, model: 'devise_security_extension/models/expirable'
97
+ Devise.add_module :security_questionable, model: 'devise_security_extension/models/security_questionable'
98
+ Devise.add_module :paranoid_verification, controller: :paranoid_verification_code, model: 'devise_security_extension/models/paranoid_verification', route: :verification_code
89
99
 
90
100
  # requires
91
101
  require 'devise_security_extension/routes'
92
102
  require 'devise_security_extension/rails'
93
103
  require 'devise_security_extension/orm/active_record'
94
104
  require 'devise_security_extension/models/old_password'
95
- require 'devise_security_extension/models/security_question'
96
105
  require 'devise_security_extension/models/database_authenticatable_patch'
106
+ require 'devise_security_extension/models/paranoid_verification'
@@ -5,13 +5,14 @@ module DeviseSecurityExtension
5
5
 
6
6
  included do
7
7
  before_filter :handle_password_change
8
+ before_filter :handle_paranoid_verification
8
9
  end
9
-
10
+
10
11
  module ClassMethods
11
12
  # helper for captcha
12
13
  def init_recover_password_captcha
13
14
  include RecoverPasswordCaptcha
14
- end
15
+ end
15
16
  end
16
17
 
17
18
  module RecoverPasswordCaptcha
@@ -26,11 +27,33 @@ module DeviseSecurityExtension
26
27
 
27
28
  # lookup if an password change needed
28
29
  def handle_password_change
30
+ return if warden.nil?
31
+
29
32
  if not devise_controller? and not ignore_password_expire? and not request.format.nil? and request.format.html?
30
33
  Devise.mappings.keys.flatten.any? do |scope|
31
34
  if signed_in?(scope) and warden.session(scope)['password_expired']
32
- session["#{scope}_return_to"] = request.path if request.get?
33
- redirect_for_password_change scope
35
+ # re-check to avoid infinite loop if date changed after login attempt
36
+ if send(:"current_#{scope}").try(:need_change_password?)
37
+ session["#{scope}_return_to"] = request.original_fullpath if request.get?
38
+ redirect_for_password_change scope
39
+ return
40
+ else
41
+ warden.session(scope)[:password_expired] = false
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ # lookup if extra (paranoid) code verification is needed
49
+ def handle_paranoid_verification
50
+ return if warden.nil?
51
+
52
+ if !devise_controller? && !request.format.nil? && request.format.html?
53
+ Devise.mappings.keys.flatten.any? do |scope|
54
+ if signed_in?(scope) && warden.session(scope)['paranoid_verify']
55
+ session["#{scope}_return_to"] = request.original_fullpath if request.get?
56
+ redirect_for_paranoid_verification scope
34
57
  return
35
58
  end
36
59
  end
@@ -42,15 +65,25 @@ module DeviseSecurityExtension
42
65
  redirect_to change_password_required_path_for(scope), :alert => I18n.t('change_required', {:scope => 'devise.password_expired'})
43
66
  end
44
67
 
68
+ def redirect_for_paranoid_verification(scope)
69
+ redirect_to paranoid_verification_code_path_for(scope), :alert => I18n.t('code_required', {:scope => 'devise.paranoid_verify'})
70
+ end
71
+
45
72
  # path for change password
46
73
  def change_password_required_path_for(resource_or_scope = nil)
47
74
  scope = Devise::Mapping.find_scope!(resource_or_scope)
48
75
  change_path = "#{scope}_password_expired_path"
49
76
  send(change_path)
50
77
  end
51
-
78
+
79
+ def paranoid_verification_code_path_for(resource_or_scope = nil)
80
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
81
+ change_path = "#{scope}_paranoid_verification_code_path"
82
+ send(change_path)
83
+ end
84
+
52
85
  protected
53
-
86
+
54
87
  # allow to overwrite for some special handlings
55
88
  def ignore_password_expire?
56
89
  false