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.
- 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/recovery_codes_controller.rb +32 -0
- data/app/controllers/admin/sessions_controller.rb +65 -0
- data/app/controllers/admin/users_controller.rb +2 -8
- data/app/controllers/concerns/pages_core/authentication.rb +12 -10
- data/app/controllers/pages_core/admin_controller.rb +1 -1
- data/app/helpers/pages_core/admin/admin_helper.rb +11 -0
- data/app/javascript/index.ts +0 -2
- data/app/mailers/admin_mailer.rb +2 -2
- data/app/models/concerns/pages_core/has_otp.rb +27 -0
- data/app/models/otp_secret.rb +101 -0
- data/app/models/user.rb +15 -37
- data/app/policies/user_policy.rb +4 -0
- 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/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/20240126160700_add_2fa_fields.rb +22 -0
- data/db/migrate/20240129201300_remove_password_reset_tokens.rb +13 -0
- data/lib/pages_core.rb +6 -0
- metadata +51 -9
- 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
@@ -1,85 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Admin
|
4
|
-
class PasswordResetsController < Admin::AdminController
|
5
|
-
before_action :find_password_reset_token, only: %i[show update]
|
6
|
-
before_action :check_for_expired_token, only: %i[show update]
|
7
|
-
before_action :require_authentication, except: %i[create show update]
|
8
|
-
|
9
|
-
layout "admin"
|
10
|
-
|
11
|
-
def show
|
12
|
-
@user = @password_reset_token.user
|
13
|
-
end
|
14
|
-
|
15
|
-
def create
|
16
|
-
@user = find_user_by_email(params[:email])
|
17
|
-
if @user
|
18
|
-
@password_reset_token = @user.password_reset_tokens.create
|
19
|
-
deliver_password_reset(@user, @password_reset_token)
|
20
|
-
flash[:notice] = t("pages_core.password_reset.sent")
|
21
|
-
else
|
22
|
-
flash[:notice] = t("pages_core.password_reset.not_found")
|
23
|
-
end
|
24
|
-
redirect_to login_admin_users_url
|
25
|
-
end
|
26
|
-
|
27
|
-
def update
|
28
|
-
@user = @password_reset_token.user
|
29
|
-
if user_params[:password].present? && @user.update(user_params)
|
30
|
-
@password_reset_token.destroy
|
31
|
-
authenticate!(@user)
|
32
|
-
flash[:notice] = t("pages_core.password_reset.changed")
|
33
|
-
redirect_to login_admin_users_url
|
34
|
-
else
|
35
|
-
render action: :show
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def deliver_password_reset(user, password_reset)
|
42
|
-
AdminMailer.password_reset(
|
43
|
-
user,
|
44
|
-
admin_password_reset_with_token_url(
|
45
|
-
password_reset, password_reset.token
|
46
|
-
)
|
47
|
-
).deliver_later
|
48
|
-
end
|
49
|
-
|
50
|
-
def find_user_by_email(email)
|
51
|
-
return unless email
|
52
|
-
|
53
|
-
User.find_by_email(params[:email])
|
54
|
-
end
|
55
|
-
|
56
|
-
def user_params
|
57
|
-
params.require(:user).permit(:password, :confirm_password)
|
58
|
-
end
|
59
|
-
|
60
|
-
def valid_token?(reset)
|
61
|
-
reset && secure_compare(reset.token, params[:token])
|
62
|
-
end
|
63
|
-
|
64
|
-
def find_password_reset_token
|
65
|
-
@password_reset_token = begin
|
66
|
-
PasswordResetToken.find(params[:id])
|
67
|
-
rescue ActiveRecord::RecordNotFound
|
68
|
-
nil
|
69
|
-
end
|
70
|
-
|
71
|
-
return if valid_token?(@password_reset_token)
|
72
|
-
|
73
|
-
flash[:notice] = t("pages_core.password_reset.invalid_request")
|
74
|
-
redirect_to(login_admin_users_url) && return
|
75
|
-
end
|
76
|
-
|
77
|
-
def check_for_expired_token
|
78
|
-
return unless @password_reset_token.expired?
|
79
|
-
|
80
|
-
@password_reset_token.destroy
|
81
|
-
flash[:notice] = t("pages_core.password_reset.expired")
|
82
|
-
redirect_to(login_admin_users_url)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class SessionsController < ApplicationController
|
4
|
-
def create
|
5
|
-
user = find_user(params[:email], params[:password])
|
6
|
-
authenticate!(user) if user
|
7
|
-
|
8
|
-
if logged_in?
|
9
|
-
redirect_to admin_default_url
|
10
|
-
else
|
11
|
-
flash[:notice] = t("pages_core.invalid_login")
|
12
|
-
redirect_to login_admin_users_url
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def destroy
|
17
|
-
flash[:notice] = t("pages_core.logged_out")
|
18
|
-
deauthenticate!
|
19
|
-
redirect_to login_admin_users_url
|
20
|
-
end
|
21
|
-
|
22
|
-
protected
|
23
|
-
|
24
|
-
def find_user(email, password)
|
25
|
-
User.authenticate(email, password:) if email && password
|
26
|
-
end
|
27
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
import { Controller } from "@hotwired/stimulus";
|
2
|
-
|
3
|
-
export default class LoginController extends Controller {
|
4
|
-
declare readonly tabTargets: HTMLDivElement[];
|
5
|
-
|
6
|
-
static get targets() {
|
7
|
-
return ["tab"];
|
8
|
-
}
|
9
|
-
|
10
|
-
connect() {
|
11
|
-
if (this.tabTargets.length > 0) {
|
12
|
-
this.showTab(this.tabTargets[0].dataset.tab);
|
13
|
-
}
|
14
|
-
}
|
15
|
-
|
16
|
-
changeTab(evt: Event) {
|
17
|
-
evt.preventDefault();
|
18
|
-
if ("dataset" in evt.target && "tab" in evt.target.dataset) {
|
19
|
-
this.showTab(evt.target.dataset.tab);
|
20
|
-
}
|
21
|
-
}
|
22
|
-
|
23
|
-
showTab(tab: string) {
|
24
|
-
this.tabTargets.forEach((t) => {
|
25
|
-
if (t.dataset.tab == tab) {
|
26
|
-
t.classList.remove("hidden");
|
27
|
-
} else {
|
28
|
-
t.classList.add("hidden");
|
29
|
-
}
|
30
|
-
});
|
31
|
-
}
|
32
|
-
}
|
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class PasswordResetToken < ApplicationRecord
|
4
|
-
belongs_to :user
|
5
|
-
before_create :ensure_token
|
6
|
-
before_create :ensure_expiration
|
7
|
-
|
8
|
-
scope :active, -> { where("expires_at >= ?", Time.now.utc) }
|
9
|
-
scope :expired, -> { where("expires_at < ?", Time.now.utc) }
|
10
|
-
|
11
|
-
class << self
|
12
|
-
def default_expiration
|
13
|
-
24.hours
|
14
|
-
end
|
15
|
-
|
16
|
-
def expire!
|
17
|
-
expired.delete_all
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def expired?
|
22
|
-
expires_at < Time.now.utc
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def ensure_expiration
|
28
|
-
self.expires_at ||= Time.now.utc + self.class.default_expiration
|
29
|
-
end
|
30
|
-
|
31
|
-
def ensure_token
|
32
|
-
self.token ||= SecureRandom.hex(32)
|
33
|
-
end
|
34
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
<% content_for :page_title, "Reset password" %>
|
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_password_reset_path(@password_reset_token, token: @password_reset_token.token),
|
8
|
-
builder: PagesCore::Admin::FormBuilder,
|
9
|
-
class: 'form') do |f| %>
|
10
|
-
<%= f.labelled_password_field(:password,
|
11
|
-
autocomplete: "new-password") %>
|
12
|
-
<%= f.labelled_password_field(:confirm_password,
|
13
|
-
autocomplete: "new-password") %>
|
14
|
-
<p>
|
15
|
-
<button type="submit">
|
16
|
-
Change Password
|
17
|
-
</button>
|
18
|
-
or <%= link_to "Return to login screen", login_admin_users_path %>
|
19
|
-
</p>
|
20
|
-
<% end %>
|
21
|
-
</div>
|
@@ -1,65 +0,0 @@
|
|
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
|
-
data-controller="login">
|
15
|
-
<div class="login-tab password"
|
16
|
-
data-login-target="tab"
|
17
|
-
data-tab="password">
|
18
|
-
<%= form_tag session_path do %>
|
19
|
-
<p>
|
20
|
-
<label>Email address</label>
|
21
|
-
<%= text_field_tag(:email, "", autocomplete: "email") %>
|
22
|
-
</p>
|
23
|
-
<p>
|
24
|
-
<label>Password</label>
|
25
|
-
<%= password_field_tag(:password, "", autocomplete: "current-password") %>
|
26
|
-
</p>
|
27
|
-
<p>
|
28
|
-
<button type="submit">Sign in</button>
|
29
|
-
</p>
|
30
|
-
<ul>
|
31
|
-
<li>
|
32
|
-
<%= link_to("<b>Help!</b> I forgot my password!".html_safe,
|
33
|
-
login_admin_users_path,
|
34
|
-
data: {
|
35
|
-
action: "click->login#changeTab",
|
36
|
-
tab: "password-reset"
|
37
|
-
}) %>
|
38
|
-
</li>
|
39
|
-
</ul>
|
40
|
-
<% end %>
|
41
|
-
</div>
|
42
|
-
|
43
|
-
<div class="login-tab password-reset"
|
44
|
-
data-login-target="tab"
|
45
|
-
data-tab="password-reset">
|
46
|
-
<%= form_tag admin_password_resets_path do %>
|
47
|
-
<h2>
|
48
|
-
Forgot your password?
|
49
|
-
</h2>
|
50
|
-
<p>
|
51
|
-
Don't worry, it happens.
|
52
|
-
Enter your email address below,
|
53
|
-
and we'll send you a link where you can reset your password.
|
54
|
-
</p>
|
55
|
-
<p>
|
56
|
-
<%= text_field_tag(:email, "", autocomplete: "email") %>
|
57
|
-
</p>
|
58
|
-
<p>
|
59
|
-
<button type="submit">
|
60
|
-
Send
|
61
|
-
</button>
|
62
|
-
</p>
|
63
|
-
<% end %>
|
64
|
-
</div>
|
65
|
-
</div>
|
@@ -1,11 +0,0 @@
|
|
1
|
-
Hi, <%= @user.name %>!
|
2
|
-
|
3
|
-
We've received a request to reset the password for your account on <%= PagesCore.config(:site_name) %>.
|
4
|
-
|
5
|
-
If you want to reset your password, please click the following link:
|
6
|
-
|
7
|
-
<%= @url %>
|
8
|
-
|
9
|
-
This will take you to a web page where you can set a new password of your choosing. The link will expire in 24 hours.
|
10
|
-
|
11
|
-
If you do not want to change your password, please ignore this email.
|