CORS, trusted domain
This commit is contained in:
parent
a238ad3b7f
commit
3851b7f289
@ -105,6 +105,8 @@ namespace Core\API\Logs {
|
||||
"message" => $content,
|
||||
"timestamp" => $date->format(Parameter::DATE_TIME_FORMAT)
|
||||
];
|
||||
|
||||
$this->result["pagination"]["total"] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,6 +141,7 @@ namespace Core\API\Logs {
|
||||
"timestamp" => (new \DateTime())->format(Parameter::DATE_TIME_FORMAT)
|
||||
]
|
||||
];
|
||||
$this->result["pagination"]["total"] += 1;
|
||||
}
|
||||
|
||||
$this->loadFromFileSystem($this->result["logs"]);
|
||||
|
@ -161,8 +161,20 @@ abstract class Request {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getCORS(): array {
|
||||
$settings = $this->context->getSettings();
|
||||
return $settings->getTrustedDomains();
|
||||
}
|
||||
|
||||
public final function execute($values = array()): bool {
|
||||
|
||||
if ($this->externalCall) {
|
||||
$trustedDomains = $this->getCORS();
|
||||
if (!empty($trustedDomains)) {
|
||||
header("Access-Control-Allow-Origin: " . implode(", ", $trustedDomains));
|
||||
}
|
||||
}
|
||||
|
||||
$this->params = array_merge([], $this->defaultParams);
|
||||
$this->success = false;
|
||||
$this->result = array();
|
||||
|
@ -53,6 +53,7 @@ namespace Core\API\Settings {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: we need additional validation for built-in settings here, e.g. csv-values, bool values, etc.
|
||||
class Set extends SettingsAPI {
|
||||
public function __construct(Context $context, bool $externalCall = false) {
|
||||
parent::__construct($context, $externalCall, array(
|
||||
|
@ -13,9 +13,12 @@ class Swagger extends Request {
|
||||
$this->csrfTokenRequired = false;
|
||||
}
|
||||
|
||||
protected function getCORS(): array {
|
||||
return ["*"];
|
||||
}
|
||||
|
||||
public function _execute(): bool {
|
||||
header("Content-Type: application/x-yaml");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
die($this->getDocumentation());
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ class Settings {
|
||||
// general settings
|
||||
private string $siteName;
|
||||
private string $baseUrl;
|
||||
private array $trustedDomains;
|
||||
private bool $registrationAllowed;
|
||||
private array $allowedExtensions;
|
||||
private string $timeZone;
|
||||
@ -45,7 +46,7 @@ class Settings {
|
||||
}
|
||||
|
||||
public static function getAll(?SQL $sql, ?string $pattern = null, bool $external = false): ?array {
|
||||
$query = $sql->select("name", "value") ->from("Settings");
|
||||
$query = $sql->select("name", "value")->from("Settings");
|
||||
|
||||
if ($pattern) {
|
||||
$query->where(new CondRegex(new Column("name"), $pattern));
|
||||
@ -91,6 +92,7 @@ class Settings {
|
||||
// General
|
||||
$settings->siteName = "WebBase";
|
||||
$settings->baseUrl = "$protocol://$hostname";
|
||||
$settings->trustedDomains = [$hostname];
|
||||
$settings->allowedExtensions = ['png', 'jpg', 'jpeg', 'gif', 'htm', 'html'];
|
||||
$settings->installationComplete = false;
|
||||
$settings->registrationAllowed = false;
|
||||
@ -130,6 +132,7 @@ class Settings {
|
||||
$this->mailFooter = $result["mail_footer"] ?? $this->mailFooter;
|
||||
$this->mailAsync = $result["mail_async"] ?? $this->mailAsync;
|
||||
$this->allowedExtensions = explode(",", $result["allowed_extensions"] ?? strtolower(implode(",", $this->allowedExtensions)));
|
||||
$this->trustedDomains = explode(",", $result["trusted_domains"] ?? strtolower(implode(",", $this->trustedDomains)));
|
||||
date_default_timezone_set($this->timeZone);
|
||||
}
|
||||
|
||||
@ -139,13 +142,14 @@ class Settings {
|
||||
public function addRows(Insert $query): void {
|
||||
$query->addRow("site_name", $this->siteName, false, false)
|
||||
->addRow("base_url", $this->baseUrl, false, false)
|
||||
->addRow("trusted_domains", implode(",", $this->trustedDomains), false, false)
|
||||
->addRow("user_registration_enabled", $this->registrationAllowed ? "1" : "0", false, false)
|
||||
->addRow("installation_completed", $this->installationComplete ? "1" : "0", true, true)
|
||||
->addRow("time_zone", $this->timeZone, false, false)
|
||||
->addRow("recaptcha_enabled", $this->recaptchaEnabled ? "1" : "0", false, false)
|
||||
->addRow("recaptcha_public_key", $this->recaptchaPublicKey, false, false)
|
||||
->addRow("recaptcha_private_key", $this->recaptchaPrivateKey, true, false)
|
||||
->addRow("allowed_extensions", implode(",", $this->allowedExtensions), true, false)
|
||||
->addRow("allowed_extensions", implode(",", $this->allowedExtensions), false, false)
|
||||
->addRow("mail_host", "", false, false)
|
||||
->addRow("mail_port", "", false, false)
|
||||
->addRow("mail_username", "", false, false)
|
||||
@ -211,4 +215,26 @@ class Settings {
|
||||
public function getLogger(): Logger {
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
public function isTrustedDomain(string $domain): bool {
|
||||
$domain = strtolower($domain);
|
||||
foreach ($this->trustedDomains as $trustedDomain) {
|
||||
$trustedDomain = trim(strtolower($trustedDomain));
|
||||
if ($trustedDomain === $domain) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// *.def.com <-> abc.def.com
|
||||
if (startsWith($trustedDomain, "*.") && endsWith($domain, substr($trustedDomain, 1))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getTrustedDomains(): array {
|
||||
return $this->trustedDomains;
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@ abstract class Document {
|
||||
private bool $cspEnabled;
|
||||
private ?string $cspNonce;
|
||||
private array $cspWhitelist;
|
||||
private string $domain;
|
||||
protected bool $searchable;
|
||||
protected array $languageModules;
|
||||
|
||||
@ -31,7 +30,6 @@ abstract class Document {
|
||||
$this->cspNonce = null;
|
||||
$this->databaseRequired = true;
|
||||
$this->cspWhitelist = [];
|
||||
$this->domain = $this->getSettings()->getBaseUrl();
|
||||
$this->logger = new Logger("Document", $this->getSQL());
|
||||
$this->searchable = false;
|
||||
$this->languageModules = ["general"];
|
||||
@ -83,7 +81,7 @@ abstract class Document {
|
||||
public function addCSPWhitelist(string $path) {
|
||||
$urlParts = parse_url($path);
|
||||
if (!$urlParts || !isset($urlParts["host"])) {
|
||||
$this->cspWhitelist[] = $this->domain . $path;
|
||||
$this->cspWhitelist[] = getProtocol() . "://" . getCurrentHostName() . $path;
|
||||
} else {
|
||||
$this->cspWhitelist[] = $path;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ return [
|
||||
"base_url" => "Basis URL",
|
||||
"user_registration_enabled" => "Benutzerregistrierung erlauben",
|
||||
"allowed_extensions" => "Erlaubte Dateierweiterungen",
|
||||
"trusted_domains" => "Vertraute Ursprungs-Domains (Komma getrennt, * als Subdomain-Wildcard)",
|
||||
"time_zone" => "Zeitzone",
|
||||
|
||||
# mail settings
|
||||
|
@ -25,6 +25,7 @@ return [
|
||||
"base_url" => "Base URL",
|
||||
"user_registration_enabled" => "Allow user registration",
|
||||
"allowed_extensions" => "Allowed file extensions",
|
||||
"trusted_domains" => "Trusted origin domains (comma separated, * as subdomain-wildcard)",
|
||||
"time_zone" => "Time zone",
|
||||
|
||||
# mail settings
|
||||
|
@ -40,21 +40,22 @@ class Router {
|
||||
return $this->requestedUri;
|
||||
}
|
||||
|
||||
public function run(string $url): string {
|
||||
public function run(string $url, array &$pathParams): ?Route {
|
||||
|
||||
// TODO: do we want a global try cache and return status page 500 on any error?
|
||||
$this->requestedUri = $url;
|
||||
|
||||
$url = strtok($url, "?");
|
||||
foreach ($this->routes as $route) {
|
||||
$pathParams = $route->match($url);
|
||||
if ($pathParams !== false) {
|
||||
$match = $route->match($url);
|
||||
if ($match !== false) {
|
||||
$this->activeRoute = $route;
|
||||
return $route->call($this, $pathParams);
|
||||
$pathParams = $match;
|
||||
return $this->activeRoute;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->returnStatusCode(404);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function returnStatusCode(int $code, array $params = []): string {
|
||||
|
2
cli.php
2
cli.php
@ -959,7 +959,7 @@ $registeredCommands = [
|
||||
"mail" => ["handler" => "onMail", "description" => "send mails and process the pipeline", "requiresDocker" => true],
|
||||
"settings" => ["handler" => "onSettings", "description" => "change and view settings"],
|
||||
"impersonate" => ["handler" => "onImpersonate", "description" => "create a session and print cookies and csrf tokens", "requiresDocker" => true],
|
||||
"frontend" => ["handler" => "onFrontend", "description" => "build and manage frontend modules", "requiresDocker" => true],
|
||||
"frontend" => ["handler" => "onFrontend", "description" => "build and manage frontend modules"],
|
||||
"api" => ["handler" => "onAPI", "description" => "view and create API endpoints"],
|
||||
];
|
||||
|
||||
|
22
index.php
22
index.php
@ -18,7 +18,8 @@ use Core\Objects\Router\Router;
|
||||
|
||||
if (!is_readable(getClassPath(Configuration::class))) {
|
||||
header("Content-Type: application/json");
|
||||
die(json_encode([ "success" => false, "msg" => "Configuration class is not readable, check permissions before proceeding." ]));
|
||||
http_response_code(500);
|
||||
die(json_encode(createError("Configuration class is not readable, check permissions before proceeding.")));
|
||||
}
|
||||
|
||||
$context = Context::instance();
|
||||
@ -26,6 +27,8 @@ $sql = $context->initSQL();
|
||||
$settings = $context->getSettings();
|
||||
$context->parseCookies();
|
||||
|
||||
$currentHostName = getCurrentHostName();
|
||||
|
||||
$installation = !$sql || ($sql->isConnected() && !$settings->isInstalled());
|
||||
$requestedUri = $_GET["site"] ?? $_GET["api"] ?? $_SERVER["REQUEST_URI"];
|
||||
|
||||
@ -61,12 +64,27 @@ if ($installation) {
|
||||
}
|
||||
|
||||
if ($router !== null) {
|
||||
|
||||
if ((!isset($_GET["site"]) || $_GET["site"] === "/") && isset($_GET["error"]) &&
|
||||
is_string($_GET["error"]) && preg_match("/^\d+$/", $_GET["error"])) {
|
||||
$response = $router->returnStatusCode(intval($_GET["error"]));
|
||||
} else {
|
||||
try {
|
||||
$response = $router->run($requestedUri);
|
||||
$pathParams = [];
|
||||
$route = $router->run($requestedUri, $pathParams);
|
||||
if ($route === null) {
|
||||
$response = $router->returnStatusCode(404);
|
||||
} else if (!$settings->isTrustedDomain($currentHostName)) {
|
||||
if ($route instanceof \Core\Objects\Router\ApiRoute) {
|
||||
header("Content-Type: application/json");
|
||||
http_response_code(403);
|
||||
$response = json_encode(createError("Untrusted Origin"));
|
||||
} else {
|
||||
$response = $router->returnStatusCode(403, ["message" => "Untrusted Origin"]);
|
||||
}
|
||||
} else {
|
||||
$response = $route->call($router, $pathParams);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
http_response_code(500);
|
||||
$router->getLogger()->error($e->getMessage());
|
||||
|
@ -46,6 +46,7 @@ export default function SettingsView(props) {
|
||||
"user_registration_enabled",
|
||||
"time_zone",
|
||||
"allowed_extensions",
|
||||
"trusted_domains",
|
||||
],
|
||||
"mail": [
|
||||
"mail_enabled",
|
||||
@ -275,6 +276,7 @@ export default function SettingsView(props) {
|
||||
return [
|
||||
renderTextInput("site_name"),
|
||||
renderTextInput("base_url"),
|
||||
renderTextInput("trusted_domains"),
|
||||
renderCheckBox("user_registration_enabled"),
|
||||
renderTextInput("allowed_extensions"),
|
||||
renderSelection("time_zone", TIME_ZONES),
|
||||
|
Loading…
Reference in New Issue
Block a user