UserToken / UserAPI
This commit is contained in:
parent
f6bae08c05
commit
b5b8f9b856
@ -32,6 +32,7 @@ namespace Core\API {
|
||||
$connectionData->setProperty("from", $settings["mail_from"] ?? "");
|
||||
$connectionData->setProperty("last_sync", $settings["mail_last_sync"] ?? "");
|
||||
$connectionData->setProperty("mail_footer", $settings["mail_footer"] ?? "");
|
||||
$connectionData->setProperty("mail_async", $settings["mail_async"] ?? false);
|
||||
return $connectionData;
|
||||
}
|
||||
|
||||
@ -89,7 +90,7 @@ namespace Core\API\Mail {
|
||||
'replyTo' => new Parameter('replyTo', Parameter::TYPE_EMAIL, true, null),
|
||||
'replyName' => new StringType('replyName', 32, true, ""),
|
||||
'gpgFingerprint' => new StringType("gpgFingerprint", 64, true, null),
|
||||
'async' => new Parameter("async", Parameter::TYPE_BOOLEAN, true, true)
|
||||
'async' => new Parameter("async", Parameter::TYPE_BOOLEAN, true, null)
|
||||
));
|
||||
$this->isPublic = false;
|
||||
}
|
||||
@ -110,7 +111,13 @@ namespace Core\API\Mail {
|
||||
$body = $this->getParam('body');
|
||||
$gpgFingerprint = $this->getParam("gpgFingerprint");
|
||||
|
||||
if ($this->getParam("async")) {
|
||||
$mailAsync = $this->getParam("async");
|
||||
if ($mailAsync === null) {
|
||||
// not set? grab from settings
|
||||
$mailAsync = $mailConfig->getProperty("mail_async", false);
|
||||
}
|
||||
|
||||
if ($mailAsync) {
|
||||
$sql = $this->context->getSQL();
|
||||
$this->success = $sql->insert("MailQueue", ["from", "to", "subject", "body",
|
||||
"replyTo", "replyName", "gpgFingerprint"])
|
||||
|
@ -223,7 +223,7 @@ abstract class Request {
|
||||
}
|
||||
|
||||
// Check for permission
|
||||
if (!($this instanceof \API\Permission\Save)) {
|
||||
if (!($this instanceof \Core\API\Permission\Save)) {
|
||||
$req = new \Core\API\Permission\Check($this->context);
|
||||
$this->success = $req->execute(array("method" => $this->getMethod()));
|
||||
$this->lastError = $req->getLastError();
|
||||
@ -242,8 +242,8 @@ abstract class Request {
|
||||
}
|
||||
|
||||
$sql = $this->context->getSQL();
|
||||
if (!$sql->isConnected()) {
|
||||
$this->lastError = $sql->getLastError();
|
||||
if ($sql === null || !$sql->isConnected()) {
|
||||
$this->lastError = $sql ? $sql->getLastError() : "Database not connected yet.";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -265,8 +265,8 @@ abstract class Request {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getParam($name, $obj = NULL) {
|
||||
// i don't know why phpstorm
|
||||
protected function getParam($name, $obj = NULL): mixed {
|
||||
// I don't know why phpstorm
|
||||
if ($obj === NULL) {
|
||||
$obj = $this->params;
|
||||
}
|
||||
|
@ -45,16 +45,23 @@ namespace Core\API\Template {
|
||||
return $this->createError("Invalid template file extension. Allowed: " . implode(",", $allowedExtensions));
|
||||
}
|
||||
|
||||
$templateDir = WEBROOT . "/Core/Templates/";
|
||||
$templateCache = WEBROOT . "/Core/Cache/Templates/";
|
||||
$path = realpath($templateDir . $templateFile);
|
||||
if (!startsWith($path, realpath($templateDir))) {
|
||||
return $this->createError("Template file not in template directory");
|
||||
} else if (!is_file($path)) {
|
||||
return $this->createError("Template file not found");
|
||||
$baseDirs = ["Site", "Core"];
|
||||
$valid = false;
|
||||
|
||||
foreach ($baseDirs as $baseDir) {
|
||||
$path = realpath(implode("/", [WEBROOT, $baseDir, "Templates", $templateFile]));
|
||||
if ($path && is_file($path)) {
|
||||
$valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$twigLoader = new FilesystemLoader($templateDir);
|
||||
if (!$valid) {
|
||||
return $this->createError("Template file not found or not inside template directory");
|
||||
}
|
||||
|
||||
$twigLoader = new FilesystemLoader(dirname($path));
|
||||
$twigEnvironment = new Environment($twigLoader, [
|
||||
'cache' => $templateCache,
|
||||
'auto_reload' => true
|
||||
|
@ -123,10 +123,11 @@ namespace Core\API\TFA {
|
||||
if ($this->success) {
|
||||
$body = $req->getResult()["html"];
|
||||
$gpg = $currentUser->getGPG();
|
||||
$siteName = $settings->getSiteName();
|
||||
$req = new \Core\API\Mail\Send($this->context);
|
||||
$this->success = $req->execute([
|
||||
"to" => $currentUser->getEmail(),
|
||||
"subject" => "[Security Lab] 2FA-Authentication removed",
|
||||
"subject" => "[$siteName] 2FA-Authentication removed",
|
||||
"body" => $body,
|
||||
"gpgFingerprint" => $gpg?->getFingerprint()
|
||||
]);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -19,14 +19,6 @@ class CreateDatabase extends DatabaseScript {
|
||||
->addRow("en_US", 'American English')
|
||||
->addRow("de_DE", 'Deutsch Standard');
|
||||
|
||||
$queries[] = $sql->createTable("UserToken")
|
||||
->addInt("user_id")
|
||||
->addString("token", 36)
|
||||
->addEnum("token_type", array("password_reset", "email_confirm", "invite", "gpg_confirm"))
|
||||
->addDateTime("valid_until")
|
||||
->addBool("used", false)
|
||||
->foreignKey("user_id", "User", "id", new CascadeStrategy());
|
||||
|
||||
$queries[] = $sql->insert("Group", array("name", "color"))
|
||||
->addRow(USER_GROUP_MODERATOR_NAME, "#007bff")
|
||||
->addRow(USER_GROUP_SUPPORT_NAME, "#28a745")
|
||||
|
@ -15,20 +15,28 @@ class Settings {
|
||||
//
|
||||
private bool $installationComplete;
|
||||
|
||||
// settings
|
||||
// general settings
|
||||
private string $siteName;
|
||||
private string $baseUrl;
|
||||
private bool $registrationAllowed;
|
||||
private array $allowedExtensions;
|
||||
private string $timeZone;
|
||||
|
||||
// jwt
|
||||
private ?string $jwtPublicKey;
|
||||
private ?string $jwtSecretKey;
|
||||
private string $jwtAlgorithm;
|
||||
private bool $registrationAllowed;
|
||||
|
||||
// recaptcha
|
||||
private bool $recaptchaEnabled;
|
||||
private bool $mailEnabled;
|
||||
private string $recaptchaPublicKey;
|
||||
private string $recaptchaPrivateKey;
|
||||
|
||||
// mail
|
||||
private bool $mailEnabled;
|
||||
private string $mailSender;
|
||||
private string $mailFooter;
|
||||
private array $allowedExtensions;
|
||||
private bool $mailAsync;
|
||||
|
||||
//
|
||||
private Logger $logger;
|
||||
@ -55,7 +63,11 @@ class Settings {
|
||||
}
|
||||
|
||||
public static function loadDefaults(): Settings {
|
||||
$hostname = $_SERVER["SERVER_NAME"] ?? "localhost";
|
||||
$hostname = $_SERVER["SERVER_NAME"];
|
||||
if (empty($hostname)) {
|
||||
$hostname = "localhost";
|
||||
}
|
||||
|
||||
$protocol = getProtocol();
|
||||
$settings = new Settings();
|
||||
|
||||
@ -65,6 +77,7 @@ class Settings {
|
||||
$settings->allowedExtensions = ['png', 'jpg', 'jpeg', 'gif', 'htm', 'html'];
|
||||
$settings->installationComplete = false;
|
||||
$settings->registrationAllowed = false;
|
||||
$settings->timeZone = date_default_timezone_get();
|
||||
|
||||
// JWT
|
||||
$settings->jwtSecretKey = null;
|
||||
@ -80,7 +93,7 @@ class Settings {
|
||||
$settings->mailEnabled = false;
|
||||
$settings->mailSender = "webmaster@localhost";
|
||||
$settings->mailFooter = "";
|
||||
|
||||
$settings->mailAsync = false;
|
||||
|
||||
return $settings;
|
||||
}
|
||||
@ -118,7 +131,7 @@ class Settings {
|
||||
return in_array(strtoupper($algorithm), ["HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "EDDSA"]);
|
||||
}
|
||||
|
||||
public function saveJwtKey(Context $context) {
|
||||
public function saveJwtKey(Context $context): \Core\API\Settings\Set {
|
||||
$req = new \Core\API\Settings\Set($context);
|
||||
$req->execute(array("settings" => array(
|
||||
"jwt_secret_key" => $this->jwtSecretKey,
|
||||
@ -140,6 +153,7 @@ class Settings {
|
||||
$this->baseUrl = $result["base_url"] ?? $this->baseUrl;
|
||||
$this->registrationAllowed = $result["user_registration_enabled"] ?? $this->registrationAllowed;
|
||||
$this->installationComplete = $result["installation_completed"] ?? $this->installationComplete;
|
||||
$this->timeZone = $result["time_zone"] ?? $this->timeZone;
|
||||
$this->jwtSecretKey = $result["jwt_secret_key"] ?? $this->jwtSecretKey;
|
||||
$this->jwtPublicKey = $result["jwt_public_key"] ?? $this->jwtPublicKey;
|
||||
$this->jwtAlgorithm = $result["jwt_algorithm"] ?? $this->jwtAlgorithm;
|
||||
@ -149,6 +163,7 @@ class Settings {
|
||||
$this->mailEnabled = $result["mail_enabled"] ?? $this->mailEnabled;
|
||||
$this->mailSender = $result["mail_from"] ?? $this->mailSender;
|
||||
$this->mailFooter = $result["mail_footer"] ?? $this->mailFooter;
|
||||
$this->mailAsync = $result["mail_async"] ?? $this->mailAsync;
|
||||
$this->allowedExtensions = explode(",", $result["allowed_extensions"] ?? strtolower(implode(",", $this->allowedExtensions)));
|
||||
|
||||
if (!isset($result["jwt_secret_key"])) {
|
||||
@ -156,16 +171,19 @@ class Settings {
|
||||
$this->saveJwtKey($context);
|
||||
}
|
||||
}
|
||||
|
||||
date_default_timezone_set($this->timeZone);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addRows(Insert $query) {
|
||||
public function addRows(Insert $query): void {
|
||||
$query->addRow("site_name", $this->siteName, false, false)
|
||||
->addRow("base_url", $this->baseUrl, 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("jwt_secret_key", $this->jwtSecretKey, true, false)
|
||||
->addRow("jwt_public_key", $this->jwtPublicKey, false, false)
|
||||
->addRow("jwt_algorithm", $this->jwtAlgorithm, false, false)
|
||||
@ -179,6 +197,14 @@ class Settings {
|
||||
return $this->siteName;
|
||||
}
|
||||
|
||||
public function getTimeZone(): string {
|
||||
return $this->timeZone;
|
||||
}
|
||||
|
||||
public function setTimeZone(string $tz) {
|
||||
$this->timeZone = $tz;
|
||||
}
|
||||
|
||||
public function getBaseUrl(): string {
|
||||
return $this->baseUrl;
|
||||
}
|
||||
@ -203,6 +229,10 @@ class Settings {
|
||||
return $this->mailEnabled;
|
||||
}
|
||||
|
||||
public function isMailAsync(): bool {
|
||||
return $this->mailAsync;
|
||||
}
|
||||
|
||||
public function getMailSender(): string {
|
||||
return $this->mailSender;
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ namespace Documents\Install {
|
||||
$username = posix_getpwuid($userId)['name'];
|
||||
$failedRequirements[] = sprintf("<b>%s</b> is not owned by current user: $username ($userId). " .
|
||||
"Try running <b>chown -R $userId %s</b> or give the required directories write permissions: " .
|
||||
"<b>core/Configuration</b>, <b>core/Cache</b>, <b>core/External</b>",
|
||||
"<b>Site/Configuration</b>, <b>Core/Cache</b>, <b>Core/External</b>",
|
||||
WEBROOT, WEBROOT);
|
||||
$success = false;
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class Logger {
|
||||
$module = preg_replace("/[^a-zA-Z0-9-]/", "-", $this->module);
|
||||
$date = (\DateTime::createFromFormat('U.u', microtime(true)))->format(self::LOG_FILE_DATE_FORMAT);
|
||||
$logFile = implode("_", [$module, $severity, $date]) . ".log";
|
||||
$logPath = implode(DIRECTORY_SEPARATOR, [WEBROOT, "core", "Logs", $logFile]);
|
||||
$logPath = implode(DIRECTORY_SEPARATOR, [WEBROOT, "Core", "Logs", $logFile]);
|
||||
@file_put_contents($logPath, $message);
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ class TemplateDocument extends Document {
|
||||
$this->parameters = $params;
|
||||
$this->twigLoader = new FilesystemLoader(self::TEMPLATE_PATH);
|
||||
$this->twigEnvironment = new Environment($this->twigLoader, [
|
||||
'cache' => WEBROOT . '/core/Cache/Templates/',
|
||||
'cache' => WEBROOT . '/Core/Cache/Templates/',
|
||||
'auto_reload' => true
|
||||
]);
|
||||
$this->twigEnvironment->addExtension(new CustomTwigFunctions());
|
||||
|
@ -7,6 +7,7 @@ use Core\Configuration\Settings;
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Driver\SQL\Condition\CondLike;
|
||||
use Core\Driver\SQL\Condition\CondOr;
|
||||
use Core\Driver\SQL\Join;
|
||||
use Core\Driver\SQL\SQL;
|
||||
use Firebase\JWT\JWT;
|
||||
use Core\Objects\DatabaseEntity\Language;
|
||||
@ -92,6 +93,9 @@ class Context {
|
||||
private function loadSession(int $userId, int $sessionId) {
|
||||
$this->session = Session::init($this, $userId, $sessionId);
|
||||
$this->user = $this->session?->getUser();
|
||||
if ($this->user) {
|
||||
$this->user->session = $this->session;
|
||||
}
|
||||
}
|
||||
|
||||
public function parseCookies() {
|
||||
@ -173,7 +177,7 @@ class Context {
|
||||
|
||||
public function loadApiKey(string $apiKey): bool {
|
||||
$this->user = User::findBuilder($this->sql)
|
||||
->addJoin(new \Driver\SQL\Join("INNER","ApiKey", "ApiKey.user_id", "User.id"))
|
||||
->addJoin(new Join("INNER","ApiKey", "ApiKey.user_id", "User.id"))
|
||||
->where(new Compare("ApiKey.api_key", $apiKey))
|
||||
->where(new Compare("valid_until", $this->sql->currentTimestamp(), ">"))
|
||||
->where(new Compare("ApiKey.active", true))
|
||||
@ -184,20 +188,19 @@ class Context {
|
||||
return $this->user !== null;
|
||||
}
|
||||
|
||||
public function createSession(int $userId, bool $stayLoggedIn): ?Session {
|
||||
$this->user = User::find($this->sql, $userId);
|
||||
if ($this->user) {
|
||||
public function createSession(User $user, bool $stayLoggedIn): ?Session {
|
||||
$this->user = $user;
|
||||
$this->session = new Session($this, $this->user);
|
||||
$this->session->stayLoggedIn = $stayLoggedIn;
|
||||
if ($this->session->update()) {
|
||||
$user->session = $this->session;
|
||||
return $this->session;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$this->user = null;
|
||||
$this->session = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function getLanguage(): Language {
|
||||
return $this->language;
|
||||
|
12
Core/Objects/DatabaseEntity/Attribute/EnumArr.class.php
Normal file
12
Core/Objects/DatabaseEntity/Attribute/EnumArr.class.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Core\Objects\DatabaseEntity\Attribute;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||
class EnumArr extends Enum {
|
||||
|
||||
public function __construct(array $values) {
|
||||
parent::__construct(...$values);
|
||||
}
|
||||
|
||||
}
|
@ -322,7 +322,7 @@ class DatabaseEntityHandler {
|
||||
if ($property->isInitialized($entity)) {
|
||||
$value = $property->getValue($entity);
|
||||
if (isset($this->relations[$propertyName])) {
|
||||
$value = $value->getId();
|
||||
$value = $value?->getId();
|
||||
}
|
||||
} else if (!$this->columns[$propertyName]->notNull()) {
|
||||
$value = null;
|
||||
@ -411,4 +411,8 @@ class DatabaseEntityHandler {
|
||||
$this->logger->error($message);
|
||||
throw new Exception($message);
|
||||
}
|
||||
|
||||
public function getSQL(): SQL {
|
||||
return $this->sql;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Core\Objects\DatabaseEntity;
|
||||
|
||||
use Core\Driver\Logger\Logger;
|
||||
use Core\Driver\SQL\Condition\Condition;
|
||||
use Core\Driver\SQL\Join;
|
||||
use Core\Driver\SQL\Query\Select;
|
||||
@ -13,20 +14,29 @@ use Core\Driver\SQL\SQL;
|
||||
*/
|
||||
class DatabaseEntityQuery {
|
||||
|
||||
private Logger $logger;
|
||||
private DatabaseEntityHandler $handler;
|
||||
private Select $selectQuery;
|
||||
private int $resultType;
|
||||
private bool $logVerbose;
|
||||
|
||||
private function __construct(DatabaseEntityHandler $handler, int $resultType) {
|
||||
$this->handler = $handler;
|
||||
$this->selectQuery = $handler->getSelectQuery();
|
||||
$this->logger = new Logger("DB-EntityQuery", $handler->getSQL());
|
||||
$this->resultType = $resultType;
|
||||
$this->logVerbose = false;
|
||||
|
||||
if ($this->resultType === SQL::FETCH_ONE) {
|
||||
$this->selectQuery->first();
|
||||
}
|
||||
}
|
||||
|
||||
public function debug(): DatabaseEntityQuery {
|
||||
$this->logVerbose = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function fetchAll(DatabaseEntityHandler $handler): DatabaseEntityQuery {
|
||||
return new DatabaseEntityQuery($handler, SQL::FETCH_ALL);
|
||||
}
|
||||
@ -106,6 +116,13 @@ class DatabaseEntityQuery {
|
||||
}
|
||||
|
||||
public function execute(): DatabaseEntity|array|null {
|
||||
|
||||
if ($this->logVerbose) {
|
||||
$params = [];
|
||||
$query = $this->selectQuery->build($params);
|
||||
$this->logger->debug("QUERY: $query\nARGS: " . print_r($params, true));
|
||||
}
|
||||
|
||||
$res = $this->selectQuery->execute();
|
||||
if ($res === null || $res === false) {
|
||||
return null;
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Core\Objects\DatabaseEntity;
|
||||
|
||||
use Core\Driver\SQL\Expression\CurrentTimeStamp;
|
||||
use Core\Driver\SQL\SQL;
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
||||
|
||||
@ -16,12 +17,13 @@ class GpgKey extends DatabaseEntity {
|
||||
private \DateTime $expires;
|
||||
#[DefaultValue(CurrentTimeStamp::class)] private \DateTime $added;
|
||||
|
||||
public function __construct(int $id, bool $confirmed, string $fingerprint, string $algorithm, string $expires) {
|
||||
parent::__construct($id);
|
||||
$this->confirmed = $confirmed;
|
||||
public function __construct(string $fingerprint, string $algorithm, \DateTime $expires) {
|
||||
parent::__construct();
|
||||
$this->confirmed = false;
|
||||
$this->fingerprint = $fingerprint;
|
||||
$this->algorithm = $algorithm;
|
||||
$this->expires = new \DateTime($expires);
|
||||
$this->expires = $expires;
|
||||
$this->added = new \DateTime();
|
||||
}
|
||||
|
||||
public static function encrypt(string $body, string $gpgFingerprint): array {
|
||||
@ -130,4 +132,9 @@ class GpgKey extends DatabaseEntity {
|
||||
"confirmed" => $this->confirmed
|
||||
];
|
||||
}
|
||||
|
||||
public function confirm(SQL $sql): bool {
|
||||
$this->confirmed = true;
|
||||
return $this->save($sql);
|
||||
}
|
||||
}
|
@ -96,7 +96,7 @@ class Session extends DatabaseEntity {
|
||||
return array(
|
||||
'id' => $this->getId(),
|
||||
'active' => $this->active,
|
||||
'expires' => $this->expires,
|
||||
'expires' => $this->expires->getTimestamp(),
|
||||
'ipAddress' => $this->ipAddress,
|
||||
'os' => $this->os,
|
||||
'browser' => $this->browser,
|
||||
|
@ -17,12 +17,12 @@ class User extends DatabaseEntity {
|
||||
#[MaxLength(128)] public string $password;
|
||||
#[MaxLength(64)] public string $fullName;
|
||||
#[MaxLength(64)] #[Unique] public ?string $email;
|
||||
#[MaxLength(64)] private ?string $profilePicture;
|
||||
#[MaxLength(64)] public ?string $profilePicture;
|
||||
private ?\DateTime $lastOnline;
|
||||
#[DefaultValue(CurrentTimeStamp::class)] public \DateTime $registeredAt;
|
||||
public bool $confirmed;
|
||||
#[DefaultValue(1)] public Language $language;
|
||||
private ?GpgKey $gpgKey;
|
||||
public ?GpgKey $gpgKey;
|
||||
private ?TwoFactorToken $twoFactorToken;
|
||||
|
||||
#[Transient] private array $groups;
|
||||
@ -37,7 +37,6 @@ class User extends DatabaseEntity {
|
||||
$this->groups = [];
|
||||
|
||||
$groups = Group::findAllBuilder($sql)
|
||||
->fetchEntities()
|
||||
->addJoin(new Join("INNER", "UserGroup", "UserGroup.group_id", "Group.id"))
|
||||
->where(new Compare("UserGroup.user_id", $this->id))
|
||||
->execute();
|
||||
@ -99,6 +98,9 @@ class User extends DatabaseEntity {
|
||||
'session' => (isset($this->session) ? $this->session->jsonSerialize() : null),
|
||||
"gpg" => (isset($this->gpgKey) ? $this->gpgKey->jsonSerialize() : null),
|
||||
"2fa" => (isset($this->twoFactorToken) ? $this->twoFactorToken->jsonSerialize() : null),
|
||||
"reqisteredAt" => $this->registeredAt->getTimestamp(),
|
||||
"lastOnline" => $this->lastOnline->getTimestamp(),
|
||||
"confirmed" => $this->confirmed
|
||||
];
|
||||
}
|
||||
|
||||
|
72
Core/Objects/DatabaseEntity/UserToken.class.php
Normal file
72
Core/Objects/DatabaseEntity/UserToken.class.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Core\Objects\DatabaseEntity;
|
||||
|
||||
use Core\Driver\SQL\SQL;
|
||||
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
||||
use Core\Objects\DatabaseEntity\Attribute\EnumArr;
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
|
||||
class UserToken extends DatabaseEntity {
|
||||
|
||||
const TYPE_PASSWORD_RESET = "password_reset";
|
||||
const TYPE_EMAIL_CONFIRM = "email_confirm";
|
||||
const TYPE_INVITE = "invite";
|
||||
const TYPE_GPG_CONFIRM = "gpg_confirm";
|
||||
|
||||
const TOKEN_TYPES = [
|
||||
self::TYPE_PASSWORD_RESET, self::TYPE_EMAIL_CONFIRM,
|
||||
self::TYPE_INVITE, self::TYPE_GPG_CONFIRM
|
||||
];
|
||||
|
||||
#[MaxLength(36)]
|
||||
private string $token;
|
||||
|
||||
#[EnumArr(self::TOKEN_TYPES)]
|
||||
private string $tokenType;
|
||||
|
||||
private User $user;
|
||||
private \DateTime $validUntil;
|
||||
|
||||
#[DefaultValue(false)]
|
||||
private bool $used;
|
||||
|
||||
public function __construct(User $user, string $token, string $type, int $validHours) {
|
||||
parent::__construct();
|
||||
$this->user = $user;
|
||||
$this->token = $token;
|
||||
$this->tokenType = $type;
|
||||
$this->validUntil = (new \DateTime())->modify("+$validHours HOUR");
|
||||
$this->used = false;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array {
|
||||
return [
|
||||
"id" => $this->getId(),
|
||||
"token" => $this->token,
|
||||
"tokenType" => $this->tokenType
|
||||
];
|
||||
}
|
||||
|
||||
public function getType(): string {
|
||||
return $this->tokenType;
|
||||
}
|
||||
|
||||
public function invalidate(SQL $sql): bool {
|
||||
$this->used = true;
|
||||
return $this->save($sql);
|
||||
}
|
||||
|
||||
public function getUser(): User {
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function updateDurability(SQL $sql, int $validHours): bool {
|
||||
$this->validUntil = (new \DateTime())->modify("+$validHours HOURS");
|
||||
return $this->save($sql);
|
||||
}
|
||||
|
||||
public function getToken(): string {
|
||||
return $this->token;
|
||||
}
|
||||
}
|
0
Site/Templates/.gitkeep
Normal file
0
Site/Templates/.gitkeep
Normal file
25
cli.php
25
cli.php
@ -48,17 +48,6 @@ if ($database !== null && $database->getProperty("isDocker", false) && !is_file(
|
||||
}
|
||||
}
|
||||
|
||||
/*function getUser(): ?User {
|
||||
global $config;
|
||||
$user = new User($config);
|
||||
if (!$user->getSQL() || !$user->getSQL()->isConnected()) {
|
||||
printLine("Could not establish database connection");
|
||||
return null;
|
||||
}
|
||||
|
||||
return $user;
|
||||
}*/
|
||||
|
||||
function connectSQL(): ?SQL {
|
||||
global $context;
|
||||
$sql = $context->initSQL();
|
||||
@ -76,7 +65,7 @@ function printHelp() {
|
||||
|
||||
function applyPatch(\Core\Driver\SQL\SQL $sql, string $patchName): bool {
|
||||
$class = str_replace('/', '\\', $patchName);
|
||||
$className = "\\Configuration\\$class";
|
||||
$className = "\\Core\\Configuration\\$class";
|
||||
$classPath = getClassPath($className);
|
||||
if (!file_exists($classPath) || !is_readable($classPath)) {
|
||||
printLine("Database script file does not exist or is not readable");
|
||||
@ -282,7 +271,7 @@ function onMaintenance(array $argv) {
|
||||
_exit("Maintenance disabled");
|
||||
} else if ($action === "update") {
|
||||
|
||||
$oldPatchFiles = glob('core/Configuration/Patch/*.php');
|
||||
$oldPatchFiles = glob('Core/Configuration/Patch/*.php');
|
||||
printLine("$ git remote -v");
|
||||
exec("git remote -v", $gitRemote, $ret);
|
||||
if ($ret !== 0) {
|
||||
@ -339,14 +328,15 @@ function onMaintenance(array $argv) {
|
||||
die();
|
||||
}
|
||||
|
||||
$newPatchFiles = glob('core/Configuration/Patch/*.php');
|
||||
// TODO: also collect patches from Site/Configuration/Patch ... and what about database entities?
|
||||
$newPatchFiles = glob('Core/Configuration/Patch/*.php');
|
||||
$newPatchFiles = array_diff($newPatchFiles, $oldPatchFiles);
|
||||
if (count($newPatchFiles) > 0) {
|
||||
printLine("Applying new database patches");
|
||||
$sql = connectSQL();
|
||||
if ($sql) {
|
||||
foreach ($newPatchFiles as $patchFile) {
|
||||
if (preg_match("/core\/Configuration\/(Patch\/.*)\.class\.php/", $patchFile, $match)) {
|
||||
if (preg_match("/Core\/Configuration\/(Patch\/.*)\.class\.php/", $patchFile, $match)) {
|
||||
$patchName = $match[1];
|
||||
applyPatch($sql, $patchName);
|
||||
}
|
||||
@ -415,7 +405,7 @@ function printTable(array $head, array $body) {
|
||||
|
||||
function onSettings(array $argv) {
|
||||
global $context;
|
||||
$sql = connectSQL() or die();
|
||||
connectSQL() or die();
|
||||
$action = $argv[2] ?? "list";
|
||||
|
||||
if ($action === "list" || $action === "get") {
|
||||
@ -461,7 +451,7 @@ function onSettings(array $argv) {
|
||||
|
||||
function onRoutes(array $argv) {
|
||||
global $context;
|
||||
$sql = connectSQL() or die();
|
||||
connectSQL() or die();
|
||||
$action = $argv[2] ?? "list";
|
||||
|
||||
if ($action === "list") {
|
||||
@ -607,6 +597,7 @@ function onMail($argv) {
|
||||
global $context;
|
||||
$action = $argv[2] ?? null;
|
||||
if ($action === "send_queue") {
|
||||
connectSQL() or die();
|
||||
$req = new \Core\API\Mail\SendQueue($context);
|
||||
$debug = in_array("debug", $argv);
|
||||
if (!$req->execute(["debug" => $debug])) {
|
||||
|
@ -1,11 +1,14 @@
|
||||
FROM composer:latest AS composer
|
||||
FROM php:8.0-fpm
|
||||
WORKDIR "/application"
|
||||
RUN mkdir -p /application/core/Configuration
|
||||
RUN chown -R www-data:www-data /application
|
||||
RUN mkdir -p /application/core/Configuration /var/www/.gnupg && \
|
||||
chown -R www-data:www-data /application /var/www/ && \
|
||||
chmod 700 /var/www/.gnupg
|
||||
|
||||
# YAML + dev dependencies
|
||||
RUN apt-get update -y && apt-get install libyaml-dev libzip-dev libgmp-dev -y && apt-get clean && \
|
||||
RUN apt-get update -y && \
|
||||
apt-get install -y libyaml-dev libzip-dev libgmp-dev gnupg2 && \
|
||||
apt-get clean && \
|
||||
pecl install yaml && docker-php-ext-enable yaml
|
||||
|
||||
# Runkit (no stable release available)
|
||||
|
Loading…
Reference in New Issue
Block a user