devise_security_extension 0.7.2 → 0.10.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.
- checksums.yaml +7 -0
- data/.gitignore +39 -0
- data/.rubocop.yml +38 -0
- data/Gemfile +2 -15
- data/Gemfile.lock +157 -112
- data/README.md +264 -0
- data/Rakefile +13 -29
- data/app/controllers/devise/paranoid_verification_code_controller.rb +42 -0
- data/app/controllers/devise/password_expired_controller.rb +20 -7
- data/app/views/devise/paranoid_verification_code/show.html.erb +10 -0
- data/config/locales/de.yml +3 -0
- data/config/locales/en.yml +7 -4
- data/config/locales/it.yml +10 -0
- data/devise_security_extension.gemspec +24 -88
- data/lib/devise_security_extension/controllers/helpers.rb +40 -7
- data/lib/devise_security_extension/hooks/paranoid_verification.rb +5 -0
- data/lib/devise_security_extension/hooks/password_expirable.rb +1 -1
- data/lib/devise_security_extension/hooks/session_limitable.rb +3 -2
- data/lib/devise_security_extension/models/database_authenticatable_patch.rb +26 -0
- data/lib/devise_security_extension/models/expirable.rb +1 -2
- data/lib/devise_security_extension/models/old_password.rb +1 -2
- data/lib/devise_security_extension/models/paranoid_verification.rb +35 -0
- data/lib/devise_security_extension/models/password_archivable.rb +11 -11
- data/lib/devise_security_extension/models/password_expirable.rb +9 -5
- data/lib/devise_security_extension/models/secure_validatable.rb +35 -9
- data/lib/devise_security_extension/models/security_questionable.rb +4 -1
- data/lib/devise_security_extension/patches/confirmations_controller_captcha.rb +3 -1
- data/lib/devise_security_extension/patches/confirmations_controller_security_question.rb +3 -1
- data/lib/devise_security_extension/patches/passwords_controller_captcha.rb +3 -1
- data/lib/devise_security_extension/patches/passwords_controller_security_question.rb +3 -1
- data/lib/devise_security_extension/patches/registrations_controller_captcha.rb +11 -8
- data/lib/devise_security_extension/patches/sessions_controller_captcha.rb +8 -5
- data/lib/devise_security_extension/patches/unlocks_controller_captcha.rb +3 -1
- data/lib/devise_security_extension/patches/unlocks_controller_security_question.rb +3 -1
- data/lib/devise_security_extension/routes.rb +4 -0
- data/lib/devise_security_extension/version.rb +3 -0
- data/lib/devise_security_extension.rb +20 -10
- data/lib/generators/devise_security_extension/install_generator.rb +16 -33
- data/lib/generators/templates/devise_security_extension.rb +38 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/controllers/application_controller.rb +2 -0
- data/test/dummy/app/controllers/foos_controller.rb +0 -0
- data/test/dummy/app/models/.gitkeep +0 -0
- data/test/dummy/app/models/user.rb +4 -0
- data/test/dummy/app/views/foos/index.html.erb +0 -0
- data/test/dummy/config/application.rb +24 -0
- data/test/dummy/config/boot.rb +6 -0
- data/test/dummy/config/database.yml +7 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/test.rb +21 -0
- data/test/dummy/config/initializers/devise.rb +9 -0
- data/test/dummy/config/routes.rb +6 -0
- data/test/dummy/config/secrets.yml +3 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/migrate/20120508165529_create_tables.rb +26 -0
- data/test/dummy/db/migrate/20150402165590_add_verification_columns.rb +11 -0
- data/test/dummy/db/migrate/20150407162345_add_verification_attempt_column.rb +9 -0
- data/test/test_helper.rb +10 -0
- data/test/test_install_generator.rb +16 -0
- data/test/test_paranoid_verification.rb +124 -0
- data/test/test_password_archivable.rb +61 -0
- data/test/test_password_expired_controller.rb +24 -0
- metadata +142 -62
- data/README.rdoc +0 -193
- data/VERSION +0 -1
- data/lib/devise_security_extension/models/security_question.rb +0 -3
- data/test/helper.rb +0 -17
- data/test/test_devise_security_extension.rb +0 -7
data/Rakefile
CHANGED
@@ -1,39 +1,23 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), 'lib')
|
1
2
|
require 'rubygems'
|
2
3
|
require 'bundler'
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
13
|
-
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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 =
|
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,18 +1,16 @@
|
|
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
|
-
|
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
|
14
|
-
|
15
|
-
|
11
|
+
resource.extend(Devise::Models::DatabaseAuthenticatablePatch)
|
12
|
+
if resource.update_with_password(resource_params)
|
13
|
+
warden.session(scope)['password_expired'] = false
|
16
14
|
set_flash_message :notice, :updated
|
17
15
|
sign_in scope, resource, :bypass => true
|
18
16
|
redirect_to stored_location_for(scope) || :root
|
@@ -24,6 +22,21 @@ class Devise::PasswordExpiredController < DeviseController
|
|
24
22
|
|
25
23
|
private
|
26
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)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
27
40
|
def scope
|
28
41
|
resource_name.to_sym
|
29
42
|
end
|
@@ -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 %>
|
data/config/locales/de.yml
CHANGED
@@ -3,8 +3,11 @@ de:
|
|
3
3
|
messages:
|
4
4
|
taken_in_past: "wurde bereits in der Vergangenheit verwendet!"
|
5
5
|
equal_to_current_password: "darf nicht dem aktuellen Passwort entsprechen!"
|
6
|
+
password_format: "müssen große, kleine Buchstaben und Ziffern enthalten"
|
6
7
|
devise:
|
7
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"
|
8
11
|
password_expired:
|
9
12
|
updated: "Das neue Passwort wurde übernommen."
|
10
13
|
change_required: "Ihr Passwort ist abgelaufen. Bitte vergeben sie ein neues Passwort!"
|
data/config/locales/en.yml
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
en:
|
2
2
|
errors:
|
3
3
|
messages:
|
4
|
-
taken_in_past: "was
|
5
|
-
equal_to_current_password: "must be different
|
4
|
+
taken_in_past: "was used previously."
|
5
|
+
equal_to_current_password: "must be different than the current password."
|
6
|
+
password_format: "must contain big, small letters and digits"
|
6
7
|
devise:
|
7
|
-
invalid_captcha: "The captcha input
|
8
|
+
invalid_captcha: "The captcha input was invalid."
|
9
|
+
paranoid_verify:
|
10
|
+
code_required: "Please enter the code our support team provided"
|
8
11
|
password_expired:
|
9
12
|
updated: "Your new password is saved."
|
10
|
-
change_required: "Your password is expired. Please renew your password
|
13
|
+
change_required: "Your password is expired. Please renew your password."
|
11
14
|
failure:
|
12
15
|
session_limited: 'Your login credentials were used in another browser. Please sign in again to continue in this browser.'
|
13
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,95 +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 -*-
|
2
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
|
3
|
+
require 'devise_security_extension/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version
|
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']
|
9
15
|
|
10
|
-
s.
|
11
|
-
s.authors = ["Marco Scholl", "Alexander Dreher"]
|
12
|
-
s.date = "2012-11-22"
|
13
|
-
s.description = "An enterprise security extension for devise, trying to meet industrial standard security demands for web applications."
|
14
|
-
s.email = "team@phatworx.de"
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"LICENSE.txt",
|
17
|
-
"README.rdoc"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
".document",
|
21
|
-
"Gemfile",
|
22
|
-
"Gemfile.lock",
|
23
|
-
"LICENSE.txt",
|
24
|
-
"README.rdoc",
|
25
|
-
"Rakefile",
|
26
|
-
"VERSION",
|
27
|
-
"app/controllers/devise/password_expired_controller.rb",
|
28
|
-
"app/views/devise/password_expired/show.html.erb",
|
29
|
-
"config/locales/de.yml",
|
30
|
-
"config/locales/en.yml",
|
31
|
-
"devise_security_extension.gemspec",
|
32
|
-
"lib/devise_security_extension.rb",
|
33
|
-
"lib/devise_security_extension/controllers/helpers.rb",
|
34
|
-
"lib/devise_security_extension/hooks/expirable.rb",
|
35
|
-
"lib/devise_security_extension/hooks/password_expirable.rb",
|
36
|
-
"lib/devise_security_extension/hooks/session_limitable.rb",
|
37
|
-
"lib/devise_security_extension/models/expirable.rb",
|
38
|
-
"lib/devise_security_extension/models/old_password.rb",
|
39
|
-
"lib/devise_security_extension/models/password_archivable.rb",
|
40
|
-
"lib/devise_security_extension/models/password_expirable.rb",
|
41
|
-
"lib/devise_security_extension/models/secure_validatable.rb",
|
42
|
-
"lib/devise_security_extension/models/security_question.rb",
|
43
|
-
"lib/devise_security_extension/models/security_questionable.rb",
|
44
|
-
"lib/devise_security_extension/models/session_limitable.rb",
|
45
|
-
"lib/devise_security_extension/orm/active_record.rb",
|
46
|
-
"lib/devise_security_extension/patches.rb",
|
47
|
-
"lib/devise_security_extension/patches/confirmations_controller_captcha.rb",
|
48
|
-
"lib/devise_security_extension/patches/confirmations_controller_security_question.rb",
|
49
|
-
"lib/devise_security_extension/patches/passwords_controller_captcha.rb",
|
50
|
-
"lib/devise_security_extension/patches/passwords_controller_security_question.rb",
|
51
|
-
"lib/devise_security_extension/patches/registrations_controller_captcha.rb",
|
52
|
-
"lib/devise_security_extension/patches/sessions_controller_captcha.rb",
|
53
|
-
"lib/devise_security_extension/patches/unlocks_controller_captcha.rb",
|
54
|
-
"lib/devise_security_extension/patches/unlocks_controller_security_question.rb",
|
55
|
-
"lib/devise_security_extension/rails.rb",
|
56
|
-
"lib/devise_security_extension/routes.rb",
|
57
|
-
"lib/devise_security_extension/schema.rb",
|
58
|
-
"lib/generators/devise_security_extension/install_generator.rb",
|
59
|
-
"test/helper.rb",
|
60
|
-
"test/test_devise_security_extension.rb"
|
61
|
-
]
|
62
|
-
s.homepage = "http://github.com/phatworx/devise_security_extension"
|
63
|
-
s.licenses = ["MIT"]
|
64
|
-
s.require_paths = ["lib"]
|
65
|
-
s.rubygems_version = "1.8.24"
|
66
|
-
s.summary = "Security extension for devise"
|
16
|
+
s.rubyforge_project = 'devise_security_extension'
|
67
17
|
|
68
|
-
|
69
|
-
|
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'
|
70
22
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
s.add_dependency(%q<rails>, [">= 3.1.1"])
|
80
|
-
s.add_dependency(%q<devise>, [">= 2.0.0"])
|
81
|
-
s.add_dependency(%q<rails_email_validator>, [">= 0"])
|
82
|
-
s.add_dependency(%q<easy_captcha>, [">= 0"])
|
83
|
-
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
84
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
85
|
-
end
|
86
|
-
else
|
87
|
-
s.add_dependency(%q<rails>, [">= 3.1.1"])
|
88
|
-
s.add_dependency(%q<devise>, [">= 2.0.0"])
|
89
|
-
s.add_dependency(%q<rails_email_validator>, [">= 0"])
|
90
|
-
s.add_dependency(%q<easy_captcha>, [">= 0"])
|
91
|
-
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
92
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
93
|
-
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'
|
94
31
|
end
|
95
|
-
|
@@ -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
|
-
if signed_in?(scope) and warden.session(scope)[
|
32
|
-
|
33
|
-
|
34
|
+
if signed_in?(scope) and warden.session(scope)['password_expired']
|
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
|
@@ -1,5 +1,5 @@
|
|
1
1
|
Warden::Manager.after_authentication do |record, warden, options|
|
2
2
|
if record.respond_to?(:need_change_password?)
|
3
|
-
warden.session(options[:scope])[
|
3
|
+
warden.session(options[:scope])['password_expired'] = record.need_change_password?
|
4
4
|
end
|
5
5
|
end
|
@@ -17,10 +17,11 @@ Warden::Manager.after_set_user :only => :fetch do |record, warden, options|
|
|
17
17
|
scope = options[:scope]
|
18
18
|
env = warden.request.env
|
19
19
|
|
20
|
-
if warden.authenticated?(scope) && options[:store] != false
|
20
|
+
if record.respond_to?(:unique_session_id) && warden.authenticated?(scope) && options[:store] != false
|
21
21
|
if record.unique_session_id != warden.session(scope)['unique_session_id'] && !env['devise.skip_session_limitable']
|
22
|
+
warden.raw_session.clear
|
22
23
|
warden.logout(scope)
|
23
24
|
throw :warden, :scope => scope, :message => :session_limited
|
24
25
|
end
|
25
26
|
end
|
26
|
-
end
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Devise
|
2
|
+
module Models
|
3
|
+
module DatabaseAuthenticatablePatch
|
4
|
+
def update_with_password(params, *options)
|
5
|
+
current_password = params.delete(:current_password)
|
6
|
+
|
7
|
+
new_password = params[:password]
|
8
|
+
new_password_confirmation = params[:password_confirmation]
|
9
|
+
|
10
|
+
result = if valid_password?(current_password) && new_password.present? && new_password_confirmation.present?
|
11
|
+
update_attributes(params, *options)
|
12
|
+
else
|
13
|
+
self.assign_attributes(params, *options)
|
14
|
+
self.valid?
|
15
|
+
self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
|
16
|
+
self.errors.add(:password, new_password.blank? ? :blank : :invalid)
|
17
|
+
self.errors.add(:password_confirmation, new_password_confirmation.blank? ? :blank : :invalid)
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
clean_up_passwords
|
22
|
+
result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -20,8 +20,7 @@ module Devise
|
|
20
20
|
|
21
21
|
# Updates +last_activity_at+, called from a Warden::Manager.after_set_user hook.
|
22
22
|
def update_last_activity!
|
23
|
-
self.last_activity_at
|
24
|
-
save(:validate => false)
|
23
|
+
self.update_column(:last_activity_at, Time.now.utc)
|
25
24
|
end
|
26
25
|
|
27
26
|
# Tells if the account has expired
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'devise_security_extension/hooks/paranoid_verification'
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Models
|
5
|
+
# PasswordExpirable takes care of change password after
|
6
|
+
module ParanoidVerification
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def need_paranoid_verification?
|
10
|
+
!!paranoid_verification_code
|
11
|
+
end
|
12
|
+
|
13
|
+
def verify_code(code)
|
14
|
+
attempt = paranoid_verification_attempt
|
15
|
+
|
16
|
+
if (attempt += 1) >= Devise.paranoid_code_regenerate_after_attempt
|
17
|
+
generate_paranoid_code
|
18
|
+
elsif code == paranoid_verification_code
|
19
|
+
attempt = 0
|
20
|
+
update_without_password paranoid_verification_code: nil, paranoid_verified_at: Time.now, paranoid_verification_attempt: attempt
|
21
|
+
else
|
22
|
+
update_without_password paranoid_verification_attempt: attempt
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def paranoid_attempts_remaining
|
27
|
+
Devise.paranoid_code_regenerate_after_attempt - paranoid_verification_attempt
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate_paranoid_code
|
31
|
+
update_without_password paranoid_verification_code: Devise.verification_code_generator.call(), paranoid_verification_attempt: 0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -18,8 +18,8 @@ module Devise
|
|
18
18
|
# validate is the password used in the past
|
19
19
|
def password_archive_included?
|
20
20
|
unless self.class.deny_old_passwords.is_a? Fixnum
|
21
|
-
if self.class.deny_old_passwords.is_a? TrueClass and
|
22
|
-
self.class.deny_old_passwords =
|
21
|
+
if self.class.deny_old_passwords.is_a? TrueClass and archive_count > 0
|
22
|
+
self.class.deny_old_passwords = archive_count
|
23
23
|
else
|
24
24
|
self.class.deny_old_passwords = 0
|
25
25
|
end
|
@@ -31,14 +31,13 @@ module Devise
|
|
31
31
|
old_passwords_including_cur_change.each do |old_password|
|
32
32
|
dummy = self.class.new
|
33
33
|
dummy.encrypted_password = old_password.encrypted_password
|
34
|
-
dummy.password_salt = old_password.password_salt if dummy.respond_to?(:password_salt)
|
35
34
|
return true if dummy.valid_password?(self.password)
|
36
35
|
end
|
37
36
|
end
|
38
37
|
|
39
38
|
false
|
40
39
|
end
|
41
|
-
|
40
|
+
|
42
41
|
def password_changed_to_same?
|
43
42
|
pass_change = encrypted_password_change
|
44
43
|
pass_change && pass_change.first == pass_change.last
|
@@ -46,23 +45,24 @@ module Devise
|
|
46
45
|
|
47
46
|
private
|
48
47
|
|
48
|
+
def archive_count
|
49
|
+
self.class.password_archiving_count
|
50
|
+
end
|
51
|
+
|
49
52
|
# archive the last password before save and delete all to old passwords from archive
|
50
53
|
def archive_password
|
51
54
|
if self.encrypted_password_changed?
|
52
|
-
if
|
55
|
+
if archive_count.to_i > 0
|
53
56
|
self.old_passwords.create! old_password_params
|
54
|
-
self.old_passwords.order(:id).reverse_order.offset(
|
57
|
+
self.old_passwords.order(:id).reverse_order.offset(archive_count).destroy_all
|
55
58
|
else
|
56
59
|
self.old_passwords.destroy_all
|
57
60
|
end
|
58
61
|
end
|
59
62
|
end
|
60
|
-
|
63
|
+
|
61
64
|
def old_password_params
|
62
|
-
|
63
|
-
self.password_salt_change.first
|
64
|
-
end
|
65
|
-
{ :encrypted_password => self.encrypted_password_change.first, :password_salt => salt_change }
|
65
|
+
{ encrypted_password: self.encrypted_password_change.first }
|
66
66
|
end
|
67
67
|
|
68
68
|
module ClassMethods
|
@@ -13,8 +13,8 @@ module Devise
|
|
13
13
|
|
14
14
|
# is an password change required?
|
15
15
|
def need_change_password?
|
16
|
-
if self.
|
17
|
-
self.password_changed_at.nil? or self.password_changed_at < self.
|
16
|
+
if self.expire_password_after.is_a? Fixnum or self.expire_password_after.is_a? Float
|
17
|
+
self.password_changed_at.nil? or self.password_changed_at < self.expire_password_after.ago
|
18
18
|
else
|
19
19
|
false
|
20
20
|
end
|
@@ -22,7 +22,7 @@ module Devise
|
|
22
22
|
|
23
23
|
# set a fake datetime so a password change is needed and save the record
|
24
24
|
def need_change_password!
|
25
|
-
if self.
|
25
|
+
if self.expire_password_after.is_a? Fixnum or self.expire_password_after.is_a? Float
|
26
26
|
need_change_password
|
27
27
|
self.save(:validate => false)
|
28
28
|
end
|
@@ -30,8 +30,8 @@ module Devise
|
|
30
30
|
|
31
31
|
# set a fake datetime so a password change is needed
|
32
32
|
def need_change_password
|
33
|
-
if self.
|
34
|
-
self.password_changed_at = self.
|
33
|
+
if self.expire_password_after.is_a? Fixnum or self.expire_password_after.is_a? Float
|
34
|
+
self.password_changed_at = self.expire_password_after.ago
|
35
35
|
end
|
36
36
|
|
37
37
|
# is date not set it will set default to need set new password next login
|
@@ -39,6 +39,10 @@ module Devise
|
|
39
39
|
|
40
40
|
self.password_changed_at
|
41
41
|
end
|
42
|
+
|
43
|
+
def expire_password_after
|
44
|
+
self.class.expire_password_after
|
45
|
+
end
|
42
46
|
|
43
47
|
private
|
44
48
|
|