mini_auth 0.1.0 → 0.2.0.beta

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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## 0.2.0 (2011-12-19)
2
+
3
+ * Two attributes `changing_password` and `setting_password` are introduced.
4
+ * When `changing_password` is set to `true`, users should enter `current_password`
5
+ and `new_password` to change their password. If they enter wrong `current_passwod`,
6
+ validation on `current_password` fails.
7
+ * When `setting_password` is set to `true`, users should enter not-blank `password`.
8
+ Unlike the version 0.1.0, nil password is not accepted.
9
+ * When neither of these two attributes is set to `true`, users can't set or change
10
+ their password. If the `password` parameter is passed to the object,
11
+ it is neglected and all validations relating password are skipped.
12
+ * Besides, two attributes `password_confirmation` and `new_password_confirmation`
13
+ are added.
14
+
1
15
  ## 0.1.0 (2011-12-13)
2
16
 
3
17
  * The `authenticate` method returns `self` instead of `true`
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  mini_auth
2
2
  =========
3
3
 
4
- A replacement for `has_secure_password` of ActiveModel
4
+ A replacement for `has_secure_password` of ActiveModel with some enhancements
5
+
5
6
 
6
7
  Install
7
8
  -------
@@ -15,8 +16,15 @@ or install as a plugin
15
16
  $ cd RAILS_ROOT
16
17
  $ rails plugin install git://github.com/kuroda/mini_auth.git
17
18
 
18
- Usage
19
- -----
19
+
20
+ Requirements
21
+ ------------
22
+
23
+ * Ruby on Rails 3.1 or higher
24
+
25
+
26
+ Synopsis
27
+ --------
20
28
 
21
29
  class CreateUsers < ActiveRecord::Migration
22
30
  def change
@@ -34,62 +42,174 @@ Usage
34
42
  end
35
43
 
36
44
  a = User.new(:name => "alice", :password => "hotyoga")
45
+ a.setting_password => true
37
46
 
38
47
  a.save # => true
39
48
  a.password_digest # => "$2a$10$F5YbEd..."
40
49
  a.authenticate("hotyoga) # => a
41
50
  a.authenticate("wrong") # => false
51
+
52
+ a.attributes = { :current_password => 'hotyoga', :new_password => 'almond' }
53
+ a.changing_password = true
54
+ a.save
55
+ a.authenticate("hotyoga) # => false
56
+ a.authenticate("almond") # => a
42
57
 
43
- Requirements
44
- ------------
58
+ Usage
59
+ -----
45
60
 
46
- * Ruby on Rails 3.1 or higher
61
+ ### Migration
47
62
 
48
- Remarks
49
- -------
63
+ To use `mini_auth`, you must add the `password_digest` column to the relevant table.
64
+
65
+ class CreateUsers < ActiveRecord::Migration
66
+ def change
67
+ create_table :users do |t|
68
+ t.string :name, :null => false
69
+ t.string :password_digest, :null => true
70
+
71
+ t.timestamps
72
+ end
73
+ end
74
+ end
75
+
76
+ The raw (unencrypted) password will never be saved on the database. Only its hash value
77
+ is recorded on the `password_digest` column.
78
+
79
+
80
+ ### Default behavior
81
+
82
+ The module `MiniAuth` introduces two basic attributes: `setting_password` and `changing_password`
83
+
84
+ When neither of them is set to `true`, you can NOT set or change the user's `password_digest`.
85
+
86
+ a = User.find_by_name("alice")
87
+ a.password = 'foobar'
88
+ a.password_digest_changed? # => false
89
+ a.valid? # => true
90
+
91
+
92
+ ### `setting_password` attribute
93
+
94
+ When the user's `setting_password` attribute is set to `true`, its password can
95
+ be set without knowing the current password.
96
+
97
+ a = User.find_by_name("alice")
98
+ a.setting_password => true
99
+ a.update_attributes(:password => 'p@ssword')
100
+ a.authenticate("p@ssword") # => a
50
101
 
51
102
  Password can't be blank.
52
103
 
104
+ b = User.new(:name => "bob", :password => "")
105
+ b.setting_password => true
106
+ b.valid? # => false
107
+ b.errors[:password] # => "can't be blank"
108
+
109
+ Password can be nil.
110
+
111
+ b = User.new(:name => "bob", :password => nil)
112
+ b.setting_password => true
113
+ b.valid? # => false
114
+ b.errors[:password] # => "can't be blank"
115
+
116
+ Password should be given.
117
+
53
118
  b = User.new(:name => "bob")
54
-
55
- b.password = ""
119
+ b.setting_password => true
56
120
  b.valid? # => false
57
121
  b.errors[:password] # => "can't be blank"
58
122
 
59
- But, password can be nil.
60
123
 
61
- b.password = nil
62
- b.valid? # => true
124
+ ### `changing_password` attribute
63
125
 
64
- You can save a user whose `password_digest` is nil.
126
+ When the user's `changing_password` attribute is set to `true`, its password can
127
+ NOT be set without knowing the current password. You should provide `current_password`
128
+ and `new_password` attributes to change its password.
65
129
 
66
- b.save!
67
- b.password_digest # => nil
130
+ a = User.find_by_name("alice")
131
+ a.changing_password => true
132
+ a.update_attributes(:current_password => 'p@ssword', :new_password => 'opensesame')
133
+ a.authenticate("opensesame") # => a
68
134
 
69
- Such a user can't get authenticated.
135
+ If the `current_password` is wrong, the validation fails.
136
+
137
+ a = User.find_by_name("alice")
138
+ a.changing_password => true
139
+ a.attributes = { :current_password => 'pumpkin', :new_password => '0000' }
140
+ a.valid? # => false
141
+ a.errors[:current_password] # => [ "is invalid" ]
70
142
 
71
- b.authenticate(nil) # => false
143
+ When both of the `setting_password` and the `changing_password` are set to `true`,
144
+ only the latter is effective.
72
145
 
73
- The `password_digest` field is protected against mass assignment.
74
146
 
75
- b.update_attributes :password_digest => 'dummy'
76
- b.password_digest # => nil (unchanged)
147
+ ### A user whose `password_digest` is nil
77
148
 
78
- The `password_confirmation` field is not created automatically. If you need it, add it for yourself.
149
+ You can save a user whose `password_digest` is nil.
150
+
151
+ c = User.new(:name => "carol")
152
+ c.save!
153
+ c.password_digest # => nil
154
+
155
+ Such a user can't get authenticated.
156
+
157
+ c.authenticate(nil) # => false
158
+
159
+ If you don't want such a user to be created, add a validation to your class.
79
160
 
80
161
  class User < ActiveRecord::Base
81
162
  include MiniAuth
82
163
 
83
- attr_accessor :password_confirmation
84
- validates :password, :confirmation => true
164
+ validates :password_digest, :presence => true
85
165
  end
86
166
 
167
+
168
+ ### Confirmation of password
169
+
170
+ The `password_confirmation` and `new_password_confirmation` attributes are created automatically.
171
+
172
+ c = User.find_by_name("carol")
173
+ c.setting_password = true
174
+ c.attributes = { :password => 'snowman', :password_confirmation => 'iceman' }
175
+ c.valid? # => false
176
+ c.errors[:password] # => [ "doesn't match confirmation" ]
177
+
178
+ a = User.find_by_name("alice")
179
+ a.changing_password => true
180
+ a.attributes = { :current_password => 'opensesame',
181
+ :new_password => 'snowman', :new_password_confirmation => 'iceman' }
182
+ a.valid? # => false
183
+ a.errors[:new_password] # => [ "doesn't match confirmation" ]
184
+
185
+ You don't have to use them, however.
186
+
187
+ c = User.find_by_name("carol")
188
+ c.setting_password = true
189
+ c.attributes = { :password => 'snowman' }
190
+ c.valid? # => true
191
+
192
+
193
+ ### Mass assignment security
194
+
195
+ The `password_digest` column is protected against mass assignment.
196
+
197
+ c.update_attributes(:password_digest => 'dummy')
198
+ c.password_digest # => nil (unchanged)
199
+
200
+ Similarly, the `setting_password` and `changing_password` attributes are protected.
201
+
202
+ c.attributes = { :setting_password => true, :password => '0000' }
203
+ c.setting_password? # => false
204
+
205
+
87
206
  License
88
207
  -------
89
208
 
90
209
  `mini_auth` is distributed under the MIT license. ([MIT-LICENSE](https://github.com/kuroda/mini_auth/blob/master/MIT-LICENSE))
91
210
 
211
+
92
212
  Copyright
93
213
  ---------
94
214
 
95
- Copyright (c) 2011 Tsutomu Kuroda.
215
+ Copyright (c) 2011 Tsutomu Kuroda <t-kuroda@oiax.jp>.
data/lib/mini_auth.rb CHANGED
@@ -4,34 +4,67 @@ require "bcrypt"
4
4
  module MiniAuth
5
5
  extend ActiveSupport::Concern
6
6
 
7
+ BASIC_ATTRIBUTES = [
8
+ :password, :password_confirmation,
9
+ :current_password,
10
+ :new_password, :new_password_confirmation
11
+ ]
12
+
7
13
  included do
8
- attr_accessor :password
14
+ attr_accessor :changing_password, :setting_password
15
+ attr_accessor *BASIC_ATTRIBUTES
16
+ attr_accessible *BASIC_ATTRIBUTES
17
+
18
+ validates :password, :new_password, :confirmation => true
9
19
 
10
20
  if respond_to?(:attributes_protected_by_default)
11
21
  def self.attributes_protected_by_default
12
- super + [ 'password_digest' ]
22
+ super + [ 'password_digest', 'changing_password', 'setting_password' ]
13
23
  end
14
24
  end
15
25
 
16
26
  validate do
17
- if password && password.blank?
18
- errors.add(:password, :blank)
27
+ if changing_password?
28
+ unless authenticate(current_password)
29
+ errors.add(:current_password, :invalid)
30
+ end
31
+
32
+ if new_password.blank?
33
+ errors.add(:new_password, :blank)
34
+ end
35
+ elsif setting_password?
36
+ if password.blank?
37
+ errors.add(:password, :blank)
38
+ end
19
39
  end
20
40
  end
21
41
 
22
- before_save do
23
- if password
42
+ after_validation do
43
+ if changing_password?
44
+ self.password_digest = BCrypt::Password.create(new_password)
45
+ elsif setting_password?
24
46
  self.password_digest = BCrypt::Password.create(password)
25
- self.password = nil
26
47
  end
27
48
  end
49
+
50
+ after_save do
51
+ self.password = nil
52
+ end
28
53
  end
29
54
 
30
- def authenticate(password)
31
- if password_digest && BCrypt::Password.new(password_digest) == password
55
+ def authenticate(raw_password)
56
+ if password_digest && BCrypt::Password.new(password_digest) == raw_password
32
57
  self
33
58
  else
34
59
  false
35
60
  end
36
61
  end
62
+
63
+ def changing_password?
64
+ !!changing_password
65
+ end
66
+
67
+ def setting_password?
68
+ !!setting_password
69
+ end
37
70
  end
@@ -1,3 +1,3 @@
1
1
  module MiniAuth
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0.beta"
3
3
  end
data/spec/fake_app.rb CHANGED
@@ -24,11 +24,3 @@ migration.change
24
24
  class User < ActiveRecord::Base
25
25
  include MiniAuth
26
26
  end
27
-
28
- class Administrator < ActiveRecord::Base
29
- attr_accessible :name, :password, :password_confirmation
30
-
31
- include MiniAuth
32
-
33
- validates :password, :presence => { :on => :create }, :confirmation => true
34
- end
@@ -3,6 +3,7 @@ require 'spec_helper'
3
3
  describe "authenticate" do
4
4
  it "should authenticate with a valid password" do
5
5
  u = User.new(:name => 'alice', :password => 'hotyoga')
6
+ u.setting_password = true
6
7
  u.save!
7
8
 
8
9
  u.authenticate('hotyoga').should == u
@@ -10,15 +11,9 @@ describe "authenticate" do
10
11
 
11
12
  it "should not authenticate with a wrong password" do
12
13
  u = User.new(:name => 'alice', :password => 'hotyoga')
14
+ u.setting_password = true
13
15
  u.save!
14
16
 
15
17
  u.authenticate('wrong').should be_false
16
18
  end
17
-
18
- it "should not authenticate with ni; password" do
19
- u = User.new(:name => 'alice', :password => nil)
20
- u.save!
21
-
22
- u.authenticate(nil).should be_false
23
- end
24
19
  end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe "change_password" do
4
+ let(:user) do
5
+ u = User.create!(:name => 'alice', :new_password => 'password')
6
+ u.changing_password = true
7
+ u
8
+ end
9
+
10
+ it "should change password" do
11
+ user.update_attributes(:current_password => 'password', :new_password => 'banana')
12
+
13
+ user.authenticate('banana').should be_true
14
+ end
15
+
16
+ it "should reject wrong current password" do
17
+ user.assign_attributes(:current_password => 'lemon', :new_password => 'banana')
18
+
19
+ user.should_not be_valid
20
+ user.should have(1).error_on(:current_password)
21
+ user.errors[:current_password].first.should == "is invalid"
22
+ end
23
+
24
+ it "should reject blank string as new password" do
25
+ user.assign_attributes(:current_password => 'password', :new_password => '')
26
+
27
+ user.should_not be_valid
28
+ user.should have(1).error_on(:new_password)
29
+ user.errors[:new_password].first.should == "can't be blank"
30
+ end
31
+ end
@@ -7,11 +7,4 @@ describe "password_digest" do
7
7
  u.update_attributes :password_digest => 'dummy'
8
8
  u.password_digest.to_s.should == d
9
9
  end
10
-
11
- it "should be protected against mass assignment also for Administrator" do
12
- a = Administrator.create!(:name => 'alice', :password => 'hotyoga', :password_confirmation => 'hotyoga')
13
- d = a.password_digest.to_s
14
- a.update_attributes :password_digest => 'dummy'
15
- a.password_digest.to_s.should == d
16
- end
17
10
  end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe "setting_password" do
4
+ it "should set password" do
5
+ u = User.new(:name => 'alice', :password => 'hotyoga')
6
+ u.setting_password = true
7
+ u.should be_valid
8
+ u.save!
9
+ u.authenticate('hotyoga').should be_true
10
+ end
11
+
12
+ it "should update password" do
13
+ u = User.create!(:name => 'alice')
14
+ u.setting_password = true
15
+ u.update_attributes(:password => 'hotyoga')
16
+ u.authenticate('hotyoga').should be_true
17
+ end
18
+
19
+ it "should reject blank password" do
20
+ u = User.new(:name => 'alice', :password => '')
21
+ u.setting_password = true
22
+ u.should_not be_valid
23
+ u.should have(1).error_on(:password)
24
+ u.errors[:password].first.should == "can't be blank"
25
+ end
26
+
27
+ it "should reject nil password" do
28
+ u = User.new(:name => 'alice', :password => nil)
29
+ u.setting_password = true
30
+ u.should_not be_valid
31
+ u.should have(1).error_on(:password)
32
+ u.errors[:password].first.should == "can't be blank"
33
+ end
34
+
35
+ it "should set user's password without confirmation" do
36
+ u = User.new(:name => 'alice', :password => 'apple')
37
+ u.setting_password = true
38
+ u.should be_valid
39
+ u.save!
40
+ u.authenticate('apple').should be_true
41
+ end
42
+
43
+ it "should validate password_confirmation if it is given" do
44
+ u = User.new(:name => 'alice', :password => 'apple', :password_confirmation => 'almond')
45
+ u.setting_password = true
46
+ u.should_not be_valid
47
+ u.should have(1).error_on(:password)
48
+ u.errors[:password].first.should == "doesn't match confirmation"
49
+ end
50
+ end
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
5
- prerelease:
4
+ version: 0.2.0.beta
5
+ prerelease: 6
6
6
  platform: ruby
7
7
  authors:
8
8
  - Tsutomu Kuroda
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-13 00:00:00.000000000 Z
12
+ date: 2011-12-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &13259840 !ruby/object:Gem::Requirement
16
+ requirement: &19514420 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 3.1.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *13259840
24
+ version_requirements: *19514420
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: bcrypt-ruby
27
- requirement: &13259180 !ruby/object:Gem::Requirement
27
+ requirement: &19513860 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *13259180
35
+ version_requirements: *19513860
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec-rails
38
- requirement: &13258540 !ruby/object:Gem::Requirement
38
+ requirement: &19512860 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 2.7.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *13258540
46
+ version_requirements: *19512860
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sqlite3
49
- requirement: &13258120 !ruby/object:Gem::Requirement
49
+ requirement: &19512320 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *13258120
57
+ version_requirements: *19512320
58
58
  description: A minimal authentication module for Rails
59
59
  email:
60
60
  - t-kuroda@oiax.jp
@@ -73,8 +73,9 @@ files:
73
73
  - mini_auth.gemspec
74
74
  - spec/fake_app.rb
75
75
  - spec/mini_auth/authenticate_spec.rb
76
+ - spec/mini_auth/change_password_spec.rb
76
77
  - spec/mini_auth/password_digest_spec.rb
77
- - spec/mini_auth/password_spec.rb
78
+ - spec/mini_auth/setting_password_spec.rb
78
79
  - spec/spec_helper.rb
79
80
  homepage: ''
80
81
  licenses: []
@@ -91,9 +92,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
91
92
  required_rubygems_version: !ruby/object:Gem::Requirement
92
93
  none: false
93
94
  requirements:
94
- - - ! '>='
95
+ - - ! '>'
95
96
  - !ruby/object:Gem::Version
96
- version: '0'
97
+ version: 1.3.1
97
98
  requirements: []
98
99
  rubyforge_project: mini_auth
99
100
  rubygems_version: 1.8.10
@@ -1,29 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "password" do
4
- it "should accept nil password" do
5
- u = User.new(:name => 'alice', :password => nil)
6
- u.should be_valid
7
- end
8
-
9
- it "should reject blank password" do
10
- u = User.new(:name => 'alice', :password => '')
11
- u.should_not be_valid
12
- u.should have(1).error_on(:password)
13
- u.errors[:password].first.should == "can't be blank"
14
- end
15
-
16
- it "should reject nil password for Administrator" do
17
- a = Administrator.new(:name => 'alice', :password => nil)
18
- a.should_not be_valid
19
- a.should have(1).error_on(:password)
20
- a.errors[:password].first.should == "can't be blank"
21
- end
22
-
23
- it "should validate password_confirmation for Administrator" do
24
- a = Administrator.new(:name => 'alice', :password => 'apple', :password_confirmation => 'almond')
25
- a.should_not be_valid
26
- a.should have(1).error_on(:password)
27
- a.errors[:password].first.should == "doesn't match confirmation"
28
- end
29
- end