devise_security_extension 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -9,12 +9,13 @@ An enterprise security extension for devise, trying to meet industrial standard
9
9
  === Model modules
10
10
 
11
11
  * :password_expirable - passwords will expire after a configured time (and will need an update)
12
- * :secure_validatable - better way to validate model (email, stronger password validation). don't use with :validatable!
13
- * :password_archivable - save used password in an old_passwords table for history checks (don't be able to use a formerly used password)
14
- * :session_limitable - ensures, that there is only one session usable per account at once.
12
+ * :secure_validatable - better way to validate a model (email, stronger password validation). Don't use with :validatable!
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
+ * :session_limitable - ensures, that there is only one session usable per account at once
15
+ * :expirable - expires a user account after x days of inactivity (default 90 days)
15
16
 
16
17
  == Installation
17
- add to Gemfile
18
+ Add to Gemfile
18
19
  gem 'devise_security_extension'
19
20
 
20
21
  after bundle install
@@ -49,6 +50,10 @@ for :secure_validatable you need to add
49
50
 
50
51
  # captcha integration for unlock form
51
52
  # config.captcha_for_unlock = true
53
+
54
+ # ==> Configuration for :expirable
55
+ # Time period for account expiry from last_activity_at
56
+ config.expire_after = 90.days
52
57
  end
53
58
 
54
59
  == Captcha-Support
@@ -58,7 +63,7 @@ for :secure_validatable you need to add
58
63
  1. add to Gemfile "gem 'easy_captcha'"
59
64
  2. install easy_captcha "rails g easy_captcha:install"
60
65
  3. enable captcha - see "Configuration"
61
- 4. add captcha source in views of devise for each controller you have activate
66
+ 4. add captcha source in the devise views for each controller you have activated
62
67
 
63
68
  <p><%= captcha_tag %></p>
64
69
  <p><%= text_field_tag :captcha %></p>
@@ -87,6 +92,24 @@ That's it!
87
92
  t.session_limitable
88
93
  end
89
94
 
95
+ === Expirable
96
+ Devise 2.0 style migrations on new resource:
97
+
98
+ create_table :the_resources do |t|
99
+ ...
100
+ t.datetime :last_activity_at
101
+ t.datetime :expired_at
102
+ ...
103
+ end
104
+
105
+ Add module to existing resource with
106
+
107
+ change_table :the_resources do |t|
108
+ t.datetime :last_activity_at
109
+ t.datetime :expired_at
110
+ end
111
+
112
+
90
113
  == Requirements
91
114
 
92
115
  * devise (https://github.com/plataformatec/devise)
@@ -95,7 +118,7 @@ That's it!
95
118
 
96
119
  == Todo
97
120
 
98
- * disable inactive users after time
121
+ * see the github issues (feature requests)
99
122
 
100
123
  == History
101
124
  * 0.1 expire passwords
@@ -103,6 +126,7 @@ That's it!
103
126
  * 0.3 password archivable with validation
104
127
  * 0.4 captcha support for sign_up, sign_in, recover and unlock
105
128
  * 0.5 session_limitable module
129
+ * 0.6 expirable module
106
130
 
107
131
  == Maintainers
108
132
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.1
1
+ 0.6.0
@@ -10,3 +10,4 @@ de:
10
10
  change_required: "Ihr Passwort ist abgelaufen. Bitte vergeben sie ein neues Passwort!"
11
11
  failure:
12
12
  session_limited: 'Ihre Anmeldedaten wurden in einem anderen Browser genutzt. Bitte melden Sie sich erneut an, um in diesem Browser fortzufahren.'
13
+ expired: 'Ihr Account ist aufgrund zu langer Inaktiviät abgelaufen. Bitte kontaktieren Sie den Administrator.'
@@ -10,3 +10,4 @@ en:
10
10
  change_required: "Your password is expired. Please renew your password!"
11
11
  failure:
12
12
  session_limited: 'Your login credentials were used in another browser. Please sign in again to continue in this browser.'
13
+ expired: 'Your account has expired due to inactivity. Please contact the site administrator.'
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "devise_security_extension"
8
- s.version = "0.5.1"
8
+ s.version = "0.6.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 = "2011-12-27"
12
+ s.date = "2011-12-28"
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 = [
@@ -31,8 +31,10 @@ Gem::Specification.new do |s|
31
31
  "devise_security_extension.gemspec",
32
32
  "lib/devise_security_extension.rb",
33
33
  "lib/devise_security_extension/controllers/helpers.rb",
34
+ "lib/devise_security_extension/hooks/expirable.rb",
34
35
  "lib/devise_security_extension/hooks/password_expirable.rb",
35
36
  "lib/devise_security_extension/hooks/session_limitable.rb",
37
+ "lib/devise_security_extension/models/expirable.rb",
36
38
  "lib/devise_security_extension/models/old_password.rb",
37
39
  "lib/devise_security_extension/models/password_archivable.rb",
38
40
  "lib/devise_security_extension/models/password_expirable.rb",
@@ -43,7 +43,12 @@ module Devise # :nodoc:
43
43
  # captcha integration for unlock form
44
44
  mattr_accessor :captcha_for_unlock
45
45
  @@captcha_for_unlock = false
46
-
46
+
47
+ # Time period for account expiry from last_activity_at
48
+ mattr_accessor :expire_after
49
+ @@expire_after = 90.days
50
+ mattr_accessor :delete_expired_after
51
+ @@delete_expired_after = 90.days
47
52
  end
48
53
 
49
54
  # an security extension for devise
@@ -51,7 +56,7 @@ module DeviseSecurityExtension
51
56
  autoload :Schema, 'devise_security_extension/schema'
52
57
  autoload :Patches, 'devise_security_extension/patches'
53
58
 
54
- module Controllers # :nodoc:
59
+ module Controllers
55
60
  autoload :Helpers, 'devise_security_extension/controllers/helpers'
56
61
  end
57
62
  end
@@ -61,10 +66,10 @@ Devise.add_module :password_expirable, :controller => :password_expirable, :mode
61
66
  Devise.add_module :secure_validatable, :model => 'devise_security_extension/models/secure_validatable'
62
67
  Devise.add_module :password_archivable, :model => 'devise_security_extension/models/password_archivable'
63
68
  Devise.add_module :session_limitable, :model => 'devise_security_extension/models/session_limitable'
69
+ Devise.add_module :expirable, :model => 'devise_security_extension/models/expirable'
64
70
 
65
71
  # requires
66
72
  require 'devise_security_extension/routes'
67
73
  require 'devise_security_extension/rails'
68
74
  require 'devise_security_extension/orm/active_record'
69
75
  require 'devise_security_extension/models/old_password'
70
-
@@ -0,0 +1,10 @@
1
+ # Updates the last_activity_at fields from the record. Only when the user is active
2
+ # for authentication and authenticated.
3
+ # An expiry of the account is only checked on sign in OR on manually setting the
4
+ # expired_at to the past (see Devise::Models::Expirable for this)
5
+ Warden::Manager.after_set_user do |record, warden, options|
6
+ if record && record.respond_to?(:active_for_authentication?) && record.active_for_authentication? &&
7
+ warden.authenticated?(options[:scope]) && record.respond_to?(:update_last_activitiy!)
8
+ record.update_last_activitiy!
9
+ end
10
+ end
@@ -0,0 +1,124 @@
1
+ require 'devise_security_extension/hooks/expirable'
2
+
3
+ module Devise
4
+ module Models
5
+ # Deactivate the account after a configurable amount of time. To be able to
6
+ # tell, it tracks activity about your account with the following columns:
7
+ #
8
+ # * last_activity_at - A timestamp updated when the user requests a page (only signed in)
9
+ #
10
+ # == Options
11
+ # +:expire_after+ - Time interval to expire accounts after
12
+ #
13
+ # == Additions
14
+ # Best used with two cron jobs. One for expiring accounts after inactivity,
15
+ # and another, that deletes accounts, which have expired for a given amount
16
+ # of time (for example 90 days).
17
+ #
18
+ module Expirable
19
+ extend ActiveSupport::Concern
20
+
21
+ module InstanceMethods
22
+ # Updates +last_activity_at+, called from a Warden::Manager.after_set_user hook.
23
+ def update_last_activitiy!
24
+ self.last_activity_at = Time.now.utc
25
+ save(:validate => false)
26
+ end
27
+
28
+ # Tells if the account has expired
29
+ #
30
+ # @return [bool]
31
+ def expired?
32
+ # expired_at set (manually, via cron, etc.)
33
+ return self.expired_at < Time.now.utc unless self.expired_at.nil?
34
+ # if it is not set, check the last activity against configured expire_after time range
35
+ return self.last_activity_at < self.class.expire_after.ago unless self.last_activity_at.nil?
36
+ # if last_activity_at is nil as well, the user has to be 'fresh' and is therefore not expired
37
+ false
38
+ end
39
+
40
+ # Expire an account. This is for cron jobs and manually expiring of accounts.
41
+ #
42
+ # @example
43
+ # User.expire!
44
+ # User.expire! 1.week.from_now
45
+ # @note +expired_at+ can be in the future as well
46
+ def expire!(at = Time.now.utc)
47
+ self.expired_at = at
48
+ save(:validate => false)
49
+ end
50
+
51
+ # Overwrites active_for_authentication? from Devise::Models::Activatable
52
+ # for verifying whether a user is active to sign in or not. If the account
53
+ # is expired, it should never be allowed.
54
+ #
55
+ # @return [bool]
56
+ def active_for_authentication?
57
+ super && !self.expired?
58
+ end
59
+
60
+ # The message sym, if {#active_for_authentication?} returns +false+. E.g. needed
61
+ # for i18n.
62
+ def inactive_message
63
+ !self.expired? ? super : :expired
64
+ end
65
+
66
+ end
67
+
68
+ module ClassMethods
69
+ ::Devise::Models.config(self, :expire_after, :delete_expired_after)
70
+
71
+ # Sample method for daily cron to mark expired entries.
72
+ #
73
+ # @example You can overide this in your +resource+ model
74
+ # def self.mark_expired
75
+ # puts 'overwritten mark_expired'
76
+ # end
77
+ def mark_expired
78
+ all.each do |u|
79
+ u.expire! if u.expired? && u.expired_at.nil?
80
+ end
81
+ return
82
+ end
83
+
84
+ # Scope method to collect all expired users since +time+ ago
85
+ def expired_for(time = delete_expired_after)
86
+ where('expired_at < ?', time.ago)
87
+ end
88
+
89
+ # Sample method for daily cron to delete all expired entries after a
90
+ # given amount of +time+.
91
+ #
92
+ # In your overwritten method you can "blank out" the object instead of
93
+ # deleting it.
94
+ #
95
+ # *Word of warning*: You have to handle the dependent method
96
+ # on the +resource+ relations (+:destroy+ or +:nullify+) and catch this
97
+ # behavior (see http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Deleting+from+associations).
98
+ #
99
+ # @example
100
+ # Resource.delete_all_expired_for 90.days
101
+ # @example You can overide this in your +resource+ model
102
+ # def self.delete_all_expired_for(time = 90.days)
103
+ # puts 'overwritten delete call'
104
+ # end
105
+ # @example Overwritten version to blank out the object.
106
+ # def self.delete_all_expired_for(time = 90.days)
107
+ # expired_for(time).each do |u|
108
+ # u.update_attributes first_name: nil, last_name: nil
109
+ # end
110
+ # end
111
+ def delete_all_expired_for(time)
112
+ expired_for(time).delete_all
113
+ end
114
+
115
+ # Version of {#delete_all_expired_for} without arguments (uses
116
+ # configured +delete_expired_after+ default value).
117
+ # @see #delete_all_expired_for
118
+ def delete_all_expired
119
+ delete_all_expired_for(delete_expired_after)
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -26,8 +26,11 @@ module DeviseSecurityExtension
26
26
  " # captcha integration for sign in form\n" +
27
27
  " # config.captcha_for_sign_in = true\n\n" +
28
28
  " # captcha integration for unlock form\n" +
29
- " # config.captcha_for_unlock = true" +
30
- "\n", :before => /end[ |\n|]+\Z/
29
+ " # config.captcha_for_unlock = true\n\n" +
30
+ " # ==> Configuration for :expirable\n" +
31
+ " # Time period for account expiry from last_activity_at\n" +
32
+ " config.expire_after = 90.days\n" +
33
+ "", :before => /end[ |\n|]+\Z/
31
34
  end
32
35
 
33
36
  def copy_locale
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.5.1
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-12-27 00:00:00.000000000 Z
13
+ date: 2011-12-28 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rails
17
- requirement: &16685220 !ruby/object:Gem::Requirement
17
+ requirement: &21216320 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 3.1.1
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *16685220
25
+ version_requirements: *21216320
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: devise
28
- requirement: &16683580 !ruby/object:Gem::Requirement
28
+ requirement: &21234920 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *16683580
36
+ version_requirements: *21234920
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: rails_email_validator
39
- requirement: &16682360 !ruby/object:Gem::Requirement
39
+ requirement: &21250840 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: '0'
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *16682360
47
+ version_requirements: *21250840
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: easy_captcha
50
- requirement: &16681140 !ruby/object:Gem::Requirement
50
+ requirement: &21246220 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ! '>='
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: '0'
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *16681140
58
+ version_requirements: *21246220
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: bundler
61
- requirement: &16696860 !ruby/object:Gem::Requirement
61
+ requirement: &21243800 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ~>
@@ -66,10 +66,10 @@ dependencies:
66
66
  version: 1.0.0
67
67
  type: :development
68
68
  prerelease: false
69
- version_requirements: *16696860
69
+ version_requirements: *21243800
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: jeweler
72
- requirement: &16692820 !ruby/object:Gem::Requirement
72
+ requirement: &21268580 !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
75
  - - ~>
@@ -77,7 +77,7 @@ dependencies:
77
77
  version: 1.5.2
78
78
  type: :development
79
79
  prerelease: false
80
- version_requirements: *16692820
80
+ version_requirements: *21268580
81
81
  description: An enterprise security extension for devise, trying to meet industrial
82
82
  standard security demands for web applications.
83
83
  email: team@phatworx.de
@@ -101,8 +101,10 @@ files:
101
101
  - devise_security_extension.gemspec
102
102
  - lib/devise_security_extension.rb
103
103
  - lib/devise_security_extension/controllers/helpers.rb
104
+ - lib/devise_security_extension/hooks/expirable.rb
104
105
  - lib/devise_security_extension/hooks/password_expirable.rb
105
106
  - lib/devise_security_extension/hooks/session_limitable.rb
107
+ - lib/devise_security_extension/models/expirable.rb
106
108
  - lib/devise_security_extension/models/old_password.rb
107
109
  - lib/devise_security_extension/models/password_archivable.rb
108
110
  - lib/devise_security_extension/models/password_expirable.rb
@@ -132,7 +134,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
132
134
  version: '0'
133
135
  segments:
134
136
  - 0
135
- hash: 2266778624520241726
137
+ hash: -3314141216685045721
136
138
  required_rubygems_version: !ruby/object:Gem::Requirement
137
139
  none: false
138
140
  requirements: