devise_password_history 0.1.1 → 0.1.2

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.
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: []