pages_core 3.12.7 → 3.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/app/assets/builds/pages_core/admin-dist.js +1 -1
- data/app/assets/builds/pages_core/admin-dist.js.map +4 -4
- data/app/assets/builds/pages_core/admin.css +27 -4
- data/app/assets/stylesheets/pages_core/admin/components/login.css +0 -6
- data/app/assets/stylesheets/pages_core/admin/components/totp.css +26 -0
- data/app/controllers/admin/account_recoveries_controller.rb +87 -0
- data/app/controllers/admin/invites_controller.rb +3 -2
- data/app/controllers/admin/otp_secrets_controller.rb +45 -0
- data/app/controllers/admin/pages_controller.rb +1 -1
- data/app/controllers/admin/recovery_codes_controller.rb +32 -0
- data/app/controllers/admin/sessions_controller.rb +65 -0
- data/app/controllers/admin/users_controller.rb +3 -9
- data/app/controllers/concerns/pages_core/authentication.rb +12 -10
- data/app/controllers/concerns/pages_core/error_reporting.rb +9 -19
- data/app/controllers/concerns/pages_core/static_cache_controller.rb +13 -2
- data/app/controllers/pages_core/admin_controller.rb +1 -1
- data/app/controllers/pages_core/attachments_controller.rb +1 -1
- data/app/controllers/pages_core/frontend/pages_controller.rb +1 -1
- data/app/controllers/pages_core/frontend_controller.rb +1 -10
- data/app/formatters/pages_core/image_embedder.rb +3 -3
- data/app/helpers/admin/pages_helper.rb +2 -2
- data/app/helpers/pages_core/admin/admin_helper.rb +12 -1
- data/app/helpers/pages_core/admin/content_tabs_helper.rb +3 -3
- data/app/helpers/pages_core/admin/form_builder.rb +1 -1
- data/app/helpers/pages_core/admin/image_uploads_helper.rb +6 -6
- data/app/helpers/pages_core/admin/labelled_field_helper.rb +1 -1
- data/app/helpers/pages_core/admin/locales_helper.rb +1 -1
- data/app/helpers/pages_core/application_helper.rb +3 -3
- data/app/helpers/pages_core/head_tags_helper.rb +8 -9
- data/app/helpers/pages_core/images_helper.rb +7 -7
- data/app/helpers/pages_core/open_graph_tags_helper.rb +2 -2
- data/app/helpers/pages_core/page_path_helper.rb +2 -2
- data/app/javascript/index.ts +0 -2
- data/app/jobs/pages_core/autopublish_job.rb +2 -0
- data/app/mailers/admin_mailer.rb +2 -2
- data/app/models/concerns/pages_core/has_otp.rb +27 -0
- data/app/models/concerns/pages_core/has_roles.rb +2 -2
- data/app/models/concerns/pages_core/page_model/dated_page.rb +1 -1
- data/app/models/concerns/pages_core/page_model/searchable.rb +1 -1
- data/app/models/concerns/pages_core/page_model/templateable.rb +22 -0
- data/app/models/concerns/pages_core/searchable_document.rb +3 -3
- data/app/models/otp_secret.rb +101 -0
- data/app/models/page.rb +1 -1
- data/app/models/page_builder.rb +9 -9
- data/app/models/page_exporter.rb +1 -1
- data/app/models/page_image.rb +1 -1
- data/app/models/page_path.rb +3 -3
- data/app/models/search_document.rb +3 -3
- data/app/models/tag.rb +1 -1
- data/app/models/user.rb +15 -37
- data/app/policies/user_policy.rb +4 -0
- data/app/services/pages_core/create_user_service.rb +2 -2
- data/app/services/pages_core/destroy_invite_service.rb +2 -2
- data/app/services/pages_core/invite_service.rb +2 -2
- data/app/views/admin/account_recoveries/new.html.erb +22 -0
- data/app/views/admin/account_recoveries/show.html.erb +37 -0
- data/app/views/admin/invites/show.html.erb +1 -1
- data/app/views/admin/otp_secrets/create.html.erb +7 -0
- data/app/views/admin/otp_secrets/new.html.erb +60 -0
- data/app/views/admin/pages/_edit_content.html.erb +1 -1
- data/app/views/admin/pages/_form.html.erb +12 -0
- data/app/views/admin/recovery_codes/_codes.html.erb +14 -0
- data/app/views/admin/recovery_codes/create.html.erb +7 -0
- data/app/views/admin/recovery_codes/new.html.erb +11 -0
- data/app/views/admin/sessions/_otp_form.html.erb +13 -0
- data/app/views/admin/sessions/new.html.erb +33 -0
- data/app/views/admin/sessions/verify_otp.html.erb +19 -0
- data/app/views/admin/users/edit.html.erb +31 -1
- data/app/views/admin/users/new.html.erb +1 -1
- data/app/views/admin_mailer/account_recovery.text.erb +10 -0
- data/app/views/layouts/admin/_header.html.erb +1 -1
- data/app/views/layouts/admin/_toast.html.erb +12 -0
- data/app/views/layouts/admin.html.erb +1 -1
- data/config/locales/en.yml +11 -3
- data/config/routes.rb +11 -6
- data/db/migrate/20111219033112_create_pages_tables.rb +0 -14
- data/db/migrate/20240126160700_add_2fa_fields.rb +22 -0
- data/db/migrate/20240129201300_remove_password_reset_tokens.rb +13 -0
- data/lib/pages_core/cache_sweeper.rb +3 -3
- data/lib/pages_core/extensions/string_extensions.rb +1 -1
- data/lib/pages_core/templates/configuration.rb +1 -1
- data/lib/pages_core/templates/template_configuration.rb +1 -1
- data/lib/pages_core.rb +7 -2
- data/lib/rails/generators/pages_core/install/install_generator.rb +0 -15
- data/lib/rails/generators/pages_core/rspec/templates/page_templates_spec.rb +1 -1
- metadata +53 -56
- data/app/controllers/admin/password_resets_controller.rb +0 -85
- data/app/controllers/sessions_controller.rb +0 -27
- data/app/javascript/controllers/LoginController.ts +0 -32
- data/app/models/password_reset_token.rb +0 -34
- data/app/views/admin/password_resets/show.html.erb +0 -21
- data/app/views/admin/users/login.html.erb +0 -65
- data/app/views/admin_mailer/password_reset.text.erb +0 -11
- data/lib/rails/generators/pages_core/install/templates/active_job_initializer.rb +0 -3
- data/lib/rails/generators/pages_core/install/templates/delayed_job +0 -7
- data/lib/rails/generators/pages_core/install/templates/delayed_job_initializer.rb +0 -18
@@ -11,7 +11,7 @@ module PagesCore
|
|
11
11
|
|
12
12
|
class << self
|
13
13
|
def call(attrs, invite: nil)
|
14
|
-
new(attrs, invite:
|
14
|
+
new(attrs, invite:).call
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -19,7 +19,7 @@ module PagesCore
|
|
19
19
|
User.transaction do
|
20
20
|
user = User.create(attributes.merge(invite_attributes))
|
21
21
|
if user.valid?
|
22
|
-
PagesCore::PubSub.publish(:create_user, user
|
22
|
+
PagesCore::PubSub.publish(:create_user, user:, invite:)
|
23
23
|
invite&.destroy
|
24
24
|
end
|
25
25
|
user
|
@@ -10,14 +10,14 @@ module PagesCore
|
|
10
10
|
|
11
11
|
class << self
|
12
12
|
def call(invite:)
|
13
|
-
new(invite:
|
13
|
+
new(invite:).call
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
17
|
def call
|
18
18
|
Invite.transaction do
|
19
19
|
invite.destroy
|
20
|
-
PagesCore::PubSub.publish(:destroy_invite, invite:
|
20
|
+
PagesCore::PubSub.publish(:destroy_invite, invite:)
|
21
21
|
invite
|
22
22
|
end
|
23
23
|
end
|
@@ -15,7 +15,7 @@ module PagesCore
|
|
15
15
|
|
16
16
|
class << self
|
17
17
|
def call(attrs, user:, host:, protocol: "http")
|
18
|
-
new(attrs, user
|
18
|
+
new(attrs, user:, host:, protocol:).call
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -36,7 +36,7 @@ module PagesCore
|
|
36
36
|
AdminMailer.invite(
|
37
37
|
invite,
|
38
38
|
admin_invite_with_token_url(invite, invite.token,
|
39
|
-
host
|
39
|
+
host:, protocol:)
|
40
40
|
).deliver_later
|
41
41
|
end
|
42
42
|
end
|
@@ -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(:
|
17
|
+
<%= f.labelled_password_field(:password_confirmation,
|
18
18
|
autocomplete: "new-password") %>
|
19
19
|
<p>
|
20
20
|
<button type="submit">
|
@@ -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 %>
|
@@ -2,6 +2,18 @@
|
|
2
2
|
<%= render partial: "edit_content", locals: { f: f } %>
|
3
3
|
<% end %>
|
4
4
|
|
5
|
+
<% if @page.unconfigured_blocks.any? %>
|
6
|
+
<%= content_tab "Unconfigured content" do %>
|
7
|
+
<p>
|
8
|
+
This page has additional content fields not enabled by the
|
9
|
+
selected template.
|
10
|
+
</p>
|
11
|
+
<% @page.unconfigured_blocks do |block_name, block_options| %>
|
12
|
+
<%= page_block_field(f, block_name, block_options) %>
|
13
|
+
<% end %>
|
14
|
+
<% end %>
|
15
|
+
<% end %>
|
16
|
+
|
5
17
|
<% if @page.template_config.value(:images) || @page.template_config.value(:image) %>
|
6
18
|
<%= content_tab "Images" do %>
|
7
19
|
<%= render partial: "edit_images", locals: { f: f } %>
|
@@ -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,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 :
|
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(:
|
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",
|
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 %>
|
data/config/locales/en.yml
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
55
|
-
controller :
|
56
|
-
get "/
|
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,
|
@@ -21,20 +21,6 @@ class CreatePagesTables < ActiveRecord::Migration[5.0]
|
|
21
21
|
t.index :slug
|
22
22
|
end
|
23
23
|
|
24
|
-
create_table :delayed_jobs do |t|
|
25
|
-
t.integer :priority, default: 0
|
26
|
-
t.integer :attempts, default: 0
|
27
|
-
t.text :handler
|
28
|
-
t.text :last_error
|
29
|
-
t.datetime :run_at
|
30
|
-
t.datetime :locked_at
|
31
|
-
t.datetime :failed_at
|
32
|
-
t.string :locked_by
|
33
|
-
t.datetime :created_at, null: false
|
34
|
-
t.datetime :updated_at, null: false
|
35
|
-
t.string :queue
|
36
|
-
end
|
37
|
-
|
38
24
|
create_table :images do |t|
|
39
25
|
t.string :filename, null: false
|
40
26
|
t.string :content_type, null: false
|
@@ -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
|
@@ -5,15 +5,15 @@ module PagesCore
|
|
5
5
|
class << self
|
6
6
|
attr_accessor :enabled
|
7
7
|
|
8
|
-
def disable(&
|
8
|
+
def disable(&)
|
9
9
|
old_value = enabled
|
10
10
|
self.enabled = false
|
11
11
|
yield if block_given?
|
12
12
|
self.enabled = old_value
|
13
13
|
end
|
14
14
|
|
15
|
-
def once(&
|
16
|
-
disable(&
|
15
|
+
def once(&)
|
16
|
+
disable(&)
|
17
17
|
PagesCore::StaticCache.handler.sweep!
|
18
18
|
end
|
19
19
|
end
|
@@ -149,7 +149,7 @@ module PagesCore
|
|
149
149
|
def template_config(setting, value, options)
|
150
150
|
value = true if value == :enabled
|
151
151
|
value = false if value == :disabled
|
152
|
-
{ setting => { value
|
152
|
+
{ setting => { value:, options: } }
|
153
153
|
end
|
154
154
|
|
155
155
|
def template_path(name)
|
data/lib/pages_core.rb
CHANGED
@@ -21,7 +21,6 @@ require "acts_as_list"
|
|
21
21
|
require "alba"
|
22
22
|
require "bcrypt"
|
23
23
|
require "country_select"
|
24
|
-
require "delayed_job_active_record"
|
25
24
|
require "dis"
|
26
25
|
require "dynamic_image"
|
27
26
|
require "healthcheck"
|
@@ -33,6 +32,8 @@ require "pg_search"
|
|
33
32
|
require "progress_bar"
|
34
33
|
require "rails_i18n"
|
35
34
|
require "RedCloth"
|
35
|
+
require "rotp"
|
36
|
+
require "rqrcode"
|
36
37
|
require "sass-rails"
|
37
38
|
require "typhoeus"
|
38
39
|
require "will_paginate"
|
@@ -71,7 +72,7 @@ module PagesCore
|
|
71
72
|
Pathname.new(File.dirname(__FILE__)).join("..").expand_path
|
72
73
|
end
|
73
74
|
|
74
|
-
def configure(_options = {}, &
|
75
|
+
def configure(_options = {}, &)
|
75
76
|
yield configuration if block_given?
|
76
77
|
end
|
77
78
|
|
@@ -84,5 +85,9 @@ module PagesCore
|
|
84
85
|
end
|
85
86
|
end
|
86
87
|
alias config configuration
|
88
|
+
|
89
|
+
def reset_configuration!
|
90
|
+
@configuration = PagesCore::Configuration::Pages.new
|
91
|
+
end
|
87
92
|
end
|
88
93
|
end
|