rails_jwt_auth 0.17.1 → 0.18.0

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