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 +5 -0
- data/README.rdoc +31 -14
- data/app/controllers/devise/invitations_controller.rb +10 -4
- data/app/views/devise/mailer/invitation_instructions.html.erb +1 -1
- data/lib/devise_invitable/mailer.rb +3 -7
- data/lib/devise_invitable/model.rb +37 -39
- data/lib/devise_invitable/version.rb +1 -1
- data/lib/generators/active_record/templates/migration.rb +1 -1
- data/test/integration/invitation_remove_test.rb +5 -5
- data/test/integration/invitation_test.rb +5 -6
- data/test/mailers/invitation_mail_test.rb +12 -2
- data/test/models/invitable_test.rb +51 -26
- data/test/rails_app/app/models/user.rb +6 -1
- data/test/rails_app/config/initializers/devise.rb +7 -1
- metadata +13 -13
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
|
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
|
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
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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 =
|
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.
|
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.
|
80
|
+
devise_parameter_sanitizer.sanitize(:invite)
|
75
81
|
end
|
76
82
|
|
77
83
|
def update_resource_params
|
78
|
-
devise_parameter_sanitizer.
|
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 => @
|
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
|
-
|
9
|
-
|
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
|
162
|
-
|
163
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
254
|
-
|
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
|
-
|
277
|
-
invitable
|
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)
|
@@ -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
|
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 =>
|
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 =>
|
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 =
|
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 =>
|
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 =>
|
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 =>
|
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 =>
|
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
|
-
|
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
|
-
|
57
|
-
|
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
|
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
|
-
|
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.
|
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 =>
|
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 =>
|
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.
|
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 =>
|
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.
|
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 =>
|
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.
|
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 =>
|
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 =>
|
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 =>
|
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 =>
|
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 =>
|
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
|
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:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 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-
|
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:
|
66
|
+
hash: 3
|
67
67
|
segments:
|
68
68
|
- 3
|
69
|
+
- 1
|
69
70
|
- 0
|
70
|
-
|
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.
|
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:
|