lesli_shield 1.1.6 → 1.1.7
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/Rakefile +3 -0
- data/app/assets/stylesheets/lesli_shield/application.css +140 -1
- data/app/assets/stylesheets/lesli_shield/confirmations.css +917 -18
- data/app/assets/stylesheets/lesli_shield/confirmations.tailwind.css +918 -0
- data/app/assets/stylesheets/lesli_shield/passwords.css +872 -18
- data/app/assets/stylesheets/lesli_shield/passwords.tailwind.css +873 -0
- data/app/assets/stylesheets/lesli_shield/registrations.css +904 -832
- data/app/assets/stylesheets/lesli_shield/registrations.tailwind.css +908 -0
- data/app/assets/stylesheets/lesli_shield/sessions.css +984 -32
- data/app/assets/stylesheets/lesli_shield/sessions.tailwind.css +983 -1
- data/app/controllers/lesli_shield/profile/passwords_controller.rb +119 -0
- data/app/controllers/lesli_shield/profiles_controller.rb +63 -0
- data/app/controllers/lesli_shield/sessions_controller.rb +1 -2
- data/app/controllers/users/confirmations_controller.rb +162 -49
- data/app/controllers/users/passwords_controller.rb +211 -28
- data/app/controllers/users/registrations_controller.rb +108 -83
- data/app/controllers/users/sessions_controller.rb +122 -84
- data/app/interfaces/lesli_shield/authorization_interface.rb +2 -2
- data/app/mailers/lesli_shield/devise_mailer.rb +73 -17
- data/app/models/concerns/lesli_shield/user_authentication.rb +54 -0
- data/app/models/lesli_shield/account.rb +4 -1
- data/app/models/lesli_shield/invite.rb +1 -1
- data/app/models/lesli_shield/user/role.rb +1 -1
- data/app/models/lesli_shield/user.rb +5 -2
- data/app/services/lesli_shield/profile/password_service.rb +47 -0
- data/app/services/lesli_shield/profile_service.rb +24 -0
- data/app/services/lesli_shield/user_registration_service.rb +41 -74
- data/app/services/lesli_shield/user_session_service.rb +1 -1
- data/app/services/lesli_shield/user_validator_service.rb +30 -32
- data/app/views/devise/confirmations/new.html.erb +58 -12
- data/app/views/devise/confirmations/show.html.erb +27 -60
- data/app/views/devise/passwords/edit.html.erb +59 -25
- data/app/views/devise/passwords/new.html.erb +61 -26
- data/app/views/devise/registrations/new.html.erb +80 -53
- data/app/views/devise/sessions/new.html.erb +53 -36
- data/app/views/devise/shared/_application-devise-simple.erb +18 -19
- data/app/views/devise/shared/_demo.html.erb +8 -6
- data/app/views/devise/shared/_error_messages.html.erb +45 -10
- data/app/views/devise/shared/_links.html.erb +6 -13
- data/app/views/lesli_shield/invites/_form.html.erb +1 -1
- data/app/views/lesli_shield/invites/index.html.erb +3 -2
- data/app/views/lesli_shield/profiles/_password-form.html.erb +68 -0
- data/app/views/lesli_shield/profiles/show.html.erb +29 -0
- data/app/views/lesli_shield/users/_information-form.html.erb +9 -1
- data/config/initializers/devise.rb +52 -328
- data/config/locales/translations.en.yml +65 -2
- data/config/locales/translations.es.yml +65 -2
- data/config/routes.rb +4 -0
- data/lib/lesli_shield/engine.rb +38 -4
- data/lib/lesli_shield/router.rb +52 -18
- data/lib/lesli_shield/version.rb +2 -2
- data/readme.md +4 -2
- metadata +30 -5
- data/app/assets/config/lesli_shield_manifest.js +0 -48
- data/app/assets/stylesheets/lesli_shield/application.tailwind.css +0 -2
|
@@ -32,7 +32,10 @@ Building a better future, one line of code at a time.
|
|
|
32
32
|
|
|
33
33
|
module LesliShield
|
|
34
34
|
class Account < ApplicationRecord
|
|
35
|
-
belongs_to :account,
|
|
35
|
+
belongs_to :account,
|
|
36
|
+
class_name: "Lesli::Account",
|
|
37
|
+
inverse_of: :shield
|
|
38
|
+
validates :account_id, uniqueness: true
|
|
36
39
|
has_many :dashboards
|
|
37
40
|
has_many :invites
|
|
38
41
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
module LesliShield
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
# Namespace for Shield-owned records associated with Lesli::User, such as
|
|
3
|
+
# User::Role and User::Session. The authenticatable model remains
|
|
4
|
+
# Lesli::User; defining a second ActiveRecord subclass for the same table
|
|
5
|
+
# causes association type mismatches and can confuse Devise mappings.
|
|
6
|
+
module User
|
|
4
7
|
end
|
|
5
8
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module LesliShield
|
|
2
|
+
module Profile
|
|
3
|
+
class PasswordService < Lesli::ApplicationLesliService
|
|
4
|
+
def initialize(user)
|
|
5
|
+
super(user)
|
|
6
|
+
self.resource = user
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def update(params, current_session_id: nil)
|
|
10
|
+
password_updated = false
|
|
11
|
+
|
|
12
|
+
resource.transaction do
|
|
13
|
+
password_updated = resource.update_with_password(params)
|
|
14
|
+
|
|
15
|
+
if password_updated
|
|
16
|
+
clear_password_expiration
|
|
17
|
+
revoke_other_sessions(current_session_id)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
unless password_updated
|
|
22
|
+
resource.errors.full_messages.each { |message| error(message) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def clear_password_expiration
|
|
31
|
+
return unless resource.has_attribute?(:password_expiration_at)
|
|
32
|
+
return if resource.password_expiration_at.nil?
|
|
33
|
+
|
|
34
|
+
resource.update_column(:password_expiration_at, nil)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def revoke_other_sessions(current_session_id)
|
|
38
|
+
return if current_session_id.blank?
|
|
39
|
+
|
|
40
|
+
current_session = resource.sessions.find_by(id: current_session_id)
|
|
41
|
+
return unless current_session
|
|
42
|
+
|
|
43
|
+
resource.sessions.where.not(id: current_session.id).destroy_all
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module LesliShield
|
|
2
|
+
class ProfileService < Lesli::ApplicationLesliService
|
|
3
|
+
def initialize(user)
|
|
4
|
+
super(user)
|
|
5
|
+
self.resource = user
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def show
|
|
9
|
+
resource
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def update(params)
|
|
13
|
+
resource.assign_attributes(params)
|
|
14
|
+
resource.alias = resource.full_name_initials if resource.alias.blank?
|
|
15
|
+
|
|
16
|
+
unless resource.save
|
|
17
|
+
error(resource.errors.full_messages.to_sentence)
|
|
18
|
+
return self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -33,96 +33,63 @@ Building a better future, one line of code at a time.
|
|
|
33
33
|
module LesliShield
|
|
34
34
|
class UserRegistrationService < Lesli::ApplicationLesliService
|
|
35
35
|
|
|
36
|
-
def initialize(
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
def initialize(user)
|
|
37
|
+
super(user)
|
|
38
|
+
self.resource = user
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# force token deletion so we are sure nobody
|
|
48
|
-
# will be able to use the token again
|
|
49
|
-
current_user.update(confirmation_token: nil)
|
|
50
|
-
|
|
51
|
-
# Minimum security settings required
|
|
52
|
-
#self.settings.create_with(:value => false).find_or_create_by(:name => "mfa_enabled")
|
|
53
|
-
#self.settings.create_with(:value => :email).find_or_create_by(:name => "mfa_method")
|
|
41
|
+
# Devise owns confirmation. This service performs only the Lesli account
|
|
42
|
+
# provisioning that must happen after Devise accepts the token.
|
|
43
|
+
def provision_account!
|
|
44
|
+
assign_account! if resource.account.blank?
|
|
45
|
+
assign_initial_role! unless resource.user_roles.exists?
|
|
46
|
+
synchronize_role_privileges!
|
|
54
47
|
|
|
48
|
+
resource
|
|
55
49
|
end
|
|
56
50
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
add_account() if resource.account.blank?
|
|
60
|
-
add_role() if resource.user_roles.empty?
|
|
51
|
+
private
|
|
61
52
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
def assign_account!
|
|
54
|
+
account = if multiaccount?
|
|
55
|
+
create_owned_account!
|
|
56
|
+
else
|
|
57
|
+
Lesli::Account.first!
|
|
65
58
|
end
|
|
66
59
|
|
|
67
|
-
|
|
68
|
-
resource.save
|
|
69
|
-
|
|
70
|
-
# # initialize user dependencies
|
|
71
|
-
# def after_account_assignation
|
|
72
|
-
# return unless self.account
|
|
73
|
-
|
|
74
|
-
# #Courier::One::Firebase::User.sync_user(self)
|
|
75
|
-
# # Lesli::Courier.new(:lesli_calendar).from(:calendar_service, self).create({
|
|
76
|
-
# # name: "Personal Calendar",
|
|
77
|
-
# # default: true
|
|
78
|
-
# # })
|
|
79
|
-
# end
|
|
80
|
-
|
|
60
|
+
resource.update!(account: account)
|
|
81
61
|
end
|
|
82
62
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
# proprietary instance (only one company is using the platform)
|
|
92
|
-
# Another scenario where it is not needed to create a new account for the
|
|
93
|
-
# user is when the user is confirming the account through an invitation
|
|
94
|
-
def add_account
|
|
95
|
-
|
|
96
|
-
if ALLOW_MULTI_ACCOUNT === true
|
|
97
|
-
# create new account for the new user only if multi-account is allowed
|
|
98
|
-
account = Lesli::Account.create!({
|
|
99
|
-
user: resource, # set user as owner of his just created account
|
|
100
|
-
name: "Lesli", # temporary company name
|
|
101
|
-
email: resource.email,
|
|
102
|
-
status: :active # account is active due user already confirmed his email
|
|
103
|
-
})
|
|
104
|
-
else
|
|
105
|
-
# if multi-account is not allowed user belongs to the first account in instance
|
|
106
|
-
account = Lesli::Account.first
|
|
107
|
-
end
|
|
63
|
+
def create_owned_account!
|
|
64
|
+
Lesli::Account.create!(
|
|
65
|
+
user: resource,
|
|
66
|
+
name: "Lesli",
|
|
67
|
+
email: resource.email,
|
|
68
|
+
status: :active
|
|
69
|
+
)
|
|
70
|
+
end
|
|
108
71
|
|
|
109
|
-
|
|
110
|
-
resource.account
|
|
72
|
+
def assign_initial_role!
|
|
73
|
+
role = resource.account.roles.find_by!(name: initial_role_name)
|
|
74
|
+
resource.user_roles.create!(role: role)
|
|
111
75
|
end
|
|
112
76
|
|
|
113
|
-
def
|
|
114
|
-
|
|
115
|
-
# add owner role to user only if multi-account is allowed
|
|
116
|
-
resource.user_roles.create({ role: account.roles.find_by(name: "owner") })
|
|
117
|
-
else
|
|
118
|
-
# Assigning default role if defined in account settings
|
|
119
|
-
# Otherwise, the default role is "limited"
|
|
120
|
-
default_role_name = Lesli.config.shield.dig(:default_role) || "guest"
|
|
77
|
+
def initial_role_name
|
|
78
|
+
return "owner" if multiaccount?
|
|
121
79
|
|
|
122
|
-
|
|
80
|
+
Lesli.config.shield.dig(:default_role).presence || "guest"
|
|
81
|
+
end
|
|
123
82
|
|
|
124
|
-
|
|
83
|
+
def synchronize_role_privileges!
|
|
84
|
+
resource.roles.find_each do |role|
|
|
85
|
+
LesliShield::RolePrivilegeService.new(nil).synchronize(role)
|
|
125
86
|
end
|
|
126
87
|
end
|
|
88
|
+
|
|
89
|
+
# Read the setting at execution time. Applications and tests can change
|
|
90
|
+
# Lesli configuration after this service class has been loaded.
|
|
91
|
+
def multiaccount?
|
|
92
|
+
Lesli.config.security.dig(:allow_multiaccount) == true
|
|
93
|
+
end
|
|
127
94
|
end
|
|
128
95
|
end
|
|
@@ -55,7 +55,7 @@ module LesliShield
|
|
|
55
55
|
def create(remote_ip, user_agent=nil, session_source="devise_standard_session")
|
|
56
56
|
|
|
57
57
|
# register a new unique session
|
|
58
|
-
current_session = current_user.sessions.create({
|
|
58
|
+
current_session = current_user.sessions.create!({
|
|
59
59
|
:remote => remote_ip,
|
|
60
60
|
|
|
61
61
|
:agent_os => user_agent&.dig(:os) || "unknown",
|
|
@@ -33,11 +33,17 @@ Building a better future, one line of code at a time.
|
|
|
33
33
|
module LesliShield
|
|
34
34
|
class UserValidatorService < Lesli::ApplicationLesliService
|
|
35
35
|
|
|
36
|
-
def
|
|
36
|
+
def initialize(user)
|
|
37
|
+
raise ArgumentError, "user is required" unless user
|
|
38
|
+
|
|
39
|
+
super(user)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def validate
|
|
43
|
+
account_present?()
|
|
37
44
|
active?()
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
active_roles?()
|
|
45
|
+
active_roles?() unless roles_empty?()
|
|
46
|
+
self
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
|
|
@@ -69,10 +75,10 @@ module LesliShield
|
|
|
69
75
|
password_settings = []
|
|
70
76
|
|
|
71
77
|
# get password settings from the database
|
|
72
|
-
if
|
|
78
|
+
if current_user.account
|
|
73
79
|
|
|
74
80
|
# if user is already registered
|
|
75
|
-
password_settings =
|
|
81
|
+
password_settings = current_user.account.settings.where(password_values.join(" or "))
|
|
76
82
|
|
|
77
83
|
elseif Account.first
|
|
78
84
|
|
|
@@ -85,7 +91,7 @@ module LesliShield
|
|
|
85
91
|
|
|
86
92
|
# exit validations if password complexity is not enabled
|
|
87
93
|
if settings[:name] == 'password_enforce_complexity' && settings[:value] != "1"
|
|
88
|
-
failures
|
|
94
|
+
failures.clear
|
|
89
95
|
break
|
|
90
96
|
end
|
|
91
97
|
|
|
@@ -147,42 +153,40 @@ module LesliShield
|
|
|
147
153
|
|
|
148
154
|
private
|
|
149
155
|
|
|
150
|
-
|
|
151
|
-
#
|
|
152
|
-
def
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
# save a locked log for the requested user
|
|
157
|
-
log_failure("user_locked")
|
|
156
|
+
# Devise owns password, confirmation, and failed-attempt locking. This
|
|
157
|
+
# service contains only the additional access rules defined by Shield.
|
|
158
|
+
def account_present?
|
|
159
|
+
unless current_user.account.present?
|
|
160
|
+
log_failure("user_has_no_account")
|
|
158
161
|
|
|
159
162
|
return false
|
|
160
|
-
|
|
161
163
|
end
|
|
162
164
|
|
|
163
|
-
|
|
165
|
+
true
|
|
164
166
|
end
|
|
165
167
|
|
|
166
|
-
def confirmed?
|
|
167
168
|
|
|
168
|
-
|
|
169
|
-
|
|
169
|
+
# check if user is able to create a new session
|
|
170
|
+
def active?
|
|
171
|
+
temporarily_locked = current_user.locked_until.present? &&
|
|
172
|
+
current_user.locked_until > Time.current
|
|
170
173
|
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
unless current_user.active? && !temporarily_locked
|
|
175
|
+
|
|
176
|
+
# save a locked log for the requested user
|
|
177
|
+
log_failure("user_locked")
|
|
173
178
|
|
|
174
179
|
return false
|
|
175
180
|
|
|
176
181
|
end
|
|
177
182
|
|
|
178
183
|
return true
|
|
179
|
-
|
|
180
184
|
end
|
|
181
185
|
|
|
182
186
|
def roles_empty?
|
|
183
187
|
|
|
184
188
|
# check if user has roles assigned
|
|
185
|
-
|
|
189
|
+
unless current_user.roles.exists?
|
|
186
190
|
|
|
187
191
|
log_failure("user_has_no_assigned_role")
|
|
188
192
|
|
|
@@ -197,7 +201,7 @@ module LesliShield
|
|
|
197
201
|
def active_roles?
|
|
198
202
|
|
|
199
203
|
# check user has at least one active role before authorize the sign-in request
|
|
200
|
-
unless
|
|
204
|
+
unless current_user.roles.exists?(active: true)
|
|
201
205
|
log_failure("user_has_no_active_role")
|
|
202
206
|
|
|
203
207
|
return false
|
|
@@ -207,13 +211,7 @@ module LesliShield
|
|
|
207
211
|
end
|
|
208
212
|
|
|
209
213
|
def log_failure(description)
|
|
210
|
-
|
|
211
|
-
engine: LesliShield,
|
|
212
|
-
source: self.class.name,
|
|
213
|
-
action: "validate",
|
|
214
|
-
operation: "session_creation_failed",
|
|
215
|
-
description: description
|
|
216
|
-
)
|
|
214
|
+
failures.push(description) unless failures.include?(description)
|
|
217
215
|
end
|
|
218
216
|
end
|
|
219
217
|
end
|
|
@@ -1,16 +1,62 @@
|
|
|
1
|
-
|
|
1
|
+
<%= render(
|
|
2
|
+
"devise/shared/application-devise-simple",
|
|
3
|
+
title: I18n.t("lesli_shield.devise/confirmations.view_title")
|
|
4
|
+
) do %>
|
|
2
5
|
|
|
3
|
-
<%= form_for(
|
|
4
|
-
|
|
6
|
+
<%= form_for(
|
|
7
|
+
resource,
|
|
8
|
+
as: resource_name,
|
|
9
|
+
url: user_confirmation_path,
|
|
10
|
+
html: { method: :post, class: "text-left" },
|
|
11
|
+
builder: LesliView::Forms::Builder
|
|
12
|
+
) do |form| %>
|
|
5
13
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
<p class="mb-6 text-center text-sm leading-6 text-slate-600">
|
|
15
|
+
<%= I18n.t(
|
|
16
|
+
"lesli_shield.devise/confirmations.view_description"
|
|
17
|
+
) %>
|
|
18
|
+
</p>
|
|
10
19
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
<%= render "devise/shared/error_messages", resource: resource %>
|
|
21
|
+
|
|
22
|
+
<%= form.field_control_email(
|
|
23
|
+
:email,
|
|
24
|
+
icon: "mail",
|
|
25
|
+
label: I18n.t(
|
|
26
|
+
"lesli_shield.devise/confirmations.field_email"
|
|
27
|
+
),
|
|
28
|
+
message: resource.errors[:email].to_sentence.presence,
|
|
29
|
+
category: resource.errors[:email].any? ? :danger : nil,
|
|
30
|
+
value: resource.pending_reconfirmation? ?
|
|
31
|
+
resource.unconfirmed_email : resource.email,
|
|
32
|
+
required: true,
|
|
33
|
+
autofocus: true,
|
|
34
|
+
autocomplete: "email",
|
|
35
|
+
autocapitalize: "none",
|
|
36
|
+
spellcheck: false
|
|
37
|
+
) %>
|
|
15
38
|
|
|
16
|
-
<%=
|
|
39
|
+
<%= form.field_control_button(
|
|
40
|
+
I18n.t(
|
|
41
|
+
"lesli_shield.devise/confirmations.button_send_instructions"
|
|
42
|
+
),
|
|
43
|
+
{
|
|
44
|
+
class: "w-full",
|
|
45
|
+
data: {
|
|
46
|
+
turbo_submits_with: I18n.t(
|
|
47
|
+
"lesli_shield.devise/confirmations.button_sending"
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
icon: "mark_email_unread"
|
|
52
|
+
) %>
|
|
53
|
+
<% end %>
|
|
54
|
+
|
|
55
|
+
<%= render(
|
|
56
|
+
"devise/shared/links",
|
|
57
|
+
login: true,
|
|
58
|
+
password_reset: false,
|
|
59
|
+
magic_link: false,
|
|
60
|
+
otp: false
|
|
61
|
+
) %>
|
|
62
|
+
<% end %>
|
|
@@ -1,61 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
|
|
29
|
-
|
|
30
|
-
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
31
|
-
// ·
|
|
32
|
-
=end
|
|
33
|
-
%>
|
|
34
|
-
|
|
35
|
-
<% content_for :application_custom_assets do %>
|
|
36
|
-
<script>
|
|
37
|
-
//setTimeout(() => { window.location.href = "/"; }, 10000)
|
|
38
|
-
</script>
|
|
39
|
-
<% end %>
|
|
40
|
-
|
|
41
|
-
<%= render("devise/shared/application-devise-simple", title:"Account confirmation") do %>
|
|
42
|
-
|
|
43
|
-
<% if flash[:danger] %>
|
|
44
|
-
<% flash.each do |type, msg| %>
|
|
45
|
-
<div class="notification is-<%= type %>">
|
|
46
|
-
<%= msg %>
|
|
47
|
-
</div>
|
|
48
|
-
<% end %>
|
|
49
|
-
<% end %>
|
|
50
|
-
|
|
51
|
-
<% if flash[:success] %>
|
|
52
|
-
<div class="confirmation-message">
|
|
53
|
-
<p>Your account has been successfully verified</p>
|
|
54
|
-
</div>
|
|
55
|
-
<% end %>
|
|
56
|
-
|
|
57
|
-
<%= render("devise/shared/links", login:true) %>
|
|
58
|
-
|
|
1
|
+
<%= render(
|
|
2
|
+
"devise/shared/application-devise-simple",
|
|
3
|
+
title: I18n.t("lesli_shield.devise/confirmations.view_success_title")
|
|
4
|
+
) do %>
|
|
5
|
+
<section
|
|
6
|
+
class="my-8 w-full max-w-lg rounded-xl border border-emerald-200 bg-white p-8 text-center shadow-sm"
|
|
7
|
+
role="status"
|
|
8
|
+
>
|
|
9
|
+
<span
|
|
10
|
+
class="material-symbols-outlined text-5xl text-emerald-600"
|
|
11
|
+
aria-hidden="true"
|
|
12
|
+
>
|
|
13
|
+
verified
|
|
14
|
+
</span>
|
|
15
|
+
|
|
16
|
+
<p class="mt-4 text-sm leading-6 text-slate-600">
|
|
17
|
+
<%= I18n.t(
|
|
18
|
+
"lesli_shield.devise/confirmations.view_success_description"
|
|
19
|
+
) %>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<%= link_to(
|
|
23
|
+
I18n.t("lesli_shield.devise/shared.button_login"),
|
|
24
|
+
new_user_session_path,
|
|
25
|
+
class: "mt-6 inline-flex min-h-11 items-center justify-center rounded-lg bg-[var(--lesli-color-primary)] px-5 py-2.5 text-sm font-semibold text-white transition-opacity hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--lesli-color-primary)]"
|
|
26
|
+
) %>
|
|
27
|
+
</section>
|
|
59
28
|
<% end %>
|
|
60
|
-
|
|
61
|
-
|
|
@@ -1,36 +1,70 @@
|
|
|
1
|
-
<%= render(
|
|
1
|
+
<%= render(
|
|
2
|
+
"devise/shared/application-devise-simple",
|
|
3
|
+
title: I18n.t("lesli_shield.devise/passwords.view_update_title")
|
|
4
|
+
) do %>
|
|
2
5
|
|
|
3
6
|
<%= form_for(
|
|
4
|
-
resource,
|
|
5
|
-
as: resource_name,
|
|
6
|
-
url: user_password_path
|
|
7
|
-
html: { class: "
|
|
7
|
+
resource,
|
|
8
|
+
as: resource_name,
|
|
9
|
+
url: user_password_path,
|
|
10
|
+
html: { class: "text-left", method: :put },
|
|
8
11
|
builder: LesliView::Forms::Builder
|
|
9
|
-
|
|
12
|
+
) do |form| %>
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
<p class="mb-6 text-center text-sm leading-6 text-slate-600">
|
|
15
|
+
<%= I18n.t("lesli_shield.devise/passwords.view_update_description") %>
|
|
16
|
+
</p>
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
label_html:form.label(:password),
|
|
15
|
-
control_html:form.password_field(:password, autocomplete: "current-password", placeholder: "* * * * * *"),
|
|
16
|
-
message_text: flash.first&.second,
|
|
17
|
-
category: flash.first&.first,
|
|
18
|
-
icon: "key"
|
|
19
|
-
) %>
|
|
18
|
+
<%= render "devise/shared/error_messages", resource: resource %>
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
label_html:form.label(:password_confirmation),
|
|
23
|
-
control_html:form.password_field(:password_confirmation, autocomplete: "current-password", placeholder: "* * * * * *"),
|
|
24
|
-
message_text: flash.first&.second,
|
|
25
|
-
category: flash.first&.first,
|
|
26
|
-
icon: "key"
|
|
27
|
-
) %>
|
|
20
|
+
<%= form.hidden_field :reset_password_token %>
|
|
28
21
|
|
|
29
|
-
|
|
22
|
+
<%= form.field_control_password(
|
|
23
|
+
:password,
|
|
24
|
+
icon: "key",
|
|
25
|
+
label: I18n.t("lesli_shield.devise/passwords.field_password"),
|
|
26
|
+
message: I18n.t(
|
|
27
|
+
"lesli_shield.devise/passwords.field_password_hint",
|
|
28
|
+
count: @minimum_password_length
|
|
29
|
+
),
|
|
30
|
+
required: true,
|
|
31
|
+
autofocus: true,
|
|
32
|
+
autocomplete: "new-password",
|
|
33
|
+
minlength: @minimum_password_length,
|
|
34
|
+
value: ""
|
|
35
|
+
) %>
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
<%= form.field_control_password(
|
|
38
|
+
:password_confirmation,
|
|
39
|
+
icon: "key",
|
|
40
|
+
label: I18n.t(
|
|
41
|
+
"lesli_shield.devise/passwords.field_password_confirmation"
|
|
42
|
+
),
|
|
43
|
+
required: true,
|
|
44
|
+
autocomplete: "new-password",
|
|
45
|
+
minlength: @minimum_password_length,
|
|
46
|
+
value: ""
|
|
47
|
+
) %>
|
|
32
48
|
|
|
33
|
-
|
|
49
|
+
<%= form.field_control_button(
|
|
50
|
+
I18n.t("lesli_shield.devise/passwords.button_update_password"),
|
|
51
|
+
{
|
|
52
|
+
class: "is-fullwidth",
|
|
53
|
+
data: {
|
|
54
|
+
turbo_submits_with: I18n.t(
|
|
55
|
+
"lesli_shield.devise/passwords.button_updating"
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
icon: "key"
|
|
60
|
+
) %>
|
|
61
|
+
<% end %>
|
|
34
62
|
|
|
35
|
-
<%= render(
|
|
63
|
+
<%= render(
|
|
64
|
+
"devise/shared/links",
|
|
65
|
+
login: true,
|
|
66
|
+
password_reset: false,
|
|
67
|
+
magic_link: false,
|
|
68
|
+
otp: false
|
|
69
|
+
) %>
|
|
36
70
|
<% end %>
|