devise_invitable 1.4.0 → 1.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG +2 -0
- data/README.rdoc +10 -3
- data/app/controllers/devise/invitations_controller.rb +19 -10
- data/config/locales/en.yml +10 -9
- data/lib/devise_invitable/model.rb +39 -16
- data/lib/devise_invitable/parameter_sanitizer.rb +1 -1
- data/lib/devise_invitable/version.rb +1 -1
- data/lib/devise_invitable.rb +5 -0
- data/lib/generators/active_record/templates/migration.rb +3 -3
- data/lib/generators/devise_invitable/install_generator.rb +5 -0
- data/lib/generators/devise_invitable/templates/simple_form_for/invitations/edit.html.erb +1 -1
- data/lib/generators/devise_invitable/templates/simple_form_for/invitations/new.html.erb +1 -1
- data/test/functional/controller_helpers_test.rb +3 -3
- data/test/integration/invitation_remove_test.rb +1 -1
- data/test/integration/invitation_test.rb +49 -4
- data/test/models/invitable_test.rb +27 -8
- data/test/rails_app/app/controllers/application_controller.rb +7 -0
- data/test/rails_app/app/controllers/free_invitations_controller.rb +6 -0
- data/test/rails_app/app/views/devise/sessions/new.html.erb +17 -0
- data/test/rails_app/config/initializers/devise.rb +5 -0
- metadata +8 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9ebdddd2e1426fdde9ad700f7f60e4cedf3e48ab
|
|
4
|
+
data.tar.gz: 3a783c0d76c73a4a6af2c07cda15b80904fecdcf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b85b1eabee1011a5175f98ca92f05f39a1f49e3da2666f17bafa8a87d22115700d6f7beee89261a5096d70dcfeec473834dc43bfcaa5a967b63fc78b7789bb7f
|
|
7
|
+
data.tar.gz: 483b574690f8a3064b7d4f941a7fad4e321fe89d677192b82e68d0d0f3feba49d92125054f17e89521c2810e9feb80de12ee135895728d7d1f633bdd845845ea
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
Override valid_password? and unauthenticated_message instead of active_for_authentication? and inactive_message, active_for_authentication? doesn't work for default behavior of invited users without password
|
|
2
|
+
|
|
1
3
|
= 1.4.0
|
|
2
4
|
Override active_for_authentication? and inactive_message instead of valid_password?
|
|
3
5
|
To use counter_cache, invited_by_counter_cache must be set, no more checking of invitations_count to enable counter cache
|
data/README.rdoc
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
= DeviseInvitable
|
|
2
|
-
{<img src="https://travis-ci.org/scambra/devise_invitable.png"/>}[http://travis-ci.org/scambra/devise_invitable]
|
|
2
|
+
{<img src="https://travis-ci.org/scambra/devise_invitable.png"/>}[http://travis-ci.org/scambra/devise_invitable] {<img src="https://codeclimate.com/github/scambra/devise_invitable/badges/gpa.svg"/}[https://codeclimate.com/github/scambra/devise_invitable]
|
|
3
3
|
|
|
4
4
|
It adds support to devise[http://github.com/plataformatec/devise] for sending invitations by email (it requires to be authenticated) and accept the invitation setting the password.
|
|
5
5
|
|
|
@@ -132,6 +132,8 @@ or directly as parameters to the <tt>devise</tt> method:
|
|
|
132
132
|
|
|
133
133
|
* invited_by_class_name: The class name of the inviting model. If this is nil, polymorphic association is used.
|
|
134
134
|
|
|
135
|
+
* allow_insecure_sign_in_after_accept: automatically sign in the user after they set a password. Enabled by default.
|
|
136
|
+
|
|
135
137
|
For more details, see <tt>config/initializers/devise.rb</tt> (after you invoked the "devise_invitable:install" generator described above).
|
|
136
138
|
|
|
137
139
|
== Configuring views
|
|
@@ -254,9 +256,11 @@ You can add :skip_invitation to attributes hash if skip_invitation is added to a
|
|
|
254
256
|
User.invite!(:email => "new_user@example.com", :name => "John Doe", :skip_invitation => true)
|
|
255
257
|
# => the record will be created, but the invitation email will not be sent
|
|
256
258
|
|
|
257
|
-
Skip_invitation skips sending the email, but sets invitation_token, so invited_to_sign_up
|
|
259
|
+
Skip_invitation skips sending the email, but sets invitation_token, so <tt>invited_to_sign_up?</tt> on the
|
|
258
260
|
resulting user returns true.
|
|
259
261
|
|
|
262
|
+
To check if a perticular user is created by invitation, irrespective to state of invitation one can use <tt>created_by_invite?</tt>
|
|
263
|
+
|
|
260
264
|
**Warning**
|
|
261
265
|
|
|
262
266
|
When using skip_invitation you must send the email with the user object instance that generated the tokens, as
|
|
@@ -295,8 +299,9 @@ The callbacks support all options and arguments available to the standard callba
|
|
|
295
299
|
|
|
296
300
|
A pair of scopes to find those users that have accepted, and those that have not accepted, invitations are defined:
|
|
297
301
|
|
|
298
|
-
User.invitation_accepted
|
|
302
|
+
User.invitation_accepted # => returns all Users for whom the invitation_accepted_at attribute is not nil
|
|
299
303
|
User.invitation_not_accepted # => returns all Users for whom the invitation_accepted_at attribute is nil
|
|
304
|
+
User.created_by_invite # => returns all Users who are created by invitations, irrespective to invitation status
|
|
300
305
|
|
|
301
306
|
== Integration in a Rails application
|
|
302
307
|
|
|
@@ -355,6 +360,7 @@ DeviseInvitable uses flash messages with I18n with the flash keys <tt>:send_inst
|
|
|
355
360
|
send_instructions: 'An invitation email has been sent to %{email}.'
|
|
356
361
|
invitation_token_invalid: 'The invitation token provided is not valid!'
|
|
357
362
|
updated: 'Your password was set successfully. You are now signed in.'
|
|
363
|
+
updated_not_active: 'Your password was set successfully.'
|
|
358
364
|
|
|
359
365
|
You can also create distinct messages based on the resource you've configured using the singular name given in routes:
|
|
360
366
|
|
|
@@ -365,6 +371,7 @@ You can also create distinct messages based on the resource you've configured us
|
|
|
365
371
|
send_instructions: 'A new user invitation has been sent to %{email}.'
|
|
366
372
|
invitation_token_invalid: 'Your invitation token is not valid!'
|
|
367
373
|
updated: 'Welcome on board! You are now signed in.'
|
|
374
|
+
updated_not_active: 'Welcome on board! Sign in to continue.'
|
|
368
375
|
|
|
369
376
|
The DeviseInvitable mailer uses the same pattern as Devise to create mail subject messages:
|
|
370
377
|
|
|
@@ -15,13 +15,15 @@ class Devise::InvitationsController < DeviseController
|
|
|
15
15
|
# POST /resource/invitation
|
|
16
16
|
def create
|
|
17
17
|
self.resource = invite_resource
|
|
18
|
+
resource_invited = resource.errors.empty?
|
|
18
19
|
|
|
19
|
-
if
|
|
20
|
-
|
|
20
|
+
yield resource if block_given?
|
|
21
|
+
|
|
22
|
+
if resource_invited
|
|
21
23
|
if is_flashing_format? && self.resource.invitation_sent_at
|
|
22
24
|
set_flash_message :notice, :send_instructions, :email => self.resource.email
|
|
23
25
|
end
|
|
24
|
-
respond_with resource, :location => after_invite_path_for(
|
|
26
|
+
respond_with resource, :location => after_invite_path_for(current_inviter)
|
|
25
27
|
else
|
|
26
28
|
respond_with_navigational(resource) { render :new }
|
|
27
29
|
end
|
|
@@ -36,13 +38,20 @@ class Devise::InvitationsController < DeviseController
|
|
|
36
38
|
# PUT /resource/invitation
|
|
37
39
|
def update
|
|
38
40
|
self.resource = accept_resource
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
invitation_accepted = resource.errors.empty?
|
|
42
|
+
|
|
43
|
+
yield resource if block_given?
|
|
44
|
+
|
|
45
|
+
if invitation_accepted
|
|
46
|
+
if Devise.allow_insecure_sign_in_after_accept
|
|
47
|
+
flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
|
|
48
|
+
set_flash_message :notice, flash_message if is_flashing_format?
|
|
49
|
+
sign_in(resource_name, resource)
|
|
50
|
+
respond_with resource, :location => after_accept_path_for(resource)
|
|
51
|
+
else
|
|
52
|
+
set_flash_message :notice, :updated_not_active if is_flashing_format?
|
|
53
|
+
respond_with resource, :location => new_session_path(resource_name)
|
|
54
|
+
end
|
|
46
55
|
else
|
|
47
56
|
respond_with_navigational(resource){ render :edit }
|
|
48
57
|
end
|
data/config/locales/en.yml
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
en:
|
|
2
2
|
devise:
|
|
3
3
|
failure:
|
|
4
|
-
invited:
|
|
4
|
+
invited: "You have a pending invitation, accept it to finish creating your account."
|
|
5
5
|
invitations:
|
|
6
|
-
send_instructions:
|
|
7
|
-
invitation_token_invalid:
|
|
8
|
-
updated:
|
|
6
|
+
send_instructions: "An invitation email has been sent to %{email}."
|
|
7
|
+
invitation_token_invalid: "The invitation token provided is not valid!"
|
|
8
|
+
updated: "Your password was set successfully. You are now signed in."
|
|
9
|
+
updated_not_active: "Your password was set successfully."
|
|
9
10
|
no_invitations_remaining: "No invitations remaining"
|
|
10
|
-
invitation_removed:
|
|
11
|
+
invitation_removed: "Your invitation was removed."
|
|
11
12
|
new:
|
|
12
13
|
header: "Send invitation"
|
|
13
14
|
submit_button: "Send an invitation"
|
|
@@ -16,8 +17,8 @@ en:
|
|
|
16
17
|
submit_button: "Set my password"
|
|
17
18
|
mailer:
|
|
18
19
|
invitation_instructions:
|
|
19
|
-
subject:
|
|
20
|
-
hello:
|
|
21
|
-
someone_invited_you:
|
|
22
|
-
accept:
|
|
20
|
+
subject: "Invitation instructions"
|
|
21
|
+
hello: "Hello %{email}"
|
|
22
|
+
someone_invited_you: "Someone has invited you to %{url}, you can accept it through the link below."
|
|
23
|
+
accept: "Accept invitation"
|
|
23
24
|
ignore: "If you don't want to accept the invitation, please ignore this email.<br />Your account won't be created until you access the link above and set your password."
|
|
@@ -47,9 +47,11 @@ module Devise
|
|
|
47
47
|
|
|
48
48
|
scope :no_active_invitation, lambda { where(:invitation_token => nil) }
|
|
49
49
|
if defined?(Mongoid) && defined?(Mongoid::Document) && self < Mongoid::Document
|
|
50
|
+
scope :created_by_invite, lambda { where(:invitation_sent_at.ne => nil) }
|
|
50
51
|
scope :invitation_not_accepted, lambda { where(:invitation_accepted_at => nil, :invitation_token.ne => nil) }
|
|
51
52
|
scope :invitation_accepted, lambda { where(:invitation_accepted_at.ne => nil) }
|
|
52
53
|
else
|
|
54
|
+
scope :created_by_invite, lambda { where(arel_table[:invitation_sent_at].not_eq(nil)) }
|
|
53
55
|
scope :invitation_not_accepted, lambda { where(arel_table[:invitation_token].not_eq(nil)).where(:invitation_accepted_at => nil) }
|
|
54
56
|
scope :invitation_accepted, lambda { where(arel_table[:invitation_accepted_at].not_eq(nil)) }
|
|
55
57
|
|
|
@@ -85,6 +87,11 @@ module Devise
|
|
|
85
87
|
end
|
|
86
88
|
end
|
|
87
89
|
|
|
90
|
+
# Verify wheather a user is created by invitation, irrespective to invitation status
|
|
91
|
+
def created_by_invite?
|
|
92
|
+
invitation_sent_at.present?
|
|
93
|
+
end
|
|
94
|
+
|
|
88
95
|
# Verifies whether a user has been invited or not
|
|
89
96
|
def invited_to_sign_up?
|
|
90
97
|
persisted? && invitation_token.present?
|
|
@@ -102,27 +109,28 @@ module Devise
|
|
|
102
109
|
|
|
103
110
|
# Reset invitation token and send invitation again
|
|
104
111
|
def invite!(invited_by = nil)
|
|
112
|
+
# This is an order-dependant assignment, this can't be moved
|
|
105
113
|
was_invited = invited_to_sign_up?
|
|
106
114
|
|
|
107
115
|
# Required to workaround confirmable model's confirmation_required? method
|
|
108
116
|
# being implemented to check for non-nil value of confirmed_at
|
|
109
|
-
if
|
|
117
|
+
if new_record_and_responds_to?(:confirmation_required?)
|
|
110
118
|
def self.confirmation_required?; false; end
|
|
111
119
|
end
|
|
112
120
|
|
|
113
121
|
yield self if block_given?
|
|
114
|
-
generate_invitation_token if
|
|
122
|
+
generate_invitation_token if no_token_present_or_skip_invitation?
|
|
115
123
|
self.invitation_created_at = Time.now.utc
|
|
116
|
-
self.invitation_sent_at = self.invitation_created_at unless
|
|
124
|
+
self.invitation_sent_at = self.invitation_created_at unless skip_invitation
|
|
117
125
|
self.invited_by = invited_by if invited_by
|
|
118
126
|
|
|
119
127
|
# Call these before_validate methods since we aren't validating on save
|
|
120
|
-
self.downcase_keys if
|
|
121
|
-
self.strip_whitespace if
|
|
128
|
+
self.downcase_keys if new_record_and_responds_to?(:downcase_keys)
|
|
129
|
+
self.strip_whitespace if new_record_and_responds_to?(:strip_whitespace)
|
|
122
130
|
|
|
123
131
|
if save(:validate => false)
|
|
124
132
|
self.invited_by.decrement_invitation_limit! if !was_invited and self.invited_by.present?
|
|
125
|
-
deliver_invitation unless
|
|
133
|
+
deliver_invitation unless skip_invitation
|
|
126
134
|
end
|
|
127
135
|
end
|
|
128
136
|
|
|
@@ -134,16 +142,16 @@ module Devise
|
|
|
134
142
|
end
|
|
135
143
|
|
|
136
144
|
# Only verify password when is not invited
|
|
137
|
-
def
|
|
138
|
-
super unless
|
|
145
|
+
def valid_password?(password)
|
|
146
|
+
super unless block_from_invitation?
|
|
139
147
|
end
|
|
140
148
|
|
|
141
|
-
def
|
|
142
|
-
|
|
149
|
+
def unauthenticated_message
|
|
150
|
+
block_from_invitation? ? :invited : super
|
|
143
151
|
end
|
|
144
152
|
|
|
145
153
|
def after_password_reset
|
|
146
|
-
super
|
|
154
|
+
super if defined?(super)
|
|
147
155
|
accept_invitation! if invited_to_sign_up?
|
|
148
156
|
end
|
|
149
157
|
|
|
@@ -172,9 +180,16 @@ module Devise
|
|
|
172
180
|
protected
|
|
173
181
|
# Overriding the method in Devise's :validatable module so password is not required on inviting
|
|
174
182
|
def password_required?
|
|
175
|
-
|
|
183
|
+
!skip_password && super
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def skip_password
|
|
187
|
+
@skip_password ||= false
|
|
176
188
|
end
|
|
177
189
|
|
|
190
|
+
def block_from_invitation?
|
|
191
|
+
invited_to_sign_up?
|
|
192
|
+
end
|
|
178
193
|
|
|
179
194
|
# Checks if the invitation for the user is within the limit time.
|
|
180
195
|
# We do this by calculating if the difference between today and the
|
|
@@ -212,6 +227,14 @@ module Devise
|
|
|
212
227
|
generate_invitation_token && save(:validate => false)
|
|
213
228
|
end
|
|
214
229
|
|
|
230
|
+
def new_record_and_responds_to?(method)
|
|
231
|
+
self.new_record? && self.respond_to?(method, true)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def no_token_present_or_skip_invitation?
|
|
235
|
+
self.invitation_token.nil? || (!skip_invitation || @raw_invitation_token.nil?)
|
|
236
|
+
end
|
|
237
|
+
|
|
215
238
|
module ClassMethods
|
|
216
239
|
# Return fields to invite
|
|
217
240
|
def invite_key_fields
|
|
@@ -254,13 +277,11 @@ module Devise
|
|
|
254
277
|
end
|
|
255
278
|
|
|
256
279
|
def invite!(attributes={}, invited_by=nil, &block)
|
|
257
|
-
|
|
258
|
-
invitable
|
|
280
|
+
_invite(attributes.with_indifferent_access, invited_by, &block).first
|
|
259
281
|
end
|
|
260
282
|
|
|
261
283
|
def invite_mail!(attributes={}, invited_by=nil, &block)
|
|
262
|
-
|
|
263
|
-
mail
|
|
284
|
+
_invite(attributes, invited_by, &block).last
|
|
264
285
|
end
|
|
265
286
|
|
|
266
287
|
# Attempt to find a user by it's invitation_token to set it's password.
|
|
@@ -302,6 +323,8 @@ module Devise
|
|
|
302
323
|
Devise::Models.config(self, :invitation_limit)
|
|
303
324
|
Devise::Models.config(self, :invite_key)
|
|
304
325
|
Devise::Models.config(self, :resend_invitation)
|
|
326
|
+
Devise::Models.config(self, :allow_insecure_sign_in_after_accept)
|
|
327
|
+
|
|
305
328
|
end
|
|
306
329
|
end
|
|
307
330
|
end
|
|
@@ -20,7 +20,7 @@ module DeviseInvitable
|
|
|
20
20
|
def attributes_for_with_invitable(kind)
|
|
21
21
|
case kind
|
|
22
22
|
when :invite
|
|
23
|
-
resource_class.invite_key_fields
|
|
23
|
+
resource_class.respond_to?(:invite_key_fields) ? resource_class.invite_key_fields : []
|
|
24
24
|
when :accept_invitation
|
|
25
25
|
[:password, :password_confirmation, :invitation_token]
|
|
26
26
|
else attributes_for_without_invitable(kind)
|
data/lib/devise_invitable.rb
CHANGED
|
@@ -66,6 +66,11 @@ module Devise
|
|
|
66
66
|
# the #invited_by association is declared without counter_cache. (default: nil)
|
|
67
67
|
mattr_accessor :invited_by_counter_cache
|
|
68
68
|
@@invited_by_counter_cache = nil
|
|
69
|
+
|
|
70
|
+
# Public: Auto-login after the user accepts the invite. If this is false,
|
|
71
|
+
# the user will need to manually log in after accepting the invite (default: false).
|
|
72
|
+
mattr_accessor :allow_insecure_sign_in_after_accept
|
|
73
|
+
@@allow_insecure_sign_in_after_accept = true
|
|
69
74
|
end
|
|
70
75
|
|
|
71
76
|
Devise.add_module :invitable, :controller => :invitations, :model => 'devise_invitable/model', :route => {:invitation => [nil, :new, :accept]}
|
|
@@ -6,10 +6,10 @@ class DeviseInvitableAddTo<%= table_name.camelize %> < ActiveRecord::Migration
|
|
|
6
6
|
t.datetime :invitation_sent_at
|
|
7
7
|
t.datetime :invitation_accepted_at
|
|
8
8
|
t.integer :invitation_limit
|
|
9
|
-
t.references :invited_by, :
|
|
9
|
+
t.references :invited_by, polymorphic: true
|
|
10
10
|
t.integer :invitations_count, default: 0
|
|
11
11
|
t.index :invitations_count
|
|
12
|
-
t.index :invitation_token, :
|
|
12
|
+
t.index :invitation_token, unique: true # for invitable
|
|
13
13
|
t.index :invited_by_id
|
|
14
14
|
end
|
|
15
15
|
|
|
@@ -22,7 +22,7 @@ class DeviseInvitableAddTo<%= table_name.camelize %> < ActiveRecord::Migration
|
|
|
22
22
|
|
|
23
23
|
def down
|
|
24
24
|
change_table :<%= table_name %> do |t|
|
|
25
|
-
t.remove_references :invited_by, :
|
|
25
|
+
t.remove_references :invited_by, polymorphic: true
|
|
26
26
|
t.remove :invitations_count, :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token, :invitation_created_at
|
|
27
27
|
end
|
|
28
28
|
change_column_null :<%= table_name %>, :encrypted_password, false
|
|
@@ -57,6 +57,11 @@ module DeviseInvitable
|
|
|
57
57
|
# Default: nil
|
|
58
58
|
# config.invited_by_counter_cache = :invitations_count
|
|
59
59
|
|
|
60
|
+
# Auto-login after the user accepts the invite. If this is false,
|
|
61
|
+
# the user will need to manually log in after accepting the invite.
|
|
62
|
+
# Default: false
|
|
63
|
+
# config.allow_insecure_sign_in_after_accept = true
|
|
64
|
+
|
|
60
65
|
CONTENT
|
|
61
66
|
end
|
|
62
67
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<h2><%= t 'devise.invitations.edit.header' %></h2>
|
|
2
2
|
|
|
3
|
-
<%= simple_form_for resource, :
|
|
3
|
+
<%= simple_form_for resource, as: resource_name, url: invitation_path(resource_name), html: { method: :put } do |f| %>
|
|
4
4
|
<%= devise_error_messages! %>
|
|
5
5
|
<%= f.hidden_field :invitation_token %>
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<h2><%= t "devise.invitations.new.header" %></h2>
|
|
2
2
|
|
|
3
|
-
<%= simple_form_for resource, :
|
|
3
|
+
<%= simple_form_for resource, as: resource_name, url: invitation_path(resource_name), html: { method: :post} do |f| %>
|
|
4
4
|
<%= devise_error_messages! %>
|
|
5
5
|
|
|
6
6
|
<% resource.class.invite_key_fields.each do |field| -%>
|
|
@@ -4,11 +4,11 @@ class ControllerHelpersTest < ActionController::TestCase
|
|
|
4
4
|
tests ApplicationController
|
|
5
5
|
|
|
6
6
|
test "after invite path defaults to after sign in path" do
|
|
7
|
-
assert_equal @controller.
|
|
7
|
+
assert_equal @controller.send(:after_sign_in_path_for, :user), @controller.after_invite_path_for(:user)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
test "after accept path defaults to after sign in path" do
|
|
11
|
-
assert_equal @controller.
|
|
11
|
+
assert_equal @controller.send(:after_sign_in_path_for, :user), @controller.after_accept_path_for(:user)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
test 'after invite path is customizable from application controller' do
|
|
@@ -36,4 +36,4 @@ class ControllerHelpersTest < ActionController::TestCase
|
|
|
36
36
|
assert Devise::InvitationsController.method_defined? :after_accept_path_for
|
|
37
37
|
assert !Devise::InvitationsController.instance_methods(false).include?(:after_accept_path_for)
|
|
38
38
|
end
|
|
39
|
-
end
|
|
39
|
+
end
|
|
@@ -4,7 +4,7 @@ require 'integration_tests_helper'
|
|
|
4
4
|
class InvitationRemoveTest < ActionDispatch::IntegrationTest
|
|
5
5
|
|
|
6
6
|
test 'invited user can choose to remove his account/invite' do
|
|
7
|
-
|
|
7
|
+
User.invite!(:email => "valid@email.com")
|
|
8
8
|
|
|
9
9
|
# remove!
|
|
10
10
|
visit remove_user_invitation_path(:invitation_token => Thread.current[:token])
|
|
@@ -64,6 +64,25 @@ class InvitationTest < ActionDispatch::IntegrationTest
|
|
|
64
64
|
assert_equal root_path, current_path
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
test 'invited user without password should not be able to sign in' do
|
|
68
|
+
user = User.invite!(:email => "valid@email.com")
|
|
69
|
+
user.password = 'test'
|
|
70
|
+
sign_in_as_user user
|
|
71
|
+
|
|
72
|
+
assert_equal new_user_session_path, current_path
|
|
73
|
+
assert page.has_css?('p#alert', :text => 'You have a pending invitation, accept it to finish creating your account.')
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
test 'invited user with password should not be able to sign in' do
|
|
77
|
+
user = User.invite!(:email => "valid@email.com")
|
|
78
|
+
user.password = '987654321'
|
|
79
|
+
user.save
|
|
80
|
+
sign_in_as_user user
|
|
81
|
+
|
|
82
|
+
assert_equal new_user_session_path, current_path
|
|
83
|
+
assert page.has_css?('p#alert', :text => 'You have a pending invitation, accept it to finish creating your account.')
|
|
84
|
+
end
|
|
85
|
+
|
|
67
86
|
test 'not authenticated user with invalid invitation token should not be able to set his password' do
|
|
68
87
|
user = User.invite!(:email => "valid@email.com")
|
|
69
88
|
user.accept_invitation!
|
|
@@ -106,10 +125,26 @@ class InvitationTest < ActionDispatch::IntegrationTest
|
|
|
106
125
|
assert user.reload.valid_password?('987654321')
|
|
107
126
|
end
|
|
108
127
|
|
|
109
|
-
test 'sign in user automatically after setting it\'s password' do
|
|
110
|
-
|
|
128
|
+
test 'sign in user automatically after setting it\'s password if config.allow_insecure_sign_in_after_accept is true' do
|
|
129
|
+
original_option_value = Devise.allow_insecure_sign_in_after_accept
|
|
130
|
+
Devise.allow_insecure_sign_in_after_accept = true
|
|
131
|
+
|
|
132
|
+
User.invite!(:email => "valid@email.com")
|
|
111
133
|
set_password :invitation_token => Thread.current[:token]
|
|
134
|
+
|
|
112
135
|
assert_equal root_path, current_path
|
|
136
|
+
Devise.allow_insecure_sign_in_after_accept = original_option_value
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
test 'does not sign in user automatically after setting it\'s password if config.allow_insecure_sign_in_after_accept is false' do
|
|
140
|
+
original_option_value = Devise.allow_insecure_sign_in_after_accept
|
|
141
|
+
Devise.allow_insecure_sign_in_after_accept = false
|
|
142
|
+
|
|
143
|
+
User.invite!(:email => "valid@email.com")
|
|
144
|
+
set_password :invitation_token => Thread.current[:token]
|
|
145
|
+
|
|
146
|
+
assert_equal new_user_session_path, current_path
|
|
147
|
+
Devise.allow_insecure_sign_in_after_accept = original_option_value
|
|
113
148
|
end
|
|
114
149
|
|
|
115
150
|
test 'clear token and set invitation_accepted_at after recover password instead of accept_invitation' do
|
|
@@ -213,10 +248,20 @@ class InvitationTest < ActionDispatch::IntegrationTest
|
|
|
213
248
|
end
|
|
214
249
|
|
|
215
250
|
test 'authenticated admin should be able to send an admin invitation' do
|
|
216
|
-
|
|
251
|
+
admin = Admin.create(:email => 'admin@test.com', :password => '123456', :password_confirmation => '123456')
|
|
252
|
+
sign_in_as_user admin
|
|
217
253
|
|
|
218
254
|
send_invitation new_admin_path
|
|
219
|
-
assert_equal
|
|
255
|
+
assert_equal edit_admin_registration_path(admin), current_path
|
|
256
|
+
assert page.has_css?('p#notice', :text => 'An invitation email has been sent to user@test.com.')
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
test 'authenticated admin should be redirected to own page after send a free invitation' do
|
|
260
|
+
admin = Admin.create(:email => 'admin@test.com', :password => '123456', :password_confirmation => '123456')
|
|
261
|
+
sign_in_as_user admin
|
|
262
|
+
|
|
263
|
+
send_invitation new_free_invitation_path
|
|
264
|
+
assert_equal edit_admin_registration_path(admin), current_path
|
|
220
265
|
assert page.has_css?('p#notice', :text => 'An invitation email has been sent to user@test.com.')
|
|
221
266
|
end
|
|
222
267
|
end
|
|
@@ -169,7 +169,7 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
test 'should set password and password confirmation from params' do
|
|
172
|
-
|
|
172
|
+
User.invite!(:email => "valid@email.com")
|
|
173
173
|
user = User.accept_invitation!(:invitation_token => Thread.current[:token], :password => '123456789', :password_confirmation => '123456789')
|
|
174
174
|
assert user.valid_password?('123456789')
|
|
175
175
|
end
|
|
@@ -406,7 +406,7 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
406
406
|
user = new_user(:password => nil, :password_confirmation => nil)
|
|
407
407
|
user.invite!
|
|
408
408
|
|
|
409
|
-
|
|
409
|
+
User.accept_invitation!(
|
|
410
410
|
:invitation_token => Thread.current[:token],
|
|
411
411
|
:password => 'new_password',
|
|
412
412
|
:password_confirmation => 'new_password'
|
|
@@ -431,6 +431,21 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
431
431
|
user.reload
|
|
432
432
|
assert !user.valid_password?('new_password')
|
|
433
433
|
end
|
|
434
|
+
|
|
435
|
+
test 'should check if created by invitation' do
|
|
436
|
+
user = User.invite!(:email => "valid@email.com")
|
|
437
|
+
assert user.created_by_invite?
|
|
438
|
+
|
|
439
|
+
invited_user = User.accept_invitation!(
|
|
440
|
+
:invitation_token => Thread.current[:token],
|
|
441
|
+
:password => 'new_password',
|
|
442
|
+
:password_confirmation => 'new_password',
|
|
443
|
+
:username => 'a'
|
|
444
|
+
)
|
|
445
|
+
user.reload
|
|
446
|
+
assert user.created_by_invite?
|
|
447
|
+
end
|
|
448
|
+
|
|
434
449
|
|
|
435
450
|
test 'should set other attributes on accepting invitation' do
|
|
436
451
|
user = new_user(:password => nil, :password_confirmation => nil)
|
|
@@ -480,13 +495,13 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
480
495
|
|
|
481
496
|
test 'should not send an invitation if we want to skip the invitation' do
|
|
482
497
|
assert_no_difference('ActionMailer::Base.deliveries.size') do
|
|
483
|
-
|
|
498
|
+
User.invite!(:email => "valid@email.com", :username => "a"*50, :skip_invitation => true)
|
|
484
499
|
end
|
|
485
500
|
end
|
|
486
501
|
|
|
487
502
|
test 'should not send an invitation if we want to skip the invitation with block' do
|
|
488
503
|
assert_no_difference('ActionMailer::Base.deliveries.size') do
|
|
489
|
-
|
|
504
|
+
User.invite!(:email => "valid@email.com", :username => "a"*50) do |u|
|
|
490
505
|
u.skip_invitation = true
|
|
491
506
|
end
|
|
492
507
|
end
|
|
@@ -606,24 +621,28 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
606
621
|
end
|
|
607
622
|
|
|
608
623
|
test 'should return instance with errors if invitation_token is nil' do
|
|
609
|
-
|
|
624
|
+
User.create(:email => 'admin@test.com', :password => '123456', :password_confirmation => '123456')
|
|
610
625
|
user = User.accept_invitation!
|
|
611
626
|
assert !user.errors.empty?
|
|
612
627
|
end
|
|
613
628
|
|
|
614
|
-
test "should count accepted and not accepted invitations" do
|
|
629
|
+
test "should count invited, created_by_invite, accepted and not accepted invitations" do
|
|
615
630
|
assert_equal 0, User.invitation_not_accepted.count
|
|
616
631
|
assert_equal 0, User.invitation_accepted.count
|
|
632
|
+
assert_equal 0, User.created_by_invite.count
|
|
617
633
|
|
|
618
634
|
User.invite!(:email => "invalid@email.com")
|
|
635
|
+
User.invite!(:email => "another_invalid@email.com")
|
|
619
636
|
user = User.invite!(:email => "valid@email.com")
|
|
620
637
|
|
|
621
|
-
assert_equal
|
|
638
|
+
assert_equal 3, User.invitation_not_accepted.count
|
|
622
639
|
assert_equal 0, User.invitation_accepted.count
|
|
640
|
+
assert_equal 3, User.created_by_invite.count
|
|
623
641
|
|
|
624
642
|
user.accept_invitation!
|
|
625
|
-
assert_equal
|
|
643
|
+
assert_equal 2, User.invitation_not_accepted.count
|
|
626
644
|
assert_equal 1, User.invitation_accepted.count
|
|
645
|
+
assert_equal 3, User.created_by_invite.count
|
|
627
646
|
end
|
|
628
647
|
|
|
629
648
|
test "should preserve return values of Devise::Recoverable#reset_password!" do
|
|
@@ -3,6 +3,13 @@ class ApplicationController < ActionController::Base
|
|
|
3
3
|
before_filter :configure_permitted_parameters, :if => :devise_controller?
|
|
4
4
|
|
|
5
5
|
protected
|
|
6
|
+
def after_sign_in_path_for(resource)
|
|
7
|
+
if resource.is_a? Admin
|
|
8
|
+
edit_admin_registration_path(resource)
|
|
9
|
+
else
|
|
10
|
+
super
|
|
11
|
+
end
|
|
12
|
+
end
|
|
6
13
|
|
|
7
14
|
def configure_permitted_parameters
|
|
8
15
|
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:email, :password, :bio) } if defined?(ActionController::StrongParameters)
|
|
@@ -3,4 +3,10 @@ class FreeInvitationsController < Devise::InvitationsController
|
|
|
3
3
|
def authenticate_inviter!
|
|
4
4
|
# everyone can invite
|
|
5
5
|
end
|
|
6
|
+
def current_inviter
|
|
7
|
+
current_admin || current_user
|
|
8
|
+
end
|
|
9
|
+
def after_invite_path_for(resource)
|
|
10
|
+
resource ? super : root_path
|
|
11
|
+
end
|
|
6
12
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<h2>Log in</h2>
|
|
2
|
+
|
|
3
|
+
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
|
|
4
|
+
<div><%= f.label :email %><br />
|
|
5
|
+
<%= f.email_field :email, autofocus: true %></div>
|
|
6
|
+
|
|
7
|
+
<div><%= f.label :password %><br />
|
|
8
|
+
<%= f.password_field :password, autocomplete: "off" %></div>
|
|
9
|
+
|
|
10
|
+
<% if devise_mapping.rememberable? -%>
|
|
11
|
+
<div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
|
|
12
|
+
<% end -%>
|
|
13
|
+
|
|
14
|
+
<div><%= f.submit "Log in" %></div>
|
|
15
|
+
<% end %>
|
|
16
|
+
|
|
17
|
+
<%= render "devise/shared/links" %>
|
|
@@ -129,6 +129,11 @@ Devise.setup do |config|
|
|
|
129
129
|
# Default: nil
|
|
130
130
|
config.invited_by_counter_cache = :invitations_count
|
|
131
131
|
|
|
132
|
+
# Auto-login after the user accepts the invite. If this is false,
|
|
133
|
+
# the user will need to manually log in after accepting the invite.
|
|
134
|
+
# Default: true
|
|
135
|
+
# config.allow_insecure_sign_in_after_accept = false
|
|
136
|
+
|
|
132
137
|
# ==> Configuration for :confirmable
|
|
133
138
|
# A period that the user is allowed to access the website even without
|
|
134
139
|
# confirming his account. For instance, if set to 2.days, the user will be
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: devise_invitable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sergio Cambra
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2015-06-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -66,6 +66,9 @@ executables: []
|
|
|
66
66
|
extensions: []
|
|
67
67
|
extra_rdoc_files: []
|
|
68
68
|
files:
|
|
69
|
+
- CHANGELOG
|
|
70
|
+
- LICENSE
|
|
71
|
+
- README.rdoc
|
|
69
72
|
- app/controllers/devise/invitations_controller.rb
|
|
70
73
|
- app/controllers/devise_invitable/registrations_controller.rb
|
|
71
74
|
- app/views/devise/invitations/edit.html.erb
|
|
@@ -90,9 +93,6 @@ files:
|
|
|
90
93
|
- lib/generators/devise_invitable/templates/simple_form_for/invitations/new.html.erb
|
|
91
94
|
- lib/generators/devise_invitable/views_generator.rb
|
|
92
95
|
- lib/generators/mongoid/devise_invitable_generator.rb
|
|
93
|
-
- LICENSE
|
|
94
|
-
- README.rdoc
|
|
95
|
-
- CHANGELOG
|
|
96
96
|
- test/functional/controller_helpers_test.rb
|
|
97
97
|
- test/functional/registrations_controller_test.rb
|
|
98
98
|
- test/generators/views_generator_test.rb
|
|
@@ -117,6 +117,7 @@ files:
|
|
|
117
117
|
- test/rails_app/app/models/octopussy.rb
|
|
118
118
|
- test/rails_app/app/models/user.rb
|
|
119
119
|
- test/rails_app/app/views/admins/new.html.erb
|
|
120
|
+
- test/rails_app/app/views/devise/sessions/new.html.erb
|
|
120
121
|
- test/rails_app/app/views/free_invitations/new.html.erb
|
|
121
122
|
- test/rails_app/app/views/home/index.html.erb
|
|
122
123
|
- test/rails_app/app/views/layouts/application.html.erb
|
|
@@ -167,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
167
168
|
version: 1.3.6
|
|
168
169
|
requirements: []
|
|
169
170
|
rubyforge_project:
|
|
170
|
-
rubygems_version: 2.
|
|
171
|
+
rubygems_version: 2.4.5
|
|
171
172
|
signing_key:
|
|
172
173
|
specification_version: 4
|
|
173
174
|
summary: An invitation strategy for Devise
|
|
@@ -196,6 +197,7 @@ test_files:
|
|
|
196
197
|
- test/rails_app/app/models/octopussy.rb
|
|
197
198
|
- test/rails_app/app/models/user.rb
|
|
198
199
|
- test/rails_app/app/views/admins/new.html.erb
|
|
200
|
+
- test/rails_app/app/views/devise/sessions/new.html.erb
|
|
199
201
|
- test/rails_app/app/views/free_invitations/new.html.erb
|
|
200
202
|
- test/rails_app/app/views/home/index.html.erb
|
|
201
203
|
- test/rails_app/app/views/layouts/application.html.erb
|