has_secure_password 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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