Commit 08116b2f authored by Oliver Hader's avatar Oliver Hader Committed by Benjamin Franzke
Browse files

[BUGFIX] Avoid reloading backend login form for checking HTTP referrer

In order to determine whether HTTP referrer headers would be submitted
to server-side backend API, a corresponding "reload" was triggered for
the backend's main login form.

Since the behavior resulted in reduced user experience, the refreshing
mechanism was replaced by an equivalent AJAX endpoint and request.

Resolves: #92293
Releases: master, 10.4, 9.5
Change-Id: I745d266a97135496276437b8eaf9dd206bc5806e
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/69336

Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Michael Telgkamp's avatarMichael Telgkamp <michael.telgkamp@mindscreen.de>
Tested-by: Benjamin Franzke's avatarBenjamin Franzke <bfr@qbus.de>
Reviewed-by: Michael Telgkamp's avatarMichael Telgkamp <michael.telgkamp@mindscreen.de>
Reviewed-by: Benjamin Franzke's avatarBenjamin Franzke <bfr@qbus.de>
parent 6bd8291b
......@@ -14,7 +14,17 @@
import 'bootstrap';
import $ from 'jquery';
import 'TYPO3/CMS/Backend/Input/Clearable';
import Client = require('TYPO3/CMS/Backend/Storage/Client');
import AjaxRequest = require('TYPO3/CMS/Core/Ajax/AjaxRequest');
import {AjaxResponse} from 'TYPO3/CMS/Core/Ajax/AjaxResponse';
interface PreflightResponse {
capabilities: PreflightResponseCapabilities;
}
interface PreflightResponseCapabilities {
cookie: boolean,
referrer: boolean
}
/**
* Module: TYPO3/CMS/Backend/Login
......@@ -113,31 +123,25 @@ class BackendLogin {
}
private checkDocumentReferrerSupport(): void {
const referrerRefreshed = Client.get('referrerRefresh') === '1';
const loginUrlLink = document.getElementById(this.options.loginUrlLink) as HTMLAnchorElement;
if (loginUrlLink === null
|| typeof loginUrlLink.dataset.referrerCheckEnabled === 'undefined'
|| loginUrlLink.dataset.referrerCheckEnabled !== '1'
// skip referrer check if explicitly disabled
if (loginUrlLink !== null
&& typeof loginUrlLink.dataset.referrerCheckEnabled === 'undefined'
&& loginUrlLink.dataset.referrerCheckEnabled !== '1'
) {
return;
}
if (typeof document.referrer === 'string' && document.referrer !== '') {
if (referrerRefreshed) {
Client.unset('referrerRefresh');
}
if (typeof TYPO3.settings === 'undefined' || typeof TYPO3.settings.ajaxUrls === 'undefined') {
return;
}
if (referrerRefreshed) {
Client.unset('referrerRefresh');
document.querySelectorAll(this.options.errorNoReferrer)
.forEach((element: HTMLElement): void => element.classList.remove('hidden'));
} else {
this.ready = false;
Client.set('referrerRefresh', '1');
loginUrlLink.click();
}
new AjaxRequest(TYPO3.settings.ajaxUrls.login_preflight).get()
.then(async (response: AjaxResponse) => {
const result = await response.resolve('application/json') as PreflightResponse;
if (result.capabilities.referrer !== true) {
document.querySelectorAll(this.options.errorNoReferrer)
.forEach((element: HTMLElement): void => element.classList.remove('hidden'));
}
});
}
/**
......
......@@ -72,6 +72,18 @@ class AjaxLoginController
]);
}
public function preflightAction(ServerRequestInterface $request): ResponseInterface
{
$headers = $request->getHeaders();
return new JsonResponse([
'capabilities' => [
'cookie' => !empty($request->getCookieParams()),
// using legacy `Referer` (sic!) header name
'referrer' => array_filter($headers['referer'] ?? []) !== [],
],
]);
}
/**
* Refreshes the login without needing login information. We just refresh the session.
*
......
......@@ -54,6 +54,7 @@ class BackendUserAuthenticator extends \TYPO3\CMS\Core\Middleware\BackendUserAut
'/login/password-reset/finish',
'/ajax/login',
'/ajax/logout',
'/ajax/login/preflight',
'/ajax/login/refresh',
'/ajax/login/timedout',
'/ajax/rsa/publickey',
......
......@@ -190,6 +190,13 @@ return [
'access' => 'public'
],
// Preflight check for login form
'login_preflight' => [
'path' => '/login/preflight',
'target' => \TYPO3\CMS\Backend\Controller\AjaxLoginController::class . '::preflightAction',
'access' => 'public'
],
// Refresh login of backend
'login_refresh' => [
'path' => '/login/refresh',
......
......@@ -10,4 +10,4 @@
*
* The TYPO3 project - inspiring people to share!
*/
var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","TYPO3/CMS/Backend/Storage/Client","bootstrap","TYPO3/CMS/Backend/Input/Clearable"],(function(e,t,o,i){"use strict";o=__importDefault(o);return new class{constructor(){this.ready=!0,this.options={error:".t3js-login-error",errorNoCookies:".t3js-login-error-nocookies",errorNoReferrer:".t3js-login-error-noreferrer",formFields:".t3js-login-formfields",interfaceField:".t3js-login-interface-field",loginForm:"#typo3-login-form",loginUrlLink:"t3js-login-url",submitButton:".t3js-login-submit",submitHandler:null,useridentField:".t3js-login-userident-field"},this.checkCookieSupport(),this.checkForInterfaceCookie(),this.checkDocumentReferrerSupport(),this.initializeEvents(),top.location.href!==location.href&&(this.ready=!1,top.location.href=location.href),this.ready&&document.body.setAttribute("data-typo3-login-ready","true")}showLoginProcess(){this.showLoadingIndicator(),o.default(this.options.error).addClass("hidden"),o.default(this.options.errorNoCookies).addClass("hidden")}showLoadingIndicator(){const e=o.default(this.options.submitButton);e.html(e.data("loading-text"))}handleSubmit(e){this.showLoginProcess(),"function"==typeof this.options.submitHandler&&this.options.submitHandler(e)}interfaceSelectorChanged(){const e=new Date,t=new Date(e.getTime()+31536e6);document.cookie="typo3-login-interface="+o.default(this.options.interfaceField).val()+"; expires="+t.toUTCString()+";"}checkForInterfaceCookie(){if(o.default(this.options.interfaceField).length){const e=document.cookie.indexOf("typo3-login-interface=");if(-1!==e){let t=document.cookie.substr(e+22);t=t.substr(0,t.indexOf(";")),o.default(this.options.interfaceField).val(t)}}}checkDocumentReferrerSupport(){const e="1"===i.get("referrerRefresh"),t=document.getElementById(this.options.loginUrlLink);null!==t&&void 0!==t.dataset.referrerCheckEnabled&&"1"===t.dataset.referrerCheckEnabled&&("string"!=typeof document.referrer||""===document.referrer?e?(i.unset("referrerRefresh"),document.querySelectorAll(this.options.errorNoReferrer).forEach(e=>e.classList.remove("hidden"))):(this.ready=!1,i.set("referrerRefresh","1"),t.click()):e&&i.unset("referrerRefresh"))}showCookieWarning(){o.default(this.options.formFields).addClass("hidden"),o.default(this.options.errorNoCookies).removeClass("hidden")}hideCookieWarning(){o.default(this.options.formFields).removeClass("hidden"),o.default(this.options.errorNoCookies).addClass("hidden")}checkCookieSupport(){const e=navigator.cookieEnabled;!1===e?this.showCookieWarning():document.cookie||null!==e||(document.cookie="typo3-login-cookiecheck=1",document.cookie?document.cookie="typo3-login-cookiecheck=; expires="+new Date(0).toUTCString():this.showCookieWarning())}initializeEvents(){o.default(document).ajaxStart(this.showLoadingIndicator.bind(this)),o.default(this.options.loginForm).on("submit",this.handleSubmit.bind(this)),o.default(this.options.interfaceField).length>0&&o.default(document).on("change blur",this.options.interfaceField,this.interfaceSelectorChanged.bind(this)),document.querySelectorAll(".t3js-clearable").forEach(e=>e.clearable()),o.default(".t3js-login-news-carousel").on("slide.bs.carousel",e=>{const t=o.default(e.relatedTarget).height();o.default(e.target).find("div.active").parent().animate({height:t},500)})}}}));
\ No newline at end of file
var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","TYPO3/CMS/Core/Ajax/AjaxRequest","bootstrap","TYPO3/CMS/Backend/Input/Clearable"],(function(e,t,o,i){"use strict";o=__importDefault(o);return new class{constructor(){this.ready=!0,this.options={error:".t3js-login-error",errorNoCookies:".t3js-login-error-nocookies",errorNoReferrer:".t3js-login-error-noreferrer",formFields:".t3js-login-formfields",interfaceField:".t3js-login-interface-field",loginForm:"#typo3-login-form",loginUrlLink:"t3js-login-url",submitButton:".t3js-login-submit",submitHandler:null,useridentField:".t3js-login-userident-field"},this.checkCookieSupport(),this.checkForInterfaceCookie(),this.checkDocumentReferrerSupport(),this.initializeEvents(),top.location.href!==location.href&&(this.ready=!1,top.location.href=location.href),this.ready&&document.body.setAttribute("data-typo3-login-ready","true")}showLoginProcess(){this.showLoadingIndicator(),o.default(this.options.error).addClass("hidden"),o.default(this.options.errorNoCookies).addClass("hidden")}showLoadingIndicator(){const e=o.default(this.options.submitButton);e.html(e.data("loading-text"))}handleSubmit(e){this.showLoginProcess(),"function"==typeof this.options.submitHandler&&this.options.submitHandler(e)}interfaceSelectorChanged(){const e=new Date,t=new Date(e.getTime()+31536e6);document.cookie="typo3-login-interface="+o.default(this.options.interfaceField).val()+"; expires="+t.toUTCString()+";"}checkForInterfaceCookie(){if(o.default(this.options.interfaceField).length){const e=document.cookie.indexOf("typo3-login-interface=");if(-1!==e){let t=document.cookie.substr(e+22);t=t.substr(0,t.indexOf(";")),o.default(this.options.interfaceField).val(t)}}}checkDocumentReferrerSupport(){const e=document.getElementById(this.options.loginUrlLink);null!==e&&void 0===e.dataset.referrerCheckEnabled&&"1"!==e.dataset.referrerCheckEnabled||void 0!==TYPO3.settings&&void 0!==TYPO3.settings.ajaxUrls&&new i(TYPO3.settings.ajaxUrls.login_preflight).get().then(async e=>{!0!==(await e.resolve("application/json")).capabilities.referrer&&document.querySelectorAll(this.options.errorNoReferrer).forEach(e=>e.classList.remove("hidden"))})}showCookieWarning(){o.default(this.options.formFields).addClass("hidden"),o.default(this.options.errorNoCookies).removeClass("hidden")}hideCookieWarning(){o.default(this.options.formFields).removeClass("hidden"),o.default(this.options.errorNoCookies).addClass("hidden")}checkCookieSupport(){const e=navigator.cookieEnabled;!1===e?this.showCookieWarning():document.cookie||null!==e||(document.cookie="typo3-login-cookiecheck=1",document.cookie?document.cookie="typo3-login-cookiecheck=; expires="+new Date(0).toUTCString():this.showCookieWarning())}initializeEvents(){o.default(document).ajaxStart(this.showLoadingIndicator.bind(this)),o.default(this.options.loginForm).on("submit",this.handleSubmit.bind(this)),o.default(this.options.interfaceField).length>0&&o.default(document).on("change blur",this.options.interfaceField,this.interfaceSelectorChanged.bind(this)),document.querySelectorAll(".t3js-clearable").forEach(e=>e.clearable()),o.default(".t3js-login-news-carousel").on("slide.bs.carousel",e=>{const t=o.default(e.relatedTarget).height();o.default(e.target).find("div.active").parent().animate({height:t},500)})}}}));
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment