devise_password_history 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .DS_Store
3
+ .bundle
4
+ Gemfile.lock
5
+ coverage/*
6
+ doc/*
7
+ log/*
8
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in devise_password_history.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Ryan Heath
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # DevisePasswordHistory
2
+
3
+ This extension provides password history support for Devise,
4
+ which allows you to prevent users from re-using the same password
5
+ they've used in the past (the actual limit is configurable).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'devise_password_history'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install devise_password_history
20
+
21
+ ## Usage
22
+
23
+ After installation, you need to "install" the extension into your
24
+ app via the following command:
25
+
26
+ $ bundle exec rails g devise_password_history:install
27
+
28
+ That generator will do three things:
29
+
30
+ 1. Modifies the `config/initializers/devise.rb` file with two new config options:
31
+ - `config.deny_old_passwords`: turns the validations on/off
32
+ - `config.password_history_count`: the threshold of how many passwords to store
33
+ 2. Creates an `OldPassword` polymorphic model in `app/models`
34
+ 3. Creates the migration for the `old_passwords` table
35
+
36
+ So once you run the generator, you just need to:
37
+
38
+ $ bundle exec rake db:migrate
39
+
40
+ Now the extension has been installed. To use it, you tell `devise` like with
41
+ any of the other extensions:
42
+
43
+ class User < ActiveRecord::Base
44
+ devise :database_authenticatable, :password_history
45
+ end
46
+
47
+ ## Contributing
48
+
49
+ 1. Fork it
50
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
51
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
52
+ 4. Push to the branch (`git push origin my-new-feature`)
53
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'devise_password_history/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "devise_password_history"
8
+ gem.version = DevisePasswordHistory::VERSION
9
+ gem.authors = ["Ryan Heath"]
10
+ gem.email = ["ryan@rpheath.com"]
11
+ gem.description = %q{Maintains password history and ensures old passwords aren't reused}
12
+ gem.summary = %q{Password history support for devise}
13
+ gem.homepage = ""
14
+
15
+ gem.add_dependency("devise", ["~> 2.2.0"])
16
+
17
+ gem.files = `git ls-files`.split($/)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^(test)/})
20
+ gem.require_paths = ["lib"]
21
+ end
@@ -0,0 +1,35 @@
1
+ require "rails/generators"
2
+ require "rails/generators/base"
3
+
4
+ module DevisePasswordHistory
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ source_root File.expand_path("../templates", __FILE__)
10
+
11
+ desc "Install the devise password history extension"
12
+
13
+ def self.next_migration_number(path)
14
+ Time.now.utc.strftime("%Y%m%d%H%M%S").to_i.to_s
15
+ end
16
+
17
+ def add_configs
18
+ inject_into_file "config/initializers/devise.rb", "\n # ==> Password History\n" +
19
+ " # How many old passwords to keep and validate against\n" +
20
+ " config.password_history_count = 8\n\n" +
21
+ " # Toggles behavior for Deny/Allow old passwords\n" +
22
+ " config.deny_old_passwords = true\n\n" +
23
+ "", :before => /end[\s|\n|]+\Z/
24
+ end
25
+
26
+ def copy_models
27
+ copy_file "models/old_password.rb", "app/models/old_password.rb"
28
+ end
29
+
30
+ def copy_migrations
31
+ migration_template "migrations/create_old_passwords.rb", "db/migrate/create_old_passwords.rb"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ class CreateOldPasswords < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :old_passwords do |t|
4
+ t.string :encrypted_password, :null => false
5
+ t.string :password_salt
6
+ t.string :password_history_type, :null => false
7
+ t.integer :password_history_id, :null => false
8
+ t.datetime :created_at
9
+ end
10
+ end
11
+
12
+ def self.down
13
+ drop_table :old_passwords
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ class OldPassword < ActiveRecord::Base
2
+ belongs_to :password_history, :polymorphic => true
3
+ end
@@ -0,0 +1,87 @@
1
+ require "active_support/concern"
2
+
3
+ module Devise
4
+ module Models
5
+ module PasswordHistory
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ Devise::Models.config(self, :password_history_count, :deny_old_passwords)
10
+ end
11
+
12
+ included do
13
+ has_many :old_passwords, :as => :password_history, :dependent => :destroy
14
+ before_update :store_old_password
15
+ validate :validate_old_passwords
16
+ end
17
+
18
+ # validation applied here
19
+ def validate_old_passwords
20
+ if self.encrypted_password_changed? && self.old_password_being_used?
21
+ self.errors.add(:password, "has been used already (you can't use your last #{self.class.password_history_count} passwords)")
22
+ end
23
+ end
24
+
25
+ def old_password_being_used?
26
+ if active_password_history_support?
27
+ if self.password.present?
28
+ # we need to go through each of the old passwords
29
+ # and check to see if the new password would authenticate
30
+ # the user (via valid_password?); if so that indicates
31
+ # the password has been used in the past
32
+ self.old_passwords.each do |old_pw|
33
+ temp = self.class.new
34
+ temp.encrypted_password = old_pw.encrypted_password
35
+ temp.password_salt = old_pw.password_salt
36
+
37
+ # return true if this password "passes" (authenticates)
38
+ return true if temp.valid_password?(self.password)
39
+ end
40
+ end
41
+ end
42
+
43
+ # otherwise, we're safe to let this
44
+ # password go through
45
+ false
46
+ end
47
+
48
+ protected
49
+ # make sure this is turned on in the Devise config
50
+ def should_deny_old_passwords?
51
+ self.class.deny_old_passwords.is_a?(TrueClass)
52
+ end
53
+
54
+ # count needs to be above zero
55
+ def valid_password_history_count?
56
+ self.class.password_history_count > 0
57
+ end
58
+
59
+ # is this extension configured properly (active)?
60
+ def active_password_history_support?
61
+ self.should_deny_old_passwords? && self.valid_password_history_count?
62
+ end
63
+
64
+ # stores the old password in the database;
65
+ # removes passwords past the configured threshold
66
+ def store_old_password
67
+ # only do this if the password has been modified
68
+ if self.encrypted_password_changed?
69
+ if self.valid_password_history_count?
70
+ # record the old password
71
+ old_pw = self.old_passwords.new
72
+ old_pw.encrypted_password = self.encrypted_password_change.first
73
+ old_pw.password_salt = self.password_salt_change.first if self.password_salt_change.present?
74
+ old_pw.save!
75
+
76
+ # wipe out passwords beyond our desired count
77
+ expired_passwords = self.old_passwords.order(:id).reverse_order.offset(self.class.password_history_count)
78
+ expired_passwords.destroy_all if expired_passwords.present?
79
+ else
80
+ # if the count is zero, no password history
81
+ self.old_passwords.destroy_all
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,3 @@
1
+ module DevisePasswordHistory
2
+ VERSION = "0.1.2"
3
+ end
@@ -0,0 +1,22 @@
1
+ require "active_support/concern"
2
+
3
+ require "devise"
4
+ require "devise_password_history/version"
5
+ require "devise_password_history/generators/install_generator"
6
+
7
+ # adds out config options to Devise
8
+ # (see config/initializers/devise.rb)
9
+ module Devise
10
+ # How many old passwords to save
11
+ mattr_accessor :password_history_count
12
+ @@password_history_count = 8
13
+
14
+ # Toggles the behavior of denying/allowing old passwords
15
+ mattr_accessor :deny_old_passwords
16
+ @@deny_old_passwords = true
17
+ end
18
+
19
+ module DevisePasswordHistory; end
20
+
21
+ # makes this module available to the `devise` command
22
+ Devise.add_module :password_history, :model => "devise_password_history/models/password_history"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise_password_history
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 31
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 1
10
- version: 0.1.1
9
+ - 2
10
+ version: 0.1.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ryan Heath
@@ -43,8 +43,19 @@ extensions: []
43
43
 
44
44
  extra_rdoc_files: []
45
45
 
46
- files: []
47
-
46
+ files:
47
+ - .gitignore
48
+ - Gemfile
49
+ - LICENSE.txt
50
+ - README.md
51
+ - Rakefile
52
+ - devise_password_history.gemspec
53
+ - lib/devise_password_history.rb
54
+ - lib/devise_password_history/generators/install_generator.rb
55
+ - lib/devise_password_history/generators/templates/migrations/create_old_passwords.rb
56
+ - lib/devise_password_history/generators/templates/models/old_password.rb
57
+ - lib/devise_password_history/models/password_history.rb
58
+ - lib/devise_password_history/version.rb
48
59
  has_rdoc: true
49
60
  homepage: ""
50
61
  licenses: []