devise_invitable 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of devise_invitable might be problematic. Click here for more details.

data/CHANGELOG ADDED
@@ -0,0 +1,5 @@
1
+ = 1.3.0
2
+ Now devise 3.1 compatible, @token must be used instead of @resource.invitation_token in mail views
3
+
4
+ = 1.2.0
5
+ Add invitation_created_at column which is set when invitation is created even when sending is skipped. This new field is used to check invitation period valid
data/README.rdoc CHANGED
@@ -3,11 +3,9 @@
3
3
 
4
4
  It adds support to devise[http://github.com/plataformatec/devise] for send invitations by email (it requires to be authenticated) and accept the invitation setting the password.
5
5
 
6
- DeviseInvitable currently only support Rails 3, if you want to use it with Rails 2.3 you must install version {0.2.3}[http://rubygems.org/gems/devise_invitable/versions/0.2.3]
6
+ DeviseInvitable currently supports Rails 3 and 4, if you want to use it with Rails 2.3 you must install version {0.2.3}[http://rubygems.org/gems/devise_invitable/versions/0.2.3]
7
7
 
8
- If you want to use Rails 4 or devise 3, you must use git:
9
-
10
- gem 'devise_invitable', :github => 'scambra/devise_invitable'
8
+ If you want to use devise 3.0.x, you must use 1.2.1, newer versions require devise >= 3.1.0
11
9
 
12
10
  == Installation
13
11
 
@@ -49,7 +47,7 @@ Add t.invitable to your Devise model migration:
49
47
  create_table :users do
50
48
  ...
51
49
  ## Invitable
52
- t.string :invitation_token, :limit => 60
50
+ t.string :invitation_token
53
51
  t.datetime :invitation_created_at
54
52
  t.datetime :invitation_sent_at
55
53
  t.datetime :invitation_accepted_at
@@ -62,15 +60,15 @@ Add t.invitable to your Devise model migration:
62
60
 
63
61
  or for a model that already exists, define a migration to add DeviseInvitable to your model:
64
62
 
65
- change_table :users do |t|
66
- t.string :invitation_token, :limit => 60
67
- t.datetime :invitation_created_at
68
- t.datetime :invitation_sent_at
69
- t.datetime :invitation_accepted_at
70
- t.integer :invitation_limit
71
- t.integer :invited_by_id
72
- t.string :invited_by_type
73
-
63
+ def change
64
+ add_column :users, :invitation_token, :string
65
+ add_column :users, :invitation_created_at, :datetime
66
+ add_column :users, :invitation_sent_at, :datetime
67
+ add_column :users, :invitation_accepted_at, :datetime
68
+ add_column :users, :invitation_limit, :integer
69
+ add_column :users, :invited_by_id, :integer
70
+ add_column :users, :invited_by_type, :string
71
+ add_index :users, :invitation_token, :unique => true
74
72
  end
75
73
 
76
74
  # Allow null encrypted_password
@@ -78,6 +76,16 @@ or for a model that already exists, define a migration to add DeviseInvitable to
78
76
  # Allow null password_salt (add it if you are using Devise's encryptable module)
79
77
  change_column :users, :password_salt, :string, :null => true
80
78
 
79
+ If you previously used devise_invitable with a :limit on :invitation_token, remove it:
80
+
81
+ def up
82
+ change_column :users, :invitation_token, :string, :limit => nil
83
+ end
84
+
85
+ def down
86
+ change_column :users, :invitation_token, :string, :limit => 60
87
+ end
88
+
81
89
  == Mongoid Field Definitions
82
90
  If you are using Mongoid, define the following fields and indexes within your invitable model:
83
91
 
@@ -164,6 +172,15 @@ be sure that you generate the views and put them into the controller that you ge
164
172
 
165
173
  rails generate devise_invitable:views users/invitations
166
174
 
175
+ == Strong Parameters
176
+
177
+ When you customize your own views, you may end up adding new attributes to forms. Rails 4 moved the parameter sanitization from the model to the controller, causing DeviseInvitable to handle this concern at the controller as well. Read about it in {devise README}[http://github.com/plataformatec/devise#strong-parameters]
178
+
179
+ There are just two actions in DeviseInvitable that allows any set of parameters to be passed down to the model, therefore requiring sanitization. Their names and the permited parameters by default are:
180
+
181
+ * invite (Devise::InvitationsController#create) - Permits only the authentication keys (like email)
182
+ * accept_invitation (Devise::InvitationsController#update) - Permits invitation_token plus password and password_confirmation
183
+
167
184
  == Usage
168
185
 
169
186
  === Send an invitation
@@ -14,7 +14,7 @@ class Devise::InvitationsController < DeviseController
14
14
 
15
15
  # POST /resource/invitation
16
16
  def create
17
- self.resource = resource_class.invite!(invite_params, current_inviter)
17
+ self.resource = invite_resource
18
18
 
19
19
  if resource.errors.empty?
20
20
  set_flash_message :notice, :send_instructions, :email => self.resource.email if self.resource.invitation_sent_at
@@ -26,6 +26,7 @@ class Devise::InvitationsController < DeviseController
26
26
 
27
27
  # GET /resource/invitation/accept?invitation_token=abcdef
28
28
  def edit
29
+ resource.invitation_token = params[:invitation_token]
29
30
  render :edit
30
31
  end
31
32
 
@@ -51,6 +52,11 @@ class Devise::InvitationsController < DeviseController
51
52
  end
52
53
 
53
54
  protected
55
+
56
+ def invite_resource
57
+ resource_class.invite!(invite_params, current_inviter)
58
+ end
59
+
54
60
  def current_inviter
55
61
  @current_inviter ||= authenticate_inviter!
56
62
  end
@@ -64,18 +70,18 @@ class Devise::InvitationsController < DeviseController
64
70
  end
65
71
 
66
72
  def resource_from_invitation_token
67
- unless params[:invitation_token] && self.resource = resource_class.to_adapter.find_first(params.slice(:invitation_token))
73
+ unless params[:invitation_token] && self.resource = resource_class.find_by_invitation_token(params[:invitation_token], true)
68
74
  set_flash_message(:alert, :invitation_token_invalid)
69
75
  redirect_to after_sign_out_path_for(resource_name)
70
76
  end
71
77
  end
72
78
 
73
79
  def invite_params
74
- devise_parameter_sanitizer.for(:invite)
80
+ devise_parameter_sanitizer.sanitize(:invite)
75
81
  end
76
82
 
77
83
  def update_resource_params
78
- devise_parameter_sanitizer.for(:accept_invitation)
84
+ devise_parameter_sanitizer.sanitize(:accept_invitation)
79
85
  end
80
86
 
81
87
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  <p>Someone has invited you to <%= root_url %>, you can accept it through the link below.</p>
4
4
 
5
- <p><%= link_to 'Accept invitation', accept_invitation_url(@resource, :invitation_token => @resource.invitation_token) %></p>
5
+ <p><%= link_to 'Accept invitation', accept_invitation_url(@resource, :invitation_token => @token) %></p>
6
6
 
7
7
  <p>If you don't want to accept the invitation, please ignore this email.<br />
8
8
  Your account won't be created until you access the link above and set your password.</p>
@@ -4,13 +4,9 @@ module DeviseInvitable
4
4
  module Mailer
5
5
 
6
6
  # Deliver an invitation email
7
- def invitation_instructions(record, opts={})
8
- # optional arguments introduced in Devise 2.2.0, remove check once support for < 2.2.0 is dropped.
9
- if Gem::Version.new(Devise::VERSION.dup) < Gem::Version.new('2.2.0')
10
- devise_mail(record, :invitation_instructions)
11
- else
12
- devise_mail(record, :invitation_instructions, opts)
13
- end
7
+ def invitation_instructions(record, token, opts={})
8
+ @token = token
9
+ devise_mail(record, :invitation_instructions, opts)
14
10
  end
15
11
  end
16
12
  end
@@ -64,14 +64,6 @@ module Devise
64
64
  fields
65
65
  end
66
66
 
67
- def invitation_fields
68
- fields = [:invitation_created_at, :invitation_sent_at, :invited_by_id, :invited_by_type]
69
- if Devise.invited_by_class_name
70
- fields -= [:invited_by_type]
71
- end
72
- fields
73
- end
74
-
75
67
  # Accept an invitation by clearing invitation token and and setting invitation_accepted_at
76
68
  def accept_invitation
77
69
  self.invitation_accepted_at = Time.now.utc
@@ -111,11 +103,6 @@ module Devise
111
103
  invitation_accepted? || !invited_to_sign_up?
112
104
  end
113
105
 
114
- def invited?
115
- ActiveSupport::Deprecation.warn "invited? is deprecated and will be removed from DeviseInvitable 1.1.0 (use invited_to_sign_up? instead)"
116
- invited_to_sign_up?
117
- end
118
-
119
106
  # Reset invitation token and send invitation again
120
107
  def invite!(invited_by = nil)
121
108
  was_invited = invited_to_sign_up?
@@ -126,7 +113,7 @@ module Devise
126
113
  def self.confirmation_required?; false; end
127
114
  end
128
115
 
129
- generate_invitation_token if self.invitation_token.nil?
116
+ generate_invitation_token if self.invitation_token.nil? || (!@skip_invitation && @raw_invitation_token.nil?)
130
117
  self.invitation_created_at = Time.now.utc
131
118
  self.invitation_sent_at = self.invitation_created_at unless @skip_invitation
132
119
  self.invited_by = invited_by if invited_by
@@ -158,13 +145,19 @@ module Devise
158
145
  accept_invitation! if invited_to_sign_up?
159
146
  end
160
147
 
161
- def invite_key_valid?
162
- return true unless self.class.invite_key.is_a? Hash # FIXME: remove this line when deprecation is removed
163
- self.class.invite_key.all? do |key, regexp|
164
- regexp.nil? || self.send(key).try(:match, regexp)
148
+ def clear_errors_on_valid_keys
149
+ self.class.invite_key.each do |key, regexp|
150
+ self.errors.delete(key) if regexp.nil? || self.send(key).try(:match, regexp)
165
151
  end
166
152
  end
167
153
 
154
+ # Deliver the invitation email
155
+ def deliver_invitation
156
+ generate_invitation_token! unless @raw_invitation_token
157
+ self.update_attribute :invitation_sent_at, Time.now.utc unless self.invitation_sent_at
158
+ send_devise_notification(:invitation_instructions, @raw_invitation_token)
159
+ end
160
+
168
161
  protected
169
162
  # Overriding the method in Devise's :validatable module so password is not required on inviting
170
163
  def password_required?
@@ -175,12 +168,6 @@ module Devise
175
168
  respond_to?(:confirmation_required?, true) && confirmation_required? && invitation_accepted?
176
169
  end
177
170
 
178
- # Deliver the invitation email
179
- def deliver_invitation
180
- self.update_attribute :invitation_sent_at, Time.now.utc unless self.invitation_sent_at
181
- send_devise_notification(:invitation_instructions)
182
- end
183
-
184
171
  # Checks if the invitation for the user is within the limit time.
185
172
  # We do this by calculating if the difference between today and the
186
173
  # invitation sent date does not exceed the invite for time configured.
@@ -208,18 +195,19 @@ module Devise
208
195
  # Generates a new random token for invitation, and stores the time
209
196
  # this token is being generated
210
197
  def generate_invitation_token
211
- self.invitation_token = self.class.invitation_token
198
+ raw, enc = Devise.token_generator.generate(self.class, :invitation_token)
199
+ @raw_invitation_token = raw
200
+ self.invitation_token = enc
201
+ end
202
+
203
+ def generate_invitation_token!
204
+ generate_invitation_token && save(:validate => false)
212
205
  end
213
206
 
214
207
  module ClassMethods
215
208
  # Return fields to invite
216
209
  def invite_key_fields
217
- if invite_key.is_a? Hash
218
- invite_key.keys
219
- else
220
- ActiveSupport::Deprecation.warn("invite_key should be a hash like {#{invite_key.inspect} => /.../}")
221
- Array(invite_key)
222
- end
210
+ invite_key.keys
223
211
  end
224
212
 
225
213
  # Attempt to find a user by its email. If a record is not found,
@@ -243,22 +231,20 @@ module Devise
243
231
  invitable.skip_password = true
244
232
  invitable.valid? if self.validate_on_invite
245
233
  if invitable.new_record?
246
- invitable.errors.clear if !self.validate_on_invite and invitable.invite_key_valid?
234
+ invitable.clear_errors_on_valid_keys if !self.validate_on_invite
247
235
  elsif !invitable.invited_to_sign_up? || !self.resend_invitation
248
236
  invite_key_array.each do |key|
249
237
  invitable.errors.add(key, :taken)
250
238
  end
251
239
  end
252
240
 
253
- if invitable.errors.empty?
254
- yield invitable if block_given?
255
- mail = invitable.invite!
256
- end
241
+ yield invitable if block_given?
242
+ mail = invitable.invite! if invitable.errors.empty?
257
243
  [invitable, mail]
258
244
  end
259
245
 
260
246
  def invite!(attributes={}, invited_by=nil, &block)
261
- invitable, mail = _invite(attributes, invited_by, &block)
247
+ invitable, mail = _invite(attributes.with_indifferent_access, invited_by, &block)
262
248
  invitable
263
249
  end
264
250
 
@@ -273,8 +259,8 @@ module Devise
273
259
  # error in invitation_token attribute.
274
260
  # Attributes must contain invitation_token, password and confirmation
275
261
  def accept_invitation!(attributes={})
276
- invitable = find_or_initialize_with_error_by(:invitation_token, attributes.delete(:invitation_token))
277
- invitable.errors.add(:invitation_token, :invalid) if invitable.invitation_token && invitable.persisted? && !invitable.valid_invitation?
262
+ original_token = attributes.delete(:invitation_token)
263
+ invitable = find_by_invitation_token(original_token, false)
278
264
  if invitable.errors.empty?
279
265
  invitable.assign_attributes(attributes)
280
266
  invitable.accept_invitation!
@@ -282,6 +268,18 @@ module Devise
282
268
  invitable
283
269
  end
284
270
 
271
+ def find_by_invitation_token(original_token, only_valid)
272
+ invitation_token = Devise.token_generator.digest(self, :invitation_token, original_token)
273
+
274
+ invitable = find_or_initialize_with_error_by(:invitation_token, invitation_token)
275
+ if !invitable.persisted? && Devise.allow_insecure_token_lookup
276
+ invitable = find_or_initialize_with_error_by(:invitation_token, original_token)
277
+ end
278
+ invitable.errors.add(:invitation_token, :invalid) if invitable.invitation_token && invitable.persisted? && !invitable.valid_invitation?
279
+ invitable.invitation_token = original_token
280
+ invitable unless only_valid && invitable.errors.present?
281
+ end
282
+
285
283
  # Generate a token checking if one does not already exist in the database.
286
284
  def invitation_token
287
285
  generate_token(:invitation_token)
@@ -1,3 +1,3 @@
1
1
  module DeviseInvitable
2
- VERSION = '1.2.1'
2
+ VERSION = '1.3.0'
3
3
  end
@@ -21,7 +21,7 @@ class DeviseInvitableAddTo<%= table_name.camelize %> < ActiveRecord::Migration
21
21
  def down
22
22
  change_table :<%= table_name %> do |t|
23
23
  t.remove_references :invited_by, :polymorphic => true
24
- t.remove :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token
24
+ t.remove :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token, :invitation_created_at
25
25
  end
26
26
  end
27
27
  end
@@ -1,29 +1,29 @@
1
1
  require 'test_helper'
2
2
  require 'integration_tests_helper'
3
3
 
4
- class InvitationTest < ActionDispatch::IntegrationTest
4
+ class InvitationRemoveTest < ActionDispatch::IntegrationTest
5
5
 
6
6
  test 'invited user can choose to remove his account/invite' do
7
7
  user = User.invite!(:email => "valid@email.com")
8
8
 
9
9
  # remove!
10
- visit remove_user_invitation_path(:invitation_token => user.invitation_token)
10
+ visit remove_user_invitation_path(:invitation_token => Thread.current[:token])
11
11
  assert_equal root_path, current_path
12
12
  assert page.has_css?('p#notice', :text => 'Your invitation was removed.')
13
13
 
14
14
  # try to remove again!
15
- visit remove_user_invitation_path(:invitation_token => user.invitation_token)
15
+ visit remove_user_invitation_path(:invitation_token => Thread.current[:token])
16
16
  assert_equal root_path, current_path
17
17
  assert page.has_css?('p#alert', :text => 'The invitation token provided is not valid!')
18
18
  end
19
19
 
20
20
  test 'accepted user cannot remove his account (by using the original invitation token)' do
21
21
  user = User.invite!(:email => "valid@email.com")
22
- saved_token = user.invitation_token
22
+ saved_token = Thread.current[:token]
23
23
  user.accept_invitation!
24
24
 
25
25
  visit remove_user_invitation_path(:invitation_token => saved_token)
26
26
  assert_equal root_path, current_path
27
27
  assert page.has_css?('p#alert', :text => 'The invitation token provided is not valid!')
28
28
  end
29
- end
29
+ end
@@ -75,7 +75,7 @@ class InvitationTest < ActionDispatch::IntegrationTest
75
75
 
76
76
  test 'not authenticated user with valid invitation token but invalid password should not be able to set his password' do
77
77
  user = User.invite!(:email => "valid@email.com")
78
- set_password :invitation_token => user.invitation_token do
78
+ set_password :invitation_token => Thread.current[:token] do
79
79
  fill_in 'Password confirmation', :with => 'other_password'
80
80
  end
81
81
  assert_equal user_invitation_path, current_path
@@ -85,7 +85,7 @@ class InvitationTest < ActionDispatch::IntegrationTest
85
85
 
86
86
  test 'not authenticated user with valid data should be able to change his password' do
87
87
  user = User.invite!(:email => "valid@email.com")
88
- set_password :invitation_token => user.invitation_token
88
+ set_password :invitation_token => Thread.current[:token]
89
89
 
90
90
  assert_equal root_path, current_path
91
91
  assert page.has_css?('p#notice', :text => 'Your password was set successfully. You are now signed in.')
@@ -94,7 +94,7 @@ class InvitationTest < ActionDispatch::IntegrationTest
94
94
 
95
95
  test 'after entering invalid data user should still be able to set his password' do
96
96
  user = User.invite!(:email => "valid@email.com")
97
- set_password :invitation_token => user.invitation_token do
97
+ set_password :invitation_token => Thread.current[:token] do
98
98
  fill_in 'Password confirmation', :with => 'other_password'
99
99
  end
100
100
  assert_equal user_invitation_path, current_path
@@ -108,7 +108,7 @@ class InvitationTest < ActionDispatch::IntegrationTest
108
108
 
109
109
  test 'sign in user automatically after setting it\'s password' do
110
110
  user = User.invite!(:email => "valid@email.com")
111
- set_password :invitation_token => user.invitation_token
111
+ set_password :invitation_token => Thread.current[:token]
112
112
  assert_equal root_path, current_path
113
113
  end
114
114
 
@@ -119,8 +119,7 @@ class InvitationTest < ActionDispatch::IntegrationTest
119
119
  fill_in 'user_email', :with => 'valid@email.com'
120
120
  click_button 'Send me reset password instructions'
121
121
 
122
- user.reload
123
- visit edit_user_password_path(:reset_password_token => user.reset_password_token)
122
+ visit edit_user_password_path(:reset_password_token => Thread.current[:token])
124
123
  set_password :visit => false, :button => 'Change my password'
125
124
 
126
125
  user.reload
@@ -53,7 +53,17 @@ class InvitationMailTest < ActionMailer::TestCase
53
53
 
54
54
  test 'body should have link to confirm the account' do
55
55
  host = ActionMailer::Base.default_url_options[:host]
56
- invitation_url_regexp = %r{<a href=\"http://#{host}/users/invitation/accept\?invitation_token=#{user.invitation_token}">}
57
- assert_match invitation_url_regexp, mail.body.decoded
56
+ body = mail.body.decoded
57
+ invitation_url_regexp = %r{<a href=\"http://#{host}/users/invitation/accept\?invitation_token=#{Thread.current[:token]}">}
58
+ assert_match invitation_url_regexp, body
59
+ end
60
+
61
+ test 'body should have link to confirm the account on resend' do
62
+ host = ActionMailer::Base.default_url_options[:host]
63
+ user
64
+ @user = User.find(user.id).invite!
65
+ body = mail.body.decoded
66
+ invitation_url_regexp = %r{<a href=\"http://#{host}/users/invitation/accept\?invitation_token=#{Thread.current[:token]}">}
67
+ assert_match invitation_url_regexp, body
58
68
  end
59
69
  end
@@ -11,15 +11,17 @@ class InvitableTest < ActiveSupport::TestCase
11
11
  assert_nil new_user.invitation_token
12
12
  end
13
13
 
14
- test 'should not regenerate invitation token each time' do
14
+ test 'should regenerate invitation token each time' do
15
15
  user = new_user
16
16
  user.invite!
17
17
  token = user.invitation_token
18
18
  assert_not_nil user.invitation_token
19
19
  assert_not_nil user.invitation_created_at
20
20
  3.times do
21
+ user = User.find(user.id)
21
22
  user.invite!
22
- assert_equal token, user.invitation_token
23
+ assert_not_equal token, user.invitation_token
24
+ token = user.invitation_token
23
25
  end
24
26
  end
25
27
 
@@ -32,22 +34,11 @@ class InvitableTest < ActiveSupport::TestCase
32
34
  3.times do
33
35
  user.invite!
34
36
  assert_not_equal old_invitation_sent_at, user.invitation_sent_at
35
- assert_not_equal old_invitation_created_at, user.invitation_sent_at
37
+ assert_not_equal old_invitation_created_at, user.invitation_created_at
36
38
  user.update_attributes(:invitation_sent_at => old_invitation_sent_at, :invitation_created_at => old_invitation_created_at)
37
39
  end
38
40
  end
39
41
 
40
- test 'should not regenerate invitation token even after the invitation token is not valid' do
41
- User.stubs(:invite_for).returns(1.day)
42
- user = new_user
43
- user.invite!
44
- token = user.invitation_token
45
- user.invitation_created_at = 3.days.ago
46
- user.save
47
- user.invite!
48
- assert_equal token, user.invitation_token
49
- end
50
-
51
42
  test 'should test invitation sent at with invite_for configuration value' do
52
43
  user = User.invite!(:email => "valid@email.com")
53
44
 
@@ -99,6 +90,8 @@ class InvitableTest < ActiveSupport::TestCase
99
90
  user = User.invite!(:email => "valid@email.com")
100
91
  assert user.new_record?
101
92
  assert user.errors.present?
93
+ assert user.errors[:username]
94
+ assert user.errors[:email].empty?
102
95
  end
103
96
 
104
97
  test 'should return mail object' do
@@ -113,14 +106,14 @@ class InvitableTest < ActiveSupport::TestCase
113
106
 
114
107
  test 'should set password and password confirmation from params' do
115
108
  invited_user = User.invite!(:email => "valid@email.com")
116
- user = User.accept_invitation!(:invitation_token => invited_user.invitation_token, :password => '123456789', :password_confirmation => '123456789')
109
+ user = User.accept_invitation!(:invitation_token => Thread.current[:token], :password => '123456789', :password_confirmation => '123456789')
117
110
  assert user.valid_password?('123456789')
118
111
  end
119
112
 
120
113
  test 'should set password and save the record' do
121
114
  user = User.invite!(:email => "valid@email.com")
122
115
  old_encrypted_password = user.encrypted_password
123
- user = User.accept_invitation!(:invitation_token => user.invitation_token, :password => '123456789', :password_confirmation => '123456789')
116
+ user = User.accept_invitation!(:invitation_token => Thread.current[:token], :password => '123456789', :password_confirmation => '123456789')
124
117
  assert_not_equal old_encrypted_password, user.encrypted_password
125
118
  end
126
119
 
@@ -147,10 +140,14 @@ class InvitableTest < ActiveSupport::TestCase
147
140
  test 'should clear invitation token while resetting the password' do
148
141
  user = User.invite!(:email => "valid@email.com")
149
142
  assert user.invited_to_sign_up?
150
- user.send(:generate_reset_password_token!)
143
+ token, user.reset_password_token = Devise.token_generator.generate(User, :reset_password_token)
144
+ user.reset_password_sent_at = Time.now.utc
145
+ user.save
146
+
151
147
  assert_present user.reset_password_token
152
148
  assert_present user.invitation_token
153
- User.reset_password_by_token(:reset_password_token => user.reset_password_token, :password => '123456789', :password_confirmation => '123456789')
149
+ User.reset_password_by_token(:reset_password_token => token, :password => '123456789', :password_confirmation => '123456789')
150
+ assert_nil user.reload.reset_password_token
154
151
  assert_nil user.reload.invitation_token
155
152
  assert !user.invited_to_sign_up?
156
153
  end
@@ -158,10 +155,14 @@ class InvitableTest < ActiveSupport::TestCase
158
155
  test 'should not accept invitation on failing to reset the password' do
159
156
  user = User.invite!(:email => "valid@email.com")
160
157
  assert user.invited_to_sign_up?
161
- user.send(:generate_reset_password_token!)
158
+ token, user.reset_password_token = Devise.token_generator.generate(User, :reset_password_token)
159
+ user.reset_password_sent_at = Time.now.utc
160
+ user.save
161
+
162
162
  assert_present user.reset_password_token
163
163
  assert_present user.invitation_token
164
- User.reset_password_by_token(:reset_password_token => user.reset_password_token, :password => '123456789', :password_confirmation => '12345678')
164
+ User.reset_password_by_token(:reset_password_token => token, :password => '123456789', :password_confirmation => '12345678')
165
+ assert_present user.reload.reset_password_token
165
166
  assert_present user.reload.invitation_token
166
167
  assert user.invited_to_sign_up?
167
168
  end
@@ -169,10 +170,13 @@ class InvitableTest < ActiveSupport::TestCase
169
170
  test 'should not set invitation_accepted_at if just resetting password' do
170
171
  user = User.create!(:email => "valid@email.com", :password => "123456780")
171
172
  assert !user.invited_to_sign_up?
172
- user.send(:generate_reset_password_token!)
173
+ token, user.reset_password_token = Devise.token_generator.generate(User, :reset_password_token)
174
+ user.reset_password_sent_at = Time.now.utc
175
+ user.save
176
+
173
177
  assert_present user.reset_password_token
174
178
  assert_nil user.invitation_token
175
- User.reset_password_by_token(:reset_password_token => user.reset_password_token, :password => '123456789', :password_confirmation => '123456789')
179
+ User.reset_password_by_token(:reset_password_token => token, :password => '123456789', :password_confirmation => '123456789')
176
180
  assert_nil user.reload.invitation_token
177
181
  assert_nil user.reload.invitation_accepted_at
178
182
  end
@@ -241,6 +245,9 @@ class InvitableTest < ActiveSupport::TestCase
241
245
  user = User.invite!(:email => "valid@email.com")
242
246
  assert_equal user, existing_user
243
247
  assert_equal ['has already been taken'], user.errors[:email]
248
+ same_user = User.invite!("email" => "valid@email.com")
249
+ assert_equal same_user, existing_user
250
+ assert_equal ['has already been taken'], same_user.errors[:email]
244
251
  end
245
252
 
246
253
  test 'should return a record with errors if user with pending invitation was found by e-mail' do
@@ -297,7 +304,7 @@ class InvitableTest < ActiveSupport::TestCase
297
304
  test 'should find a user to set his password based on invitation_token' do
298
305
  user = new_user
299
306
  user.invite!
300
- invited_user = User.accept_invitation!(:invitation_token => user.invitation_token)
307
+ invited_user = User.accept_invitation!(:invitation_token => Thread.current[:token])
301
308
  assert_equal invited_user, user
302
309
  end
303
310
 
@@ -318,7 +325,7 @@ class InvitableTest < ActiveSupport::TestCase
318
325
  invited_user = User.invite!(:email => "valid@email.com")
319
326
  invited_user.invitation_created_at = 2.days.ago
320
327
  invited_user.save(:validate => false)
321
- user = User.accept_invitation!(:invitation_token => invited_user.invitation_token)
328
+ user = User.accept_invitation!(:invitation_token => Thread.current[:token])
322
329
  assert_equal user, invited_user
323
330
  assert_equal ["is invalid"], user.errors[:invitation_token]
324
331
  end
@@ -336,7 +343,7 @@ class InvitableTest < ActiveSupport::TestCase
336
343
  user.invite!
337
344
 
338
345
  invited_user = User.accept_invitation!(
339
- :invitation_token => user.invitation_token,
346
+ :invitation_token => Thread.current[:token],
340
347
  :password => 'new_password',
341
348
  :password_confirmation => 'new_password'
342
349
  )
@@ -350,16 +357,34 @@ class InvitableTest < ActiveSupport::TestCase
350
357
  user.invite!
351
358
 
352
359
  invited_user = User.accept_invitation!(
353
- :invitation_token => user.invitation_token,
360
+ :invitation_token => Thread.current[:token],
354
361
  :password => 'new_password',
355
362
  :password_confirmation => 'new_password',
356
363
  :username => 'a'*50
357
364
  )
358
365
  assert invited_user.errors[:username].present?
359
366
 
367
+ user.reload
360
368
  assert !user.valid_password?('new_password')
361
369
  end
362
370
 
371
+ test 'should set other attributes on accepting invitation' do
372
+ user = new_user(:password => nil, :password_confirmation => nil)
373
+ user.invite!
374
+
375
+ invited_user = User.accept_invitation!(
376
+ :invitation_token => Thread.current[:token],
377
+ :password => 'new_password',
378
+ :password_confirmation => 'new_password',
379
+ :username => 'a'
380
+ )
381
+ assert invited_user.errors[:username].blank?
382
+
383
+ user.reload
384
+ assert_equal 'a', user.username
385
+ assert user.valid_password?('new_password')
386
+ end
387
+
363
388
  test 'should not confirm user on invite' do
364
389
  user = new_user
365
390
 
@@ -34,7 +34,7 @@ class User < PARENT_MODEL_CLASS
34
34
 
35
35
  devise :database_authenticatable, :registerable, :validatable, :confirmable, :invitable, :recoverable
36
36
 
37
- attr_accessor :callback_works, :bio
37
+ attr_accessor :callback_works, :bio, :token
38
38
  validates :username, :length => { :maximum => 20 }
39
39
 
40
40
  attr_accessor :testing_accepting_or_not_invited
@@ -48,4 +48,9 @@ class User < PARENT_MODEL_CLASS
48
48
  after_invitation_accepted do |object|
49
49
  object.callback_works = true
50
50
  end
51
+
52
+ def send_devise_notification(method, raw, *args)
53
+ Thread.current[:token] = raw
54
+ super
55
+ end
51
56
  end
@@ -1,9 +1,15 @@
1
1
  # Use this hook to configure devise mailer, warden hooks and so forth.
2
2
  # Many of these configuration options can be set straight in your model.
3
3
  Devise.setup do |config|
4
+ # The secret key used by Devise. Devise uses this key to generate
5
+ # random tokens. Changing this key will render invalid all existing
6
+ # confirmation, reset password and unlock tokens in the database.
7
+ config.secret_key = 'e5151770530bba4c845256482be72bf598f723bebc057f4b8c15a7e63d7ffb61e7b8a0615e92a78127498ac509c43589b39bd13df397a4a2a7b3796c836e6ef8'
8
+
4
9
  # ==> Mailer Configuration
5
10
  # Configure the e-mail address which will be shown in Devise::Mailer,
6
- # note that it will be overwritten if you use your own mailer class with default "from" parameter.
11
+ # note that it will be overwritten if you use your own mailer class
12
+ # with default "from" parameter.
7
13
  config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com"
8
14
 
9
15
  # Configure the class responsible to send e-mails.
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise_invitable
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 2
9
- - 1
10
- version: 1.2.1
8
+ - 3
9
+ - 0
10
+ version: 1.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Sergio Cambra
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2013-08-22 00:00:00 Z
18
+ date: 2013-10-03 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: bundler
@@ -61,14 +61,14 @@ dependencies:
61
61
  requirement: &id003 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
- - - ~>
64
+ - - ">="
65
65
  - !ruby/object:Gem::Version
66
- hash: 7
66
+ hash: 3
67
67
  segments:
68
68
  - 3
69
+ - 1
69
70
  - 0
70
- - 0
71
- version: 3.0.0
71
+ version: 3.1.0
72
72
  type: :runtime
73
73
  version_requirements: *id003
74
74
  description: It adds support for send invitations by email (it requires to be authenticated) and accept the invitation by setting a password.
@@ -108,6 +108,7 @@ files:
108
108
  - lib/generators/mongoid/devise_invitable_generator.rb
109
109
  - LICENSE
110
110
  - README.rdoc
111
+ - CHANGELOG
111
112
  - test/functional/controller_helpers_test.rb
112
113
  - test/functional/registrations_controller_test.rb
113
114
  - test/generators/views_generator_test.rb
@@ -160,8 +161,8 @@ files:
160
161
  - test/routes_test.rb
161
162
  - test/test_helper.rb
162
163
  homepage: https://github.com/scambra/devise_invitable
163
- licenses: []
164
-
164
+ licenses:
165
+ - MIT
165
166
  post_install_message:
166
167
  rdoc_options:
167
168
  - --main
@@ -194,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
195
  requirements: []
195
196
 
196
197
  rubyforge_project:
197
- rubygems_version: 1.8.23
198
+ rubygems_version: 1.8.24
198
199
  signing_key:
199
200
  specification_version: 3
200
201
  summary: An invitation strategy for Devise
@@ -250,4 +251,3 @@ test_files:
250
251
  - test/rails_app/script/rails
251
252
  - test/routes_test.rb
252
253
  - test/test_helper.rb
253
- has_rdoc: