rails_jwt_auth 1.7.2 → 2.0.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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +186 -87
  3. data/app/controllers/concerns/rails_jwt_auth/authenticable_helper.rb +15 -7
  4. data/app/controllers/concerns/rails_jwt_auth/params_helper.rb +18 -4
  5. data/app/controllers/concerns/rails_jwt_auth/render_helper.rb +10 -2
  6. data/app/controllers/rails_jwt_auth/confirmations_controller.rb +48 -10
  7. data/app/controllers/rails_jwt_auth/invitations_controller.rb +26 -9
  8. data/app/controllers/rails_jwt_auth/profiles_controller.rb +50 -0
  9. data/app/controllers/rails_jwt_auth/reset_passwords_controller.rb +65 -0
  10. data/app/controllers/rails_jwt_auth/sessions_controller.rb +5 -21
  11. data/app/controllers/rails_jwt_auth/{unlocks_controller.rb → unlock_accounts_controller.rb} +2 -2
  12. data/app/mailers/rails_jwt_auth/mailer.rb +23 -28
  13. data/app/models/concerns/rails_jwt_auth/authenticatable.rb +59 -18
  14. data/app/models/concerns/rails_jwt_auth/confirmable.rb +41 -38
  15. data/app/models/concerns/rails_jwt_auth/invitable.rb +42 -77
  16. data/app/models/concerns/rails_jwt_auth/lockable.rb +28 -45
  17. data/app/models/concerns/rails_jwt_auth/recoverable.rb +20 -28
  18. data/app/models/concerns/rails_jwt_auth/trackable.rb +13 -2
  19. data/app/views/rails_jwt_auth/mailer/confirmation_instructions.html.erb +1 -1
  20. data/app/views/rails_jwt_auth/mailer/{email_changed.html.erb → email_change_requested_notification.html.erb} +0 -0
  21. data/app/views/rails_jwt_auth/mailer/{send_invitation.html.erb → invitation_instructions.html.erb} +1 -1
  22. data/app/views/rails_jwt_auth/mailer/password_changed_notification.html.erb +3 -0
  23. data/app/views/rails_jwt_auth/mailer/reset_password_instructions.html.erb +1 -1
  24. data/app/views/rails_jwt_auth/mailer/{send_unlock_instructions.html.erb → unlock_instructions.html.erb} +1 -1
  25. data/config/locales/en.yml +6 -6
  26. data/lib/generators/rails_jwt_auth/install_generator.rb +11 -3
  27. data/lib/generators/templates/initializer.rb +43 -29
  28. data/lib/generators/templates/migration.rb +2 -1
  29. data/lib/rails_jwt_auth.rb +44 -47
  30. data/lib/rails_jwt_auth/jwt_manager.rb +0 -4
  31. data/lib/rails_jwt_auth/session.rb +132 -0
  32. data/lib/rails_jwt_auth/version.rb +1 -1
  33. metadata +10 -8
  34. data/app/controllers/rails_jwt_auth/passwords_controller.rb +0 -32
  35. data/app/views/rails_jwt_auth/mailer/set_password_instructions.html.erb +0 -5
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailsJwtAuth
2
4
  module Confirmable
3
5
  def self.included(base)
@@ -20,43 +22,12 @@ module RailsJwtAuth
20
22
  send_confirmation_instructions
21
23
  end
22
24
  end
23
-
24
- before_update do
25
- email_field = RailsJwtAuth.email_field_name!
26
-
27
- if public_send("#{email_field}_changed?") &&
28
- public_send("#{email_field}_was") &&
29
- !confirmed_at_changed? &&
30
- !self['invitation_token']
31
- self.unconfirmed_email = self[email_field]
32
- self[email_field] = public_send("#{email_field}_was")
33
-
34
- self.confirmation_token = SecureRandom.base58(24)
35
- self.confirmation_sent_at = Time.current
36
- end
37
- end
38
-
39
- if defined?(ActiveRecord) && ancestors.include?(ActiveRecord::Base)
40
- after_commit do
41
- if unconfirmed_email && saved_change_to_unconfirmed_email?
42
- deliver_email_changed_emails
43
- end
44
- end
45
- elsif defined?(Mongoid) && ancestors.include?(Mongoid::Document)
46
- after_update do
47
- if unconfirmed_email && unconfirmed_email_changed?
48
- deliver_email_changed_emails
49
- end
50
- end
51
- end
52
25
  end
53
26
  end
54
27
 
55
28
  def send_confirmation_instructions
56
- email_field = RailsJwtAuth.email_field_name!
57
-
58
29
  if confirmed? && !unconfirmed_email
59
- errors.add(email_field, :already_confirmed)
30
+ errors.add(RailsJwtAuth.email_field_name, :already_confirmed)
60
31
  return false
61
32
  end
62
33
 
@@ -72,12 +43,12 @@ module RailsJwtAuth
72
43
  confirmed_at.present?
73
44
  end
74
45
 
75
- def confirm!
46
+ def confirm
76
47
  self.confirmed_at = Time.current
77
48
  self.confirmation_token = nil
78
49
 
79
50
  if unconfirmed_email
80
- email_field = RailsJwtAuth.email_field_name!
51
+ email_field = RailsJwtAuth.email_field_name
81
52
 
82
53
  self[email_field] = unconfirmed_email
83
54
  self.unconfirmed_email = nil
@@ -91,17 +62,49 @@ module RailsJwtAuth
91
62
  save
92
63
  end
93
64
 
94
- def skip_confirmation!
65
+ def skip_confirmation
95
66
  self.confirmed_at = Time.current
96
67
  self.confirmation_token = nil
97
68
  end
98
69
 
70
+ def update_email(params)
71
+ email_field = RailsJwtAuth.email_field_name.to_sym
72
+ params = HashWithIndifferentAccess.new(params)
73
+
74
+ # email change must be protected by password
75
+ password_error = if (password = params[:password]).blank?
76
+ :blank
77
+ elsif !authenticate(password)
78
+ :invalid
79
+ end
80
+
81
+ self.email = params[email_field]
82
+ self.confirmation_token = SecureRandom.base58(24)
83
+ self.confirmation_sent_at = Time.current
84
+
85
+ valid? # validates first other fields
86
+ errors.add(:password, password_error) if password_error
87
+ errors.add(email_field, :not_change) unless email_changed?
88
+
89
+ return false unless errors.empty?
90
+
91
+ # move email to unconfirmed_email field and restore
92
+ self.unconfirmed_email = email
93
+ self.email = email_was
94
+
95
+ return false unless save
96
+
97
+ deliver_email_changed_emails
98
+
99
+ true
100
+ end
101
+
99
102
  protected
100
103
 
101
104
  def validate_confirmation
102
105
  return true unless confirmed_at
103
106
 
104
- email_field = RailsJwtAuth.email_field_name!
107
+ email_field = RailsJwtAuth.email_field_name
105
108
 
106
109
  if confirmed_at_was && !public_send("#{email_field}_changed?")
107
110
  errors.add(email_field, :already_confirmed)
@@ -116,8 +119,8 @@ module RailsJwtAuth
116
119
  RailsJwtAuth.send_email(:confirmation_instructions, self)
117
120
 
118
121
  # send notify to old email
119
- if RailsJwtAuth.send_email_changed_notification
120
- RailsJwtAuth.send_email(:email_changed, self)
122
+ if RailsJwtAuth.send_email_change_requested_notification
123
+ RailsJwtAuth.send_email(:email_change_requested_notification, self)
121
124
  end
122
125
  end
123
126
  end
@@ -11,115 +11,80 @@ module RailsJwtAuth
11
11
  field :invitation_token, type: String
12
12
  field :invitation_sent_at, type: Time
13
13
  field :invitation_accepted_at, type: Time
14
- field :invitation_created_at, type: Time
15
14
  end
16
15
  end
17
16
  end
18
17
 
19
18
  module ClassMethods
20
19
  # Creates an user and sends an invitation to him.
21
- # If the user is already invited and pending of completing registration
22
- # the invitation is resent by email.
23
- # If the user is already registered, it returns the user with a
24
- # <tt>:taken</tt> on the email field.
25
- #
26
- # @param [Hash] attributes Hash containing user's attributes to be filled.
27
- # Must contain an email key.
28
- #
29
- # @return [user] The user created or found by email.
30
- def invite!(attributes={})
20
+ def invite(attributes={})
31
21
  attrs = ActiveSupport::HashWithIndifferentAccess.new(attributes.to_h)
32
- auth_field = RailsJwtAuth.auth_field_name!
22
+ auth_field = RailsJwtAuth.auth_field_name
33
23
  auth_attribute = attrs.delete(auth_field)
34
24
 
35
- raise ArgumentError unless auth_attribute
36
-
37
25
  record = RailsJwtAuth.model.find_or_initialize_by(auth_field => auth_attribute)
38
26
  record.assign_attributes(attrs)
39
27
 
40
- record.invite!
28
+ record.invite
41
29
  record
42
30
  end
43
31
  end
44
32
 
45
- # Accept an invitation by clearing token and setting invitation_accepted_at
46
- def accept_invitation
47
- self.invitation_accepted_at = Time.current
48
- self.invitation_token = nil
49
- end
50
-
51
- def accept_invitation!
52
- return unless invited?
53
-
54
- if valid_invitation?
55
- accept_invitation
56
- self.confirmed_at = Time.current if respond_to?(:confirmed_at) && confirmed_at.nil?
57
- else
58
- errors.add(:invitation_token, :invalid)
59
- end
60
- end
61
-
62
- def invite!
63
- self.invitation_created_at = Time.current if new_record?
64
-
65
- unless password || password_digest
66
- passw = SecureRandom.base58(16)
67
- self.password = passw
68
- self.password_confirmation = passw
69
- end
70
-
71
- valid?
72
-
73
- # users that are registered and were not invited are not reinvitable
74
- if !new_record? && !invited?
75
- errors.add(RailsJwtAuth.auth_field_name!, :taken)
33
+ # Sends an invitation to user
34
+ # If the user has pending invitation, new one is sent
35
+ def invite
36
+ if persisted? && !invitation_token
37
+ errors.add(RailsJwtAuth.auth_field_name, :registered)
38
+ return false
76
39
  end
77
40
 
78
- # users that have already accepted an invitation are not reinvitable
79
- if !new_record? && invited? && invitation_accepted_at.present?
80
- errors.add(RailsJwtAuth.auth_field_name!, :taken)
81
- end
82
-
83
- return self unless errors.empty?
84
-
85
- generate_invitation_token if invitation_token.nil?
41
+ @inviting = true
42
+ self.invitation_token = RailsJwtAuth.friendly_token
86
43
  self.invitation_sent_at = Time.current
87
44
 
88
- send_invitation_mail if save(validate: false)
89
- self
90
- end
45
+ return false unless save_without_password
91
46
 
92
- def invited?
93
- (persisted? && invitation_token.present?)
47
+ RailsJwtAuth.send_email(:invitation_instructions, self)
48
+ true
49
+ ensure
50
+ @inviting = false
94
51
  end
95
52
 
96
- def generate_invitation_token!
97
- generate_invitation_token && save(validate: false)
98
- end
53
+ # Finishes invitation process setting user password
54
+ def accept_invitation(params)
55
+ return false unless invitation_token.present?
99
56
 
100
- def valid_invitation?
101
- invited? && invitation_period_valid?
102
- end
57
+ self.assign_attributes(params)
103
58
 
104
- def accepted_invitation?
105
- invitation_token.nil? && invitation_accepted_at.present?
106
- end
59
+ valid?
60
+ errors.add(:password, :blank) if params[:password].blank?
61
+ errors.add(:invitation_token, :expired) if expired_invitation_token?
62
+
63
+ return false unless errors.empty?
107
64
 
108
- protected
65
+ self.invitation_accepted_at = Time.current
66
+ self.invitation_token = nil
67
+ self.invitation_sent_at = nil
68
+ self.confirmed_at = Time.current if respond_to?(:confirmed_at) && confirmed_at.nil?
69
+ save
70
+ end
109
71
 
110
- def generate_invitation_token
111
- self.invitation_token = SecureRandom.base58(128)
72
+ def inviting?
73
+ @inviting || false
112
74
  end
113
75
 
114
- def send_invitation_mail
115
- RailsJwtAuth.email_field_name! # ensure email field is valid
116
- RailsJwtAuth.send_email(:send_invitation, self)
76
+ def valid_for_invite?
77
+ @inviting = true
78
+ valid_without_password?
79
+ ensure
80
+ @inviting = false
117
81
  end
118
82
 
119
- def invitation_period_valid?
120
- time = invitation_sent_at || invitation_created_at
83
+ def expired_invitation_token?
121
84
  expiration_time = RailsJwtAuth.invitation_expiration_time
122
- time && (expiration_time.to_i.zero? || time >= expiration_time.ago)
85
+ return false if expiration_time.to_i.zero?
86
+
87
+ invitation_sent_at && invitation_sent_at < expiration_time.ago
123
88
  end
124
89
  end
125
90
  end
@@ -13,52 +13,41 @@ module RailsJwtAuth
13
13
  end
14
14
  end
15
15
 
16
- def lock_access!
17
- self.locked_at = Time.now.utc
16
+ def lock_access
17
+ self.locked_at = Time.current
18
+
18
19
  save(validate: false).tap do |result|
19
20
  send_unlock_instructions if result && unlock_strategy_enabled?(:email)
20
21
  end
21
22
  end
22
23
 
23
- def unlock_access!
24
+ def clean_lock
24
25
  self.locked_at = nil
25
- self.failed_attempts = 0
26
- self.first_failed_attempt_at = nil
27
26
  self.unlock_token = nil
28
- save(validate: false)
27
+ reset_attempts
29
28
  end
30
29
 
31
- def reset_attempts!
32
- self.failed_attempts = 0
33
- self.first_failed_attempt_at = nil
34
- save(validate: false)
30
+ def unlock_access
31
+ clean_lock
32
+
33
+ save(validate: false) if changed?
35
34
  end
36
35
 
37
- def authentication?(pass)
38
- return super(pass) unless lock_strategy_enabled?(:failed_attempts)
36
+ def access_locked?
37
+ locked_at && !lock_expired?
38
+ end
39
39
 
40
- reset_attempts! if !access_locked? && attempts_expired?
41
- unlock_access! if lock_expired?
40
+ def failed_attempt
41
+ return if access_locked?
42
42
 
43
- if access_locked?
44
- false
45
- elsif super(pass)
46
- unlock_access!
47
- self
48
- else
49
- failed_attempt!
50
- lock_access! if attempts_exceeded?
51
- false
52
- end
53
- end
43
+ reset_attempts if attempts_expired?
54
44
 
55
- def unauthenticated_error
56
- return super unless lock_strategy_enabled?(:failed_attempts)
45
+ self.failed_attempts ||= 0
46
+ self.failed_attempts += 1
47
+ self.first_failed_attempt_at = Time.current if failed_attempts == 1
57
48
 
58
- if access_locked?
59
- {error: :locked}
60
- else
61
- {error: :invalid_session, remaining_attempts: remaining_attempts}
49
+ save(validate: false).tap do |result|
50
+ lock_access if result && attempts_exceeded?
62
51
  end
63
52
  end
64
53
 
@@ -68,11 +57,7 @@ module RailsJwtAuth
68
57
  self.unlock_token = SecureRandom.base58(24)
69
58
  save(validate: false)
70
59
 
71
- RailsJwtAuth.send_email(:send_unlock_instructions, self)
72
- end
73
-
74
- def access_locked?
75
- locked_at && !lock_expired?
60
+ RailsJwtAuth.send_email(:unlock_instructions, self)
76
61
  end
77
62
 
78
63
  def lock_expired?
@@ -83,21 +68,19 @@ module RailsJwtAuth
83
68
  end
84
69
  end
85
70
 
86
- def failed_attempt!
87
- self.failed_attempts ||= 0
88
- self.failed_attempts += 1
89
- self.first_failed_attempt_at = Time.now.utc if failed_attempts == 1
90
- save(validate: false)
91
- end
92
-
93
- def attempts_exceeded?
94
- failed_attempts && failed_attempts >= RailsJwtAuth.maximum_attempts
71
+ def reset_attempts
72
+ self.failed_attempts = 0
73
+ self.first_failed_attempt_at = nil
95
74
  end
96
75
 
97
76
  def remaining_attempts
98
77
  RailsJwtAuth.maximum_attempts - failed_attempts.to_i
99
78
  end
100
79
 
80
+ def attempts_exceeded?
81
+ !remaining_attempts.positive?
82
+ end
83
+
101
84
  def attempts_expired?
102
85
  first_failed_attempt_at && first_failed_attempt_at < RailsJwtAuth.reset_attempts_in.ago
103
86
  end
@@ -10,20 +10,11 @@ module RailsJwtAuth
10
10
  field :reset_password_token, type: String
11
11
  field :reset_password_sent_at, type: Time
12
12
  end
13
-
14
- validate :validate_reset_password_token, if: :password_digest_changed?
15
-
16
- before_update do
17
- if password_digest_changed? && reset_password_token
18
- self.reset_password_token = nil
19
- self.auth_tokens = []
20
- end
21
- end
22
13
  end
23
14
  end
24
15
 
25
16
  def send_reset_password_instructions
26
- email_field = RailsJwtAuth.email_field_name! # ensure email field es valid
17
+ email_field = RailsJwtAuth.email_field_name # ensure email field es valid
27
18
 
28
19
  if self.class.ancestors.include?(RailsJwtAuth::Confirmable) && !confirmed?
29
20
  errors.add(email_field, :unconfirmed)
@@ -36,36 +27,37 @@ module RailsJwtAuth
36
27
  return false
37
28
  end
38
29
 
39
- self.reset_password_token = SecureRandom.base58(24)
30
+ self.reset_password_token = RailsJwtAuth.friendly_token
40
31
  self.reset_password_sent_at = Time.current
41
32
  return false unless save
42
33
 
43
34
  RailsJwtAuth.send_email(:reset_password_instructions, self)
44
35
  end
45
36
 
46
- def set_and_send_password_instructions
47
- RailsJwtAuth.email_field_name! # ensure email field es valid
48
- return if password.present?
37
+ def set_reset_password(params)
38
+ self.assign_attributes(params)
49
39
 
50
- self.password = SecureRandom.base58(48)
51
- self.password_confirmation = self.password
52
- self.skip_confirmation! if self.class.ancestors.include?(RailsJwtAuth::Confirmable)
40
+ valid?
41
+ errors.add(:password, :blank) if params[:password].blank?
42
+ errors.add(:reset_password_token, :expired) if expired_reset_password_token?
53
43
 
54
- self.reset_password_token = SecureRandom.base58(24)
55
- self.reset_password_sent_at = Time.current
56
- return false unless save
44
+ return false unless errors.empty?
57
45
 
58
- RailsJwtAuth.send_email(:set_password_instructions, self)
59
- true
46
+ clean_reset_password
47
+ self.auth_tokens = [] # reset all sessions
48
+ save
60
49
  end
61
50
 
62
- protected
51
+ def expired_reset_password_token?
52
+ expiration_time = RailsJwtAuth.reset_password_expiration_time
53
+ return false if expiration_time.to_i.zero?
63
54
 
64
- def validate_reset_password_token
65
- if reset_password_sent_at &&
66
- (reset_password_sent_at < (Time.current - RailsJwtAuth.reset_password_expiration_time))
67
- errors.add(:reset_password_token, :expired)
68
- end
55
+ reset_password_sent_at && reset_password_sent_at < expiration_time.ago
56
+ end
57
+
58
+ def clean_reset_password
59
+ self.reset_password_sent_at = nil
60
+ self.reset_password_token = nil
69
61
  end
70
62
  end
71
63
  end