devise_security_extension 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +49 -8
- data/VERSION +1 -1
- data/devise_security_extension.gemspec +7 -2
- data/lib/devise_security_extension.rb +22 -5
- data/lib/devise_security_extension/hooks/expirable.rb +2 -2
- data/lib/devise_security_extension/models/expirable.rb +1 -1
- data/lib/devise_security_extension/models/security_question.rb +3 -0
- data/lib/devise_security_extension/models/security_questionable.rb +15 -0
- data/lib/devise_security_extension/patches.rb +12 -4
- data/lib/devise_security_extension/patches/confirmations_controller_security_question.rb +25 -0
- data/lib/devise_security_extension/patches/passwords_controller_security_question.rb +24 -0
- data/lib/devise_security_extension/patches/unlocks_controller_security_question.rb +24 -0
- metadata +8 -3
data/README.rdoc
CHANGED
@@ -5,7 +5,7 @@ An enterprise security extension for devise, trying to meet industrial standard
|
|
5
5
|
== Features
|
6
6
|
|
7
7
|
* captcha support for sign_up, sign_in, recover and unlock (to make automated mass creation and brute forcing of accounts harder)
|
8
|
-
|
8
|
+
|
9
9
|
=== Model modules
|
10
10
|
|
11
11
|
* :password_expirable - passwords will expire after a configured time (and will need an update)
|
@@ -13,6 +13,7 @@ An enterprise security extension for devise, trying to meet industrial standard
|
|
13
13
|
* :password_archivable - save used passwords in an old_passwords table for history checks (don't be able to use a formerly used password)
|
14
14
|
* :session_limitable - ensures, that there is only one session usable per account at once
|
15
15
|
* :expirable - expires a user account after x days of inactivity (default 90 days)
|
16
|
+
* :security_questionable - as accessible substitution for captchas (security question with captcha fallback)
|
16
17
|
|
17
18
|
== Installation
|
18
19
|
Add to Gemfile
|
@@ -22,7 +23,7 @@ after bundle install
|
|
22
23
|
rails g devise_security_extension:install
|
23
24
|
|
24
25
|
for :secure_validatable you need to add
|
25
|
-
gem 'rails_email_validator'
|
26
|
+
gem 'rails_email_validator'
|
26
27
|
|
27
28
|
== Configuration
|
28
29
|
|
@@ -39,23 +40,35 @@ for :secure_validatable you need to add
|
|
39
40
|
# Deny old password (true, false, count)
|
40
41
|
# config.deny_old_passwords = true
|
41
42
|
|
42
|
-
# captcha integration for recover form
|
43
|
+
# captcha integration for recover form
|
43
44
|
# config.captcha_for_recover = true
|
44
|
-
|
45
|
+
|
45
46
|
# captcha integration for sign up form
|
46
47
|
# config.captcha_for_sign_up = true
|
47
|
-
|
48
|
+
|
48
49
|
# captcha integration for sign in form
|
49
50
|
# config.captcha_for_sign_in = true
|
50
|
-
|
51
|
+
|
51
52
|
# captcha integration for unlock form
|
52
53
|
# config.captcha_for_unlock = true
|
53
54
|
|
55
|
+
# security_question integration for recover form
|
56
|
+
# this automatically enables captchas (captcha_for_recover, as fallback)
|
57
|
+
# config.security_question_for_recover = false
|
58
|
+
|
59
|
+
# security_question integration for unlock form
|
60
|
+
# this automatically enables captchas (captcha_for_unlock, as fallback)
|
61
|
+
# config.security_question_for_unlock = false
|
62
|
+
|
63
|
+
# security_question integration for confirmation form
|
64
|
+
# this automatically enables captchas (captcha_for_confirmation, as fallback)
|
65
|
+
# config.security_question_for_confirmation = false
|
66
|
+
|
54
67
|
# ==> Configuration for :expirable
|
55
68
|
# Time period for account expiry from last_activity_at
|
56
69
|
config.expire_after = 90.days
|
57
70
|
end
|
58
|
-
|
71
|
+
|
59
72
|
== Captcha-Support
|
60
73
|
|
61
74
|
=== Installation
|
@@ -112,6 +125,33 @@ That's it!
|
|
112
125
|
add_index :the_resources, :last_activity_at
|
113
126
|
add_index :the_resources, :expired_at
|
114
127
|
|
128
|
+
=== Security questionable
|
129
|
+
|
130
|
+
create_table :security_questions do |t|
|
131
|
+
t.string :locale, :null => false
|
132
|
+
t.string :name, :null => false
|
133
|
+
end
|
134
|
+
|
135
|
+
SecurityQuestion.create! locale: :de, name: 'Wie lautet der Geburstname Ihrer Mutter?'
|
136
|
+
SecurityQuestion.create! locale: :de, name: 'Wo sind sie geboren?'
|
137
|
+
SecurityQuestion.create! locale: :de, name: 'Wie lautet der Name Ihres ersten Haustieres?'
|
138
|
+
SecurityQuestion.create! locale: :de, name: 'Was ist Ihr Lieblingsfilm?'
|
139
|
+
SecurityQuestion.create! locale: :de, name: 'Was ist Ihr Lieblingsbuch?'
|
140
|
+
SecurityQuestion.create! locale: :de, name: 'Was ist Ihr Lieblingstier?'
|
141
|
+
SecurityQuestion.create! locale: :de, name: 'Was ist Ihr Lieblings-Reiseland?'
|
142
|
+
|
143
|
+
add_column :the_resources, :security_question_id, :integer
|
144
|
+
add_column :the_resources, :security_question_answer, :string
|
145
|
+
|
146
|
+
or
|
147
|
+
|
148
|
+
create_table :the_resources do |t|
|
149
|
+
# other devise fields
|
150
|
+
|
151
|
+
t.integer :security_question_id
|
152
|
+
t.string :security_question_answer
|
153
|
+
end
|
154
|
+
|
115
155
|
== Requirements
|
116
156
|
|
117
157
|
* devise (https://github.com/plataformatec/devise)
|
@@ -129,6 +169,7 @@ That's it!
|
|
129
169
|
* 0.4 captcha support for sign_up, sign_in, recover and unlock
|
130
170
|
* 0.5 session_limitable module
|
131
171
|
* 0.6 expirable module
|
172
|
+
* 0.7 security questionable module for recover and unlock
|
132
173
|
|
133
174
|
== Maintainers
|
134
175
|
|
@@ -138,7 +179,7 @@ That's it!
|
|
138
179
|
* Christoph Chilian (http://github.com/cc-web)
|
139
180
|
|
140
181
|
== Contributing to devise_security_extension
|
141
|
-
|
182
|
+
|
142
183
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
143
184
|
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
144
185
|
* Fork the project
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "devise_security_extension"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.7.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Marco Scholl", "Alexander Dreher"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-10-11"
|
13
13
|
s.description = "An enterprise security extension for devise, trying to meet industrial standard security demands for web applications."
|
14
14
|
s.email = "team@phatworx.de"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -39,14 +39,19 @@ Gem::Specification.new do |s|
|
|
39
39
|
"lib/devise_security_extension/models/password_archivable.rb",
|
40
40
|
"lib/devise_security_extension/models/password_expirable.rb",
|
41
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",
|
42
44
|
"lib/devise_security_extension/models/session_limitable.rb",
|
43
45
|
"lib/devise_security_extension/orm/active_record.rb",
|
44
46
|
"lib/devise_security_extension/patches.rb",
|
45
47
|
"lib/devise_security_extension/patches/confirmations_controller_captcha.rb",
|
48
|
+
"lib/devise_security_extension/patches/confirmations_controller_security_question.rb",
|
46
49
|
"lib/devise_security_extension/patches/passwords_controller_captcha.rb",
|
50
|
+
"lib/devise_security_extension/patches/passwords_controller_security_question.rb",
|
47
51
|
"lib/devise_security_extension/patches/registrations_controller_captcha.rb",
|
48
52
|
"lib/devise_security_extension/patches/sessions_controller_captcha.rb",
|
49
53
|
"lib/devise_security_extension/patches/unlocks_controller_captcha.rb",
|
54
|
+
"lib/devise_security_extension/patches/unlocks_controller_security_question.rb",
|
50
55
|
"lib/devise_security_extension/rails.rb",
|
51
56
|
"lib/devise_security_extension/routes.rb",
|
52
57
|
"lib/devise_security_extension/schema.rb",
|
@@ -27,23 +27,38 @@ module Devise
|
|
27
27
|
# dependency: need an email validator like rails_email_validator
|
28
28
|
mattr_accessor :email_validation
|
29
29
|
@@email_validation = true
|
30
|
-
|
30
|
+
|
31
31
|
# captcha integration for recover form
|
32
32
|
mattr_accessor :captcha_for_recover
|
33
33
|
@@captcha_for_recover = false
|
34
|
-
|
34
|
+
|
35
35
|
# captcha integration for sign up form
|
36
36
|
mattr_accessor :captcha_for_sign_up
|
37
37
|
@@captcha_for_sign_up = false
|
38
|
-
|
38
|
+
|
39
39
|
# captcha integration for sign in form
|
40
40
|
mattr_accessor :captcha_for_sign_in
|
41
41
|
@@captcha_for_sign_in = false
|
42
|
-
|
42
|
+
|
43
43
|
# captcha integration for unlock form
|
44
44
|
mattr_accessor :captcha_for_unlock
|
45
45
|
@@captcha_for_unlock = false
|
46
|
-
|
46
|
+
|
47
|
+
# security_question integration for recover form
|
48
|
+
# this automatically enables captchas (captcha_for_recover, as fallback)
|
49
|
+
mattr_accessor :security_question_for_recover
|
50
|
+
@@security_question_for_recover = false
|
51
|
+
|
52
|
+
# security_question integration for unlock form
|
53
|
+
# this automatically enables captchas (captcha_for_unlock, as fallback)
|
54
|
+
mattr_accessor :security_question_for_unlock
|
55
|
+
@@security_question_for_unlock = false
|
56
|
+
|
57
|
+
# security_question integration for confirmation form
|
58
|
+
# this automatically enables captchas (captcha_for_confirmation, as fallback)
|
59
|
+
mattr_accessor :security_question_for_confirmation
|
60
|
+
@@security_question_for_confirmation = false
|
61
|
+
|
47
62
|
# captcha integration for confirmation form
|
48
63
|
mattr_accessor :captcha_for_confirmation
|
49
64
|
@@captcha_for_confirmation = false
|
@@ -71,9 +86,11 @@ Devise.add_module :secure_validatable, :model => 'devise_security_extension/mode
|
|
71
86
|
Devise.add_module :password_archivable, :model => 'devise_security_extension/models/password_archivable'
|
72
87
|
Devise.add_module :session_limitable, :model => 'devise_security_extension/models/session_limitable'
|
73
88
|
Devise.add_module :expirable, :model => 'devise_security_extension/models/expirable'
|
89
|
+
Devise.add_module :security_questionable, :model => 'devise_security_extension/models/security_questionable'
|
74
90
|
|
75
91
|
# requires
|
76
92
|
require 'devise_security_extension/routes'
|
77
93
|
require 'devise_security_extension/rails'
|
78
94
|
require 'devise_security_extension/orm/active_record'
|
79
95
|
require 'devise_security_extension/models/old_password'
|
96
|
+
require 'devise_security_extension/models/security_question'
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# expired_at to the past (see Devise::Models::Expirable for this)
|
5
5
|
Warden::Manager.after_set_user do |record, warden, options|
|
6
6
|
if record && record.respond_to?(:active_for_authentication?) && record.active_for_authentication? &&
|
7
|
-
warden.authenticated?(options[:scope]) && record.respond_to?(:
|
8
|
-
record.
|
7
|
+
warden.authenticated?(options[:scope]) && record.respond_to?(:update_last_activity!)
|
8
|
+
record.update_last_activity!
|
9
9
|
end
|
10
10
|
end
|
@@ -19,7 +19,7 @@ module Devise
|
|
19
19
|
extend ActiveSupport::Concern
|
20
20
|
|
21
21
|
# Updates +last_activity_at+, called from a Warden::Manager.after_set_user hook.
|
22
|
-
def
|
22
|
+
def update_last_activity!
|
23
23
|
self.last_activity_at = Time.now.utc
|
24
24
|
save(:validate => false)
|
25
25
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Devise
|
2
|
+
module Models
|
3
|
+
# SecurityQuestionable is an accessible add-on for visually handicapped people,
|
4
|
+
# to ship around the captcha with screenreader compatibility.
|
5
|
+
#
|
6
|
+
# You need to add two text_field_tags to the associated form:
|
7
|
+
# :security_question_answer and :captcha
|
8
|
+
#
|
9
|
+
# And add the security_question to the register/edit form.
|
10
|
+
module SecurityQuestionable
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,18 +1,26 @@
|
|
1
1
|
module DeviseSecurityExtension
|
2
2
|
module Patches
|
3
|
+
autoload :UnlocksControllerSecurityQuestion, 'devise_security_extension/patches/unlocks_controller_security_question'
|
4
|
+
autoload :PasswordsControllerSecurityQuestion, 'devise_security_extension/patches/passwords_controller_security_question'
|
5
|
+
autoload :ConfirmationsControllerSecurityQuestion, 'devise_security_extension/patches/confirmations_controller_security_question'
|
3
6
|
autoload :UnlocksControllerCaptcha, 'devise_security_extension/patches/unlocks_controller_captcha'
|
4
7
|
autoload :PasswordsControllerCaptcha, 'devise_security_extension/patches/passwords_controller_captcha'
|
5
8
|
autoload :SessionsControllerCaptcha, 'devise_security_extension/patches/sessions_controller_captcha'
|
6
9
|
autoload :RegistrationsControllerCaptcha, 'devise_security_extension/patches/registrations_controller_captcha'
|
7
10
|
autoload :ConfirmationsControllerCaptcha, 'devise_security_extension/patches/confirmations_controller_captcha'
|
8
|
-
|
11
|
+
|
9
12
|
class << self
|
10
13
|
def apply
|
11
|
-
Devise::PasswordsController.send(:include, Patches::PasswordsControllerCaptcha) if Devise.captcha_for_recover
|
12
|
-
Devise::UnlocksController.send(:include, Patches::UnlocksControllerCaptcha) if Devise.captcha_for_unlock
|
14
|
+
Devise::PasswordsController.send(:include, Patches::PasswordsControllerCaptcha) if Devise.captcha_for_recover or Devise.security_question_for_recover
|
15
|
+
Devise::UnlocksController.send(:include, Patches::UnlocksControllerCaptcha) if Devise.captcha_for_unlock or Devise.security_question_for_unlock
|
16
|
+
Devise::ConfirmationsController.send(:include, Patches::ConfirmationsControllerCaptcha) if Devise.captcha_for_confirmation
|
17
|
+
|
18
|
+
Devise::PasswordsController.send(:include, Patches::PasswordsControllerSecurityQuestion) if Devise.security_question_for_recover
|
19
|
+
Devise::UnlocksController.send(:include, Patches::UnlocksControllerSecurityQuestion) if Devise.security_question_for_unlock
|
20
|
+
Devise::ConfirmationsController.send(:include, Patches::ConfirmationsControllerSecurityQuestion) if Devise.security_question_for_confirmation
|
21
|
+
|
13
22
|
Devise::RegistrationsController.send(:include, Patches::RegistrationsControllerCaptcha) if Devise.captcha_for_sign_up
|
14
23
|
Devise::SessionsController.send(:include, Patches::SessionsControllerCaptcha) if Devise.captcha_for_sign_in
|
15
|
-
Devise::ConfirmationsController.send(:include, Patches::ConfirmationsControllerCaptcha) if Devise.captcha_for_confirmation
|
16
24
|
end
|
17
25
|
end
|
18
26
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module DeviseSecurityExtension::Patches
|
2
|
+
module ConfirmationsControllerSecurityQuestion
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
included do
|
5
|
+
define_method :create do
|
6
|
+
# only find via email, not login
|
7
|
+
resource = resource_class.find_or_initialize_with_error_by(:email, params[resource_name][:email], :not_found)
|
8
|
+
|
9
|
+
if valid_captcha? params[:captcha] or
|
10
|
+
(resource.security_question_answer.present? and resource.security_question_answer == params[:security_question_answer])
|
11
|
+
self.resource = resource_class.send_confirmation_instructions(params[resource_name])
|
12
|
+
|
13
|
+
if successfully_sent?(resource)
|
14
|
+
respond_with({}, :location => after_resending_confirmation_instructions_path_for(resource_name))
|
15
|
+
else
|
16
|
+
respond_with(resource)
|
17
|
+
end
|
18
|
+
else
|
19
|
+
flash[:alert] = t('devise.invalid_security_question') if is_navigational_format?
|
20
|
+
respond_with({}, :location => new_confirmation_path(resource_name))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module DeviseSecurityExtension::Patches
|
2
|
+
module PasswordsControllerSecurityQuestion
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
included do
|
5
|
+
define_method :create do
|
6
|
+
# only find via email, not login
|
7
|
+
resource = resource_class.find_or_initialize_with_error_by(:email, params[resource_name][:email], :not_found)
|
8
|
+
|
9
|
+
if valid_captcha? params[:captcha] or
|
10
|
+
(resource.security_question_answer.present? and resource.security_question_answer == params[:security_question_answer])
|
11
|
+
self.resource = resource_class.send_reset_password_instructions(params[resource_name])
|
12
|
+
if successfully_sent?(resource)
|
13
|
+
respond_with({}, :location => new_session_path(resource_name))
|
14
|
+
else
|
15
|
+
respond_with(resource)
|
16
|
+
end
|
17
|
+
else
|
18
|
+
flash[:alert] = t('devise.invalid_security_question') if is_navigational_format?
|
19
|
+
respond_with({}, :location => new_password_path(resource_name))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module DeviseSecurityExtension::Patches
|
2
|
+
module UnlocksControllerSecurityQuestion
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
included do
|
5
|
+
define_method :create do
|
6
|
+
# only find via email, not login
|
7
|
+
resource = resource_class.find_or_initialize_with_error_by(:email, params[resource_name][:email], :not_found)
|
8
|
+
|
9
|
+
if valid_captcha? params[:captcha] or
|
10
|
+
(resource.security_question_answer.present? and resource.security_question_answer == params[:security_question_answer])
|
11
|
+
self.resource = resource_class.send_unlock_instructions(params[resource_name])
|
12
|
+
if successfully_sent?(resource)
|
13
|
+
respond_with({}, :location => new_session_path(resource_name))
|
14
|
+
else
|
15
|
+
respond_with(resource)
|
16
|
+
end
|
17
|
+
else
|
18
|
+
flash[:alert] = t('devise.invalid_security_question') if is_navigational_format?
|
19
|
+
respond_with({}, :location => new_unlock_path(resource_name))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: devise_security_extension
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-10-11 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
@@ -139,14 +139,19 @@ files:
|
|
139
139
|
- lib/devise_security_extension/models/password_archivable.rb
|
140
140
|
- lib/devise_security_extension/models/password_expirable.rb
|
141
141
|
- lib/devise_security_extension/models/secure_validatable.rb
|
142
|
+
- lib/devise_security_extension/models/security_question.rb
|
143
|
+
- lib/devise_security_extension/models/security_questionable.rb
|
142
144
|
- lib/devise_security_extension/models/session_limitable.rb
|
143
145
|
- lib/devise_security_extension/orm/active_record.rb
|
144
146
|
- lib/devise_security_extension/patches.rb
|
145
147
|
- lib/devise_security_extension/patches/confirmations_controller_captcha.rb
|
148
|
+
- lib/devise_security_extension/patches/confirmations_controller_security_question.rb
|
146
149
|
- lib/devise_security_extension/patches/passwords_controller_captcha.rb
|
150
|
+
- lib/devise_security_extension/patches/passwords_controller_security_question.rb
|
147
151
|
- lib/devise_security_extension/patches/registrations_controller_captcha.rb
|
148
152
|
- lib/devise_security_extension/patches/sessions_controller_captcha.rb
|
149
153
|
- lib/devise_security_extension/patches/unlocks_controller_captcha.rb
|
154
|
+
- lib/devise_security_extension/patches/unlocks_controller_security_question.rb
|
150
155
|
- lib/devise_security_extension/rails.rb
|
151
156
|
- lib/devise_security_extension/routes.rb
|
152
157
|
- lib/devise_security_extension/schema.rb
|
@@ -168,7 +173,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
168
173
|
version: '0'
|
169
174
|
segments:
|
170
175
|
- 0
|
171
|
-
hash:
|
176
|
+
hash: -2473272848721880190
|
172
177
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
173
178
|
none: false
|
174
179
|
requirements:
|