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.
- checksums.yaml +4 -4
- data/README.md +186 -87
- data/app/controllers/concerns/rails_jwt_auth/authenticable_helper.rb +15 -7
- data/app/controllers/concerns/rails_jwt_auth/params_helper.rb +18 -4
- data/app/controllers/concerns/rails_jwt_auth/render_helper.rb +10 -2
- data/app/controllers/rails_jwt_auth/confirmations_controller.rb +48 -10
- data/app/controllers/rails_jwt_auth/invitations_controller.rb +26 -9
- data/app/controllers/rails_jwt_auth/profiles_controller.rb +50 -0
- data/app/controllers/rails_jwt_auth/reset_passwords_controller.rb +65 -0
- data/app/controllers/rails_jwt_auth/sessions_controller.rb +5 -21
- data/app/controllers/rails_jwt_auth/{unlocks_controller.rb → unlock_accounts_controller.rb} +2 -2
- data/app/mailers/rails_jwt_auth/mailer.rb +23 -28
- data/app/models/concerns/rails_jwt_auth/authenticatable.rb +59 -18
- data/app/models/concerns/rails_jwt_auth/confirmable.rb +41 -38
- data/app/models/concerns/rails_jwt_auth/invitable.rb +42 -77
- data/app/models/concerns/rails_jwt_auth/lockable.rb +28 -45
- data/app/models/concerns/rails_jwt_auth/recoverable.rb +20 -28
- data/app/models/concerns/rails_jwt_auth/trackable.rb +13 -2
- data/app/views/rails_jwt_auth/mailer/confirmation_instructions.html.erb +1 -1
- data/app/views/rails_jwt_auth/mailer/{email_changed.html.erb → email_change_requested_notification.html.erb} +0 -0
- data/app/views/rails_jwt_auth/mailer/{send_invitation.html.erb → invitation_instructions.html.erb} +1 -1
- data/app/views/rails_jwt_auth/mailer/password_changed_notification.html.erb +3 -0
- data/app/views/rails_jwt_auth/mailer/reset_password_instructions.html.erb +1 -1
- data/app/views/rails_jwt_auth/mailer/{send_unlock_instructions.html.erb → unlock_instructions.html.erb} +1 -1
- data/config/locales/en.yml +6 -6
- data/lib/generators/rails_jwt_auth/install_generator.rb +11 -3
- data/lib/generators/templates/initializer.rb +43 -29
- data/lib/generators/templates/migration.rb +2 -1
- data/lib/rails_jwt_auth.rb +44 -47
- data/lib/rails_jwt_auth/jwt_manager.rb +0 -4
- data/lib/rails_jwt_auth/session.rb +132 -0
- data/lib/rails_jwt_auth/version.rb +1 -1
- metadata +10 -8
- data/app/controllers/rails_jwt_auth/passwords_controller.rb +0 -32
- 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(
|
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.
|
120
|
-
RailsJwtAuth.send_email(:
|
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
|
-
|
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
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
79
|
-
|
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
|
-
|
89
|
-
self
|
90
|
-
end
|
45
|
+
return false unless save_without_password
|
91
46
|
|
92
|
-
|
93
|
-
|
47
|
+
RailsJwtAuth.send_email(:invitation_instructions, self)
|
48
|
+
true
|
49
|
+
ensure
|
50
|
+
@inviting = false
|
94
51
|
end
|
95
52
|
|
96
|
-
|
97
|
-
|
98
|
-
|
53
|
+
# Finishes invitation process setting user password
|
54
|
+
def accept_invitation(params)
|
55
|
+
return false unless invitation_token.present?
|
99
56
|
|
100
|
-
|
101
|
-
invited? && invitation_period_valid?
|
102
|
-
end
|
57
|
+
self.assign_attributes(params)
|
103
58
|
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
-
|
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
|
111
|
-
|
72
|
+
def inviting?
|
73
|
+
@inviting || false
|
112
74
|
end
|
113
75
|
|
114
|
-
def
|
115
|
-
|
116
|
-
|
76
|
+
def valid_for_invite?
|
77
|
+
@inviting = true
|
78
|
+
valid_without_password?
|
79
|
+
ensure
|
80
|
+
@inviting = false
|
117
81
|
end
|
118
82
|
|
119
|
-
def
|
120
|
-
time = invitation_sent_at || invitation_created_at
|
83
|
+
def expired_invitation_token?
|
121
84
|
expiration_time = RailsJwtAuth.invitation_expiration_time
|
122
|
-
|
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.
|
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
|
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
|
-
|
27
|
+
reset_attempts
|
29
28
|
end
|
30
29
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
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
|
38
|
-
|
36
|
+
def access_locked?
|
37
|
+
locked_at && !lock_expired?
|
38
|
+
end
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
def failed_attempt
|
41
|
+
return if access_locked?
|
42
42
|
|
43
|
-
if
|
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
|
-
|
56
|
-
|
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
|
-
|
59
|
-
|
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(:
|
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
|
87
|
-
self.failed_attempts
|
88
|
-
self.
|
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
|
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 =
|
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
|
47
|
-
|
48
|
-
return if password.present?
|
37
|
+
def set_reset_password(params)
|
38
|
+
self.assign_attributes(params)
|
49
39
|
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
55
|
-
self.reset_password_sent_at = Time.current
|
56
|
-
return false unless save
|
44
|
+
return false unless errors.empty?
|
57
45
|
|
58
|
-
|
59
|
-
|
46
|
+
clean_reset_password
|
47
|
+
self.auth_tokens = [] # reset all sessions
|
48
|
+
save
|
60
49
|
end
|
61
50
|
|
62
|
-
|
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
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|