has_secure_password 0.0.1

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.
@@ -0,0 +1,20 @@
1
+ Copyright 2013 Larry Halff (See also MIT_LICENCE_RAILS.)
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.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2004-2013 David Heinemeier Hansson
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.
@@ -0,0 +1,30 @@
1
+ # has_secure_password
2
+
3
+ This is a Rails 2 plugin for "has_secure_password" as currently provided in Rails 4.
4
+
5
+ All code was copied and modified from:
6
+
7
+ https://github.com/rails/rails/blob/master/activemodel/lib/active_model/secure_password.rb
8
+
9
+ and
10
+
11
+ https://github.com/rails/rails/tree/master/activemodel/test
12
+
13
+ ## Installation
14
+
15
+ Add the following to `environment.rb` or your Gemfile:
16
+
17
+ gem "has_secure_password"
18
+
19
+ ## Usage
20
+
21
+ Use as you would the Rails has_secure_password mixin:
22
+
23
+ class User
24
+ has_secure_password
25
+ end
26
+
27
+ ## Credits
28
+
29
+ Copyright (c) 2013 Larry Halff, released under the MIT license.
30
+ Most portions Copyright (c) 2004-2013 David Heinemeier Hansson, released under the MIT license.
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test has_secure_password plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for has_secure_password plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'has_secure_password'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
@@ -0,0 +1,118 @@
1
+ module ActiveRecord
2
+ module SecurePassword #:nodoc:
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ class << self; attr_accessor :min_cost; end
9
+ self.min_cost = false
10
+
11
+ module ClassMethods
12
+ # Adds methods to set and authenticate against a BCrypt password.
13
+ # This mechanism requires you to have a password_digest attribute.
14
+ #
15
+ # Validations for presence of password on create, confirmation of password
16
+ # (using a +password_confirmation+ attribute) are automatically added. If
17
+ # you wish to turn off validations, pass <tt>validations: false</tt> as an
18
+ # argument. You can add more validations by hand if need be.
19
+ #
20
+ # If you don't need the confirmation validation, just don't set any
21
+ # value to the password_confirmation attribute and the the validation
22
+ # will not be triggered.
23
+ #
24
+ # You need to add bcrypt-ruby (~> 3.0.0) to Gemfile to use #has_secure_password:
25
+ #
26
+ # gem 'bcrypt-ruby', '~> 3.0.0'
27
+ #
28
+ # Example using Active Record (which automatically includes ActiveRecord::SecurePassword):
29
+ #
30
+ # # Schema: User(name:string, password_digest:string)
31
+ # class User < ActiveRecord::Base
32
+ # has_secure_password
33
+ # end
34
+ #
35
+ # user = User.new(name: 'david', password: '', password_confirmation: 'nomatch')
36
+ # user.save # => false, password required
37
+ # user.password = 'mUc3m00RsqyRe'
38
+ # user.save # => false, confirmation doesn't match
39
+ # user.password_confirmation = 'mUc3m00RsqyRe'
40
+ # user.save # => true
41
+ # user.authenticate('notright') # => false
42
+ # user.authenticate('mUc3m00RsqyRe') # => user
43
+ # User.find_by(name: 'david').try(:authenticate, 'notright') # => false
44
+ # User.find_by(name: 'david').try(:authenticate, 'mUc3m00RsqyRe') # => user
45
+ def has_secure_password(options = {})
46
+ # Load bcrypt-ruby only when has_secure_password is used.
47
+ # This is to avoid ActiveRecord (and by extension the entire framework)
48
+ # being dependent on a binary library.
49
+ begin
50
+ gem 'bcrypt-ruby', '~> 3.0.0'
51
+ require 'bcrypt'
52
+ rescue LoadError
53
+ $stderr.puts "You don't have bcrypt-ruby installed in your application. Please add it to your Gemfile and run bundle install"
54
+ raise
55
+ end
56
+
57
+ attr_reader :password
58
+
59
+ include InstanceMethodsOnActivation
60
+
61
+ if options[:validations] == true || options[:validations].nil?
62
+ validates_confirmation_of :password, :if => lambda { |m| m.password.present? }
63
+ validates_presence_of :password, :on => :create
64
+ validates_presence_of :password_confirmation, :if => lambda { |m| m.password.present? }
65
+
66
+ before_create { |r| raise "Password digest missing on new record" if r.password_digest.blank? }
67
+ end
68
+
69
+ if respond_to?(:attributes_protected_by_default)
70
+ def self.attributes_protected_by_default #:nodoc:
71
+ super + ['password_digest']
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ module InstanceMethodsOnActivation
78
+ # Returns +self+ if the password is correct, otherwise +false+.
79
+ #
80
+ # class User < ActiveRecord::Base
81
+ # has_secure_password validations: false
82
+ # end
83
+ #
84
+ # user = User.new(name: 'david', password: 'mUc3m00RsqyRe')
85
+ # user.save
86
+ # user.authenticate('notright') # => false
87
+ # user.authenticate('mUc3m00RsqyRe') # => user
88
+ def authenticate(unencrypted_password)
89
+ BCrypt::Password.new(password_digest) == unencrypted_password && self
90
+ end
91
+
92
+ # Encrypts the password into the +password_digest+ attribute, only if the
93
+ # new password is not blank.
94
+ #
95
+ # class User < ActiveRecord::Base
96
+ # has_secure_password validations: false
97
+ # end
98
+ #
99
+ # user = User.new
100
+ # user.password = nil
101
+ # user.password_digest # => nil
102
+ # user.password = 'mUc3m00RsqyRe'
103
+ # user.password_digest # => "$2a$10$4LEA7r4YmNHtvlAvHhsYAeZmk/xeUVtMTYqwIvYY76EW5GUqDiP4."
104
+ def password=(unencrypted_password)
105
+ unless unencrypted_password.blank?
106
+ @password = unencrypted_password
107
+ cost = ActiveRecord::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine::DEFAULT_COST
108
+ self.password_digest = BCrypt::Password.create(unencrypted_password, :cost => cost)
109
+ end
110
+ end
111
+
112
+ def password_confirmation=(unencrypted_password)
113
+ @password_confirmation = unencrypted_password
114
+ end
115
+ end
116
+
117
+ end
118
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ module SecurePassword #:nodoc:
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ $:.unshift "#{File.dirname(__FILE__)}/lib"
2
+ require 'active_record/secure_password'
3
+
4
+ ActiveRecord::Base.send :include, ActiveRecord::SecurePassword
@@ -0,0 +1,4 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: secure_password.sqlite3.db
4
+
@@ -0,0 +1,7 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :mixins, :force => true do |t|
3
+ t.column :type, :string
4
+ t.column :password_digest, :string
5
+ end
6
+
7
+ end
@@ -0,0 +1,160 @@
1
+ require 'test/unit'
2
+
3
+ require 'rubygems'
4
+ require 'active_record'
5
+
6
+ $:.unshift File.dirname(__FILE__) + '/../lib'
7
+ require File.dirname(__FILE__) + '/../rails/init'
8
+
9
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
10
+ ActiveRecord::Schema.verbose = false
11
+
12
+ def setup_db(position_options = {})
13
+ # AR caches columns options like defaults etc. Clear them!
14
+ ActiveRecord::Schema.define(:version => 1) do
15
+ create_table :mixins do |t|
16
+ t.column :type, :string
17
+ t.column :password_digest, :string
18
+ end
19
+ end
20
+ end
21
+
22
+ def teardown_db
23
+ ActiveRecord::Base.connection.tables.each do |table|
24
+ ActiveRecord::Base.connection.drop_table(table)
25
+ end
26
+ end
27
+
28
+ class Mixin < ActiveRecord::Base
29
+ end
30
+
31
+ class User < Mixin
32
+ has_secure_password
33
+ attr_accessor :password_digest, :password_salt
34
+ end
35
+
36
+ class Visitor < Mixin
37
+ has_secure_password(:validations => false)
38
+ attr_accessor :password_digest, :password_confirmation
39
+ end
40
+
41
+ class OauthedUser < Mixin
42
+ has_secure_password(:validations => false)
43
+ attr_accessor :password_digest, :password_salt
44
+ end
45
+
46
+ class SecurePasswordTest < Test::Unit::TestCase
47
+ def setup
48
+ ActiveRecord::SecurePassword.min_cost = true
49
+
50
+ setup_db
51
+ @user = User.new
52
+ @visitor = Visitor.new
53
+ @oauthed_user = OauthedUser.new
54
+ end
55
+
56
+ def teardown
57
+ ActiveRecord::SecurePassword.min_cost = false
58
+ teardown_db
59
+ end
60
+
61
+ def test_blank_password
62
+ @user.password = @visitor.password = ''
63
+ assert !@user.valid?, 'user should be invalid'
64
+ assert @visitor.valid?, 'visitor should be valid'
65
+ end
66
+
67
+ def test_nil_password
68
+ @user.password = @visitor.password = nil
69
+ assert !@user.valid?, 'user should be invalid'
70
+ assert @visitor.valid?, 'visitor should be valid'
71
+ end
72
+
73
+ def test_blank_password_doesnt_override_previous_password
74
+ @user.password = 'test'
75
+ @user.password = ''
76
+ assert_equal @user.password, 'test'
77
+ end
78
+
79
+ def test_password_must_be_present
80
+ assert !@user.valid?
81
+ assert_equal 1, @user.errors.size
82
+ end
83
+
84
+ def test_match_confirmation
85
+ @user.password = @visitor.password = "thiswillberight"
86
+ @user.password_confirmation = @visitor.password_confirmation = "wrong"
87
+
88
+ assert !@user.valid?
89
+ assert @visitor.valid?
90
+
91
+ @user.password_confirmation = "thiswillberight"
92
+
93
+ assert @user.valid?
94
+ end
95
+
96
+ def test_authenticate
97
+ @user.password = "secret"
98
+
99
+ assert !@user.authenticate("wrong")
100
+ assert @user.authenticate("secret")
101
+ end
102
+
103
+ def test_user_should_not_be_created_with_blank_digest
104
+ @user.password = "tryapassword"
105
+ @user.password_confirmation = "tryapassword"
106
+ @user.password_digest = ""
107
+ assert_raise RuntimeError do
108
+ @user.save
109
+ end
110
+ @user.password = "supersecretpassword"
111
+ @user.password_confirmation = "supersecretpassword"
112
+ assert_nothing_raised do
113
+ @user.save
114
+ end
115
+ end
116
+
117
+ def test_oauthed_user_can_be_created_with_blank_digest
118
+ @oauthed_user.password = "tryapassword"
119
+ @oauthed_user.password_confirmation = "tryapassword"
120
+ @oauthed_user.password_digest = ""
121
+ assert_nothing_raised do
122
+ @oauthed_user.save
123
+ end
124
+ end
125
+
126
+ def test_password_digest_cost_defaults_to_bcrypt_default_cost_when_min_cost_is_false
127
+ ActiveRecord::SecurePassword.min_cost = false
128
+
129
+ @user.password = "secret"
130
+ assert_equal BCrypt::Engine::DEFAULT_COST, @user.password_digest.cost
131
+ end
132
+
133
+ def test_Password_digest_cost_can_be_set_to_bcrypt_min_cost_to_speed_up_tests
134
+ ActiveRecord::SecurePassword.min_cost = true
135
+
136
+ @user.password = "secret"
137
+ assert_equal BCrypt::Engine::MIN_COST, @user.password_digest.cost
138
+ end
139
+
140
+ def test_blank_password_confirmation_does_not_result_in_a_confirmation_error
141
+ @user.password = "supersecretpassword"
142
+ @user.password_confirmation = "supersecretpassword"
143
+ @user.save
144
+ @user_reloaded = User.find(@user.id)
145
+
146
+ assert @user_reloaded.password.nil?
147
+ assert @user_reloaded.password.nil?
148
+ assert @user_reloaded.valid?, "user should be valid"
149
+ end
150
+
151
+ def test will_not_save_if_confirmation_is_blank_but_password_is_not
152
+ @user.password = "password"
153
+ @user.password_confirmation = ""
154
+ assert !@user.valid?
155
+
156
+ @user.password_confirmation = "password"
157
+ assert @user.valid?
158
+ end
159
+
160
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_secure_password
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - lhalff
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2013-06-14 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rails
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">"
27
+ - !ruby/object:Gem::Version
28
+ hash: 1
29
+ segments:
30
+ - 2
31
+ - 1
32
+ version: "2.1"
33
+ - - <
34
+ - !ruby/object:Gem::Version
35
+ hash: 7
36
+ segments:
37
+ - 3
38
+ - 0
39
+ version: "3.0"
40
+ type: :runtime
41
+ version_requirements: *id001
42
+ - !ruby/object:Gem::Dependency
43
+ name: sqlite3
44
+ prerelease: false
45
+ requirement: &id002 !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ hash: 3
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ type: :development
55
+ version_requirements: *id002
56
+ description: Provides Rails 4 has_secure_password mixin for Rails 2. Requires Rails 2.1 or higher.
57
+ email:
58
+ - email@larryhalff.com
59
+ executables: []
60
+
61
+ extensions: []
62
+
63
+ extra_rdoc_files: []
64
+
65
+ files:
66
+ - rails/init.rb
67
+ - lib/active_record/secure_password.rb
68
+ - lib/version.rb
69
+ - MIT_LICENSE
70
+ - MIT_LICENSE_RAILS
71
+ - Rakefile
72
+ - README.md
73
+ - test/database.yml
74
+ - test/schema.rb
75
+ - test/secure_password_test.rb
76
+ homepage: https://github.com/lhalff/rails_secure_password
77
+ licenses: []
78
+
79
+ post_install_message:
80
+ rdoc_options: []
81
+
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ hash: 3
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ requirements: []
103
+
104
+ rubyforge_project:
105
+ rubygems_version: 1.8.25
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: has_secure_password for Rails 2
109
+ test_files:
110
+ - test/database.yml
111
+ - test/schema.rb
112
+ - test/secure_password_test.rb