rails_jwt_auth 0.17.1 → 0.18.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7c2aed170511243b66a49dc2913ba59c5e00e5b5
4
- data.tar.gz: a7cb07b8a569c4af7f6b65211be0109ba0d88ee0
3
+ metadata.gz: f62382c4d00178ccea3aa3a95e9fce5a2ce51909
4
+ data.tar.gz: 88608b4d059215392fb2c2c5b9a22478ca3ce915
5
5
  SHA512:
6
- metadata.gz: 14f8f7d270903f22e5adffab7f158e4d4a0c24ccae6bb6abd2d9cd9908901980b7699800ab00ee5ddbdef82446e51c6f2944f7176ffcc8dcb386945eb989ed4b
7
- data.tar.gz: 7f326f34d1c084dbed94362941f7d4f9d493f23fdba54f1ccaa43ac60a8ac71f0c25088764caeb26c3ec4f254b290ae3b55b725643cd60210077bed23976b6be
6
+ metadata.gz: 6c9483c6c630e56a44880cd19e5efa76f6ab055ac64a577802dea27d8ccac3c6c926b5f3299cb2217911a1849695aae3e8abdb2c5820205597719e1ab6c21661
7
+ data.tar.gz: a179d9f7eb6d28b360c5ecaba632f4124b755cb95d5bc6698aee80db6e401144a47ac74a63e5be1a5445c017bb218a0af3794a2685db1b4331f852934bfcceed
data/README.md CHANGED
@@ -1,4 +1,6 @@
1
1
  # RailsJwtAuth
2
+ [![Gem Version](https://badge.fury.io/rb/rails_jwt_auth.svg)](https://badge.fury.io/rb/rails_jwt_auth)
3
+ ![Build Status](https://travis-ci.org/rjurado01/rails_jwt_auth.svg?branch=master)
2
4
 
3
5
  Rails-API authentication solution based on Warden and JWT and inspired by Devise.
4
6
 
@@ -48,6 +50,8 @@ You can edit configuration options into `config/initializers/auth_token_auth.rb`
48
50
  | reset_password_expiration_time | 1.day | Confirmation token expiration time |
49
51
  | set_password_url | password_path | Url used to create email link with set password token |
50
52
  | deliver_later | false | Uses `deliver_later` method to send emails |
53
+ | invitation_expiration_time | 2.days | Time an invitation is valid and can be accepted |
54
+ | invitation_url | invitation_path | URL used to create email link with invitation token |
51
55
 
52
56
  ## Authenticatable
53
57
 
@@ -211,6 +215,46 @@ class User
211
215
  end
212
216
  ```
213
217
 
218
+ ## Invitable
219
+
220
+ This module allows you to invite an user to your application sending an invitation mail with a unique link and complete registration by setting user's password.
221
+
222
+ ### ActiveRecord
223
+
224
+ Include `RailsJwtAuth::Invitable` module in your User model:
225
+
226
+ ```ruby
227
+ # app/models/user.rb
228
+ class User < ApplicationRecord
229
+ include RailsJwtAuth::Authenticatable
230
+ include RailsJwtAuth::Invitable
231
+ end
232
+ ```
233
+
234
+ And create the corresponding migration
235
+
236
+ ```ruby
237
+ # Example migration
238
+ change_table :users do |t|
239
+ t.string :invitation_token
240
+ t.datetime :invitation_sent_at
241
+ t.datetime :invitation_accepted_at
242
+ t.datetime :invitation_created_at
243
+ end
244
+ ```
245
+
246
+ ### Mongoid
247
+
248
+ Include `RailsJwtAuth::Invitable` module in your User model:
249
+
250
+ ```ruby
251
+ # app/models/user.rb
252
+ class User < ApplicationRecord
253
+ include RailsJwtAuth::Authenticatable
254
+ include RailsJwtAuth::Invitable
255
+ end
256
+ ```
257
+
214
258
  ## Controller helpers
215
259
 
216
260
  RailsJwtAuth will create some helpers to use inside your controllers.
@@ -368,6 +412,44 @@ Password api is defined by RailsJwtAuth::PasswordsController.
368
412
  }
369
413
  ```
370
414
 
415
+ ### Invitations
416
+
417
+ Invitations api is provided by RailsJwtAuth::InvitationsController.
418
+
419
+ 1. Create an invitation and send email:
420
+
421
+ ```js
422
+ {
423
+ url: host/invitation,
424
+ method: POST,
425
+ data: {
426
+ invitation: {
427
+ email: "user@example.com",
428
+ // More fields of your user
429
+ }
430
+ }
431
+ }
432
+ ```
433
+
434
+ 2. Accept an invitation:
435
+
436
+ ```js
437
+ {
438
+ url: host/invitation,
439
+ method: PUT,
440
+ data: {
441
+ accept_invitation: {
442
+ invitation_token: "token",
443
+ password: '1234',
444
+ password_confirmation: '1234',
445
+ // More fields of your user...
446
+ }
447
+ }
448
+ }
449
+ ```
450
+
451
+ Note: To add more fields, see "Custom strong parameters" below.
452
+
371
453
  ## Custom controllers
372
454
 
373
455
  You can overwrite RailsJwtAuth controllers to edit actions, responses,
@@ -420,7 +502,7 @@ end
420
502
 
421
503
  ## Register users with random password
422
504
 
423
- This is a controller example that allows admins to register users with random password and send email to reset it.
505
+ This is a controller example that allows admins to register users with random password and send email to reset it.
424
506
  If registration is sucess it will send email to `set_password_url` with reset password token.
425
507
 
426
508
  ```ruby
data/Rakefile CHANGED
@@ -17,10 +17,11 @@ end
17
17
  APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
18
18
  load 'rails/tasks/engine.rake'
19
19
 
20
-
21
20
  load 'rails/tasks/statistics.rake'
22
21
 
23
-
24
-
25
22
  require 'bundler/gem_tasks'
23
+ require "rspec/core/rake_task"
24
+
25
+ RSpec::Core::RakeTask.new(:spec)
26
26
 
27
+ task :default => :spec
@@ -23,5 +23,13 @@ module RailsJwtAuth
23
23
  def password_update_params
24
24
  params.require(:password).permit(:password, :password_confirmation)
25
25
  end
26
+
27
+ def invitation_create_params
28
+ params.require(:invitation).permit(:email)
29
+ end
30
+
31
+ def invitation_update_params
32
+ params.require(:accept_invitation).permit(:invitation_token, :password, :password_confirmation)
33
+ end
26
34
  end
27
35
  end
@@ -0,0 +1,27 @@
1
+ module RailsJwtAuth
2
+ class InvitationsController < ApplicationController
3
+ include ParamsHelper
4
+ include RenderHelper
5
+
6
+ def create
7
+ attr_hash = invitation_create_params
8
+ user = RailsJwtAuth.model.invite!(attr_hash)
9
+ user.errors.empty? ? render_204 : render_422(user.errors)
10
+ end
11
+
12
+ def update
13
+ attr_hash = invitation_update_params
14
+ token = attr_hash.delete(:invitation_token)
15
+ user = RailsJwtAuth.model.where(invitation_token: token).first
16
+ user.assign_attributes attr_hash
17
+ user.accept_invitation!
18
+
19
+ if user.errors.empty? && user.save
20
+ return render_204
21
+ else
22
+ user.update_attribute :invitation_token, token
23
+ return render_422(user.errors)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -52,5 +52,21 @@ if defined?(ActionMailer)
52
52
  subject = I18n.t('rails_jwt_auth.mailer.set_password_instructions.subject')
53
53
  mail(to: @user.email, subject: subject)
54
54
  end
55
+
56
+ def send_invitation(user)
57
+ @user = user
58
+
59
+ if RailsJwtAuth.invitation_url
60
+ url, params = RailsJwtAuth.invitation_url.split '?'
61
+ params = params ? params.split('&') : []
62
+ params.push("invitation_token=#{@user.invitation_token}")
63
+ @accept_invitation_url = "#{url}?#{params.join('&')}"
64
+ else
65
+ @accept_invitation_url = invitation_url(invitation_token: @user.invitation_token)
66
+ end
67
+
68
+ subject = I18n.t('rails_jwt_auth.mailer.send_invitation.subject')
69
+ mail(to: @user.email, subject: subject)
70
+ end
55
71
  end
56
72
  end
@@ -49,24 +49,26 @@ module RailsJwtAuth
49
49
  end
50
50
 
51
51
  def self.included(base)
52
- if defined?(Mongoid) && base.ancestors.include?(Mongoid::Document)
53
- base.send(:field, RailsJwtAuth.auth_field_name, type: String)
54
- base.send(:field, :password_digest, type: String)
55
- base.send(:field, :auth_tokens, type: Array)
56
- elsif defined?(ActiveRecord) && base.ancestors.include?(ActiveRecord::Base)
57
- base.send(:serialize, :auth_tokens, Array)
58
- end
52
+ base.extend(ClassMethods)
59
53
 
60
- base.send(:validates, RailsJwtAuth.auth_field_name, presence: true, uniqueness: true)
61
- base.send(:validates, RailsJwtAuth.auth_field_name, email: true) if RailsJwtAuth.auth_field_email
54
+ base.class_eval do
55
+ if defined?(Mongoid) && ancestors.include?(Mongoid::Document)
56
+ field RailsJwtAuth.auth_field_name, type: String
57
+ field :password_digest, type: String
58
+ field :auth_tokens, type: Array
59
+ elsif defined?(ActiveRecord) && ancestors.include?(ActiveRecord::Base)
60
+ serialize :auth_tokens, Array
61
+ end
62
62
 
63
- base.send(:has_secure_password)
63
+ validates RailsJwtAuth.auth_field_name, presence: true, uniqueness: true
64
+ validates RailsJwtAuth.auth_field_name, email: true if RailsJwtAuth.auth_field_email
64
65
 
65
- base.send(:before_validation) do
66
- self.email = email.downcase if self.email
67
- end
66
+ has_secure_password
68
67
 
69
- base.extend(ClassMethods)
68
+ before_validation do
69
+ self.email = email.downcase if email
70
+ end
71
+ end
70
72
  end
71
73
  end
72
74
  end
@@ -37,34 +37,36 @@ module RailsJwtAuth
37
37
  end
38
38
 
39
39
  def self.included(base)
40
- if base.ancestors.include? Mongoid::Document
41
- # include GlobalID::Identification to use deliver_later method
42
- # http://edgeguides.rubyonrails.org/active_job_basics.html#globalid
43
- base.send(:include, GlobalID::Identification) if RailsJwtAuth.deliver_later
44
-
45
- base.send(:field, :email, type: String)
46
- base.send(:field, :unconfirmed_email, type: String)
47
- base.send(:field, :confirmation_token, type: String)
48
- base.send(:field, :confirmation_sent_at, type: Time)
49
- base.send(:field, :confirmed_at, type: Time)
50
- end
40
+ base.class_eval do
41
+ if ancestors.include? Mongoid::Document
42
+ # include GlobalID::Identification to use deliver_later method
43
+ # http://edgeguides.rubyonrails.org/active_job_basics.html#globalid
44
+ include GlobalID::Identification if RailsJwtAuth.deliver_later
45
+
46
+ field :email, type: String
47
+ field :unconfirmed_email, type: String
48
+ field :confirmation_token, type: String
49
+ field :confirmation_sent_at, type: Time
50
+ field :confirmed_at, type: Time
51
+ end
51
52
 
52
- base.send(:validate, :validate_confirmation, if: :confirmed_at_changed?)
53
+ validate :validate_confirmation, if: :confirmed_at_changed?
53
54
 
54
- base.send(:after_create) do
55
- send_confirmation_instructions unless confirmed_at || confirmation_sent_at
56
- end
55
+ after_create do
56
+ send_confirmation_instructions unless confirmed_at || confirmation_sent_at
57
+ end
57
58
 
58
- base.send(:before_update) do
59
- if email_changed? && email_was && !confirmed_at_changed?
60
- self.unconfirmed_email = email
61
- self.email = email_was
59
+ before_update do
60
+ if email_changed? && email_was && !confirmed_at_changed?
61
+ self.unconfirmed_email = email
62
+ self.email = email_was
62
63
 
63
- self.confirmation_token = SecureRandom.base58(24)
64
- self.confirmation_sent_at = Time.now
64
+ self.confirmation_token = SecureRandom.base58(24)
65
+ self.confirmation_sent_at = Time.now
65
66
 
66
- mailer = Mailer.confirmation_instructions(self)
67
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
67
+ mailer = Mailer.confirmation_instructions(self)
68
+ RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
69
+ end
68
70
  end
69
71
  end
70
72
  end
@@ -0,0 +1,113 @@
1
+ module RailsJwtAuth
2
+ module Invitable
3
+ extend ActiveSupport::Concern
4
+
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ base.class_eval do
8
+ if ancestors.include? Mongoid::Document
9
+ # include GlobalID::Identification to use deliver_later method
10
+ # http://edgeguides.rubyonrails.org/active_job_basics.html#globalid
11
+ include GlobalID::Identification if RailsJwtAuth.deliver_later
12
+
13
+ field :invitation_token, type: String
14
+ field :invitation_sent_at, type: Time
15
+ field :invitation_accepted_at, type: Time
16
+ field :invitation_created_at, type: Time
17
+
18
+ index({invitation_token: 1}, {unique: true})
19
+ end
20
+ end
21
+ end
22
+
23
+ module ClassMethods
24
+ # Creates an user and sends an invitation to him.
25
+ # If the user is already invited and pending of completing registration
26
+ # the invitation is resent by email.
27
+ # If the user is already registered, it returns the user with a
28
+ # <tt>:taken</tt> on the email field.
29
+ #
30
+ # @param [Hash] attributes Hash containing user's attributes to be filled.
31
+ # Must contain an email key.
32
+ #
33
+ #
34
+ # @return [user] The user created or found by email.
35
+
36
+ def invite!(attributes={})
37
+ attrs = ActiveSupport::HashWithIndifferentAccess.new(attributes.to_h)
38
+ auth_field = RailsJwtAuth.auth_field_name
39
+ auth_attribute = attrs.delete(auth_field)
40
+
41
+ raise ArgumentError unless auth_attribute
42
+
43
+ record = RailsJwtAuth.model.find_or_initialize_by(auth_field => auth_attribute)
44
+ record.assign_attributes(attrs)
45
+ record.invitation_created_at = Time.now.utc if record.new_record?
46
+
47
+ unless record.password || record.password_digest
48
+ password = SecureRandom.base58(16)
49
+ record.password = password
50
+ record.password_confirmation = password
51
+ end
52
+
53
+ record.valid?
54
+
55
+ if !record.new_record? && !record.invited?
56
+ record.errors.add(RailsJwtAuth.auth_field_name, :taken)
57
+ end
58
+
59
+ record.invite! if record.errors.empty?
60
+ record
61
+ end
62
+ end
63
+
64
+ # Accept an invitation by clearing token and setting invitation_accepted_at
65
+ def accept_invitation
66
+ self.invitation_accepted_at = Time.now.utc
67
+ self.invitation_token = nil
68
+ end
69
+
70
+ def accept_invitation!
71
+ return unless invited?
72
+ if valid_invitation?
73
+ accept_invitation
74
+ # Override confirmable
75
+ self.confirmed_at = invitation_accepted_at if respond_to? :confirmed_at
76
+ else
77
+ errors.add(:invitation_token, :invalid)
78
+ end
79
+ end
80
+
81
+ def invite!
82
+ generate_invitation_token if invitation_token.nil?
83
+ self.invitation_created_at = Time.now.utc
84
+
85
+ deliver_invitation if save(validate: false)
86
+ end
87
+
88
+ def invited?
89
+ (persisted? && invitation_token.present?)
90
+ end
91
+
92
+ protected
93
+
94
+ def deliver_invitation
95
+ mailer = Mailer.send_invitation(self)
96
+ RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
97
+ end
98
+
99
+ def generate_invitation_token
100
+ self.invitation_token = SecureRandom.base58(128)
101
+ end
102
+
103
+ def valid_invitation?
104
+ invited? && invitation_period_valid?
105
+ end
106
+
107
+ def invitation_period_valid?
108
+ time = invitation_sent_at || invitation_created_at
109
+ expiration_time = RailsJwtAuth.invitation_expiration_time
110
+ time && (expiration_time.to_i.zero? || time.utc >= expiration_time.ago)
111
+ end
112
+ end
113
+ end
@@ -31,20 +31,22 @@ module RailsJwtAuth
31
31
  end
32
32
 
33
33
  def self.included(base)
34
- if base.ancestors.include? Mongoid::Document
35
- # include GlobalID::Identification to use deliver_later method
36
- # http://edgeguides.rubyonrails.org/active_job_basics.html#globalid
37
- base.send(:include, GlobalID::Identification) if RailsJwtAuth.deliver_later
38
-
39
- base.send(:field, :reset_password_token, type: String)
40
- base.send(:field, :reset_password_sent_at, type: Time)
41
- end
34
+ base.class_eval do
35
+ if base.ancestors.include? Mongoid::Document
36
+ # include GlobalID::Identification to use deliver_later method
37
+ # http://edgeguides.rubyonrails.org/active_job_basics.html#globalid
38
+ include GlobalID::Identification if RailsJwtAuth.deliver_later
39
+
40
+ field :reset_password_token, type: String
41
+ field :reset_password_sent_at, type: Time
42
+ end
42
43
 
43
- base.send(:validate, :validate_reset_password_token, if: :password_digest_changed?)
44
+ validate :validate_reset_password_token, if: :password_digest_changed?
44
45
 
45
- base.send(:before_update) do
46
- if password_digest_changed? && reset_password_token
47
- self.reset_password_token = nil
46
+ before_update do
47
+ if password_digest_changed? && reset_password_token
48
+ self.reset_password_token = nil
49
+ end
48
50
  end
49
51
  end
50
52
  end
@@ -7,9 +7,11 @@ module RailsJwtAuth
7
7
  end
8
8
 
9
9
  def self.included(base)
10
- if defined?(Mongoid) && base.ancestors.include?(Mongoid::Document)
11
- base.send(:field, :last_sign_in_at, type: Time)
12
- base.send(:field, :last_sign_in_ip, type: String)
10
+ base.class_eval do
11
+ if defined?(Mongoid) && ancestors.include?(Mongoid::Document)
12
+ field :last_sign_in_at, type: Time
13
+ field :last_sign_in_ip, type: String
14
+ end
13
15
  end
14
16
  end
15
17
  end
@@ -0,0 +1,6 @@
1
+ <p>Hello <%= @user.email %>!</p>
2
+
3
+ <p>Someone has sent you an invitation to App.</p>
4
+ <p>To complete registration setting a password, please click the following link.</p>
5
+
6
+ <p><%= link_to "Accept invitation", @accept_invitation_url.html_safe %></p>
@@ -7,6 +7,9 @@ en:
7
7
  subject: "Reset password instructions"
8
8
  set_password_instructions:
9
9
  subject: "Set password instructions"
10
+ send_invitation:
11
+ subject: "Someone has sent you an invitation!"
12
+
10
13
  errors:
11
14
  unconfirmed: "unconfirmed email"
12
15
  already_confirmed: "was already confirmed, please try signing in"
@@ -15,6 +18,7 @@ en:
15
18
  invalid: "invalid"
16
19
  blank: "blank"
17
20
  not_found: "not found"
21
+ missing: "is missing"
18
22
  email:
19
23
  invalid: "is not an email"
20
24
  current_password:
@@ -11,5 +11,7 @@ class RailsJwtAuth::InstallGenerator < Rails::Generators::Base
11
11
 
12
12
  route "resource :confirmation, controller: 'rails_jwt_auth/confirmations', only: [:create, :update]"
13
13
  route "resource :password, controller: 'rails_jwt_auth/passwords', only: [:create, :update]"
14
+
15
+ route "resource :invitation, controller: 'rails_jwt_auth/invitations', only: [:create, :update]"
14
16
  end
15
17
  end
@@ -40,4 +40,12 @@ RailsJwtAuth.setup do |config|
40
40
 
41
41
  # uses deliver_later to send emails instead of deliver method
42
42
  #config.deliver_later = false
43
+
44
+ # Invitable configuration
45
+ #
46
+ # Time an invitation is valid after sent
47
+ # config.invitation_expiration_time = 2.days
48
+ #
49
+ # URL used to create email link to activate invitation
50
+ # config.accept_invitation_url = 'http://frontend.com/accept_invitation'
43
51
  end
@@ -46,6 +46,12 @@ module RailsJwtAuth
46
46
  mattr_accessor :deliver_later
47
47
  @@deliver_later = false
48
48
 
49
+ mattr_accessor :invitation_expiration_time
50
+ @@invitation_expiration_time = 2.days
51
+
52
+ mattr_accessor :invitation_url
53
+ @@invitation_url = nil
54
+
49
55
  def self.model
50
56
  @@model_name.constantize
51
57
  end
@@ -1,3 +1,3 @@
1
1
  module RailsJwtAuth
2
- VERSION = '0.17.1'
2
+ VERSION = '0.18.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_jwt_auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.1
4
+ version: 0.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rjurado
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-19 00:00:00.000000000 Z
11
+ date: 2017-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -81,6 +81,7 @@ files:
81
81
  - app/controllers/concerns/rails_jwt_auth/render_helper.rb
82
82
  - app/controllers/concerns/rails_jwt_auth/warden_helper.rb
83
83
  - app/controllers/rails_jwt_auth/confirmations_controller.rb
84
+ - app/controllers/rails_jwt_auth/invitations_controller.rb
84
85
  - app/controllers/rails_jwt_auth/passwords_controller.rb
85
86
  - app/controllers/rails_jwt_auth/registrations_controller.rb
86
87
  - app/controllers/rails_jwt_auth/sessions_controller.rb
@@ -88,11 +89,13 @@ files:
88
89
  - app/mailers/rails_jwt_auth/mailer.rb
89
90
  - app/models/concerns/rails_jwt_auth/authenticatable.rb
90
91
  - app/models/concerns/rails_jwt_auth/confirmable.rb
92
+ - app/models/concerns/rails_jwt_auth/invitable.rb
91
93
  - app/models/concerns/rails_jwt_auth/recoverable.rb
92
94
  - app/models/concerns/rails_jwt_auth/trackable.rb
93
95
  - app/validators/email_validator.rb
94
96
  - app/views/rails_jwt_auth/mailer/confirmation_instructions.html.erb
95
97
  - app/views/rails_jwt_auth/mailer/reset_password_instructions.html.erb
98
+ - app/views/rails_jwt_auth/mailer/send_invitation.html.erb
96
99
  - app/views/rails_jwt_auth/mailer/set_password_instructions.html.erb
97
100
  - config/locales/en.yml
98
101
  - lib/generators/rails_jwt_auth/install_generator.rb