2.4.1: Settings GPG, Localization, CLI DB migrate, minor improvements
This commit is contained in:
@@ -10,7 +10,6 @@ namespace Core\API {
|
||||
$this->loginRequired = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Core\API\GpgKey {
|
||||
@@ -20,6 +19,7 @@ namespace Core\API\GpgKey {
|
||||
use Core\API\Parameter\Parameter;
|
||||
use Core\API\Parameter\StringType;
|
||||
use Core\API\Template\Render;
|
||||
use Core\API\Traits\GpgKeyValidation;
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Objects\Context;
|
||||
use Core\Objects\DatabaseEntity\GpgKey;
|
||||
@@ -28,36 +28,16 @@ namespace Core\API\GpgKey {
|
||||
|
||||
class Import extends GpgKeyAPI {
|
||||
|
||||
use GpgKeyValidation;
|
||||
|
||||
public function __construct(Context $context, bool $externalCall = false) {
|
||||
parent::__construct($context, $externalCall, [
|
||||
"pubkey" => new StringType("pubkey")
|
||||
"publicKey" => new StringType("publicKey")
|
||||
]);
|
||||
$this->loginRequired = true;
|
||||
$this->forbidMethod("GET");
|
||||
}
|
||||
|
||||
private function testKey(string $keyString) {
|
||||
$res = GpgKey::getKeyInfo($keyString);
|
||||
if (!$res["success"]) {
|
||||
return $this->createError($res["error"] ?? $res["msg"]);
|
||||
}
|
||||
|
||||
$keyData = $res["data"];
|
||||
$keyType = $keyData["type"];
|
||||
$expires = $keyData["expires"];
|
||||
|
||||
if ($keyType === "sec#") {
|
||||
return self::createError("ATTENTION! It seems like you've imported a PGP PRIVATE KEY instead of a public key.
|
||||
It is recommended to immediately revoke your private key and create a new key pair.");
|
||||
} else if ($keyType !== "pub") {
|
||||
return self::createError("Unknown key type: $keyType");
|
||||
} else if (isInPast($expires)) {
|
||||
return self::createError("It seems like the gpg key is already expired.");
|
||||
} else {
|
||||
return $keyData;
|
||||
}
|
||||
}
|
||||
|
||||
public function _execute(): bool {
|
||||
|
||||
$currentUser = $this->context->getUser();
|
||||
@@ -69,8 +49,7 @@ namespace Core\API\GpgKey {
|
||||
}
|
||||
|
||||
// fix key first, enforce a newline after
|
||||
$keyString = $this->getParam("pubkey");
|
||||
$keyString = preg_replace("/(-{2,})\n([^\n])/", "$1\n\n$2", $keyString);
|
||||
$keyString = $this->formatKey($this->getParam("publicKey"));
|
||||
$keyData = $this->testKey($keyString);
|
||||
if ($keyData === false) {
|
||||
return false;
|
||||
|
||||
@@ -215,38 +215,15 @@ abstract class Request {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isMethodAllowed("GET") && $this->isMethodAllowed("POST")) {
|
||||
$values = $_REQUEST;
|
||||
} else if ($this->isMethodAllowed("POST")) {
|
||||
$values = $_POST;
|
||||
} else if ($this->isMethodAllowed("GET")) {
|
||||
$values = $_GET;
|
||||
}
|
||||
|
||||
if (in_array($_SERVER['REQUEST_METHOD'], ['POST', 'PUT', 'PATCH'])) {
|
||||
$contentTypeData = explode(";", $_SERVER["CONTENT_TYPE"] ?? "");
|
||||
$charset = "utf-8";
|
||||
|
||||
if ($contentTypeData[0] === "application/json") {
|
||||
for ($i = 1; $i < count($contentTypeData); $i++) {
|
||||
if (preg_match("/charset=(.*)/", $contentTypeData[$i], $match)) {
|
||||
$charset = $match[1];
|
||||
}
|
||||
}
|
||||
|
||||
$body = file_get_contents('php://input');
|
||||
if (strcasecmp($charset, "utf-8") !== 0) {
|
||||
$body = iconv($charset, 'utf-8', $body);
|
||||
}
|
||||
|
||||
$jsonData = json_decode($body, true);
|
||||
if ($jsonData !== null) {
|
||||
$values = array_merge($values, $jsonData);
|
||||
} else {
|
||||
$this->lastError = "Invalid request body.";
|
||||
http_response_code(400);
|
||||
return false;
|
||||
}
|
||||
$values = $_REQUEST;
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && in_array("application/json", explode(";", $_SERVER["CONTENT_TYPE"] ?? ""))) {
|
||||
$jsonData = json_decode(file_get_contents('php://input'), true);
|
||||
if ($jsonData !== null) {
|
||||
$values = array_merge($values, $jsonData);
|
||||
} else {
|
||||
$this->lastError = 'Invalid request body.';
|
||||
http_response_code(400);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,7 +339,8 @@ abstract class Request {
|
||||
$obj = $this->params;
|
||||
}
|
||||
|
||||
return $obj[$name]?->value;
|
||||
// I don't know why phpstorm
|
||||
return (isset($obj[$name]) ? $obj[$name]->value : NULL);
|
||||
}
|
||||
|
||||
public function isMethodAllowed(string $method): bool {
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace Core\API {
|
||||
// API parameters should be more configurable, e.g. allow regexes, min/max values for numbers, etc.
|
||||
$this->predefinedKeys = [
|
||||
"allowed_extensions" => new ArrayType("allowed_extensions", Parameter::TYPE_STRING),
|
||||
"mail_contact" => new Parameter("mail_contact", Parameter::TYPE_EMAIL, true, ""),
|
||||
"trusted_domains" => new ArrayType("trusted_domains", Parameter::TYPE_STRING),
|
||||
"user_registration_enabled" => new Parameter("user_registration_enabled", Parameter::TYPE_BOOLEAN),
|
||||
"captcha_provider" => new StringType("captcha_provider", -1, true, "disabled", CaptchaProvider::PROVIDERS),
|
||||
@@ -38,29 +39,41 @@ namespace Core\API\Settings {
|
||||
use Core\API\Parameter\RegexType;
|
||||
use Core\API\Parameter\StringType;
|
||||
use Core\API\SettingsAPI;
|
||||
use Core\API\Traits\GpgKeyValidation;
|
||||
use Core\Configuration\Settings;
|
||||
use Core\Driver\SQL\Column\Column;
|
||||
use Core\Driver\SQL\Condition\CondBool;
|
||||
use Core\Driver\SQL\Condition\CondIn;
|
||||
use Core\Driver\SQL\Strategy\UpdateStrategy;
|
||||
use Core\Objects\Context;
|
||||
use Core\Objects\DatabaseEntity\GpgKey;
|
||||
use Core\Objects\DatabaseEntity\Group;
|
||||
|
||||
class Get extends SettingsAPI {
|
||||
|
||||
private ?GpgKey $contactGpgKey;
|
||||
|
||||
public function __construct(Context $context, bool $externalCall = false) {
|
||||
parent::__construct($context, $externalCall, array(
|
||||
'key' => new StringType('key', -1, true, NULL)
|
||||
));
|
||||
$this->contactGpgKey = null;
|
||||
}
|
||||
|
||||
public function _execute(): bool {
|
||||
$key = $this->getParam("key");
|
||||
$sql = $this->context->getSQL();
|
||||
$siteSettings = $this->context->getSettings();
|
||||
|
||||
$settings = Settings::getAll($sql, $key, $this->isExternalCall());
|
||||
if ($settings !== null) {
|
||||
$this->result["settings"] = $settings;
|
||||
|
||||
// TODO: improve this custom key
|
||||
$gpgKeyId = $this->result["settings"]["mail_contact_gpg_key_id"] ?? null;
|
||||
$this->contactGpgKey = $gpgKeyId === null ? null : GpgKey::find($sql, $gpgKeyId);
|
||||
unset($this->result["settings"]["mail_contact_gpg_key_id"]);
|
||||
$this->result["settings"]["mail_contact_gpg_key"] = $this->contactGpgKey?->jsonSerialize();
|
||||
} else {
|
||||
return $this->createError("Error fetching settings: " . $sql->getLastError());
|
||||
}
|
||||
@@ -68,6 +81,10 @@ namespace Core\API\Settings {
|
||||
return $this->success;
|
||||
}
|
||||
|
||||
public function getContactGpgKey(): ?GpgKey {
|
||||
return $this->contactGpgKey;
|
||||
}
|
||||
|
||||
public static function getDescription(): string {
|
||||
return "Allows users to fetch site settings";
|
||||
}
|
||||
@@ -138,7 +155,6 @@ namespace Core\API\Settings {
|
||||
["value" => new Column("value")])
|
||||
);
|
||||
|
||||
|
||||
$this->success = ($query->execute() !== FALSE);
|
||||
$this->lastError = $sql->getLastError();
|
||||
|
||||
@@ -188,4 +204,90 @@ namespace Core\API\Settings {
|
||||
return [Group::ADMIN];
|
||||
}
|
||||
}
|
||||
|
||||
class ImportGPG extends SettingsAPI {
|
||||
|
||||
use GpgKeyValidation;
|
||||
|
||||
public function __construct(Context $context, bool $externalCall = false) {
|
||||
parent::__construct($context, $externalCall, [
|
||||
"publicKey" => new StringType("publicKey")
|
||||
]);
|
||||
|
||||
$this->forbidMethod("GET");
|
||||
}
|
||||
|
||||
protected function _execute(): bool {
|
||||
|
||||
$sql = $this->context->getSQL();
|
||||
|
||||
// fix key first, enforce a newline after
|
||||
$keyString = $this->formatKey($this->getParam("publicKey"));
|
||||
$keyData = $this->testKey($keyString, null);
|
||||
if ($keyData === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$res = GpgKey::importKey($keyString);
|
||||
if (!$res["success"]) {
|
||||
return $this->createError($res["error"]);
|
||||
}
|
||||
|
||||
// we will auto-confirm this key
|
||||
$sql = $this->context->getSQL();
|
||||
$gpgKey = new GpgKey($keyData["fingerprint"], $keyData["algorithm"], $keyData["expires"], true);
|
||||
if (!$gpgKey->save($sql)) {
|
||||
return $this->createError("Error creating gpg key: " . $sql->getLastError());
|
||||
}
|
||||
|
||||
$this->success = $sql->insert("Settings", ["name", "value", "private", "readonly"])
|
||||
->addRow("mail_contact_gpg_key_id", $gpgKey->getId(), false, true)
|
||||
->onDuplicateKeyStrategy(new UpdateStrategy(
|
||||
["name"],
|
||||
["value" => new Column("value")])
|
||||
)->execute() !== false;
|
||||
|
||||
$this->lastError = $sql->getLastError();
|
||||
$this->result["gpgKey"] = $gpgKey->jsonSerialize();
|
||||
return $this->success;
|
||||
}
|
||||
|
||||
public static function getDescription(): string {
|
||||
return "Allows administrators to import a GPG-key to use it as a contact key.";
|
||||
}
|
||||
|
||||
public static function getDefaultPermittedGroups(): array {
|
||||
return [Group::ADMIN];
|
||||
}
|
||||
}
|
||||
|
||||
class RemoveGPG extends SettingsAPI {
|
||||
public function __construct(Context $context, bool $externalCall = false) {
|
||||
parent::__construct($context, $externalCall);
|
||||
}
|
||||
|
||||
protected function _execute(): bool {
|
||||
$sql = $this->context->getSQL();
|
||||
$settings = $this->context->getSettings();
|
||||
$gpgKey = $settings->getContactGPGKey();
|
||||
if ($gpgKey === null) {
|
||||
return $this->createError("No GPG-Key configured yet");
|
||||
}
|
||||
|
||||
$this->success = $sql->update("Settings")
|
||||
->set("value", NULL)
|
||||
->whereEq("name", "mail_contact_gpg_key_id")
|
||||
->execute() !== false;
|
||||
$this->lastError = $sql->getLastError();
|
||||
return $this->success;
|
||||
}
|
||||
|
||||
public static function getDescription(): string {
|
||||
return "Allows administrators to remove the GPG-key used as a contact key.";
|
||||
}
|
||||
|
||||
public static function getDefaultPermittedGroups(): array {
|
||||
return [Group::ADMIN];
|
||||
}
|
||||
}
|
||||
}
|
||||
34
Core/API/Traits/GpgKeyValidation.trait.php
Normal file
34
Core/API/Traits/GpgKeyValidation.trait.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Core\API\Traits;
|
||||
|
||||
use Core\Objects\DatabaseEntity\GpgKey;
|
||||
|
||||
trait GpgKeyValidation {
|
||||
|
||||
function testKey(string $keyString, ?string $expectedType = "pub") {
|
||||
$res = GpgKey::getKeyInfo($keyString);
|
||||
if (!$res["success"]) {
|
||||
return $this->createError($res["error"] ?? $res["msg"]);
|
||||
}
|
||||
|
||||
$keyData = $res["data"];
|
||||
$keyType = $keyData["type"];
|
||||
$expires = $keyData["expires"];
|
||||
|
||||
if ($expectedType === "pub" && $keyType === "sec#") {
|
||||
return $this->createError("ATTENTION! It seems like you've imported a PGP PRIVATE KEY instead of a public key.
|
||||
It is recommended to immediately revoke your private key and create a new key pair.");
|
||||
} else if ($expectedType !== null && $keyType !== $expectedType) {
|
||||
return $this->createError("Key has unexpected type: $keyType, expected: $expectedType");
|
||||
} else if (isInPast($expires)) {
|
||||
return $this->createError("It seems like the gpg key is already expired.");
|
||||
} else {
|
||||
return $keyData;
|
||||
}
|
||||
}
|
||||
|
||||
function formatKey(string $keyString): string {
|
||||
return preg_replace("/(-{2,})\n([^\n])/", "$1\n\n$2", $keyString);
|
||||
}
|
||||
}
|
||||
@@ -1008,7 +1008,15 @@ namespace Core\API\User {
|
||||
} else {
|
||||
$this->success = ($user->delete($sql) !== FALSE);
|
||||
$this->lastError = $sql->getLastError();
|
||||
$this->logger->info(sprintf(
|
||||
"User '%s' (id=%d) deleted by %s",
|
||||
$user->getDisplayName(),
|
||||
$id,
|
||||
$this->logUserId())
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$this->lastError = $sql->getLastError();
|
||||
}
|
||||
|
||||
return $this->success;
|
||||
|
||||
25
Core/Configuration/Patch/2024_05_11-Settings-GPG.php
Normal file
25
Core/Configuration/Patch/2024_05_11-Settings-GPG.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Core\Driver\SQL\Column\Column;
|
||||
use Core\Driver\SQL\Strategy\UpdateStrategy;
|
||||
use Core\Objects\DatabaseEntity\Group;
|
||||
|
||||
$queries[] = $sql->insert("Settings", ["name", "value", "private", "readonly"])
|
||||
->onDuplicateKeyStrategy(new UpdateStrategy(
|
||||
["name"],
|
||||
["name" => new Column("name")])
|
||||
)
|
||||
->addRow("mail_contact_gpg_key_id", null, false, true)
|
||||
->addRow("mail_contact", "''", false, false);
|
||||
|
||||
$queries[] = $sql->insert("ApiPermission", ["method", "groups", "description", "is_core"])
|
||||
->onDuplicateKeyStrategy(new UpdateStrategy(
|
||||
["method"],
|
||||
["method" => new Column("method")])
|
||||
)
|
||||
->addRow("settings/importGPG",
|
||||
json_encode(\Core\API\Settings\ImportGPG::getDefaultPermittedGroups()),
|
||||
\Core\API\Settings\ImportGPG::getDescription(), true)
|
||||
->addRow("settings/removeGPG",
|
||||
json_encode(\Core\API\Settings\RemoveGPG::getDefaultPermittedGroups()),
|
||||
\Core\API\Settings\RemoveGPG::getDescription(), true);
|
||||
@@ -17,6 +17,7 @@ use Core\Objects\Captcha\GoogleRecaptchaProvider;
|
||||
use Core\Objects\Captcha\HCaptchaProvider;
|
||||
use Core\Objects\ConnectionData;
|
||||
use Core\Objects\Context;
|
||||
use Core\Objects\DatabaseEntity\GpgKey;
|
||||
|
||||
class Settings {
|
||||
|
||||
@@ -25,6 +26,11 @@ class Settings {
|
||||
|
||||
// general settings
|
||||
private string $siteName;
|
||||
|
||||
private string $contactMail;
|
||||
|
||||
private ?GpgKey $contactGpgKey;
|
||||
|
||||
private string $baseUrl;
|
||||
private array $trustedDomains;
|
||||
private bool $registrationAllowed;
|
||||
@@ -101,6 +107,8 @@ class Settings {
|
||||
|
||||
// General
|
||||
$settings->siteName = "WebBase";
|
||||
$settings->contactMail = "webmaster@$hostname";
|
||||
$settings->contactGpgKey = null;
|
||||
$settings->baseUrl = "$protocol://$hostname";
|
||||
$settings->trustedDomains = [$hostname];
|
||||
$settings->allowedExtensions = ['png', 'jpg', 'jpeg', 'gif', 'htm', 'html'];
|
||||
@@ -137,13 +145,15 @@ class Settings {
|
||||
}
|
||||
|
||||
public function loadFromDatabase(Context $context): bool {
|
||||
$this->logger = new Logger("Settings", $context->getSQL());
|
||||
$sql = $context->getSQL();
|
||||
$this->logger = new Logger("Settings", $sql);
|
||||
$req = new \Core\API\Settings\Get($context);
|
||||
$success = $req->execute();
|
||||
|
||||
if ($success) {
|
||||
$result = $req->getResult()["settings"];
|
||||
$this->siteName = $result["site_name"] ?? $this->siteName;
|
||||
$this->contactMail = $result["mail_contact"] ?? $this->contactMail;
|
||||
$this->baseUrl = $result["base_url"] ?? $this->baseUrl;
|
||||
$this->registrationAllowed = $result["user_registration_enabled"] ?? $this->registrationAllowed;
|
||||
$this->installationComplete = $result["installation_completed"] ?? $this->installationComplete;
|
||||
@@ -162,13 +172,18 @@ class Settings {
|
||||
$this->redisPort = $result["redis_port"] ?? $this->redisPort;
|
||||
$this->redisPassword = $result["redis_password"] ?? $this->redisPassword;
|
||||
date_default_timezone_set($this->timeZone);
|
||||
|
||||
$this->contactGpgKey = $req->getContactGpgKey();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addRows(Insert $query): void {
|
||||
// ["name", "value", "private", "readonly"]
|
||||
$query->addRow("site_name", json_encode($this->siteName), false, false)
|
||||
->addRow("mail_contact", json_encode($this->contactMail), false, false)
|
||||
->addRow("mail_contact_gpg_key_id", json_encode($this->contactGpgKey?->getId()), false, true)
|
||||
->addRow("base_url", json_encode($this->baseUrl), false, false)
|
||||
->addRow("trusted_domains", json_encode($this->trustedDomains), false, false)
|
||||
->addRow("user_registration_enabled", json_encode($this->registrationAllowed), false, false)
|
||||
@@ -196,6 +211,14 @@ class Settings {
|
||||
return $this->siteName;
|
||||
}
|
||||
|
||||
public function getContactMail(): string {
|
||||
return $this->contactMail;
|
||||
}
|
||||
|
||||
public function getContactGPGKey(): ?GpgKey {
|
||||
return $this->contactGpgKey;
|
||||
}
|
||||
|
||||
public function getTimeZone(): string {
|
||||
return $this->timeZone;
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ namespace Documents\Install {
|
||||
$step = self::FINISH_INSTALLATION;
|
||||
|
||||
$req = new \Core\API\Settings\Set($context);
|
||||
$success = $req->execute(["settings" => ["installation_completed" => "1"]]);
|
||||
$success = $req->execute(["settings" => ["installation_completed" => true]]);
|
||||
if (!$success) {
|
||||
$this->errorString = $req->getLastError();
|
||||
}
|
||||
@@ -633,12 +633,12 @@ namespace Documents\Install {
|
||||
}
|
||||
|
||||
$items[] = html_tag("li", $attr, [
|
||||
html_tag("div", [], [
|
||||
html_tag("h6", ["class" => "my-0"], $title),
|
||||
html_tag("small", ["class" => "text-$statusColor"], $statusText),
|
||||
], false),
|
||||
html_tag("span", ["class" => "text-$statusColor"], $statusIcon, false)
|
||||
], false);
|
||||
html_tag("div", [], [
|
||||
html_tag("h6", ["class" => "my-0"], $title),
|
||||
html_tag("small", ["class" => "text-$statusColor"], $statusText),
|
||||
], false),
|
||||
html_tag("span", ["class" => "text-$statusColor"], $statusIcon, false)
|
||||
], false);
|
||||
}
|
||||
|
||||
return $items;
|
||||
@@ -956,7 +956,7 @@ namespace Documents\Install {
|
||||
html_tag("div", ["class" => "col-md-4 order-md-2 mb-4"], [
|
||||
html_tag("h4", ["class" => "d-flex justify-content-between align-items-center mb-3"],
|
||||
html_tag("span", ["class" => "text-muted"], "Progress"),
|
||||
false
|
||||
false
|
||||
),
|
||||
html_tag("ul", ["class" => "list-group mb-3"], $progressSidebar, false)
|
||||
], false),
|
||||
@@ -968,7 +968,7 @@ namespace Documents\Install {
|
||||
], false)
|
||||
|
||||
], false),
|
||||
|
||||
|
||||
], false),
|
||||
false
|
||||
);
|
||||
|
||||
@@ -28,7 +28,7 @@ class Security extends Document {
|
||||
|
||||
$sql = $this->getContext()->getSQL();
|
||||
$settings = $this->getSettings();
|
||||
$mailSettings = Settings::getAll($sql, "^mail_");
|
||||
$gpgKey = $settings->getContactGPGKey();
|
||||
|
||||
if ($activeRoute->getPattern() === "/.well-known/security.txt") {
|
||||
|
||||
@@ -39,7 +39,7 @@ class Security extends Document {
|
||||
|
||||
$expires = (new \DateTime())->setTime(0, 0, 0)->modify("+3 months");
|
||||
$baseUrl = $settings->getBaseUrl();
|
||||
$gpgKey = null;
|
||||
// $gpgKey = null;
|
||||
|
||||
$lines = [
|
||||
"# This project is based on the open-source framework hosted on https://github.com/rhergenreder/web-base",
|
||||
@@ -53,19 +53,16 @@ class Security extends Document {
|
||||
"",
|
||||
];
|
||||
|
||||
if (isset($mailSettings["mail_contact"])) {
|
||||
$lines[] = "Contact: " . $mailSettings["mail_contact"];
|
||||
$contactAddress = $settings->getContactMail();
|
||||
if (!empty($contactAddress)) {
|
||||
$lines[] = "Contact: " . $contactAddress;
|
||||
}
|
||||
|
||||
if (isset($mailSettings["mail_contact_gpg_key_id"])) {
|
||||
$gpgKey = GpgKey::find($sql, $mailSettings["mail_contact_gpg_key_id"]);
|
||||
if ($gpgKey) {
|
||||
$lines[] = "Encryption: $baseUrl/.well-known/gpg-key.txt";
|
||||
}
|
||||
}
|
||||
if ($gpgKey !== null) {
|
||||
$lines[] = "Encryption: $baseUrl/.well-known/gpg-key.txt";
|
||||
}
|
||||
|
||||
$code = implode("\n", $lines);
|
||||
|
||||
if ($gpgKey !== null) {
|
||||
$res = GpgKey::sign($code, $gpgKey->getFingerprint());
|
||||
if ($res["success"]) {
|
||||
@@ -75,17 +72,14 @@ class Security extends Document {
|
||||
|
||||
return $code;
|
||||
} else if ($activeRoute->getPattern() === "/.well-known/gpg-key.txt") {
|
||||
|
||||
if (isset($mailSettings["mail_contact_gpg_key_id"])) {
|
||||
$gpgKey = GpgKey::find($sql, $mailSettings["mail_contact_gpg_key_id"]);
|
||||
if ($gpgKey !== null) {
|
||||
if ($gpgKey !== null) {
|
||||
$res = $gpgKey->_export(true);
|
||||
if ($res["success"]) {
|
||||
header("Content-Type: text/plain");
|
||||
$res = $gpgKey->_export(true);
|
||||
if ($res["success"]) {
|
||||
return $res["data"];
|
||||
} else {
|
||||
return "Error exporting public key: " . $res["msg"];
|
||||
}
|
||||
return $res["data"];
|
||||
} else {
|
||||
http_response_code(500);
|
||||
return "Error exporting public key: " . $res["msg"];
|
||||
}
|
||||
} else {
|
||||
http_response_code(412);
|
||||
|
||||
@@ -100,7 +100,7 @@ class TemplateDocument extends Document {
|
||||
"query" => $urlParts["query"] ?? "",
|
||||
"fragment" => $urlParts["fragment"] ?? ""
|
||||
],
|
||||
"lastModified" => date(L('Y-m-d H:i:s'), @filemtime(self::getTemplatePath($name))),
|
||||
"lastModified" => date(L('general.date_time_format'), @filemtime(self::getTemplatePath($name))),
|
||||
"registrationEnabled" => $settings->isRegistrationAllowed(),
|
||||
"title" => $this->title,
|
||||
"captcha" => [
|
||||
|
||||
@@ -91,6 +91,11 @@ return [
|
||||
"add_group_member_title" => "Mitglied hinzufügen",
|
||||
"add_group_member_text" => "Einen Benutzer suchen um ihn der Gruppe hinzuzufügen",
|
||||
"edit_profile" => "Profil bearbeiten",
|
||||
"delete_user_error" => "Fehler beim Löschen des Benutzers",
|
||||
"delete_user_title" => "Benutzer löschen",
|
||||
"delete_user_text" => "Möchten Sie wirklich diesen Benutzer löschen? Dies kann nicht rückgängig gemacht werden!",
|
||||
"error_reading_file" => "Fehler beim Lesen der Datei",
|
||||
"invalid_gpg_key" => "Die ausgewählte Datei ist kein GPG-Public Key im ASCII-Format",
|
||||
|
||||
# GPG Key
|
||||
"gpg_key_placeholder_text" => "GPG-Key im ASCII format reinziehen oder einfügen...",
|
||||
|
||||
@@ -17,6 +17,7 @@ return [
|
||||
"available_groups" => "verfügbare Gruppen",
|
||||
"routes_defined" => "Routen definiert",
|
||||
"error_count" => "Fehler in den letzten 48 Stunden",
|
||||
"more_info" => "Mehr Infos",
|
||||
|
||||
# Dialogs
|
||||
"fetch_stats_error" => "Fehler beim Holen der Stats",
|
||||
|
||||
@@ -59,6 +59,7 @@ return [
|
||||
"choose_file" => "Datei auswählen",
|
||||
"download" => "Herunterladen",
|
||||
"download_all" => "Alles Herunterladen",
|
||||
"upload_file" => "Datei hochladen",
|
||||
"upload" => "Hochladen",
|
||||
"uploading" => "Lade hoch",
|
||||
"overwrite" => "Überschreiben",
|
||||
|
||||
@@ -24,11 +24,14 @@ return [
|
||||
|
||||
# general settings
|
||||
"site_name" => "Seitenname",
|
||||
"mail_contact" => "Kontakt E-Mailadresse",
|
||||
"base_url" => "Basis URL",
|
||||
"user_registration_enabled" => "Benutzerregistrierung erlauben",
|
||||
"allowed_extensions" => "Erlaubte Dateierweiterungen",
|
||||
"trusted_domains" => "Vertraute Ursprungs-Domains (* als Subdomain-Wildcard)",
|
||||
"time_zone" => "Zeitzone",
|
||||
"mail_contact_gpg_key" => "Kontakt GPG-Schlüssel",
|
||||
"no_gpg_key_configured" => "Noch kein GPG-Schlüssel konfiguriert",
|
||||
|
||||
# mail settings
|
||||
"mail_enabled" => "E-Mail Versand aktiviert",
|
||||
@@ -65,4 +68,8 @@ return [
|
||||
"save_settings_error" => "Fehler beim Speichern der Einstellungen",
|
||||
"send_test_email_error" => "Fehler beim Senden der Test E-Mail",
|
||||
"send_test_email_success" => "Test E-Mail erfolgreich versendet, überprüfen Sie Ihren Posteingang!",
|
||||
"remove_gpg_key_error" => "Fehler beim Entfernen des GPG-Schlüssels",
|
||||
"remove_gpg_key" => "GPG-Schlüssel entfernen",
|
||||
"remove_gpg_key_text" => "Möchten Sie wirklich diesen GPG-Schlüssel entfernen?",
|
||||
"import_gpg_key_error" => "Fehler beim Importieren des GPG-Schlüssels",
|
||||
];
|
||||
@@ -92,6 +92,11 @@ return [
|
||||
"remove_group_member_text" => "Do you really want to remove user '%s' from this group?",
|
||||
"add_group_member_title" => "Add member",
|
||||
"add_group_member_text" => "Search a user to add to the group",
|
||||
"delete_user_error" => "Error deleting User",
|
||||
"delete_user_title" => "Delete User",
|
||||
"delete_user_text" => "Are you really sure you want to delete this user? This cannot be undone!",
|
||||
"error_reading_file" => "Error reading file",
|
||||
"invalid_gpg_key" => "Selected file is a not a GPG Public Key in ASCII format",
|
||||
|
||||
# GPG Key
|
||||
"gpg_key" => "GPG Key",
|
||||
|
||||
@@ -17,6 +17,7 @@ return [
|
||||
"available_groups" => "available Groups",
|
||||
"routes_defined" => "Routes defined",
|
||||
"error_count" => "Errors in the past 48 hours",
|
||||
"more_info" => "More Info",
|
||||
|
||||
# Dialogs
|
||||
"fetch_stats_error" => "Error fetching stats",
|
||||
|
||||
@@ -54,10 +54,11 @@ return [
|
||||
"sending" => "Sending",
|
||||
|
||||
# file
|
||||
"choose_file" => "Choose File",
|
||||
"choose_file" => "Choose file",
|
||||
"download" => "Download",
|
||||
"download_all" => "Download All",
|
||||
"upload" => "Upload",
|
||||
"upload_file" => "Upload file",
|
||||
"uploading" => "Uploading",
|
||||
"rename" => "Rename",
|
||||
"move" => "Move",
|
||||
|
||||
@@ -24,11 +24,14 @@ return [
|
||||
|
||||
# general settings
|
||||
"site_name" => "Site Name",
|
||||
"mail_contact" => "Contact mail address",
|
||||
"base_url" => "Base URL",
|
||||
"user_registration_enabled" => "Allow user registration",
|
||||
"allowed_extensions" => "Allowed file extensions",
|
||||
"trusted_domains" => "Trusted origin domains (* as subdomain-wildcard)",
|
||||
"time_zone" => "Time zone",
|
||||
"mail_contact_gpg_key" => "Contact GPG key",
|
||||
"no_gpg_key_configured" => "No GPG key configured yet",
|
||||
|
||||
# mail settings
|
||||
"mail_enabled" => "Enable e-mail transport",
|
||||
@@ -65,4 +68,8 @@ return [
|
||||
"save_settings_error" => "Error saving settings",
|
||||
"send_test_email_error" => "Error sending test email",
|
||||
"send_test_email_success" => "Test email successfully sent. Please check your inbox!",
|
||||
"remove_gpg_key_error" => "Error removing GPG key",
|
||||
"remove_gpg_key" => "Remove GPG key",
|
||||
"remove_gpg_key_text" => "Do you really want to remove this gpg key?",
|
||||
"import_gpg_key_error" => "Error importing GPG key",
|
||||
];
|
||||
@@ -18,9 +18,9 @@ class GpgKey extends DatabaseEntity {
|
||||
private \DateTime $expires;
|
||||
#[DefaultValue(CurrentTimeStamp::class)] private \DateTime $added;
|
||||
|
||||
public function __construct(string $fingerprint, string $algorithm, \DateTime $expires) {
|
||||
public function __construct(string $fingerprint, string $algorithm, \DateTime $expires, bool $confirmed = false) {
|
||||
parent::__construct();
|
||||
$this->confirmed = false;
|
||||
$this->confirmed = $confirmed;
|
||||
$this->fingerprint = $fingerprint;
|
||||
$this->algorithm = $algorithm;
|
||||
$this->expires = $expires;
|
||||
|
||||
@@ -10,7 +10,7 @@ if (is_file($autoLoad)) {
|
||||
require_once $autoLoad;
|
||||
}
|
||||
|
||||
const WEBBASE_VERSION = "2.4.0";
|
||||
const WEBBASE_VERSION = "2.4.1";
|
||||
|
||||
spl_autoload_extensions(".php");
|
||||
spl_autoload_register(function ($class) {
|
||||
|
||||
Reference in New Issue
Block a user