masks 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 %>
|