FEATURE: Add safe-mode toggle to `/u/admin-login` (#17930)
Previously, this would require manually adding `?safe_mode=...` multiple times during the email-based login flow. `/u/admin-login` is often used when debugging a site, so it makes sense for this to be easier. This commit introduces a new checkbox on the `/u/admin-login` screen. When checked, it'll set the safe_mode parameter on the `/email-login` link, and then pass it all the way through to the homepage redirect.
This commit is contained in:
parent
64a66cf82b
commit
3ffc213fa9
|
@ -6,8 +6,11 @@ import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
import { getWebauthnCredential } from "discourse/lib/webauthn";
|
import { getWebauthnCredential } from "discourse/lib/webauthn";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
|
router: service(),
|
||||||
|
|
||||||
lockImageUrl: getURL("/images/lock.svg"),
|
lockImageUrl: getURL("/images/lock.svg"),
|
||||||
|
|
||||||
@discourseComputed("model")
|
@discourseComputed("model")
|
||||||
|
@ -41,7 +44,20 @@ export default Controller.extend({
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
DiscourseURL.redirectTo("/");
|
let destination = "/";
|
||||||
|
|
||||||
|
const safeMode = new URL(
|
||||||
|
this.router.currentURL,
|
||||||
|
window.location.origin
|
||||||
|
).searchParams.get("safe_mode");
|
||||||
|
|
||||||
|
if (safeMode) {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set("safe_mode", safeMode);
|
||||||
|
destination += `?${params.toString()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
DiscourseURL.redirectTo(destination);
|
||||||
} else {
|
} else {
|
||||||
this.set("model.error", result.error);
|
this.set("model.error", result.error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,10 @@ import {
|
||||||
import { click, fillIn, visit } from "@ember/test-helpers";
|
import { click, fillIn, visit } from "@ember/test-helpers";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { test } from "qunit";
|
import { test } from "qunit";
|
||||||
|
import DiscourseURL from "discourse/lib/url";
|
||||||
|
import sinon from "sinon";
|
||||||
|
|
||||||
|
const TOKEN = "sometoken";
|
||||||
|
|
||||||
acceptance("Login with email", function (needs) {
|
acceptance("Login with email", function (needs) {
|
||||||
needs.settings({
|
needs.settings({
|
||||||
|
@ -18,6 +22,20 @@ acceptance("Login with email", function (needs) {
|
||||||
server.post("/u/email-login", () =>
|
server.post("/u/email-login", () =>
|
||||||
helper.response({ success: "OK", user_found: userFound })
|
helper.response({ success: "OK", user_found: userFound })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
server.get(`/session/email-login/${TOKEN}.json`, () =>
|
||||||
|
helper.response({
|
||||||
|
token: TOKEN,
|
||||||
|
can_login: true,
|
||||||
|
token_email: "blah@example.com",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
server.post(`/session/email-login/${TOKEN}`, () =>
|
||||||
|
helper.response({
|
||||||
|
success: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("with email button", async function (assert) {
|
test("with email button", async function (assert) {
|
||||||
|
@ -83,4 +101,21 @@ acceptance("Login with email", function (needs) {
|
||||||
|
|
||||||
userFound = false;
|
userFound = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("finish login UI", async function (assert) {
|
||||||
|
await visit(`/session/email-login/${TOKEN}`);
|
||||||
|
sinon.stub(DiscourseURL, "redirectTo");
|
||||||
|
await click(".email-login .btn-primary");
|
||||||
|
assert.true(DiscourseURL.redirectTo.calledWith("/"), "redirects to home");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("finish login UI - safe mode", async function (assert) {
|
||||||
|
await visit(`/session/email-login/${TOKEN}?safe_mode=no_themes,no_plugins`);
|
||||||
|
sinon.stub(DiscourseURL, "redirectTo");
|
||||||
|
await click(".email-login .btn-primary");
|
||||||
|
assert.true(
|
||||||
|
DiscourseURL.redirectTo.calledWith("/?safe_mode=no_themes%2Cno_plugins"),
|
||||||
|
"redirects to home with safe mode"
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -961,7 +961,11 @@ class UsersController < ApplicationController
|
||||||
|
|
||||||
if user = User.with_email(params[:email]).admins.human_users.first
|
if user = User.with_email(params[:email]).admins.human_users.first
|
||||||
email_token = user.email_tokens.create!(email: user.email, scope: EmailToken.scopes[:email_login])
|
email_token = user.email_tokens.create!(email: user.email, scope: EmailToken.scopes[:email_login])
|
||||||
Jobs.enqueue(:critical_user_email, type: "admin_login", user_id: user.id, email_token: email_token.token)
|
token_string = email_token.token
|
||||||
|
if params["use_safe_mode"]
|
||||||
|
token_string += "?safe_mode=no_plugins,no_themes"
|
||||||
|
end
|
||||||
|
Jobs.enqueue(:critical_user_email, type: "admin_login", user_id: user.id, email_token: token_string)
|
||||||
@message = I18n.t("admin_login.success")
|
@message = I18n.t("admin_login.success")
|
||||||
else
|
else
|
||||||
@message = I18n.t("admin_login.errors.unknown_email_address")
|
@message = I18n.t("admin_login.errors.unknown_email_address")
|
||||||
|
|
|
@ -5,6 +5,11 @@
|
||||||
<%=form_tag(u_admin_login_path, method: :put) do %>
|
<%=form_tag(u_admin_login_path, method: :put) do %>
|
||||||
<%= label_tag(:email, t('admin_login.email_input')) %>
|
<%= label_tag(:email, t('admin_login.email_input')) %>
|
||||||
<%= text_field_tag(:email, nil, autofocus: true) %><br><br>
|
<%= text_field_tag(:email, nil, autofocus: true) %><br><br>
|
||||||
|
<label for="use_safe_mode">
|
||||||
|
<%= check_box_tag 'use_safe_mode' %>
|
||||||
|
<%= t 'admin_login.safe_mode' %>
|
||||||
|
</label>
|
||||||
|
<br/>
|
||||||
<%= submit_tag t('admin_login.submit_button'), class: "btn btn-primary" %>
|
<%= submit_tag t('admin_login.submit_button'), class: "btn btn-primary" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -4833,6 +4833,7 @@ en:
|
||||||
invalid_token: "Invalid token."
|
invalid_token: "Invalid token."
|
||||||
email_input: "Admin Email"
|
email_input: "Admin Email"
|
||||||
submit_button: "Send Email"
|
submit_button: "Send Email"
|
||||||
|
safe_mode: "Safe Mode: disable all themes/plugins when logging in"
|
||||||
|
|
||||||
performance_report:
|
performance_report:
|
||||||
initial_post_raw: This topic includes daily performance reports for your site.
|
initial_post_raw: This topic includes daily performance reports for your site.
|
||||||
|
|
|
@ -535,6 +535,14 @@ RSpec.describe UsersController do
|
||||||
expect(args["user_id"]).to eq(admin.id)
|
expect(args["user_id"]).to eq(admin.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'passes through safe mode' do
|
||||||
|
put "/u/admin-login", params: { email: admin.email, use_safe_mode: true }
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(Jobs::CriticalUserEmail.jobs.size).to eq(1)
|
||||||
|
args = Jobs::CriticalUserEmail.jobs.first["args"].first
|
||||||
|
expect(args["email_token"]).to end_with("?safe_mode=no_plugins,no_themes")
|
||||||
|
end
|
||||||
|
|
||||||
context 'when email is incorrect' do
|
context 'when email is incorrect' do
|
||||||
it 'should return the right response' do
|
it 'should return the right response' do
|
||||||
put "/u/admin-login", params: { email: 'random' }
|
put "/u/admin-login", params: { email: 'random' }
|
||||||
|
|
Loading…
Reference in New Issue