mirror of
https://github.com/espocrm/espocrm.git
synced 2026-06-28 15:06:06 +00:00
client headers
This commit is contained in:
@@ -29,6 +29,8 @@
|
||||
|
||||
namespace Espo\Core\Utils\Client;
|
||||
|
||||
use Espo\Core\Api\Response;
|
||||
use Espo\Core\Utils\Client\ActionRenderer\Params;
|
||||
use Espo\Core\Utils\Json;
|
||||
use Espo\Core\Utils\ClientManager;
|
||||
|
||||
@@ -45,6 +47,18 @@ class ActionRenderer
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes to a body.
|
||||
*/
|
||||
public function write(Response $response, Params $params): void
|
||||
{
|
||||
$body = $this->render($params->getController(), $params->getAction(), $params->getData());
|
||||
|
||||
$this->clientManager->writeHeaders($response);
|
||||
$response->writeBody($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use`write`.
|
||||
* @param ?array<string,mixed> $data
|
||||
*/
|
||||
public function render(string $controller, string $action, ?array $data = null): string
|
||||
|
||||
66
application/Espo/Core/Utils/Client/ActionRenderer/Params.php
Normal file
66
application/Espo/Core/Utils/Client/ActionRenderer/Params.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Client\ActionRenderer;
|
||||
|
||||
class Params
|
||||
{
|
||||
private string $controller;
|
||||
private string $action;
|
||||
/** @var ?array<string,mixed> */
|
||||
private ?array $data;
|
||||
|
||||
/**
|
||||
* @param ?array<string,mixed> $data
|
||||
*/
|
||||
public function __construct(string $controller, string $action, ?array $data = null)
|
||||
{
|
||||
$this->controller = $controller;
|
||||
$this->action = $action;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function getController(): string
|
||||
{
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
public function getAction(): string
|
||||
{
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?array<string,mixed>
|
||||
*/
|
||||
public function getData(): ?array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
@@ -30,12 +30,17 @@
|
||||
namespace Espo\Core\Utils;
|
||||
|
||||
use Espo\Core\{
|
||||
Api\Response,
|
||||
Api\ResponseWrapper,
|
||||
Utils\File\Manager as FileManager,
|
||||
Utils\Client\DevModeJsFileListProvider,
|
||||
Utils\Module,
|
||||
Utils\Json,
|
||||
};
|
||||
|
||||
use Slim\Psr7\Response as Psr7Response;
|
||||
use Slim\ResponseEmitter;
|
||||
|
||||
/**
|
||||
* Renders the main HTML page.
|
||||
*/
|
||||
@@ -61,6 +66,8 @@ class ClientManager
|
||||
|
||||
private Module $module;
|
||||
|
||||
private string $nonce;
|
||||
|
||||
private const APP_DESCRIPTION = "EspoCRM - Open Source CRM application.";
|
||||
|
||||
public function __construct(
|
||||
@@ -77,6 +84,8 @@ class ClientManager
|
||||
$this->fileManager = $fileManager;
|
||||
$this->devModeJsFileListProvider = $devModeJsFileListProvider;
|
||||
$this->module = $module;
|
||||
|
||||
$this->nonce = Util::generateKey();
|
||||
}
|
||||
|
||||
public function setBasePath(string $basePath): void
|
||||
@@ -98,12 +107,65 @@ class ClientManager
|
||||
return $this->config->get('cacheTimestamp', 0);
|
||||
}
|
||||
|
||||
public function writeHeaders(Response $response): void
|
||||
{
|
||||
$this->writeContentSecurityPolicyHeader($response);
|
||||
$this->writeStrictTransportSecurityHeader($response);
|
||||
|
||||
/** @var array<string,?string> $headers */
|
||||
$headers = $this->config->get('clientHttpHeaders') ?? [];
|
||||
|
||||
foreach ($headers as $name => $value) {
|
||||
if ($value === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$response->setHeader($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Move to a separate class.
|
||||
*/
|
||||
private function writeContentSecurityPolicyHeader(Response $response): void
|
||||
{
|
||||
if ($this->config->get('clientCspDisabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scriptSrc = "script-src 'self' 'nonce-{$this->nonce}' 'unsafe-eval'";
|
||||
|
||||
$scriptSourceList = $this->config->get('clientCspScriptSourceList') ?? [];
|
||||
|
||||
foreach ($scriptSourceList as $src) {
|
||||
$scriptSrc .= ' ' . $src;
|
||||
}
|
||||
|
||||
$response->setHeader('Content-Security-Policy', $scriptSrc);
|
||||
}
|
||||
|
||||
private function writeStrictTransportSecurityHeader(Response $response)
|
||||
{
|
||||
$siteUrl =$this->config->get('siteUrl') ?? '';
|
||||
|
||||
if (strpos($siteUrl, 'https://') === 0) {
|
||||
$response->setHeader('Strict-Transport-Security', 'max-age=10368000');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $vars
|
||||
*/
|
||||
public function display(?string $runScript = null, ?string $htmlFilePath = null, array $vars = []): void
|
||||
{
|
||||
echo $this->render($runScript, $htmlFilePath, $vars);
|
||||
$body = $this->render($runScript, $htmlFilePath, $vars);
|
||||
|
||||
$response = new ResponseWrapper(new Psr7Response());
|
||||
|
||||
$this->writeHeaders($response);
|
||||
$response->writeBody($body);
|
||||
|
||||
(new ResponseEmitter())->emit($response->getResponse());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,6 +267,7 @@ class ClientManager
|
||||
'libsConfigPath' => $this->libsConfigPath,
|
||||
'internalModuleList' => Json::encode($internalModuleList),
|
||||
'applicationDescription' => $this->config->get('applicationDescription') ?? self::APP_DESCRIPTION,
|
||||
'nonce' => $this->nonce,
|
||||
];
|
||||
|
||||
$html = $this->fileManager->getContents($htmlFilePath);
|
||||
|
||||
@@ -92,8 +92,8 @@ class ChangePassword implements EntryPoint
|
||||
'notFound' => !$passwordChangeRequest,
|
||||
];
|
||||
|
||||
$html = $this->actionRenderer->render('controllers/password-change-request', 'passwordChange', $options);
|
||||
$params = new ActionRenderer\Params('controllers/password-change-request', 'passwordChange', $options);
|
||||
|
||||
$response->writeBody($html);
|
||||
$this->actionRenderer->write($response, $params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,23 +36,21 @@ use Espo\Core\{
|
||||
Exceptions\Error,
|
||||
EntryPoint\EntryPoint,
|
||||
EntryPoint\Traits\NoAuth,
|
||||
Utils\ClientManager,
|
||||
Utils\Client\ActionRenderer,
|
||||
Api\Request,
|
||||
Api\Response,
|
||||
};
|
||||
Api\Response};
|
||||
|
||||
class ConfirmOptIn implements EntryPoint
|
||||
{
|
||||
use NoAuth;
|
||||
|
||||
private $clientManager;
|
||||
private Service $service;
|
||||
private ActionRenderer $actionRenderer;
|
||||
|
||||
private $service;
|
||||
|
||||
public function __construct(ClientManager $clientManager, Service $service)
|
||||
public function __construct(Service $service, ActionRenderer $actionRenderer)
|
||||
{
|
||||
$this->clientManager = $clientManager;
|
||||
$this->service = $service;
|
||||
$this->actionRenderer = $actionRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,14 +74,8 @@ class ConfirmOptIn implements EntryPoint
|
||||
$action = 'optInConfirmationSuccess';
|
||||
}
|
||||
|
||||
$runScript = "
|
||||
require('controllers/lead-capture-opt-in-confirmation', Controller => {
|
||||
var controller = new Controller(app.baseController.params, app.getControllerInjection());
|
||||
controller.masterView = app.masterView;
|
||||
controller.doAction('{$action}', " . json_encode($data) . ");
|
||||
});
|
||||
";
|
||||
$params = new ActionRenderer\Params('controllers/lead-capture-opt-in-confirmation', $action, $data);
|
||||
|
||||
$this->clientManager->display($runScript);
|
||||
$this->actionRenderer->write($response, $params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,5 +215,11 @@ return [
|
||||
'passwordGenerateLength' => 10,
|
||||
'massActionIdleCountThreshold' => 100,
|
||||
'exportIdleCountThreshold' => 1000,
|
||||
'clientHttpHeaders' => [
|
||||
'X-Frame-Options' => 'SAMEORIGIN',
|
||||
'X-Content-Type-Options' => 'nosniff',
|
||||
],
|
||||
'clientCspDisabled' => false,
|
||||
'clientCspScriptSourceList' => [],
|
||||
'isInstalled' => false,
|
||||
];
|
||||
|
||||
@@ -98,6 +98,9 @@ return [
|
||||
'webSocketMessager',
|
||||
'actualDatabaseType',
|
||||
'actualDatabaseVersion',
|
||||
'clientHttpHeaders',
|
||||
'clientCspDisabled',
|
||||
'clientCspScriptSourceList',
|
||||
],
|
||||
'adminItems' => [
|
||||
'devMode',
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<link rel="shortcut icon" sizes="196x196" href="{{basePath}}{{favicon196Path}}">
|
||||
<link rel="icon" href="{{basePath}}{{faviconPath}}" type="image/x-icon">
|
||||
<link rel="shortcut icon" href="{{basePath}}{{faviconPath}}" type="image/x-icon">
|
||||
<script type="text/javascript">
|
||||
<script type="text/javascript" nonce="{{nonce}}">
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
Espo.loader.setCacheTimestamp({{loaderCacheTimestamp}});
|
||||
Espo.loader.setBasePath('{{basePath}}');
|
||||
|
||||
Reference in New Issue
Block a user