pages_core 3.13.0 → 3.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/assets/builds/pages_core/admin-dist.js +1 -1
  4. data/app/assets/builds/pages_core/admin-dist.js.map +4 -4
  5. data/app/assets/builds/pages_core/admin.css +27 -4
  6. data/app/assets/stylesheets/pages_core/admin/components/login.css +0 -6
  7. data/app/assets/stylesheets/pages_core/admin/components/totp.css +26 -0
  8. data/app/controllers/admin/account_recoveries_controller.rb +87 -0
  9. data/app/controllers/admin/invites_controller.rb +3 -2
  10. data/app/controllers/admin/otp_secrets_controller.rb +45 -0
  11. data/app/controllers/admin/recovery_codes_controller.rb +32 -0
  12. data/app/controllers/admin/sessions_controller.rb +65 -0
  13. data/app/controllers/admin/users_controller.rb +2 -8
  14. data/app/controllers/concerns/pages_core/authentication.rb +12 -10
  15. data/app/controllers/pages_core/admin_controller.rb +1 -1
  16. data/app/helpers/pages_core/admin/admin_helper.rb +11 -0
  17. data/app/javascript/index.ts +0 -2
  18. data/app/mailers/admin_mailer.rb +2 -2
  19. data/app/models/concerns/pages_core/has_otp.rb +27 -0
  20. data/app/models/otp_secret.rb +101 -0
  21. data/app/models/user.rb +15 -37
  22. data/app/policies/user_policy.rb +4 -0
  23. data/app/views/admin/account_recoveries/new.html.erb +22 -0
  24. data/app/views/admin/account_recoveries/show.html.erb +37 -0
  25. data/app/views/admin/invites/show.html.erb +1 -1
  26. data/app/views/admin/otp_secrets/create.html.erb +7 -0
  27. data/app/views/admin/otp_secrets/new.html.erb +60 -0
  28. data/app/views/admin/recovery_codes/_codes.html.erb +14 -0
  29. data/app/views/admin/recovery_codes/create.html.erb +7 -0
  30. data/app/views/admin/recovery_codes/new.html.erb +11 -0
  31. data/app/views/admin/sessions/_otp_form.html.erb +13 -0
  32. data/app/views/admin/sessions/new.html.erb +33 -0
  33. data/app/views/admin/sessions/verify_otp.html.erb +19 -0
  34. data/app/views/admin/users/edit.html.erb +31 -1
  35. data/app/views/admin/users/new.html.erb +1 -1
  36. data/app/views/admin_mailer/account_recovery.text.erb +10 -0
  37. data/app/views/layouts/admin/_header.html.erb +1 -1
  38. data/app/views/layouts/admin/_toast.html.erb +12 -0
  39. data/app/views/layouts/admin.html.erb +1 -1
  40. data/config/locales/en.yml +11 -3
  41. data/config/routes.rb +11 -6
  42. data/db/migrate/20240126160700_add_2fa_fields.rb +22 -0
  43. data/db/migrate/20240129201300_remove_password_reset_tokens.rb +13 -0
  44. data/lib/pages_core.rb +6 -0
  45. metadata +51 -9
  46. data/app/controllers/admin/password_resets_controller.rb +0 -85
  47. data/app/controllers/sessions_controller.rb +0 -27
  48. data/app/javascript/controllers/LoginController.ts +0 -32
  49. data/app/models/password_reset_token.rb +0 -34
  50. data/app/views/admin/password_resets/show.html.erb +0 -21
  51. data/app/views/admin/users/login.html.erb +0 -65
  52. data/app/views/admin_mailer/password_reset.text.erb +0 -11
@@ -0,0 +1,22 @@
1
+ <% content_for :page_title, "Account recovery" %>
2
+ <% content_for :page_description, "Account recovery" %>
3
+
4
+ <%= form_tag admin_account_recovery_path do %>
5
+ <h2>
6
+ Forgot your password or lost your authenticator?
7
+ </h2>
8
+ <p>
9
+ Don't worry, it happens.
10
+ Enter your email address below, and we'll send you a link where you
11
+ can recover your account.
12
+ </p>
13
+ <div class="field">
14
+ <label for="email">Email address</label>
15
+ <%= text_field_tag(:email, "", autocomplete: "email", autofocus: true) %>
16
+ </div>
17
+ <p>
18
+ <button type="submit">
19
+ Send
20
+ </button>
21
+ </p>
22
+ <% end %>
@@ -0,0 +1,37 @@
1
+ <% content_for :page_title, "Account recovery" %>
2
+ <% content_for :page_description, "Please choose a new password to proceed" %>
3
+ <% content_for :body_class, "login" %>
4
+
5
+ <div class="login-form">
6
+ <%= form_for(@user,
7
+ url: admin_account_recovery_path,
8
+ builder: PagesCore::Admin::FormBuilder,
9
+ class: 'form') do |f| %>
10
+ <%= hidden_field_tag :token, @token %>
11
+ <%= f.labelled_password_field(:password,
12
+ autofocus: true,
13
+ autocomplete: "new-password") %>
14
+ <%= f.labelled_password_field(:password_confirmation,
15
+ autocomplete: "new-password") %>
16
+
17
+ <% if @user.otp_enabled? %>
18
+ <div class="field">
19
+ <label for="otp">6 digit code or recovery code</label>
20
+ <%= text_field_tag(:otp, "",
21
+ autocomplete: "one-time-code",
22
+ size: 6) %>
23
+ </div>
24
+ <p>
25
+ Lost your authenticator device? You can use one of your
26
+ emergency recovery codes instead.
27
+ </p>
28
+ <% end %>
29
+
30
+ <p>
31
+ <button type="submit">
32
+ Change password
33
+ </button>
34
+ or <%= link_to "Return to login screen", admin_login_path %>
35
+ </p>
36
+ <% end %>
37
+ </div>
@@ -14,7 +14,7 @@
14
14
  <%= f.labelled_text_field :email, autocomplete: "email" %>
15
15
  <%= f.labelled_password_field(:password,
16
16
  autocomplete: "new-password") %>
17
- <%= f.labelled_password_field(:confirm_password,
17
+ <%= f.labelled_password_field(:password_confirmation,
18
18
  autocomplete: "new-password") %>
19
19
  <p>
20
20
  <button type="submit">
@@ -0,0 +1,7 @@
1
+ <% content_for :page_title, "2FA enabled" %>
2
+ <% content_for :page_description, "Two-factor authentication enabled" %>
3
+
4
+ <div class="content">
5
+ <%= render(partial: "admin/recovery_codes/codes",
6
+ locals: { recovery_codes: @recovery_codes }) %>
7
+ </div>
@@ -0,0 +1,60 @@
1
+ <% content_for :page_title, "Enable 2FA" %>
2
+ <% content_for :page_description, "Enable two-factor authentication" %>
3
+
4
+ <%= form_tag(admin_otp_secret_path, method: :post, class: "totp-enrollment") do |f| %>
5
+ <h2>
6
+ Scan the QR-code
7
+ </h2>
8
+ <p>
9
+ Use an authenticator app or browser extension to scan the QR code below.<br>
10
+ Don't have one? Some options are
11
+ <%= link_to("1Password", "https://1password.com/") %>,
12
+ <%= link_to("LastPass Authenticator", "https://www.lastpass.com/") %>,
13
+ <%= link_to("Microsoft Authenticator",
14
+ "https://www.microsoft.com/en-us/security/mobile-authenticator-app") %>
15
+ or
16
+ <%= link_to("Google Authenticator",
17
+ "https://support.google.com/accounts/answer/1066447") %>.
18
+ </p>
19
+
20
+
21
+ <div class="qr-code">
22
+ <%= qr_code(@otp_secret.provisioning_uri) %>
23
+ </div>
24
+
25
+ <p>
26
+ If you are unable to scan the code, you can enter the following
27
+ info instead:
28
+ </p>
29
+
30
+ <p>
31
+ <b>Account name:</b><br>
32
+ <%= @otp_secret.account_name %>
33
+ </p>
34
+ <p>
35
+ <b>Secret:</b><br>
36
+ <span class="otp-secret">
37
+ <%= @otp_secret.secret %>
38
+ </span>
39
+ </p>
40
+
41
+ <h2>
42
+ Enter the code from the app
43
+ </h2>
44
+
45
+ <div class="field">
46
+ <label for="otp">6 digit code</label>
47
+ <%= text_field_tag(:otp, "",
48
+ autofocus: true,
49
+ autocomplete: "one-time-code",
50
+ size: 6) %>
51
+ </div>
52
+
53
+ <%= hidden_field_tag :signed_message, @otp_secret.signed_message %>
54
+
55
+ <p>
56
+ <button type="submit">
57
+ Verify
58
+ </button>
59
+ </p>
60
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <h2>
2
+ Recovery codes
3
+ </h2>
4
+ <p>
5
+ Please save the recovery codes below in a safe place, ideally
6
+ using a secure password manager.<br>
7
+ Without them, you will lose access to your account if you lose your device.
8
+ </p>
9
+
10
+ <ul class="recovery-codes">
11
+ <% recovery_codes.each do |c| %>
12
+ <li><%= c %></li>
13
+ <% end %>
14
+ </ul>
@@ -0,0 +1,7 @@
1
+ <% content_for :page_title, "New recovery codes" %>
2
+ <% content_for :page_description, "Recovery codes updated" %>
3
+
4
+ <div class="content">
5
+ <%= render(partial: "admin/recovery_codes/codes",
6
+ locals: { recovery_codes: @recovery_codes }) %>
7
+ </div>
@@ -0,0 +1,11 @@
1
+ <% content_for :page_title, "New recovery codes" %>
2
+ <% content_for :page_description, "Generate new recovery codes" %>
3
+
4
+ <%= form_tag(admin_recovery_codes_path, method: :post) do |f| %>
5
+ <%= render(partial: "admin/sessions/otp_form") %>
6
+ <p>
7
+ <button type="submit">
8
+ Verify
9
+ </button>
10
+ </p>
11
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <h2>
2
+ Two-factor authentication
3
+ </h2>
4
+ <p>
5
+ Enter a one-time code from your authenticator app to proceed.
6
+ </p>
7
+ <div class="field">
8
+ <label for="otp">6 digit code</label>
9
+ <%= text_field_tag(:otp, "",
10
+ autofocus: true,
11
+ autocomplete: "one-time-code",
12
+ size: 6) %>
13
+ </div>
@@ -0,0 +1,33 @@
1
+ <% content_for :page_title, "Sign in" %>
2
+ <% content_for(:page_description,
3
+ "Please enter your email address and password to sign in") %>
4
+ <% content_for :body_class, "login" %>
5
+
6
+ <% content_for :sidebar do %>
7
+ <h2>Please note</h2>
8
+ <p>
9
+ Please contact support if you experience problems logging in or using Pages.
10
+ </p>
11
+ <% end %>
12
+
13
+ <div class="login-form">
14
+ <%= form_tag admin_session_path do %>
15
+ <p>
16
+ <label>Email address</label>
17
+ <%= text_field_tag(:email, "", autocomplete: "email") %>
18
+ </p>
19
+ <p>
20
+ <label>Password</label>
21
+ <%= password_field_tag(:password, "", autocomplete: "current-password") %>
22
+ </p>
23
+ <p>
24
+ <button type="submit">Sign in</button>
25
+ </p>
26
+ <ul>
27
+ <li>
28
+ <%= link_to("<b>Help!</b> I forgot my password!".html_safe,
29
+ new_admin_account_recovery_path) %>
30
+ </li>
31
+ </ul>
32
+ <% end %>
33
+ </div>
@@ -0,0 +1,19 @@
1
+ <% content_for :page_title, "Two-factor authentication" %>
2
+ <% content_for :page_description, "Two-factor authentication" %>
3
+
4
+ <%= form_tag(verify_otp_admin_session_path, method: :post) do |f| %>
5
+ <%= hidden_field_tag :signed_user_id, @signed_user_id %>
6
+ <%= render(partial: "admin/sessions/otp_form") %>
7
+
8
+ <p>
9
+ <button type="submit">
10
+ Verify
11
+ </button>
12
+ </p>
13
+
14
+ <p>
15
+ Lost your authenticator device?
16
+ <%= link_to("Recover your account here",
17
+ new_admin_account_recovery_path) %>.
18
+ </p>
19
+ <% end %>
@@ -31,12 +31,42 @@
31
31
  <% if policy(@user).change_password? %>
32
32
  <h2>Password</h2>
33
33
  <%= f.labelled_password_field :password, 'Change password' %>
34
- <%= f.labelled_password_field :confirm_password, 'Confirm password' %>
34
+ <%= f.labelled_password_field :password_confirmation, 'Confirm password' %>
35
35
  <p>
36
36
  Leave the password blank if you do not wish to change the password.
37
37
  </p>
38
38
  <% end %>
39
39
 
40
+ <% if policy(@user).otp? %>
41
+ <h2>Two-factor authentication</h2>
42
+ <% if @user.otp_enabled? %>
43
+ <p>
44
+ Two-factor authentication has been enabled.
45
+ <%= link_to("Disable",
46
+ admin_otp_secret_path,
47
+ class: :delete,
48
+ method: :delete,
49
+ data: { confirm: "Are you sure you want to disable 2FA?" }) %>
50
+ </p>
51
+ <p>
52
+
53
+ You have
54
+ <%= t("pages_core.recovery_codes",
55
+ count: @user.hashed_recovery_codes.length) %>
56
+ remaining.
57
+ <%= link_to("Generate new codes", new_admin_recovery_codes_path) %>
58
+ </p>
59
+ <% else %>
60
+ <p>
61
+ Protect your account with an additional layer of security by
62
+ requiring an authentication app to sign in.
63
+ </p>
64
+ <p>
65
+ <%= link_to("Enable 2FA", new_admin_otp_secret_path) %>
66
+ </p>
67
+ <% end %>
68
+ <% end %>
69
+
40
70
  <%= render partial: "access_control", locals: { user: @user, f: f } %>
41
71
 
42
72
  <p>
@@ -11,7 +11,7 @@
11
11
  <%= f.labelled_text_field(:email, autocomplete: "email") %>
12
12
  <%= f.labelled_password_field(:password,
13
13
  autocomplete: "new-password") %>
14
- <%= f.labelled_password_field(:confirm_password,
14
+ <%= f.labelled_password_field(:password_confirmation,
15
15
  autocomplete: "new-password") %>
16
16
 
17
17
  <p>
@@ -0,0 +1,10 @@
1
+ Hi, <%= @user.name %>!
2
+
3
+ We've received a request to recover your account on <%= PagesCore.config(:site_name) %>.
4
+
5
+ Please click the following link to continue:
6
+ <%= @url %>
7
+
8
+ The link will expire in 24 hours.
9
+
10
+ If you do not want to recover your password, please ignore this email.
@@ -10,7 +10,7 @@
10
10
  <% if logged_in? %>
11
11
  <div class="user">
12
12
  Hello, <%= link_to(current_user.name, admin_user_url(current_user)) %>
13
- <%= link_to("Log out", session_path, method: "delete") %>
13
+ <%= link_to("Log out", admin_session_path, method: "delete") %>
14
14
  </div>
15
15
  <% end %>
16
16
  <nav class="tabs">
@@ -0,0 +1,12 @@
1
+ <%= react_component "Toast", { notice: flash[:notice], error: flash[:error] } %>
2
+ <% if Rails.env.test? && flash.any? %>
3
+ <div class="flash-test-helper">
4
+ <% %i[notice error].each do |type| %>
5
+ <% if flash[type] || true %>
6
+ <div class="<%= type %>">
7
+ <%= flash[type] %>
8
+ </div>
9
+ <% end %>
10
+ <% end %>
11
+ </div>
12
+ <% end %>
@@ -58,6 +58,6 @@
58
58
  <% end %>
59
59
  </div>
60
60
  <%= react_component "Modal", {} %>
61
- <%= react_component "Toast", { notice: flash[:notice], error: flash[:error] } %>
61
+ <%= render(partial: "layouts/admin/toast") %>
62
62
  </body>
63
63
  </html>
@@ -28,13 +28,21 @@ en:
28
28
  The provided email address and password combination was not valid
29
29
  invite_expired: This invite is no longer valid.
30
30
  logged_out: You have been logged out
31
- password_reset:
31
+ otp:
32
+ already_enabled: 2FA has already been enabled
33
+ disabled: 2FA has been disabled
34
+ invalid_code: Invalid 2FA code
35
+ required: 2FA is required for this
36
+ account_recovery:
32
37
  changed: Your password has been changed
33
- expired: Your password reset link has expired
34
- invalid_request: Invalid password reset request
38
+ invalid_request: This link is no longer valid
35
39
  not_found: Couldn't find a user with that email address
36
40
  sent: An email with further instructions has been sent
37
41
  problems_saving: There were problems saving your changes
42
+ recovery_codes:
43
+ zero: "no recovery codes"
44
+ one: "one recovery code"
45
+ other: "%{count} recovery codes"
38
46
  templates:
39
47
  default:
40
48
  blocks:
data/config/routes.rb CHANGED
@@ -33,9 +33,6 @@ Rails.application.routes.draw do
33
33
  get "pages/:locale/*glob" => redirect("/%{locale}/pages/%{glob}"),
34
34
  locale: /\w\w\w/
35
35
 
36
- # Authentication
37
- resource :session, only: %i[create destroy]
38
-
39
36
  # Sitemap
40
37
  resource :sitemap, only: [:show]
41
38
 
@@ -51,9 +48,9 @@ Rails.application.routes.draw do
51
48
  end
52
49
 
53
50
  # Password resets
54
- resources :password_resets, only: %i[create show update]
55
- controller :password_resets do
56
- get "/password_resets/:id/:token" => :show, as: :password_reset_with_token
51
+ resource :account_recovery
52
+ controller :account_recoveries do
53
+ get "/account_recovery/:token" => :show, as: :account_recovery_with_token
57
54
  end
58
55
 
59
56
  # Attachments
@@ -76,6 +73,14 @@ Rails.application.routes.draw do
76
73
  # Categories
77
74
  resources :categories
78
75
 
76
+ # Authentication
77
+ resource :session, only: %i[create destroy] do
78
+ member { post :verify_otp }
79
+ end
80
+ resource :otp_secret, only: %i[new create destroy]
81
+ resource :recovery_codes, only: %i[new create]
82
+ get "login" => "sessions#new", as: "login"
83
+
79
84
  # Pages
80
85
  scope ":locale" do
81
86
  resources :news,
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Add2faFields < ActiveRecord::Migration[7.0]
4
+ def change
5
+ change_table :users do |t|
6
+ t.boolean :otp_enabled, null: false, default: false
7
+ t.string :otp_secret
8
+ t.datetime :last_otp_at
9
+ t.jsonb :hashed_recovery_codes, null: false, default: []
10
+ t.string :session_token
11
+ end
12
+
13
+ rename_column :users, :hashed_password, :password_digest
14
+
15
+ reversible do |dir|
16
+ dir.up do
17
+ User.find_each { |u| u.update(session_token: SecureRandom.hex(32)) }
18
+ change_column_null :users, :session_token, false
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RemovePasswordResetTokens < ActiveRecord::Migration[7.0]
4
+ def change
5
+ drop_table :password_reset_tokens do |t|
6
+ t.integer :user_id
7
+ t.string :token
8
+ t.datetime :expires_at
9
+ t.datetime :created_at
10
+ t.datetime :updated_at
11
+ end
12
+ end
13
+ end
data/lib/pages_core.rb CHANGED
@@ -32,6 +32,8 @@ require "pg_search"
32
32
  require "progress_bar"
33
33
  require "rails_i18n"
34
34
  require "RedCloth"
35
+ require "rotp"
36
+ require "rqrcode"
35
37
  require "sass-rails"
36
38
  require "typhoeus"
37
39
  require "will_paginate"
@@ -83,5 +85,9 @@ module PagesCore
83
85
  end
84
86
  end
85
87
  alias config configuration
88
+
89
+ def reset_configuration!
90
+ @configuration = PagesCore::Configuration::Pages.new
91
+ end
86
92
  end
87
93
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pages_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.13.0
4
+ version: 3.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Inge Jørgensen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-17 00:00:00.000000000 Z
11
+ date: 2024-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -206,6 +206,34 @@ dependencies:
206
206
  - - "~>"
207
207
  - !ruby/object:Gem::Version
208
208
  version: 4.3.2
209
+ - !ruby/object:Gem::Dependency
210
+ name: rotp
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - "~>"
214
+ - !ruby/object:Gem::Version
215
+ version: 6.3.0
216
+ type: :runtime
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - "~>"
221
+ - !ruby/object:Gem::Version
222
+ version: 6.3.0
223
+ - !ruby/object:Gem::Dependency
224
+ name: rqrcode
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - ">="
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :runtime
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
209
237
  - !ruby/object:Gem::Dependency
210
238
  name: tty-table
211
239
  requirement: !ruby/object:Gem::Requirement
@@ -377,6 +405,7 @@ files:
377
405
  - app/assets/stylesheets/pages_core/admin/components/textarea.css
378
406
  - app/assets/stylesheets/pages_core/admin/components/toast.css
379
407
  - app/assets/stylesheets/pages_core/admin/components/toolbar.css
408
+ - app/assets/stylesheets/pages_core/admin/components/totp.css
380
409
  - app/assets/stylesheets/pages_core/admin/controllers/pages.css
381
410
  - app/assets/stylesheets/pages_core/admin/controllers/users.css
382
411
  - app/assets/stylesheets/pages_core/admin/vars.css
@@ -388,14 +417,17 @@ files:
388
417
  - app/controller_dummies/page_files_controller.rb
389
418
  - app/controller_dummies/pages_controller.rb
390
419
  - app/controller_dummies/sitemaps_controller.rb
420
+ - app/controllers/admin/account_recoveries_controller.rb
391
421
  - app/controllers/admin/attachments_controller.rb
392
422
  - app/controllers/admin/calendars_controller.rb
393
423
  - app/controllers/admin/categories_controller.rb
394
424
  - app/controllers/admin/images_controller.rb
395
425
  - app/controllers/admin/invites_controller.rb
396
426
  - app/controllers/admin/news_controller.rb
427
+ - app/controllers/admin/otp_secrets_controller.rb
397
428
  - app/controllers/admin/pages_controller.rb
398
- - app/controllers/admin/password_resets_controller.rb
429
+ - app/controllers/admin/recovery_codes_controller.rb
430
+ - app/controllers/admin/sessions_controller.rb
399
431
  - app/controllers/admin/users_controller.rb
400
432
  - app/controllers/concerns/pages_core/admin/persistent_params.rb
401
433
  - app/controllers/concerns/pages_core/authentication.rb
@@ -416,7 +448,6 @@ files:
416
448
  - app/controllers/pages_core/frontend_controller.rb
417
449
  - app/controllers/pages_core/images_controller.rb
418
450
  - app/controllers/pages_core/sitemaps_controller.rb
419
- - app/controllers/sessions_controller.rb
420
451
  - app/formatters/pages_core/html_formatter.rb
421
452
  - app/formatters/pages_core/image_embedder.rb
422
453
  - app/formatters/pages_core/link_renderer.rb
@@ -492,7 +523,6 @@ files:
492
523
  - app/javascript/components/drag/useDragUploader.ts
493
524
  - app/javascript/components/drag/useDraggable.ts
494
525
  - app/javascript/controllers/EditPageController.ts
495
- - app/javascript/controllers/LoginController.ts
496
526
  - app/javascript/controllers/MainController.ts
497
527
  - app/javascript/controllers/PageOptionsController.js
498
528
  - app/javascript/features/RichText.tsx
@@ -512,6 +542,7 @@ files:
512
542
  - app/models/attachment.rb
513
543
  - app/models/autopublisher.rb
514
544
  - app/models/category.rb
545
+ - app/models/concerns/pages_core/has_otp.rb
515
546
  - app/models/concerns/pages_core/has_roles.rb
516
547
  - app/models/concerns/pages_core/humanizable_param.rb
517
548
  - app/models/concerns/pages_core/page_model/attachments.rb
@@ -532,6 +563,7 @@ files:
532
563
  - app/models/image.rb
533
564
  - app/models/invite.rb
534
565
  - app/models/invite_role.rb
566
+ - app/models/otp_secret.rb
535
567
  - app/models/page.rb
536
568
  - app/models/page_builder.rb
537
569
  - app/models/page_category.rb
@@ -539,7 +571,6 @@ files:
539
571
  - app/models/page_file.rb
540
572
  - app/models/page_image.rb
541
573
  - app/models/page_path.rb
542
- - app/models/password_reset_token.rb
543
574
  - app/models/role.rb
544
575
  - app/models/search_document.rb
545
576
  - app/models/tag.rb
@@ -563,6 +594,8 @@ files:
563
594
  - app/services/pages_core/create_user_service.rb
564
595
  - app/services/pages_core/destroy_invite_service.rb
565
596
  - app/services/pages_core/invite_service.rb
597
+ - app/views/admin/account_recoveries/new.html.erb
598
+ - app/views/admin/account_recoveries/show.html.erb
566
599
  - app/views/admin/calendars/_sidebar.html.erb
567
600
  - app/views/admin/calendars/show.html.erb
568
601
  - app/views/admin/images/show.json.jbuilder
@@ -570,6 +603,8 @@ files:
570
603
  - app/views/admin/invites/show.html.erb
571
604
  - app/views/admin/news/_sidebar.html.erb
572
605
  - app/views/admin/news/index.html.erb
606
+ - app/views/admin/otp_secrets/create.html.erb
607
+ - app/views/admin/otp_secrets/new.html.erb
573
608
  - app/views/admin/pages/_edit_content.html.erb
574
609
  - app/views/admin/pages/_edit_files.html.erb
575
610
  - app/views/admin/pages/_edit_images.html.erb
@@ -583,18 +618,22 @@ files:
583
618
  - app/views/admin/pages/index.html.erb
584
619
  - app/views/admin/pages/new.html.erb
585
620
  - app/views/admin/pages/search.html.erb
586
- - app/views/admin/password_resets/show.html.erb
621
+ - app/views/admin/recovery_codes/_codes.html.erb
622
+ - app/views/admin/recovery_codes/create.html.erb
623
+ - app/views/admin/recovery_codes/new.html.erb
624
+ - app/views/admin/sessions/_otp_form.html.erb
625
+ - app/views/admin/sessions/new.html.erb
626
+ - app/views/admin/sessions/verify_otp.html.erb
587
627
  - app/views/admin/users/_access_control.html.erb
588
628
  - app/views/admin/users/_list.html.erb
589
629
  - app/views/admin/users/deactivated.html.erb
590
630
  - app/views/admin/users/edit.html.erb
591
631
  - app/views/admin/users/index.html.erb
592
- - app/views/admin/users/login.html.erb
593
632
  - app/views/admin/users/new.html.erb
594
633
  - app/views/admin/users/new_password.html.erb
595
634
  - app/views/admin/users/show.html.erb
635
+ - app/views/admin_mailer/account_recovery.text.erb
596
636
  - app/views/admin_mailer/invite.text.erb
597
- - app/views/admin_mailer/password_reset.text.erb
598
637
  - app/views/errors/401.html.erb
599
638
  - app/views/errors/403.html.erb
600
639
  - app/views/errors/404.html.erb
@@ -606,6 +645,7 @@ files:
606
645
  - app/views/layouts/admin.html.erb
607
646
  - app/views/layouts/admin/_header.html.erb
608
647
  - app/views/layouts/admin/_page_header.html.erb
648
+ - app/views/layouts/admin/_toast.html.erb
609
649
  - app/views/layouts/errors.html.erb
610
650
  - app/views/sitemaps/show.xml.builder
611
651
  - config/locales/en.yml
@@ -615,6 +655,8 @@ files:
615
655
  - db/migrate/20210209151400_create_search_configurations.rb
616
656
  - db/migrate/20210210235200_create_search_documents.rb
617
657
  - db/migrate/20220615160300_remove_username.rb
658
+ - db/migrate/20240126160700_add_2fa_fields.rb
659
+ - db/migrate/20240129201300_remove_password_reset_tokens.rb
618
660
  - lib/pages_core.rb
619
661
  - lib/pages_core/admin_menu_item.rb
620
662
  - lib/pages_core/archive_finder.rb