rails_jwt_auth 1.6.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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 +19 -5
  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 +29 -42
  13. data/app/models/concerns/rails_jwt_auth/authenticatable.rb +59 -18
  14. data/app/models/concerns/rails_jwt_auth/confirmable.rb +58 -33
  15. data/app/models/concerns/rails_jwt_auth/invitable.rb +42 -78
  16. data/app/models/concerns/rails_jwt_auth/lockable.rb +28 -46
  17. data/app/models/concerns/rails_jwt_auth/recoverable.rb +21 -31
  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 +47 -45
  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 -28
  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,37 +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
-
37
- mailer = Mailer.confirmation_instructions(self)
38
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
39
-
40
- if RailsJwtAuth.send_email_changed_notification
41
- mailer = Mailer.email_changed(self)
42
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
43
- end
44
- end
45
- end
46
25
  end
47
26
  end
48
27
 
49
28
  def send_confirmation_instructions
50
- email_field = RailsJwtAuth.email_field_name!
51
-
52
29
  if confirmed? && !unconfirmed_email
53
- errors.add(email_field, :already_confirmed)
30
+ errors.add(RailsJwtAuth.email_field_name, :already_confirmed)
54
31
  return false
55
32
  end
56
33
 
@@ -58,8 +35,7 @@ module RailsJwtAuth
58
35
  self.confirmation_sent_at = Time.current
59
36
  return false unless save
60
37
 
61
- mailer = Mailer.confirmation_instructions(self)
62
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
38
+ RailsJwtAuth.send_email(:confirmation_instructions, self)
63
39
  true
64
40
  end
65
41
 
@@ -67,29 +43,68 @@ module RailsJwtAuth
67
43
  confirmed_at.present?
68
44
  end
69
45
 
70
- def confirm!
46
+ def confirm
71
47
  self.confirmed_at = Time.current
72
48
  self.confirmation_token = nil
73
49
 
74
50
  if unconfirmed_email
75
- self[RailsJwtAuth.email_field_name!] = unconfirmed_email
76
- self.email_confirmation = unconfirmed_email if respond_to?(:email_confirmation)
51
+ email_field = RailsJwtAuth.email_field_name
52
+
53
+ self[email_field] = unconfirmed_email
77
54
  self.unconfirmed_email = nil
55
+
56
+ # supports email confirmation attr_accessor validation
57
+ if respond_to?("#{email_field}_confirmation")
58
+ instance_variable_set("@#{email_field}_confirmation", self[email_field])
59
+ end
78
60
  end
79
61
 
80
62
  save
81
63
  end
82
64
 
83
- def skip_confirmation!
65
+ def skip_confirmation
84
66
  self.confirmed_at = Time.current
85
67
  self.confirmation_token = nil
86
68
  end
87
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
+
88
102
  protected
89
103
 
90
104
  def validate_confirmation
91
105
  return true unless confirmed_at
92
- email_field = RailsJwtAuth.email_field_name!
106
+
107
+ email_field = RailsJwtAuth.email_field_name
93
108
 
94
109
  if confirmed_at_was && !public_send("#{email_field}_changed?")
95
110
  errors.add(email_field, :already_confirmed)
@@ -98,5 +113,15 @@ module RailsJwtAuth
98
113
  errors.add(:confirmation_token, :expired)
99
114
  end
100
115
  end
116
+
117
+ def deliver_email_changed_emails
118
+ # send confirmation to new email
119
+ RailsJwtAuth.send_email(:confirmation_instructions, self)
120
+
121
+ # send notify to old email
122
+ if RailsJwtAuth.send_email_change_requested_notification
123
+ RailsJwtAuth.send_email(:email_change_requested_notification, self)
124
+ end
125
+ end
101
126
  end
102
127
  end
@@ -11,116 +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 es valid
116
- mailer = Mailer.send_invitation(self)
117
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
76
+ def valid_for_invite?
77
+ @inviting = true
78
+ valid_without_password?
79
+ ensure
80
+ @inviting = false
118
81
  end
119
82
 
120
- def invitation_period_valid?
121
- time = invitation_sent_at || invitation_created_at
83
+ def expired_invitation_token?
122
84
  expiration_time = RailsJwtAuth.invitation_expiration_time
123
- 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
124
88
  end
125
89
  end
126
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,12 +57,7 @@ module RailsJwtAuth
68
57
  self.unlock_token = SecureRandom.base58(24)
69
58
  save(validate: false)
70
59
 
71
- mailer = Mailer.send_unlock_instructions(self)
72
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
73
- end
74
-
75
- def access_locked?
76
- locked_at && !lock_expired?
60
+ RailsJwtAuth.send_email(:unlock_instructions, self)
77
61
  end
78
62
 
79
63
  def lock_expired?
@@ -84,21 +68,19 @@ module RailsJwtAuth
84
68
  end
85
69
  end
86
70
 
87
- def failed_attempt!
88
- self.failed_attempts ||= 0
89
- self.failed_attempts += 1
90
- self.first_failed_attempt_at = Time.now.utc if failed_attempts == 1
91
- save(validate: false)
92
- end
93
-
94
- def attempts_exceeded?
95
- failed_attempts && failed_attempts >= RailsJwtAuth.maximum_attempts
71
+ def reset_attempts
72
+ self.failed_attempts = 0
73
+ self.first_failed_attempt_at = nil
96
74
  end
97
75
 
98
76
  def remaining_attempts
99
77
  RailsJwtAuth.maximum_attempts - failed_attempts.to_i
100
78
  end
101
79
 
80
+ def attempts_exceeded?
81
+ !remaining_attempts.positive?
82
+ end
83
+
102
84
  def attempts_expired?
103
85
  first_failed_attempt_at && first_failed_attempt_at < RailsJwtAuth.reset_attempts_in.ago
104
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,38 +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
- mailer = Mailer.reset_password_instructions(self)
44
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
34
+ RailsJwtAuth.send_email(:reset_password_instructions, self)
45
35
  end
46
36
 
47
- def set_and_send_password_instructions
48
- RailsJwtAuth.email_field_name! # ensure email field es valid
49
- return if password.present?
37
+ def set_reset_password(params)
38
+ self.assign_attributes(params)
50
39
 
51
- self.password = SecureRandom.base58(48)
52
- self.password_confirmation = self.password
53
- 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?
54
43
 
55
- self.reset_password_token = SecureRandom.base58(24)
56
- self.reset_password_sent_at = Time.current
57
- return false unless save
44
+ return false unless errors.empty?
58
45
 
59
- mailer = Mailer.set_password_instructions(self)
60
- RailsJwtAuth.deliver_later ? mailer.deliver_later : mailer.deliver
61
- true
46
+ clean_reset_password
47
+ self.auth_tokens = [] # reset all sessions
48
+ save
62
49
  end
63
50
 
64
- protected
51
+ def expired_reset_password_token?
52
+ expiration_time = RailsJwtAuth.reset_password_expiration_time
53
+ return false if expiration_time.to_i.zero?
65
54
 
66
- def validate_reset_password_token
67
- if reset_password_sent_at &&
68
- (reset_password_sent_at < (Time.current - RailsJwtAuth.reset_password_expiration_time))
69
- errors.add(:reset_password_token, :expired)
70
- 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
71
61
  end
72
62
  end
73
63
  end