masks 0.2.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +50 -0
- data/Rakefile +11 -0
- data/app/assets/builds/application.css +4764 -0
- data/app/assets/builds/application.js +8236 -0
- data/app/assets/builds/application.js.map +7 -0
- data/app/assets/builds/masks/application.css +1 -0
- data/app/assets/builds/masks/application.js +7122 -0
- data/app/assets/builds/masks/application.js.map +7 -0
- data/app/assets/images/masks.png +0 -0
- data/app/assets/javascripts/application.js +2 -0
- data/app/assets/javascripts/controllers/application.js +9 -0
- data/app/assets/javascripts/controllers/emails_controller.js +28 -0
- data/app/assets/javascripts/controllers/index.js +12 -0
- data/app/assets/javascripts/controllers/keys_controller.js +20 -0
- data/app/assets/javascripts/controllers/recover_controller.js +21 -0
- data/app/assets/javascripts/controllers/recover_password_controller.js +21 -0
- data/app/assets/javascripts/controllers/session_controller.js +94 -0
- data/app/assets/manifest.js +2 -0
- data/app/assets/masks_manifest.js +2 -0
- data/app/assets/stylesheets/application.css +26 -0
- data/app/controllers/concerns/masks/controller.rb +114 -0
- data/app/controllers/masks/actors_controller.rb +15 -0
- data/app/controllers/masks/application_controller.rb +35 -0
- data/app/controllers/masks/backup_codes_controller.rb +34 -0
- data/app/controllers/masks/debug_controller.rb +9 -0
- data/app/controllers/masks/devices_controller.rb +20 -0
- data/app/controllers/masks/emails_controller.rb +60 -0
- data/app/controllers/masks/error_controller.rb +14 -0
- data/app/controllers/masks/keys_controller.rb +45 -0
- data/app/controllers/masks/manage/actor_controller.rb +35 -0
- data/app/controllers/masks/manage/actors_controller.rb +12 -0
- data/app/controllers/masks/manage/base_controller.rb +12 -0
- data/app/controllers/masks/one_time_code_controller.rb +49 -0
- data/app/controllers/masks/passwords_controller.rb +33 -0
- data/app/controllers/masks/recoveries_controller.rb +43 -0
- data/app/controllers/masks/sessions_controller.rb +53 -0
- data/app/helpers/masks/application_helper.rb +49 -0
- data/app/jobs/masks/application_job.rb +7 -0
- data/app/jobs/masks/expire_actors_job.rb +15 -0
- data/app/jobs/masks/expire_recoveries_job.rb +15 -0
- data/app/mailers/masks/actor_mailer.rb +22 -0
- data/app/mailers/masks/application_mailer.rb +15 -0
- data/app/models/concerns/masks/access.rb +162 -0
- data/app/models/concerns/masks/actor.rb +132 -0
- data/app/models/concerns/masks/adapter.rb +68 -0
- data/app/models/concerns/masks/role.rb +9 -0
- data/app/models/concerns/masks/scoped.rb +54 -0
- data/app/models/masks/access/actor_password.rb +20 -0
- data/app/models/masks/access/actor_scopes.rb +18 -0
- data/app/models/masks/access/actor_signup.rb +22 -0
- data/app/models/masks/actors/anonymous.rb +40 -0
- data/app/models/masks/actors/system.rb +24 -0
- data/app/models/masks/adapters/active_record.rb +85 -0
- data/app/models/masks/application_model.rb +15 -0
- data/app/models/masks/application_record.rb +8 -0
- data/app/models/masks/check.rb +192 -0
- data/app/models/masks/credential.rb +166 -0
- data/app/models/masks/credentials/backup_code.rb +30 -0
- data/app/models/masks/credentials/device.rb +59 -0
- data/app/models/masks/credentials/email.rb +48 -0
- data/app/models/masks/credentials/factor2.rb +71 -0
- data/app/models/masks/credentials/key.rb +38 -0
- data/app/models/masks/credentials/last_login.rb +12 -0
- data/app/models/masks/credentials/masquerade.rb +32 -0
- data/app/models/masks/credentials/nickname.rb +63 -0
- data/app/models/masks/credentials/one_time_code.rb +34 -0
- data/app/models/masks/credentials/password.rb +28 -0
- data/app/models/masks/credentials/recovery.rb +71 -0
- data/app/models/masks/credentials/session.rb +67 -0
- data/app/models/masks/device.rb +30 -0
- data/app/models/masks/error.rb +51 -0
- data/app/models/masks/event.rb +14 -0
- data/app/models/masks/mask.rb +255 -0
- data/app/models/masks/rails/actor.rb +190 -0
- data/app/models/masks/rails/actor_role.rb +12 -0
- data/app/models/masks/rails/device.rb +47 -0
- data/app/models/masks/rails/email.rb +96 -0
- data/app/models/masks/rails/key.rb +61 -0
- data/app/models/masks/rails/recovery.rb +116 -0
- data/app/models/masks/rails/role.rb +20 -0
- data/app/models/masks/rails/scope.rb +15 -0
- data/app/models/masks/session.rb +447 -0
- data/app/models/masks/sessions/access.rb +26 -0
- data/app/models/masks/sessions/inline.rb +16 -0
- data/app/models/masks/sessions/request.rb +42 -0
- data/app/resources/masks/actor_resource.rb +9 -0
- data/app/resources/masks/session_resource.rb +15 -0
- data/app/views/layouts/masks/application.html.erb +17 -0
- data/app/views/layouts/masks/mailer.html.erb +17 -0
- data/app/views/layouts/masks/mailer.text.erb +1 -0
- data/app/views/layouts/masks/manage.html.erb +25 -0
- data/app/views/masks/actor_mailer/recover_credentials.html.erb +33 -0
- data/app/views/masks/actor_mailer/recover_credentials.text.erb +1 -0
- data/app/views/masks/actor_mailer/verify_email.html.erb +34 -0
- data/app/views/masks/actor_mailer/verify_email.text.erb +8 -0
- data/app/views/masks/actors/current.html.erb +152 -0
- data/app/views/masks/application/_header.html.erb +31 -0
- data/app/views/masks/backup_codes/new.html.erb +103 -0
- data/app/views/masks/emails/new.html.erb +103 -0
- data/app/views/masks/emails/verify.html.erb +51 -0
- data/app/views/masks/keys/new.html.erb +127 -0
- data/app/views/masks/manage/actor/show.html.erb +126 -0
- data/app/views/masks/manage/actors/index.html.erb +40 -0
- data/app/views/masks/one_time_code/new.html.erb +150 -0
- data/app/views/masks/passwords/edit.html.erb +58 -0
- data/app/views/masks/recoveries/new.html.erb +71 -0
- data/app/views/masks/recoveries/password.html.erb +64 -0
- data/app/views/masks/sessions/new.html.erb +153 -0
- data/config/brakeman.ignore +28 -0
- data/config/locales/en.yml +286 -0
- data/config/routes.rb +46 -0
- data/db/migrate/20231205173845_create_actors.rb +94 -0
- data/lib/generators/masks/install/USAGE +8 -0
- data/lib/generators/masks/install/install_generator.rb +33 -0
- data/lib/generators/masks/install/templates/initializer.rb +5 -0
- data/lib/generators/masks/install/templates/masks.json +6 -0
- data/lib/masks/configuration.rb +236 -0
- data/lib/masks/engine.rb +25 -0
- data/lib/masks/middleware.rb +70 -0
- data/lib/masks/version.rb +5 -0
- data/lib/masks.rb +183 -0
- data/lib/tasks/masks_tasks.rake +71 -0
- data/masks.json +274 -0
- metadata +416 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<div data-controller="keys">
|
|
2
|
+
<div class="flex items-center gap-4 mb-4">
|
|
3
|
+
<div class="flex flex-col">
|
|
4
|
+
<h3 class="font-bold">
|
|
5
|
+
<%= t(".heading") %>
|
|
6
|
+
</h3>
|
|
7
|
+
<span class="text-xs">
|
|
8
|
+
<%= t(".added") %>
|
|
9
|
+
</span>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<% if flash[:key] %>
|
|
14
|
+
<div role="alert" class="alert alert-success shadow-lg mb-4 flex items-center gap-4 text-left">
|
|
15
|
+
<%= lucide_icon('check') %>
|
|
16
|
+
<div>
|
|
17
|
+
<h3 class="font-bold">created "<%= flash[:key]['name'] %>"</h3>
|
|
18
|
+
<div class="text-xs">keep the secret somewhere safe:<br /><span class="font-mono"><%= flash[:key]['secret'] %></span></div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
<% elsif flash[:notice] %>
|
|
22
|
+
<div class="alert alert-info shadow-lg mb-4 flex items-center gap-4 text-left">
|
|
23
|
+
<%= lucide_icon('info') %>
|
|
24
|
+
<span>
|
|
25
|
+
<%= flash[:notice] %>
|
|
26
|
+
</span>
|
|
27
|
+
</div>
|
|
28
|
+
<% elsif flash[:error] %>
|
|
29
|
+
<div class="alert alert-error shadow-lg mb-4 flex items-center gap-2 text-left">
|
|
30
|
+
<%= lucide_icon('x-circle') %>
|
|
31
|
+
<span>
|
|
32
|
+
<%= flash[:error] %>
|
|
33
|
+
</span>
|
|
34
|
+
</div>
|
|
35
|
+
<% end %>
|
|
36
|
+
|
|
37
|
+
<%= form_with(url: keys_path, method: :post, class: 'join join-vertical w-full') do |form| %>
|
|
38
|
+
<div class="p-4 bg-base-100 join-item rounded-xl flex gap-3 items-center max-w-full">
|
|
39
|
+
<div class="w-4 h-4 flex items-center">
|
|
40
|
+
<%= lucide_icon('key') %>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<input type="text" name="key[name]" placeholder="<%= t('.placeholder.name') %>" class="input input-bordered input-sm w-full" />
|
|
44
|
+
|
|
45
|
+
<button class="w-4 h-4 flex items-center" data-action="keys#toggleSettings">
|
|
46
|
+
<%= lucide_icon('settings-2') %>
|
|
47
|
+
</button>
|
|
48
|
+
|
|
49
|
+
<%= form.submit t('.add'), class: 'btn btn-sm' %>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div class="bg-base-200 join-item p-4 max-h-[260px] overflow-auto <%= @actor.keys.any? ? 'hidden' : '' %>" data-keys-target="settings">
|
|
53
|
+
<div class="font-bold text-sm mb-2">
|
|
54
|
+
secret
|
|
55
|
+
</div>
|
|
56
|
+
<input type="password" name="key[secret]" placeholder="<%= t('.placeholder.secret') %>" class="input input-bordered input-sm w-full" />
|
|
57
|
+
|
|
58
|
+
<% if @actor.scopes.any? %>
|
|
59
|
+
<div class="font-bold text-sm my-4 mb-2">
|
|
60
|
+
scopes
|
|
61
|
+
</div>
|
|
62
|
+
<% @actor.scopes.each do |scope| %>
|
|
63
|
+
<div class="form-control w-full">
|
|
64
|
+
<label class="cursor-pointer label">
|
|
65
|
+
<span class="label-text font-mono"><%= scope %></span>
|
|
66
|
+
<input type="checkbox" class="toggle toggle-success toggle-sm" name="key[scopes][]" value="<%= scope %>" />
|
|
67
|
+
</label>
|
|
68
|
+
</div>
|
|
69
|
+
<% end %>
|
|
70
|
+
<% end %>
|
|
71
|
+
</div>
|
|
72
|
+
<% end%>
|
|
73
|
+
|
|
74
|
+
<% if @actor.keys.any? %>
|
|
75
|
+
<div class="">
|
|
76
|
+
<table class="table">
|
|
77
|
+
<tbody>
|
|
78
|
+
<% @actor.keys.latest.each do |key| %>
|
|
79
|
+
<tr>
|
|
80
|
+
<td>
|
|
81
|
+
<div class="dropdown dropdown-start mr-1">
|
|
82
|
+
<div tabindex="0" role="button flex items-center gap-1">
|
|
83
|
+
<span class="inline-block underline decoration-wavy truncate md:max-w-full max-w-[150px] cursor-help"><%= key.name %></span>
|
|
84
|
+
|
|
85
|
+
<% if key.scopes&.any? %>
|
|
86
|
+
<span class="inline-block text-xs">(<%= key.scopes.length %>)</span>
|
|
87
|
+
<% end %>
|
|
88
|
+
</div>
|
|
89
|
+
<div tabindex="0" class="card compact dropdown-content z-[1] shadow bg-base-100 rounded-box">
|
|
90
|
+
<div tabindex="0" class="card-body">
|
|
91
|
+
<% if key.scopes&.any? %>
|
|
92
|
+
<p class="font-bold text-sm mb-2">scopes</p>
|
|
93
|
+
|
|
94
|
+
<ul class="text-left">
|
|
95
|
+
<% key.scopes.each do |scope| %>
|
|
96
|
+
<li class="font-mono"><%= scope %></li>
|
|
97
|
+
<% end %>
|
|
98
|
+
</ul>
|
|
99
|
+
<div class="divider my-0"></div>
|
|
100
|
+
<% end %>
|
|
101
|
+
|
|
102
|
+
<h2 class="whitespace-nowrap card-title font-normal italic text-sm">created <%= time_ago_in_words(key.created_at) %> ago</h2>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</td>
|
|
107
|
+
<td class="w-full whitespace-nowrap">
|
|
108
|
+
<span class="text-xs"><%= time_ago_in_words(key.created_at) %> ago</span>
|
|
109
|
+
</td>
|
|
110
|
+
<td class="whitespace-nowrap">
|
|
111
|
+
<%= form_with(url: keys_path, method: :delete) do |form| %>
|
|
112
|
+
<input type="hidden" name="id" value="<%= key.id %>" />
|
|
113
|
+
|
|
114
|
+
<button class="btn btn-circle btn-ghost btn-xs" type="submit">
|
|
115
|
+
<span class="flex items-center w-4 h-4">
|
|
116
|
+
<%= lucide_icon('trash-2') %>
|
|
117
|
+
</span>
|
|
118
|
+
</button>
|
|
119
|
+
<% end %>
|
|
120
|
+
</td>
|
|
121
|
+
</tr>
|
|
122
|
+
<% end %>
|
|
123
|
+
</tbody>
|
|
124
|
+
</table>
|
|
125
|
+
</div>
|
|
126
|
+
<% end %>
|
|
127
|
+
</div>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<div class="flex flex-col gap-1 mb-4">
|
|
2
|
+
<div class="flex items-center gap-2">
|
|
3
|
+
<div class="avatar">
|
|
4
|
+
<div class="mask mask-squircle w-6 h-6 p-0.5 bg-neutral box-border">
|
|
5
|
+
<%= lucide_icon('user', class: 'w-5 h-5') %>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div class="text-xl font-bold">
|
|
10
|
+
<%= @actor.nickname %>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div class="bg-base-300 flex flex-col gap-4 p-4 rounded-xl mb-4">
|
|
18
|
+
<div class="flex items-center gap-4">
|
|
19
|
+
<%= lucide_icon('log-in', class: 'w-4 h-4') %>
|
|
20
|
+
<% if @actor.last_login_at %>
|
|
21
|
+
<div class="text-sm flex-grow">
|
|
22
|
+
<b class="text-neutral-content">last login</b> <%= time_ago_in_words(@actor.last_login_at) %> ago
|
|
23
|
+
</div>
|
|
24
|
+
<%= form_with url: actor_path(@actor), method: :patch do |form| %>
|
|
25
|
+
<input type="submit" class="btn hover:btn-error btn-xs" name="logout" value="logout">
|
|
26
|
+
<% end %>
|
|
27
|
+
<% else %>
|
|
28
|
+
<div class="text-sm">
|
|
29
|
+
never logged in
|
|
30
|
+
</div>
|
|
31
|
+
<% end %>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div class="flex items-center gap-4">
|
|
35
|
+
<%= lucide_icon('unlock', class: 'w-4 h-4') %>
|
|
36
|
+
<div class="text-sm flex-grow">
|
|
37
|
+
<% if @actor.changed_password_at %>
|
|
38
|
+
<b class="text-neutral-content">password</b> changed <%= time_ago_in_words(@actor.changed_password_at) %> ago
|
|
39
|
+
<% else %>
|
|
40
|
+
<b class="text-neutral-content">password</b> never changed
|
|
41
|
+
<% end %>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<%= form_with url: actor_path(@actor), method: :patch do |form| %>
|
|
45
|
+
<input type="submit" class="btn hover:btn-error btn-xs" name="reset_password" value="reset">
|
|
46
|
+
<% end %>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div class="flex items-center gap-4">
|
|
50
|
+
<%= lucide_icon('key-square', class: 'w-4 h-4') %>
|
|
51
|
+
<div class="text-sm flex-grow">
|
|
52
|
+
<b class="text-neutral-content">secondary</b> credentials <span class="italic <%= @actor.factor2? ? 'text-success' : "text-error" %>"><%= @actor.factor2? ? 'enabled' : 'disabled' %></span>
|
|
53
|
+
|
|
54
|
+
</div>
|
|
55
|
+
<% if @actor.factor2? %>
|
|
56
|
+
<%= form_with url: actor_path(@actor), method: :patch do |form| %>
|
|
57
|
+
<input type="submit" class="btn hover:btn-error btn-xs" name="remove_factor2" value="remove">
|
|
58
|
+
<% end %>
|
|
59
|
+
<% end %>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="mb-4">
|
|
64
|
+
<div class="overflow-x-auto max-h-96 rounded-xl">
|
|
65
|
+
<table class="table table-pin-rows bg-base-300">
|
|
66
|
+
<thead>
|
|
67
|
+
<tr class="">
|
|
68
|
+
<th class="py-2 w-full">emails</th>
|
|
69
|
+
<th class="py-2"><%= @actor.emails.any? ? 'verified?' : '' %></th>
|
|
70
|
+
</tr>
|
|
71
|
+
</thead>
|
|
72
|
+
<tbody>
|
|
73
|
+
<% if @actor.emails.any? %>
|
|
74
|
+
<% @actor.emails.each do |email| %>
|
|
75
|
+
<tr>
|
|
76
|
+
<td class="w-full whitespace-nowrap"><%= email.email %></td>
|
|
77
|
+
<td class=""><%= lucide_icon(email.verified? ? 'check' : 'x', class: 'w-4 h-4') %></td>
|
|
78
|
+
</tr>
|
|
79
|
+
<% end %>
|
|
80
|
+
<% else %>
|
|
81
|
+
<tr>
|
|
82
|
+
<td class="italic" colspan="2">no emails added...</td>
|
|
83
|
+
</tr>
|
|
84
|
+
<% end%>
|
|
85
|
+
</tbody>
|
|
86
|
+
</table>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div class="overflow-x-auto max-h-96 rounded-xl">
|
|
91
|
+
<table class="table table-pin-rows bg-base-300">
|
|
92
|
+
<thead>
|
|
93
|
+
<tr class="">
|
|
94
|
+
<th class="py-2 w-full">scopes</th>
|
|
95
|
+
<th class="py-2"></th>
|
|
96
|
+
</tr>
|
|
97
|
+
</thead>
|
|
98
|
+
<tbody>
|
|
99
|
+
<tr>
|
|
100
|
+
<td colspan="2">
|
|
101
|
+
<%= form_with url: actor_path(@actor), method: :patch do |form| %>
|
|
102
|
+
<div class="flex items-center gap-2">
|
|
103
|
+
<%= lucide_icon('plus-square', class: 'text-base-300-content') %>
|
|
104
|
+
<input type="text" name="add_scope" class="flex-grow input input-sm" placeholder="add a scope..." />
|
|
105
|
+
<input type="submit" class="btn btn-sm" value="add" />
|
|
106
|
+
</div>
|
|
107
|
+
<% end %>
|
|
108
|
+
</td>
|
|
109
|
+
</tr>
|
|
110
|
+
<% @actor.scopes.each do |scope| %>
|
|
111
|
+
<tr>
|
|
112
|
+
<td class="font-mono w-full"><%= scope %></td>
|
|
113
|
+
<td class="">
|
|
114
|
+
<%= form_with url: actor_path(@actor), method: :patch do |form| %>
|
|
115
|
+
<input type="hidden" name="remove_scope" value="<%= scope %>">
|
|
116
|
+
|
|
117
|
+
<button type="submit" class="btn btn-ghost btn-error hover:btn-outline btn-xs">
|
|
118
|
+
<%= lucide_icon('x', class: 'w-4 h-4') %>
|
|
119
|
+
</button>
|
|
120
|
+
<% end %>
|
|
121
|
+
</td>
|
|
122
|
+
</tr>
|
|
123
|
+
<% end %>
|
|
124
|
+
</tbody>
|
|
125
|
+
</table>
|
|
126
|
+
</div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<h1 class="text-sm font-bold mb-4 mt-0 divider">
|
|
2
|
+
actors
|
|
3
|
+
</h1>
|
|
4
|
+
|
|
5
|
+
<div class="overflow-x-auto">
|
|
6
|
+
<table class="table bg-base-100">
|
|
7
|
+
<!-- head -->
|
|
8
|
+
<thead>
|
|
9
|
+
<tr>
|
|
10
|
+
<th>name</th>
|
|
11
|
+
<th>last login</th>
|
|
12
|
+
<th></th>
|
|
13
|
+
</tr>
|
|
14
|
+
</thead>
|
|
15
|
+
<tbody>
|
|
16
|
+
<% @actors.each do |actor| %>
|
|
17
|
+
<tr>
|
|
18
|
+
<td>
|
|
19
|
+
<div class="flex items-center gap-3">
|
|
20
|
+
<div class="avatar">
|
|
21
|
+
<div class="mask mask-squircle w-6 h-6 p-0.5 bg-neutral box-border">
|
|
22
|
+
<%= lucide_icon('user', class: 'w-5 h-5') %>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="font-bold"><%= actor.nickname %></div>
|
|
26
|
+
</div>
|
|
27
|
+
</td>
|
|
28
|
+
<td class="text-xs whitespace-nowrap">
|
|
29
|
+
<%= time_ago_in_words(actor.last_login_at) %> ago
|
|
30
|
+
</td>
|
|
31
|
+
<td class="text-right">
|
|
32
|
+
<a href="<%= actor_path(actor) %>" class="btn btn-ghost btn-xs">manage</a>
|
|
33
|
+
</td>
|
|
34
|
+
</tr>
|
|
35
|
+
<% end %>
|
|
36
|
+
</tbody>
|
|
37
|
+
</table>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<%== pagy_nav(@pagy) %>
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
<div data-controller="one_time_code">
|
|
2
|
+
<div class="flex items-center gap-4">
|
|
3
|
+
<div class="flex flex-col">
|
|
4
|
+
<h3 class="font-bold">
|
|
5
|
+
<%= t(".heading") %>
|
|
6
|
+
</h3>
|
|
7
|
+
<span class="text-xs">
|
|
8
|
+
<%= t(".tfa") %>
|
|
9
|
+
—
|
|
10
|
+
<span class="italic">
|
|
11
|
+
<%= @actor.totp_secret ? t(".enabled") : t(".disabled") %>
|
|
12
|
+
</span>
|
|
13
|
+
</span>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<% if !@actor.totp_secret %>
|
|
18
|
+
<div class='divider my-2 mb-4'></div>
|
|
19
|
+
<div class="">
|
|
20
|
+
<div class="">
|
|
21
|
+
<div class="flex gap-4">
|
|
22
|
+
<div class="w-[125px] h-[125px] rounded-md overflow-hidden">
|
|
23
|
+
<%= totp_svg(@actor.totp_uri, module_size: 2.35, fill: "fff", offset: 5) %>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="max-w-[175px] space-y-3 mb-2">
|
|
26
|
+
<div>
|
|
27
|
+
<%= t(".enable_qr") %>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="text-sm">
|
|
30
|
+
<%= t(".enable_manual") %>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div class="text-sm flex items-center gap-3 mt-3">
|
|
36
|
+
<span class="font-bold text-xs">
|
|
37
|
+
<%= t(".secret") %>
|
|
38
|
+
</span>
|
|
39
|
+
<span class="flex-grow">
|
|
40
|
+
<input
|
|
41
|
+
class="input input-xs w-full"
|
|
42
|
+
name="secret"
|
|
43
|
+
value="<%= @actor.random_totp_secret %>"
|
|
44
|
+
/>
|
|
45
|
+
</span>
|
|
46
|
+
</div>
|
|
47
|
+
<div class='divider mt-3 mb-2 text-xs'></div>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="mb-4 flex items-center">
|
|
50
|
+
next, enter your password and the one-time code shown in your
|
|
51
|
+
authenticator app.
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<% if flash[:errors]&.any? %>
|
|
55
|
+
<div role="alert" class="alert mb-2">
|
|
56
|
+
<div class="flex items-center gap-4">
|
|
57
|
+
<%= lucide_icon("x", class: "stroke-error") %>
|
|
58
|
+
<span><%= flash[:errors][0] %></span>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
<% end %>
|
|
62
|
+
|
|
63
|
+
<%= form_with url: one_time_code_path, method: :post, class: 'pt-2 flex flex-col gap-2', data: { one_time_code_target: 'add' } do |form| %>
|
|
64
|
+
<input
|
|
65
|
+
type="hidden"
|
|
66
|
+
name="one_time_code[secret]"
|
|
67
|
+
value="<%= @actor.random_totp_secret %>"
|
|
68
|
+
/>
|
|
69
|
+
<label class="flex gap-3 items-center">
|
|
70
|
+
<%= lucide_icon("shield-check") %>
|
|
71
|
+
<input
|
|
72
|
+
type="password"
|
|
73
|
+
data-one-time-password-target="password"
|
|
74
|
+
data-action="one_time_code#updatePassword"
|
|
75
|
+
placeholder="<%= t('.placeholder.password') %>"
|
|
76
|
+
name="session[password]"
|
|
77
|
+
class="input w-full"
|
|
78
|
+
/>
|
|
79
|
+
</label>
|
|
80
|
+
<div class="flex items-center gap-2">
|
|
81
|
+
<label class="flex gap-3 items-center">
|
|
82
|
+
<%= lucide_icon("key-square") %>
|
|
83
|
+
<input
|
|
84
|
+
type="password"
|
|
85
|
+
data-one-time-password-target="code"
|
|
86
|
+
data-action="one_time_code#updateCode"
|
|
87
|
+
placeholder="<%= t('.placeholder.otp') %>"
|
|
88
|
+
name="one_time_code[code]"
|
|
89
|
+
class="input w-full"
|
|
90
|
+
/>
|
|
91
|
+
</label>
|
|
92
|
+
<%= form.submit t(".submit"),
|
|
93
|
+
class: "btn btn-secondary",
|
|
94
|
+
data: {
|
|
95
|
+
one_time_code_target: "submit",
|
|
96
|
+
} %>
|
|
97
|
+
</div>
|
|
98
|
+
<% end %>
|
|
99
|
+
</div>
|
|
100
|
+
<% else %>
|
|
101
|
+
<div class="join join-vertical">
|
|
102
|
+
<div class="join-item alert bg-neutral mt-4">
|
|
103
|
+
<%= lucide_icon("check", class: "stroke-success") %>
|
|
104
|
+
<h3>
|
|
105
|
+
after login you will be asked to enter a one-time code, a backup
|
|
106
|
+
code, or use a different secondary credential.
|
|
107
|
+
</h3>
|
|
108
|
+
</div>
|
|
109
|
+
<div
|
|
110
|
+
class="join-item card bg-base-100 pt-2 pb-3 px-4 w-full text-right"
|
|
111
|
+
role="alert"
|
|
112
|
+
>
|
|
113
|
+
<a href="<%= backup_codes_path %>" class="text-sm underline">
|
|
114
|
+
<%= t(@actor.should_save_backup_codes? ? ".backup_codes" : ".reset_codes") %>
|
|
115
|
+
</a>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div class='divider my-4 text-xs'><%= t(".delete_div") %></div>
|
|
120
|
+
|
|
121
|
+
<%= form_with url: one_time_code_path, method: :delete, class: '', data: { one_time_code_target: 'add' } do |form| %>
|
|
122
|
+
<input
|
|
123
|
+
type="hidden"
|
|
124
|
+
name="one_time_code[secret]"
|
|
125
|
+
value="<%= @actor.random_totp_secret %>"
|
|
126
|
+
/>
|
|
127
|
+
|
|
128
|
+
<div class="alert py-2">
|
|
129
|
+
<%= lucide_icon("shield-check") %>
|
|
130
|
+
<label class="flex gap-3 items-center flex-grow">
|
|
131
|
+
<input
|
|
132
|
+
type="password"
|
|
133
|
+
data-one-time-password-target="password"
|
|
134
|
+
data-action="one_time_code#updatePassword"
|
|
135
|
+
placeholder="<%= t('.placeholder.delete') %>"
|
|
136
|
+
name="session[password]"
|
|
137
|
+
class="input input-sm input-bordered w-full"
|
|
138
|
+
/>
|
|
139
|
+
</label>
|
|
140
|
+
<div>
|
|
141
|
+
<%= form.submit t(".delete"),
|
|
142
|
+
class: "btn hover:btn-error btn-outline btn-sm",
|
|
143
|
+
data: {
|
|
144
|
+
one_time_code_target: "submit",
|
|
145
|
+
} %>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
<% end %>
|
|
149
|
+
<% end %>
|
|
150
|
+
</div>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<div class="space-y-3">
|
|
2
|
+
<div class="flex flex-col">
|
|
3
|
+
<span class="font-bold">
|
|
4
|
+
your password
|
|
5
|
+
</span>
|
|
6
|
+
<span class="text-xs">
|
|
7
|
+
last changed
|
|
8
|
+
<% if @actor.changed_password_at %>
|
|
9
|
+
<%= time_ago_in_words(@actor.changed_password_at) %>
|
|
10
|
+
ago
|
|
11
|
+
<% else %>
|
|
12
|
+
<span class="italic">
|
|
13
|
+
never
|
|
14
|
+
</span>
|
|
15
|
+
<% end %>
|
|
16
|
+
</span>
|
|
17
|
+
</div>
|
|
18
|
+
<% if flash[:errors]&.any? %>
|
|
19
|
+
<div role="alert" class="alert mb-2">
|
|
20
|
+
<div class="flex items-center gap-4">
|
|
21
|
+
<%= lucide_icon("x", class: "stroke-error") %>
|
|
22
|
+
<span><%= flash[:errors][0] %></span>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
<% end %>
|
|
26
|
+
|
|
27
|
+
<%= form_with url: password_path, method: :post, class: 'pt-2 flex flex-col gap-2', data: { password_target: 'add' } do |form| %>
|
|
28
|
+
<label class="flex gap-3 items-center">
|
|
29
|
+
<%= lucide_icon("shield-plus") %>
|
|
30
|
+
<input
|
|
31
|
+
type="password"
|
|
32
|
+
data-password-target="code"
|
|
33
|
+
data-action="password#updateChanged"
|
|
34
|
+
placeholder="<%= t('.placeholder.change') %>"
|
|
35
|
+
name="password[change]"
|
|
36
|
+
class="input w-full"
|
|
37
|
+
/>
|
|
38
|
+
</label>
|
|
39
|
+
<div class="flex items-center gap-2">
|
|
40
|
+
<label class="flex gap-3 items-center">
|
|
41
|
+
<%= lucide_icon("shield-check") %>
|
|
42
|
+
<input
|
|
43
|
+
type="password"
|
|
44
|
+
data-password-target="password"
|
|
45
|
+
data-action="password#updatePassword"
|
|
46
|
+
placeholder="<%= t('.placeholder.password') %>"
|
|
47
|
+
name="session[password]"
|
|
48
|
+
class="input w-full"
|
|
49
|
+
/>
|
|
50
|
+
</label>
|
|
51
|
+
<%= form.submit t(".submit"),
|
|
52
|
+
class: "btn btn-secondary",
|
|
53
|
+
data: {
|
|
54
|
+
password_target: "submit",
|
|
55
|
+
} %>
|
|
56
|
+
</div>
|
|
57
|
+
<% end %>
|
|
58
|
+
</div>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<div data-controller="recover">
|
|
2
|
+
<% if flash[:recovery] %>
|
|
3
|
+
<div role="alert" class="alert alert-success shadow-lg mb-4 flex items-center gap-4 text-left">
|
|
4
|
+
<%= lucide_icon('check') %>
|
|
5
|
+
<div>
|
|
6
|
+
<h3 class="font-bold"><%= t('.submitted.heading') %></h3>
|
|
7
|
+
<div class="text-xs"><%= t('.submitted.description') %></div>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
<% else %>
|
|
11
|
+
<div role="alert" class="alert mb-4 text-left">
|
|
12
|
+
<div class="flex flex-col">
|
|
13
|
+
<div class="flex items-center gap-4">
|
|
14
|
+
<%= lucide_icon("shield-question", class: "stroke-info") %>
|
|
15
|
+
<span class="flex-grow"><%= t(".heading") %></span>
|
|
16
|
+
<div class="dropdown dropdown-bottom dropdown-end">
|
|
17
|
+
<div tabindex="0" role="button" class="btn btn-xs text-sm -mr-2"><%= t(".help_button") %></div>
|
|
18
|
+
<div
|
|
19
|
+
tabindex="0"
|
|
20
|
+
class="
|
|
21
|
+
dropdown-content z-[1] mt-4 card card-compact w-[275px] px-2 pb-2 shadow bg-info
|
|
22
|
+
text-info-content
|
|
23
|
+
"
|
|
24
|
+
>
|
|
25
|
+
<div class="card-body">
|
|
26
|
+
<h4 class="card-title"><%= t(".help_title") %></h4>
|
|
27
|
+
<span><%= t(".help_body") %></span>
|
|
28
|
+
<% if t('.support_url', default: false) %>
|
|
29
|
+
<span class="italic">
|
|
30
|
+
<%= t(".help_more") %>
|
|
31
|
+
<a class="not-italic underline" href="<%= t('.support_url') %>"><%= t(".help_contact") %></a>
|
|
32
|
+
</span>
|
|
33
|
+
<% end %>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="divider my-1 -mr-4"></div>
|
|
39
|
+
<div class="text-sm -mr-2">
|
|
40
|
+
<%= t(".subheading") %>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<% end %>
|
|
45
|
+
<%= form_with url: recover_path, method: :post do |form| %>
|
|
46
|
+
<div class="flex flex-col gap-4">
|
|
47
|
+
<label class="form-control w-full">
|
|
48
|
+
<input
|
|
49
|
+
type="text"
|
|
50
|
+
data-recover-target="input"
|
|
51
|
+
data-action="recover#updateInput"
|
|
52
|
+
placeholder="<%= t('.placeholder') %>"
|
|
53
|
+
name="recover[value]"
|
|
54
|
+
class="input input-bordered w-full"
|
|
55
|
+
/>
|
|
56
|
+
</label>
|
|
57
|
+
<div class="flex items-center gap-4">
|
|
58
|
+
<%= form.submit t(".submit"),
|
|
59
|
+
class: "btn btn-active btn-info",
|
|
60
|
+
data: {
|
|
61
|
+
recover_target: "submit",
|
|
62
|
+
},
|
|
63
|
+
disabled: true %>
|
|
64
|
+
<div class="text-xs opacity-60">
|
|
65
|
+
or
|
|
66
|
+
<a href="<%= Masks.configuration.site_links[:login] %>" class="underline"><%= t(".back") %></a>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<% end %>
|
|
71
|
+
</div>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<% if @recovery %>
|
|
2
|
+
<div data-controller="recover-password">
|
|
3
|
+
<div role="alert" class="alert mb-4">
|
|
4
|
+
<div class="flex flex-col">
|
|
5
|
+
<div class="flex items-center gap-4">
|
|
6
|
+
<%= lucide_icon("rotate-cw", class: "stroke-warning") %>
|
|
7
|
+
<span class="flex-grow font-bold"><%= t(".heading") %></span>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="divider my-1 -mr-4"></div>
|
|
10
|
+
<div class="text-sm -mr-4">
|
|
11
|
+
<%= t(".subheading") %>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
<% if flash[:errors]&.any? %>
|
|
16
|
+
<div role="alert" class="alert alert-error mb-4">
|
|
17
|
+
<%= lucide_icon("x") %>
|
|
18
|
+
<%= flash[:errors][0] %>
|
|
19
|
+
</div>
|
|
20
|
+
<% end %>
|
|
21
|
+
|
|
22
|
+
<%= form_with url: recover_password_path, method: :post do |form| %>
|
|
23
|
+
<div class="flex flex-col gap-4">
|
|
24
|
+
<div class="flex items-center gap-4">
|
|
25
|
+
<input type="hidden" name="token" value="<%= @recovery.token %>"/>
|
|
26
|
+
<input
|
|
27
|
+
type="text"
|
|
28
|
+
data-recover-password-target="password"
|
|
29
|
+
data-action="recover-password#updatePassword"
|
|
30
|
+
placeholder="<%= t('.placeholder') %>"
|
|
31
|
+
name="recover[password]"
|
|
32
|
+
class="input input-bordered w-full"
|
|
33
|
+
/>
|
|
34
|
+
<%= form.submit t(".submit"),
|
|
35
|
+
class: "btn btn-warning btn-info",
|
|
36
|
+
data: {
|
|
37
|
+
recover_password_target: "submit",
|
|
38
|
+
} %>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
<% end %>
|
|
42
|
+
</div>
|
|
43
|
+
<% else %>
|
|
44
|
+
<div role="alert" class="alert mb-4">
|
|
45
|
+
<div class="flex flex-col">
|
|
46
|
+
<div class="flex items-center gap-4">
|
|
47
|
+
<%= lucide_icon(
|
|
48
|
+
flash[:reset] ? "rotate-cw" : "x",
|
|
49
|
+
class: flash[:reset] ? "stroke-success" : "stroke-error",
|
|
50
|
+
) %>
|
|
51
|
+
<span class="flex-grow font-bold"><%= flash[:reset] ? t(".heading_success") : t(".heading_expired") %></span>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="divider my-1 -mr-4"></div>
|
|
54
|
+
<div class="text-sm -mr-4">
|
|
55
|
+
<%= flash[:reset] ? t(".subheading_success") : t(".subheading_expired") %>
|
|
56
|
+
</div>
|
|
57
|
+
<% if flash[:reset] %>
|
|
58
|
+
<div class="text-sm -mr-4 mt-3">
|
|
59
|
+
<a href="<%= Masks.configuration.site_links[:login] %>" class="underline"><%= t(".login") %></a>
|
|
60
|
+
</div>
|
|
61
|
+
<% end %>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
<% end %>
|