vizjerai-devise_security_extension 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ gem "rails"
5
+ gem "devise"
6
+ gem "rails_email_validator"
7
+
8
+ # Add dependencies to develop your gem here.
9
+ # Include everything needed to run rake, tests, features, etc.
10
+ group :development do
11
+ gem "bundler", "~> 1.0.0"
12
+ gem "jeweler", "~> 1.5.2"
13
+ gem "rcov", ">= 0"
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,91 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionmailer (3.0.3)
6
+ actionpack (= 3.0.3)
7
+ mail (~> 2.2.9)
8
+ actionpack (3.0.3)
9
+ activemodel (= 3.0.3)
10
+ activesupport (= 3.0.3)
11
+ builder (~> 2.1.2)
12
+ erubis (~> 2.6.6)
13
+ i18n (~> 0.4)
14
+ rack (~> 1.2.1)
15
+ rack-mount (~> 0.6.13)
16
+ rack-test (~> 0.5.6)
17
+ tzinfo (~> 0.3.23)
18
+ activemodel (3.0.3)
19
+ activesupport (= 3.0.3)
20
+ builder (~> 2.1.2)
21
+ i18n (~> 0.4)
22
+ activerecord (3.0.3)
23
+ activemodel (= 3.0.3)
24
+ activesupport (= 3.0.3)
25
+ arel (~> 2.0.2)
26
+ tzinfo (~> 0.3.23)
27
+ activeresource (3.0.3)
28
+ activemodel (= 3.0.3)
29
+ activesupport (= 3.0.3)
30
+ activesupport (3.0.3)
31
+ arel (2.0.7)
32
+ bcrypt-ruby (2.1.4)
33
+ builder (2.1.2)
34
+ devise (1.1.5)
35
+ bcrypt-ruby (~> 2.1.2)
36
+ warden (~> 1.0.2)
37
+ erubis (2.6.6)
38
+ abstract (>= 1.0.0)
39
+ git (1.2.5)
40
+ i18n (0.5.0)
41
+ jeweler (1.5.2)
42
+ bundler (~> 1.0.0)
43
+ git (>= 1.2.5)
44
+ rake
45
+ mail (2.2.15)
46
+ activesupport (>= 2.3.6)
47
+ i18n (>= 0.4.0)
48
+ mime-types (~> 1.16)
49
+ treetop (~> 1.4.8)
50
+ mime-types (1.16)
51
+ polyglot (0.3.1)
52
+ rack (1.2.1)
53
+ rack-mount (0.6.13)
54
+ rack (>= 1.0.0)
55
+ rack-test (0.5.7)
56
+ rack (>= 1.0)
57
+ rails (3.0.3)
58
+ actionmailer (= 3.0.3)
59
+ actionpack (= 3.0.3)
60
+ activerecord (= 3.0.3)
61
+ activeresource (= 3.0.3)
62
+ activesupport (= 3.0.3)
63
+ bundler (~> 1.0)
64
+ railties (= 3.0.3)
65
+ rails_email_validator (0.1.1)
66
+ activemodel (>= 3.0.0)
67
+ activemodel (>= 3.0.0)
68
+ railties (3.0.3)
69
+ actionpack (= 3.0.3)
70
+ activesupport (= 3.0.3)
71
+ rake (>= 0.8.7)
72
+ thor (~> 0.14.4)
73
+ rake (0.8.7)
74
+ rcov (0.9.9)
75
+ thor (0.14.6)
76
+ treetop (1.4.9)
77
+ polyglot (>= 0.3.1)
78
+ tzinfo (0.3.24)
79
+ warden (1.0.3)
80
+ rack (>= 1.0.0)
81
+
82
+ PLATFORMS
83
+ ruby
84
+
85
+ DEPENDENCIES
86
+ bundler (~> 1.0.0)
87
+ devise
88
+ jeweler (~> 1.5.2)
89
+ rails
90
+ rails_email_validator
91
+ rcov
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Marco Scholl
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,90 @@
1
+ = devise_security_extension
2
+
3
+ an security extension for devise
4
+
5
+ == Installation
6
+ add to Gemfile
7
+ gem 'devise_security_extension'
8
+
9
+ after bundle execute
10
+ rails g devise_security_extension:install
11
+
12
+ == Configuration
13
+
14
+ Devise.setup do |config|
15
+ # Should the password expire (e.g 3.months)
16
+ # config.expire_password_after = 3.months
17
+
18
+ # Need 1 char of A-Z, a-z and 0-9
19
+ # config.password_regex = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/
20
+
21
+ # How often save old passwords in archive
22
+ # config.password_archiving_count = 5
23
+
24
+ # Deny old password (true, false, count)
25
+ # config.deny_old_passwords = true
26
+
27
+ == Model modules
28
+
29
+ * :password_expirable - activate that passwords will expire
30
+ * :secure_validatable - better way to validate model. don't use with :validatable!!!
31
+ * :password_archivable - save password in old_passwords for history checks
32
+
33
+ == Schema
34
+
35
+ === Password expirable
36
+
37
+ create_table :the_resources do |t|
38
+ t.password_expirable
39
+ end
40
+
41
+ === Password archive
42
+
43
+ create_table :old_passwords do |t|
44
+ t.password_archivable
45
+ end
46
+ add_index :old_passwords, [:password_archivable_type, :password_archivable_id], :name => :index_password_archivable
47
+
48
+ == Requirements
49
+
50
+ * devise (https://github.com/plataformatec/devise)
51
+ * Rails 3 (http://github.com/rails/rails)
52
+
53
+ == Features
54
+
55
+ * expire passwords (update password with current password)
56
+ * strong password validation
57
+ * save old passwords for check new passwords
58
+
59
+ == Todo
60
+
61
+ * easy_captcha for registration
62
+ * easy_captcha for password forgotten
63
+ * easy_captcha for unlock instructions
64
+ * disable inactive users after time
65
+
66
+ == History
67
+ * 0.1 expire passwords
68
+ * 0.2 strong password validation
69
+ * 0.3 password archivable with validation
70
+
71
+ == Maintainers
72
+
73
+ * Team Phatworx (http://github.com/phatworx)
74
+ * Marco Scholl (http://github.com/traxanos)
75
+
76
+ == Contributing to devise_security_extension
77
+
78
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
79
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
80
+ * Fork the project
81
+ * Start a feature/bugfix branch
82
+ * Commit and push until you are happy with your contribution
83
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
84
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
85
+
86
+ == Copyright
87
+
88
+ Copyright (c) 2011 Marco Scholl. See LICENSE.txt for
89
+ further details.
90
+
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ 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'
11
+
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 = "vizjerai-devise_security_extension"
16
+ gem.homepage = "http://github.com/phatworx/devise_security_extension"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{an security extension for devise}
19
+ gem.description = %Q{a gem for extend devise for more password security}
20
+ gem.email = "team@phatworx.de"
21
+ gem.authors = ["Marco Scholl"]
22
+ end
23
+ Jeweler::RubygemsDotOrgTasks.new
24
+
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
30
+ end
31
+
32
+ require 'rcov/rcovtask'
33
+ Rcov::RcovTask.new do |test|
34
+ test.libs << 'test'
35
+ test.pattern = 'test/**/test_*.rb'
36
+ test.verbose = true
37
+ end
38
+
39
+ task :default => :test
40
+
41
+ require 'rake/rdoctask'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "devise_security_extension #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.2
@@ -0,0 +1,35 @@
1
+ class Devise::PasswordExpiredController < ApplicationController
2
+ skip_before_filter :handle_password_change
3
+ prepend_before_filter :authenticate_scope!, :only => [:show, :update]
4
+ include Devise::Controllers::InternalHelpers
5
+
6
+ def show
7
+ if resource.need_change_password?
8
+ render_with_scope :show
9
+ else
10
+ redirect_to :root
11
+ end
12
+ end
13
+
14
+ def update
15
+ if resource.update_with_password(params[resource_name])
16
+ warden.session(scope)[:password_expired] = false
17
+ set_flash_message :notice, :updated
18
+ redirect_to stored_location_for(scope) || :root
19
+ else
20
+ clean_up_passwords(resource)
21
+ render_with_scope :show
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def scope
28
+ resource_name.to_sym
29
+ end
30
+
31
+ def authenticate_scope!
32
+ send(:"authenticate_#{resource_name}!")
33
+ self.resource = send("current_#{resource_name}")
34
+ end
35
+ end
@@ -0,0 +1,16 @@
1
+ <h2>Renew your password</h2>
2
+
3
+ <%= form_for(resource, :as => resource_name, :url => [resource_name, :password_expired], :html => { :method => :put }) do |f| %>
4
+ <%= devise_error_messages! %>
5
+
6
+ <p><%= f.label :current_password, "Current password" %><br />
7
+ <%= f.password_field :current_password %></p>
8
+
9
+ <p><%= f.label :password, "New password" %><br />
10
+ <%= f.password_field :password %></p>
11
+
12
+ <p><%= f.label :password_confirmation, "Confirm new password" %><br />
13
+ <%= f.password_field :password_confirmation %></p>
14
+
15
+ <p><%= f.submit "Change my password" %></p>
16
+ <% end %>
@@ -0,0 +1,8 @@
1
+ de:
2
+ errors:
3
+ messages:
4
+ taken_in_past: Ihr Passwort kann nicht wiederholt werden.
5
+ devise:
6
+ password_expired:
7
+ updated: "Das neue Passwort wurde übernommen"
8
+ change_required: Ihr Passwort ist abgelaufen. Bitte vergeben sie ein neues Passwort!
@@ -0,0 +1,8 @@
1
+ en:
2
+ errors:
3
+ messages:
4
+ taken_in_past: Your password cannot be repeated.
5
+ devise:
6
+ password_expired:
7
+ updated: Your new password is saved
8
+ change_required: Your password is expired. Please renew your password!
@@ -0,0 +1,44 @@
1
+ module DeviseSecurityExtension
2
+ module Controllers # :nodoc:
3
+ module Helpers # :nodoc:
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_filter :handle_password_change
8
+ end
9
+
10
+
11
+ # controller instance methods
12
+ module InstanceMethods
13
+ private
14
+
15
+ # lookup if an password change needed
16
+ def handle_password_change
17
+ Devise.mappings.keys.flatten.any? do |scope|
18
+ if signed_in? scope
19
+ if warden.session(scope)[:password_expired]
20
+ session["#{scope}_return_to"] = request.path if request.get?
21
+ redirect_for_password_change scope
22
+ break
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ # redirect for password update with alert message
29
+ def redirect_for_password_change(scope)
30
+ redirect_to change_password_required_path_for(scope), :alert => I18n.t('change_required', {:scope => 'devise.password_expired'})
31
+ end
32
+
33
+ # path for change password
34
+ def change_password_required_path_for(resource_or_scope = nil)
35
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
36
+ change_path = "#{scope}_password_expired_path"
37
+ send(change_path)
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,3 @@
1
+ Warden::Manager.after_authentication do |record, warden, options|
2
+ warden.session(options[:scope])[:password_expired] = record.need_change_password?
3
+ end
@@ -0,0 +1,3 @@
1
+ class OldPassword < ActiveRecord::Base
2
+ belongs_to :password_archivable, :polymorphic => true
3
+ end
@@ -0,0 +1,67 @@
1
+ module Devise # :nodoc:
2
+ module Models # :nodoc:
3
+
4
+ # PasswordArchivable
5
+ module PasswordArchivable
6
+
7
+ def self.included(base) # :nodoc:
8
+ base.extend ClassMethods
9
+
10
+ base.class_eval do
11
+ include InstanceMethods
12
+ has_many :old_passwords, :as => :password_archivable, :class_name => "OldPassword"
13
+ before_update :archive_password
14
+ validate :validate_password_archive
15
+ end
16
+ end
17
+
18
+ module InstanceMethods # :nodoc:
19
+
20
+ def validate_password_archive
21
+ self.errors.add(:password, :taken_in_past) if password_archive_included?
22
+ end
23
+
24
+ # validate is the password used in the past
25
+ def password_archive_included?
26
+ unless self.class.deny_old_passwords.is_a? Fixnum
27
+ if self.class.deny_old_passwords.is_a? TrueClass and self.class.password_archiving_count > 0
28
+ self.class.deny_old_passwords = self.class.password_archiving_count
29
+ else
30
+ self.class.deny_old_passwords = 0
31
+ end
32
+ end
33
+
34
+ if self.class.deny_old_passwords > 0 and not self.password.nil?
35
+ self.old_passwords.order('created_at DESC').limit(self.class.deny_old_passwords).limit(self.class.deny_old_passwords).each do |old_password|
36
+ dummy = self.class.new
37
+ dummy.encrypted_password = old_password.encrypted_password
38
+ dummy.password_salt = old_password.password_salt
39
+ return true if dummy.valid_password?(self.password)
40
+ end
41
+ end
42
+
43
+ false
44
+ end
45
+
46
+ private
47
+
48
+ # archive the last password before save and delete all to old passwords from archive
49
+ def archive_password
50
+ if self.encrypted_password_changed?
51
+ if self.class.password_archiving_count.to_i > 0
52
+ self.old_passwords.create! :encrypted_password => self.encrypted_password_change.first, :password_salt => self.password_salt_change.first
53
+ self.old_passwords.order('created_at DESC').offset(self.class.password_archiving_count).destroy_all
54
+ else
55
+ self.old_passwords.destroy_all
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ module ClassMethods #:nodoc:
62
+ ::Devise::Models.config(self, :password_archiving_count, :deny_old_passwords)
63
+ end
64
+ end
65
+ end
66
+
67
+ end
@@ -0,0 +1,63 @@
1
+ require 'devise_security_extension/hooks/password_expirable'
2
+
3
+ module Devise # :nodoc:
4
+ module Models # :nodoc:
5
+
6
+ # PasswordExpirable takes care of change password after
7
+ module PasswordExpirable
8
+
9
+ def self.included(base) # :nodoc:
10
+ base.extend ClassMethods
11
+
12
+ base.class_eval do
13
+ before_save :update_password_changed
14
+ include InstanceMethods
15
+ end
16
+ end
17
+
18
+ module InstanceMethods # :nodoc:
19
+
20
+ # is an password change required?
21
+ def need_change_password?
22
+ if self.class.expire_password_after.is_a? Fixnum
23
+ self.password_changed_at.nil? or self.password_changed_at < self.class.expire_password_after.ago
24
+ else
25
+ false
26
+ end
27
+ end
28
+
29
+ # set a fake datetime so a password change is needed and save the record
30
+ def need_change_password!
31
+ if self.class.expire_password_after.is_a? Fixnum
32
+ need_change_password
33
+ self.save(:validate => false)
34
+ end
35
+ end
36
+
37
+ # set a fake datetime so a password change is needed
38
+ def need_change_password
39
+ if self.class.expire_password_after.is_a? Fixnum
40
+ self.password_changed_at = self.class.expire_password_after.ago
41
+ end
42
+
43
+ # is date not set it will set default to need set new password next login
44
+ need_change_password if self.password_changed_at.nil?
45
+
46
+ self.password_changed_at
47
+ end
48
+
49
+ private
50
+
51
+ # is password changed then update password_cahanged_at
52
+ def update_password_changed
53
+ self.password_changed_at = Time.now if (self.new_record? or self.encrypted_password_changed?) and not self.password_changed_at_changed?
54
+ end
55
+ end
56
+
57
+ module ClassMethods #:nodoc:
58
+ ::Devise::Models.config(self, :expire_password_after)
59
+ end
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,32 @@
1
+ module Devise # :nodoc:
2
+ module Models # :nodoc:
3
+
4
+ # PasswordValidatable takes care of password validation for the password_regex
5
+ module PasswordValidatable
6
+
7
+ def self.include(base) # :nodoc:
8
+ base.extend ClassMethods
9
+ assert_password_validations_api!(base)
10
+
11
+ base.class_eval do
12
+ # validates password
13
+ validates :password, :format => password_regex, :if => :password_required?
14
+ end
15
+ end
16
+
17
+ def self.assert_password_validations_api!(base) # :nodoc:
18
+ raise "Could not use PasswordValidatable on #{base}" unless base.respond_to?(:validates)
19
+ end
20
+
21
+ protected
22
+
23
+ def password_required?
24
+ !persisted? || !password.nil? || !password_confirmation.nil?
25
+ end
26
+
27
+ module ClassMethods # :nodoc:
28
+ ::Devise::Models.config(self, :password_regex)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,55 @@
1
+ module Devise
2
+ module Models
3
+ # SecureValidatable creates better validations with more validation for security
4
+ #
5
+ # == Options
6
+ #
7
+ # SecureValidatable adds the following options to devise_for:
8
+ #
9
+ # * +email_regexp+: the regular expression used to validate e-mails;
10
+ # * +password_length+: a range expressing password length. Defaults from devise
11
+ # * +password_regex+: need strong password. Defaults to /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/
12
+ #
13
+ module SecureValidatable
14
+
15
+ def self.included(base)
16
+ base.extend ClassMethods
17
+ assert_secure_validations_api!(base)
18
+
19
+ base.class_eval do
20
+
21
+ # uniq login
22
+ validates authentication_keys[0], :uniqueness => {:scope => authentication_keys[1..-1]}#, :case_sensitive => case_insensitive_keys.exclude?(authentication_keys[0])
23
+
24
+ # validates email
25
+ validates :email, :presence => true, :if => :email_required?
26
+ validates :email, :email => true # use rails_email_validator
27
+
28
+ # validates password
29
+ validates :password, :presence => true, :length => password_length, :format => password_regex, :confirmation => true, :if => :password_required?
30
+ end
31
+ end
32
+
33
+ def self.assert_secure_validations_api!(base) #:nodoc:
34
+ raise "Could not use SecureValidatable on #{base}" unless base.respond_to?(:validates)
35
+ end
36
+
37
+ protected
38
+
39
+ # Checks whether a password is needed or not. For validations only.
40
+ # Passwords are always required if it's a new record, or if the password
41
+ # or confirmation are being set somewhere.
42
+ def password_required?
43
+ !persisted? || !password.nil? || !password_confirmation.nil?
44
+ end
45
+
46
+ def email_required?
47
+ true
48
+ end
49
+
50
+ module ClassMethods
51
+ Devise::Models.config(self, :password_regex, :password_length)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,20 @@
1
+ module DeviseSecurityExtension
2
+ module Orm
3
+ # This module contains some helpers and handle schema (migrations):
4
+ #
5
+ # create_table :accounts do |t|
6
+ # t.password_expirable
7
+ # end
8
+ #
9
+ module ActiveRecord
10
+ module Schema
11
+ include DeviseSecurityExtension::Schema
12
+
13
+
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ ActiveRecord::ConnectionAdapters::Table.send :include, DeviseSecurityExtension::Orm::ActiveRecord::Schema
20
+ ActiveRecord::ConnectionAdapters::TableDefinition.send :include, DeviseSecurityExtension::Orm::ActiveRecord::Schema
@@ -0,0 +1,7 @@
1
+ module DeviseSecurityExtension
2
+ class Engine < ::Rails::Engine # :nodoc:
3
+ ActiveSupport.on_load(:action_controller) do
4
+ include DeviseSecurityExtension::Controllers::Helpers
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ module ActionDispatch::Routing # :nodoc:
2
+ class Mapper # :nodoc:
3
+
4
+ protected
5
+
6
+ # route for handle expired passwords
7
+ def devise_password_expired(mapping, controllers)
8
+ resource :password_expired, :only => [:show, :update], :path => mapping.path_names[:password_expired], :controller => controllers[:password_expired]
9
+ end
10
+
11
+ end
12
+ end
13
+
@@ -0,0 +1,40 @@
1
+ module DeviseSecurityExtension
2
+ # add schema helper for migrations
3
+ module Schema
4
+ # Add password_changed_at columns in the resource's database table.
5
+ #
6
+ # Examples
7
+ #
8
+ # # For a new resource migration:
9
+ # create_table :the_resources do |t|
10
+ # t.password_expirable
11
+ # ...
12
+ # end
13
+ #
14
+ # # or if the resource's table already exists, define a migration and put this in:
15
+ # change_table :the_resources do |t|
16
+ # t.datetime :password_changed_at
17
+ # end
18
+ #
19
+ def password_expirable
20
+ apply_devise_schema :password_changed_at, DateTime
21
+ end
22
+
23
+ # Add password_archivable columns
24
+ #
25
+ # Examples
26
+ #
27
+ # create_table :old_passwords do
28
+ # t.password_archivable
29
+ # end
30
+ # add_index :old_passwords, [:password_archivable_type, :password_archivable_id], :name => :index_password_archivable
31
+ #
32
+ def password_archivable
33
+ apply_devise_schema :encrypted_password, String, :limit => 128, :null => false
34
+ apply_devise_schema :password_salt, String, :null => false
35
+ apply_devise_schema :password_archivable_id, Integer, :null => false
36
+ apply_devise_schema :password_archivable_type, String, :null => false
37
+ apply_devise_schema :created_at, DateTime
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,44 @@
1
+ #require 'rails/all'
2
+ require 'active_record/connection_adapters/abstract/schema_definitions'
3
+ require 'active_support/core_ext/integer'
4
+ require 'active_support/ordered_hash'
5
+ require 'active_support/concern'
6
+ require 'devise'
7
+
8
+ module Devise # :nodoc:
9
+
10
+ # Should the password expire (e.g 3.months)
11
+ mattr_accessor :expire_password_after
12
+ @@expire_password_after = 3.months
13
+
14
+ # Validate password for strongness
15
+ mattr_accessor :password_regex
16
+ @@password_regex = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/
17
+
18
+ # How often save old passwords in archive
19
+ mattr_accessor :password_archiving_count
20
+ @@password_archiving_count = 5
21
+
22
+ # Deny old password (true, false, count)
23
+ mattr_accessor :deny_old_passwords
24
+ @@deny_old_passwords = true
25
+ end
26
+
27
+ # an security extension for devise
28
+ module DeviseSecurityExtension
29
+ autoload :Schema, 'devise_security_extension/schema'
30
+
31
+ module Controllers # :nodoc:
32
+ autoload :Helpers, 'devise_security_extension/controllers/helpers'
33
+ end
34
+ end
35
+
36
+ Devise.add_module :password_expirable, :controller => :password_expirable, :model => 'devise_security_extension/models/password_expirable', :route => :password_expired
37
+ Devise.add_module :secure_validatable, :model => 'devise_security_extension/models/secure_validatable'
38
+ Devise.add_module :password_archivable, :model => 'devise_security_extension/models/password_archivable'
39
+ Devise.add_module :password_validatable, :model => 'devise_security_extension/models/password_validatable'
40
+
41
+ require 'devise_security_extension/routes'
42
+ require 'devise_security_extension/rails'
43
+ require 'devise_security_extension/orm/active_record'
44
+ require 'devise_security_extension/models/old_password'
@@ -0,0 +1,28 @@
1
+ module DeviseSecurityExtension
2
+ module Generators # :nodoc:
3
+ # Install Generator
4
+ class InstallGenerator < Rails::Generators::Base
5
+ source_root File.expand_path("../../templates", __FILE__)
6
+
7
+ desc "Install the devise security extension"
8
+
9
+ def add_configs
10
+ inject_into_file "config/initializers/devise.rb", "\n # ==> Security Extension\n # Configure security extension for devise\n\n" +
11
+ " # Should the password expire (e.g 3.months)\n" +
12
+ " # config.expire_password_after = false\n\n" +
13
+ " # Need 1 char of A-Z, a-z and 0-9\n" +
14
+ " # config.password_regex = /(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])/\n\n" +
15
+ " # How often save old passwords in archive\n" +
16
+ " # config.password_archiving_count = 5\n\n" +
17
+ " # Deny old password (true, false, count)\n" +
18
+ " # config.deny_old_passwords = true" +
19
+ "\n", :before => /end[ |\n|]+\Z/
20
+ end
21
+
22
+ def copy_locale
23
+ copy_file "../../../config/locales/en.yml", "config/locales/devise.security_extension.en.yml"
24
+ copy_file "../../../config/locales/de.yml", "config/locales/devise.security_extension.de.yml"
25
+ end
26
+ end
27
+ end
28
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ 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 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ require 'devise_security_extension'
15
+
16
+ class Test::Unit::TestCase
17
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestDeviseSecurityExtension < Test::Unit::TestCase
4
+ def test_something_for_real
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
@@ -0,0 +1,85 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{vizjerai-devise_security_extension}
8
+ s.version = "0.3.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Marco Scholl"]
12
+ s.date = %q{2011-03-07}
13
+ s.description = %q{a gem for extend devise for more password security}
14
+ s.email = %q{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/password_expirable.rb",
35
+ "lib/devise_security_extension/models/old_password.rb",
36
+ "lib/devise_security_extension/models/password_archivable.rb",
37
+ "lib/devise_security_extension/models/password_expirable.rb",
38
+ "lib/devise_security_extension/models/password_validatable.rb",
39
+ "lib/devise_security_extension/models/secure_validatable.rb",
40
+ "lib/devise_security_extension/orm/active_record.rb",
41
+ "lib/devise_security_extension/rails.rb",
42
+ "lib/devise_security_extension/routes.rb",
43
+ "lib/devise_security_extension/schema.rb",
44
+ "lib/generators/devise_security_extension/install_generator.rb",
45
+ "test/helper.rb",
46
+ "test/test_devise_security_extension.rb"
47
+ ]
48
+ s.homepage = %q{http://github.com/phatworx/devise_security_extension}
49
+ s.licenses = ["MIT"]
50
+ s.require_paths = ["lib"]
51
+ s.rubygems_version = %q{1.5.2}
52
+ s.summary = %q{an security extension for devise}
53
+ s.test_files = [
54
+ "test/helper.rb",
55
+ "test/test_devise_security_extension.rb"
56
+ ]
57
+
58
+ if s.respond_to? :specification_version then
59
+ s.specification_version = 3
60
+
61
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
62
+ s.add_runtime_dependency(%q<rails>, [">= 0"])
63
+ s.add_runtime_dependency(%q<devise>, [">= 0"])
64
+ s.add_runtime_dependency(%q<rails_email_validator>, [">= 0"])
65
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
66
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
67
+ s.add_development_dependency(%q<rcov>, [">= 0"])
68
+ else
69
+ s.add_dependency(%q<rails>, [">= 0"])
70
+ s.add_dependency(%q<devise>, [">= 0"])
71
+ s.add_dependency(%q<rails_email_validator>, [">= 0"])
72
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
73
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
74
+ s.add_dependency(%q<rcov>, [">= 0"])
75
+ end
76
+ else
77
+ s.add_dependency(%q<rails>, [">= 0"])
78
+ s.add_dependency(%q<devise>, [">= 0"])
79
+ s.add_dependency(%q<rails_email_validator>, [">= 0"])
80
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
81
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
82
+ s.add_dependency(%q<rcov>, [">= 0"])
83
+ end
84
+ end
85
+
metadata ADDED
@@ -0,0 +1,182 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vizjerai-devise_security_extension
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 3
9
+ - 2
10
+ version: 0.3.2
11
+ platform: ruby
12
+ authors:
13
+ - Marco Scholl
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-07 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ type: :runtime
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ name: rails
33
+ version_requirements: *id001
34
+ prerelease: false
35
+ - !ruby/object:Gem::Dependency
36
+ type: :runtime
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ name: devise
47
+ version_requirements: *id002
48
+ prerelease: false
49
+ - !ruby/object:Gem::Dependency
50
+ type: :runtime
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ name: rails_email_validator
61
+ version_requirements: *id003
62
+ prerelease: false
63
+ - !ruby/object:Gem::Dependency
64
+ type: :development
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
70
+ hash: 23
71
+ segments:
72
+ - 1
73
+ - 0
74
+ - 0
75
+ version: 1.0.0
76
+ name: bundler
77
+ version_requirements: *id004
78
+ prerelease: false
79
+ - !ruby/object:Gem::Dependency
80
+ type: :development
81
+ requirement: &id005 !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ~>
85
+ - !ruby/object:Gem::Version
86
+ hash: 7
87
+ segments:
88
+ - 1
89
+ - 5
90
+ - 2
91
+ version: 1.5.2
92
+ name: jeweler
93
+ version_requirements: *id005
94
+ prerelease: false
95
+ - !ruby/object:Gem::Dependency
96
+ type: :development
97
+ requirement: &id006 !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 3
103
+ segments:
104
+ - 0
105
+ version: "0"
106
+ name: rcov
107
+ version_requirements: *id006
108
+ prerelease: false
109
+ description: a gem for extend devise for more password security
110
+ email: team@phatworx.de
111
+ executables: []
112
+
113
+ extensions: []
114
+
115
+ extra_rdoc_files:
116
+ - LICENSE.txt
117
+ - README.rdoc
118
+ files:
119
+ - .document
120
+ - Gemfile
121
+ - Gemfile.lock
122
+ - LICENSE.txt
123
+ - README.rdoc
124
+ - Rakefile
125
+ - VERSION
126
+ - app/controllers/devise/password_expired_controller.rb
127
+ - app/views/devise/password_expired/show.html.erb
128
+ - config/locales/de.yml
129
+ - config/locales/en.yml
130
+ - lib/devise_security_extension.rb
131
+ - lib/devise_security_extension/controllers/helpers.rb
132
+ - lib/devise_security_extension/hooks/password_expirable.rb
133
+ - lib/devise_security_extension/models/old_password.rb
134
+ - lib/devise_security_extension/models/password_archivable.rb
135
+ - lib/devise_security_extension/models/password_expirable.rb
136
+ - lib/devise_security_extension/models/password_validatable.rb
137
+ - lib/devise_security_extension/models/secure_validatable.rb
138
+ - lib/devise_security_extension/orm/active_record.rb
139
+ - lib/devise_security_extension/rails.rb
140
+ - lib/devise_security_extension/routes.rb
141
+ - lib/devise_security_extension/schema.rb
142
+ - lib/generators/devise_security_extension/install_generator.rb
143
+ - test/helper.rb
144
+ - test/test_devise_security_extension.rb
145
+ - vizjerai-devise_security_extension.gemspec
146
+ has_rdoc: true
147
+ homepage: http://github.com/phatworx/devise_security_extension
148
+ licenses:
149
+ - MIT
150
+ post_install_message:
151
+ rdoc_options: []
152
+
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ hash: 3
161
+ segments:
162
+ - 0
163
+ version: "0"
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ none: false
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ hash: 3
170
+ segments:
171
+ - 0
172
+ version: "0"
173
+ requirements: []
174
+
175
+ rubyforge_project:
176
+ rubygems_version: 1.5.2
177
+ signing_key:
178
+ specification_version: 3
179
+ summary: an security extension for devise
180
+ test_files:
181
+ - test/helper.rb
182
+ - test/test_devise_security_extension.rb