diff --git a/app/assets/javascripts/discourse/app/controllers/email-login.js b/app/assets/javascripts/discourse/app/controllers/email-login.js
index a488daf5e77..b17304a779b 100644
--- a/app/assets/javascripts/discourse/app/controllers/email-login.js
+++ b/app/assets/javascripts/discourse/app/controllers/email-login.js
@@ -6,8 +6,11 @@ import discourseComputed from "discourse-common/utils/decorators";
import getURL from "discourse-common/lib/get-url";
import { getWebauthnCredential } from "discourse/lib/webauthn";
import { popupAjaxError } from "discourse/lib/ajax-error";
+import { inject as service } from "@ember/service";
export default Controller.extend({
+ router: service(),
+
lockImageUrl: getURL("/images/lock.svg"),
@discourseComputed("model")
@@ -41,7 +44,20 @@ export default Controller.extend({
})
.then((result) => {
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 {
this.set("model.error", result.error);
}
diff --git a/app/assets/javascripts/discourse/tests/acceptance/login-with-email-test.js b/app/assets/javascripts/discourse/tests/acceptance/login-with-email-test.js
index ee9718f0fa4..15f83901d26 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/login-with-email-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/login-with-email-test.js
@@ -6,6 +6,10 @@ import {
import { click, fillIn, visit } from "@ember/test-helpers";
import I18n from "I18n";
import { test } from "qunit";
+import DiscourseURL from "discourse/lib/url";
+import sinon from "sinon";
+
+const TOKEN = "sometoken";
acceptance("Login with email", function (needs) {
needs.settings({
@@ -18,6 +22,20 @@ acceptance("Login with email", function (needs) {
server.post("/u/email-login", () =>
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) {
@@ -83,4 +101,21 @@ acceptance("Login with email", function (needs) {
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"
+ );
+ });
});
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 8d83520d61e..662a2c3a6e9 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -961,7 +961,11 @@ class UsersController < ApplicationController
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])
- 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")
else
@message = I18n.t("admin_login.errors.unknown_email_address")
diff --git a/app/views/users/admin_login.html.erb b/app/views/users/admin_login.html.erb
index 95a74a123ff..82e061bdfd6 100644
--- a/app/views/users/admin_login.html.erb
+++ b/app/views/users/admin_login.html.erb
@@ -5,6 +5,11 @@
<%=form_tag(u_admin_login_path, method: :put) do %>
<%= label_tag(:email, t('admin_login.email_input')) %>
<%= text_field_tag(:email, nil, autofocus: true) %>
+
+
<%= submit_tag t('admin_login.submit_button'), class: "btn btn-primary" %>
<% end %>
<% end %>
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index c966b7c8097..b2b180f86bf 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -4833,6 +4833,7 @@ en:
invalid_token: "Invalid token."
email_input: "Admin Email"
submit_button: "Send Email"
+ safe_mode: "Safe Mode: disable all themes/plugins when logging in"
performance_report:
initial_post_raw: This topic includes daily performance reports for your site.
diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb
index 6a75373b0b0..b37bb8e447a 100644
--- a/spec/requests/users_controller_spec.rb
+++ b/spec/requests/users_controller_spec.rb
@@ -535,6 +535,14 @@ RSpec.describe UsersController do
expect(args["user_id"]).to eq(admin.id)
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
it 'should return the right response' do
put "/u/admin-login", params: { email: 'random' }