mini_auth 0.1.0 → 0.2.0.beta

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