rails_jwt_auth 1.7.3 → 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 +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 +17 -9
  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