diff --git a/Core/API/RoutesAPI.class.php b/Core/API/RoutesAPI.class.php index b7b6cb2..e260cbc 100644 --- a/Core/API/RoutesAPI.class.php +++ b/Core/API/RoutesAPI.class.php @@ -3,12 +3,11 @@ namespace Core\API { use Core\API\Routes\GenerateCache; - use Core\Driver\SQL\Condition\Compare; use Core\Objects\Context; + use Core\Objects\DatabaseEntity\Route; abstract class RoutesAPI extends Request { - const ACTIONS = array("redirect_temporary", "redirect_permanently", "static", "dynamic"); const ROUTER_CACHE_CLASS = "\\Core\\Cache\\RouterCache"; protected string $routerCachePath; @@ -18,38 +17,19 @@ namespace Core\API { $this->routerCachePath = getClassPath(self::ROUTER_CACHE_CLASS); } - protected function routeExists($uid): bool { + protected function toggleRoute(int $id, bool $active): bool { $sql = $this->context->getSQL(); - $res = $sql->select($sql->count()) - ->from("Route") - ->whereEq("id", $uid) - ->execute(); - - $this->success = ($res !== false); - $this->lastError = $sql->getLastError(); - if ($this->success) { - if ($res[0]["count"] === 0) { - return $this->createError("Route not found"); - } - } - - return $this->success; - } - - protected function toggleRoute($uid, $active): bool { - if (!$this->routeExists($uid)) { + $route = Route::find($sql, $id); + if ($route === false) { return false; + } else if ($route === null) { + return $this->createError("Route not found"); } - $sql = $this->context->getSQL(); - $this->success = $sql->update("Route") - ->set("active", $active) - ->whereEq("id", $uid) - ->execute(); - + $route->setActive($active); + $this->success = $route->save($sql); $this->lastError = $sql->getLastError(); - $this->success = $this->success && $this->regenerateCache(); - return $this->success; + return $this->success && $this->regenerateCache(); } protected function regenerateCache(): bool { @@ -58,6 +38,27 @@ namespace Core\API { $this->lastError = $req->getLastError(); return $this->success; } + + protected function createRoute(string $type, string $pattern, string $target, + ?string $extra, bool $exact, bool $active = true): ?Route { + + $routeClass = Route::ROUTE_TYPES[$type] ?? null; + if (!$routeClass) { + $this->createError("Invalid type: $type"); + return null; + } + + try { + $routeClass = new \ReflectionClass($routeClass); + $routeObj = $routeClass->newInstance($pattern, $exact, $target); + $routeObj->setExtra($extra); + $routeObj->setActive($active); + return $routeObj; + } catch (\ReflectionException $exception) { + $this->createError("Error instantiating route class: " . $exception->getMessage()); + return null; + } + } } } @@ -68,6 +69,7 @@ namespace Core\API\Routes { use Core\API\RoutesAPI; use Core\Driver\SQL\Condition\Compare; use Core\Driver\SQL\Condition\CondBool; + use Core\Driver\SQL\Query\StartTransaction; use Core\Objects\Context; use Core\Objects\DatabaseEntity\Route; use Core\Objects\Router\DocumentRoute; @@ -86,31 +88,15 @@ namespace Core\API\Routes { public function _execute(): bool { $sql = $this->context->getSQL(); - $res = $sql - ->select("id", "request", "action", "target", "extra", "active", "exact") - ->from("Route") - ->orderBy("id") - ->ascending() - ->execute(); - + $routes = Route::findAll($sql); $this->lastError = $sql->getLastError(); - $this->success = ($res !== FALSE); + $this->success = ($routes !== FALSE); if ($this->success) { - $routes = array(); - foreach ($res as $row) { - $routes[] = array( - "id" => intval($row["id"]), - "request" => $row["request"], - "action" => $row["action"], - "target" => $row["target"], - "extra" => $row["extra"] ?? "", - "active" => intval($sql->parseBool($row["active"])), - "exact" => intval($sql->parseBool($row["exact"])), - ); + $this->result["routes"] = []; + foreach ($routes as $route) { + $this->result["routes"][$route->getId()] = $route->jsonSerialize(); } - - $this->result["routes"] = $routes; } return $this->success; @@ -133,32 +119,35 @@ namespace Core\API\Routes { } $sql = $this->context->getSQL(); + $sql->startTransaction(); - // DELETE old rules + // DELETE old rules; $this->success = ($sql->truncate("Route")->execute() !== FALSE); $this->lastError = $sql->getLastError(); // INSERT new routes if ($this->success) { - $stmt = $sql->insert("Route", array("request", "action", "target", "extra", "active", "exact")); - - foreach ($this->routes as $route) { - $stmt->addRow($route["request"], $route["action"], $route["target"], $route["extra"], $route["active"], $route["exact"]); - } - $this->success = ($stmt->execute() !== FALSE); + $insertStatement = Route::getHandler($sql)->getInsertQuery($this->routes); + $this->success = ($insertStatement->execute() !== FALSE); $this->lastError = $sql->getLastError(); } - $this->success = $this->success && $this->regenerateCache(); - return $this->success; + if ($this->success) { + $sql->commit(); + return $this->regenerateCache(); + } else { + $sql->rollback(); + return false; + } } private function validateRoutes(): bool { $this->routes = array(); $keys = array( - "request" => [Parameter::TYPE_STRING, Parameter::TYPE_INT], - "action" => Parameter::TYPE_STRING, + "id" => Parameter::TYPE_INT, + "pattern" => [Parameter::TYPE_STRING, Parameter::TYPE_INT], + "type" => Parameter::TYPE_STRING, "target" => Parameter::TYPE_STRING, "extra" => Parameter::TYPE_STRING, "active" => Parameter::TYPE_BOOLEAN, @@ -168,7 +157,11 @@ namespace Core\API\Routes { foreach ($this->getParam("routes") as $index => $route) { foreach ($keys as $key => $expectedType) { if (!array_key_exists($key, $route)) { - return $this->createError("Route $index missing key: $key"); + if ($key !== "id") { // id is optional + return $this->createError("Route $index missing key: $key"); + } else { + continue; + } } $value = $route[$key]; @@ -191,13 +184,13 @@ namespace Core\API\Routes { } } - $action = $route["action"]; - if (!in_array($action, self::ACTIONS)) { - return $this->createError("Invalid action: $action"); + $type = $route["type"]; + if (!isset(Route::ROUTE_TYPES[$type])) { + return $this->createError("Invalid type: $type"); } - if (empty($route["request"])) { - return $this->createError("Request cannot be empty."); + if (empty($route["pattern"])) { + return $this->createError("Pattern cannot be empty."); } if (empty($route["target"])) { @@ -215,33 +208,33 @@ namespace Core\API\Routes { public function __construct(Context $context, bool $externalCall = false) { parent::__construct($context, $externalCall, array( - "request" => new StringType("request", 128), - "action" => new StringType("action"), + "pattern" => new StringType("pattern", 128), + "type" => new StringType("type"), "target" => new StringType("target", 128), "extra" => new StringType("extra", 64, true, ""), + "exact" => new Parameter("exact", Parameter::TYPE_BOOLEAN), + "active" => new Parameter("active", Parameter::TYPE_BOOLEAN, true, true), )); $this->isPublic = false; } public function _execute(): bool { - $request = $this->getParam("request"); - $action = $this->getParam("action"); + $pattern = $this->getParam("pattern"); + $type = $this->getParam("type"); $target = $this->getParam("target"); $extra = $this->getParam("extra"); - - if (!in_array($action, self::ACTIONS)) { - return $this->createError("Invalid action: $action"); + $exact = $this->getParam("exact"); + $active = $this->getParam("active"); + $route = $this->createRoute($type, $pattern, $target, $extra, $exact, $active); + if ($route === null) { + return false; } $sql = $this->context->getSQL(); - $this->success = $sql->insert("Route", ["request", "action", "target", "extra"]) - ->addRow($request, $action, $target, $extra) - ->execute(); - + $this->success = $route->save($sql) !== false; $this->lastError = $sql->getLastError(); - $this->success = $this->success && $this->regenerateCache(); - return $this->success; + return $this->success && $this->regenerateCache(); } } @@ -249,8 +242,8 @@ namespace Core\API\Routes { public function __construct(Context $context, bool $externalCall = false) { parent::__construct($context, $externalCall, array( "id" => new Parameter("id", Parameter::TYPE_INT), - "request" => new StringType("request", 128), - "action" => new StringType("action"), + "pattern" => new StringType("pattern", 128), + "type" => new StringType("type"), "target" => new StringType("target", 128), "extra" => new StringType("extra", 64, true, ""), )); @@ -260,30 +253,36 @@ namespace Core\API\Routes { public function _execute(): bool { $id = $this->getParam("id"); - if (!$this->routeExists($id)) { - return false; + $sql = $this->context->getSQL(); + $route = Route::find($sql, $id); + if ($route === false) { + return $this->createError("Error fetching route: " . $sql->getLastError()); + } else if ($route === null) { + return $this->createError("Route not found"); } - $request = $this->getParam("request"); - $action = $this->getParam("action"); $target = $this->getParam("target"); $extra = $this->getParam("extra"); - if (!in_array($action, self::ACTIONS)) { - return $this->createError("Invalid action: $action"); + $type = $this->getParam("type"); + $pattern = $this->getParam("pattern"); + $exact = $this->getParam("exact"); + $active = $this->getParam("active"); + if ($route->getType() !== $type) { + $route = $this->createRoute($type, $pattern, $target, $extra, $exact, $active); + if ($route === null) { + return false; + } + } else { + $route->setPattern($pattern); + $route->setActive($active); + $route->setExtra($extra); + $route->setTarget($target); + $route->setExact($exact); } - $sql = $this->context->getSQL(); - $this->success = $sql->update("Route") - ->set("request", $request) - ->set("action", $action) - ->set("target", $target) - ->set("extra", $extra) - ->whereEq("id", $id) - ->execute(); - + $this->success = $route->save($sql) !== false; $this->lastError = $sql->getLastError(); - $this->success = $this->success && $this->regenerateCache(); - return $this->success; + return $this->success && $this->regenerateCache(); } } @@ -297,19 +296,18 @@ namespace Core\API\Routes { public function _execute(): bool { + $sql = $this->context->getSQL(); $id = $this->getParam("id"); - if (!$this->routeExists($id)) { - return false; + $route = Route::find($sql, $id); + if ($route === false) { + return $this->createError("Error fetching route: " . $sql->getLastError()); + } else if ($route === null) { + return $this->createError("Route not found"); } - $sql = $this->context->getSQL(); - $this->success = $sql->delete("Route") - ->where("id", $id) - ->execute(); - + $this->success = $route->delete($sql) !== false; $this->lastError = $sql->getLastError(); - $this->success = $this->success && $this->regenerateCache(); - return $this->success; + return $this->success && $this->regenerateCache(); } } @@ -322,8 +320,8 @@ namespace Core\API\Routes { } public function _execute(): bool { - $uid = $this->getParam("id"); - return $this->toggleRoute($uid, true); + $id = $this->getParam("id"); + return $this->toggleRoute($id, true); } } @@ -336,8 +334,8 @@ namespace Core\API\Routes { } public function _execute(): bool { - $uid = $this->getParam("id"); - return $this->toggleRoute($uid, false); + $id = $this->getParam("id"); + return $this->toggleRoute($id, false); } } diff --git a/Core/API/TemplateAPI.class.php b/Core/API/TemplateAPI.class.php index cebc25b..da46f4e 100644 --- a/Core/API/TemplateAPI.class.php +++ b/Core/API/TemplateAPI.class.php @@ -50,10 +50,13 @@ namespace Core\API\Template { $valid = false; foreach ($baseDirs as $baseDir) { - $path = realpath(implode("/", [WEBROOT, $baseDir, "Templates", $templateFile])); - if ($path && is_file($path)) { - $valid = true; - break; + $templateDir = realpath(implode("/", [WEBROOT, $baseDir, "Templates"])); + if ($templateDir) { + $path = realpath(implode("/", [$templateDir, $templateFile])); + if ($path && is_file($path)) { + $valid = true; + break; + } } } @@ -61,7 +64,7 @@ namespace Core\API\Template { return $this->createError("Template file not found or not inside template directory"); } - $twigLoader = new FilesystemLoader(dirname($path)); + $twigLoader = new FilesystemLoader($templateDir); $twigEnvironment = new Environment($twigLoader, [ 'cache' => $templateCache, 'auto_reload' => true diff --git a/Core/API/TfaAPI.class.php b/Core/API/TfaAPI.class.php index 2ad6e69..effee18 100644 --- a/Core/API/TfaAPI.class.php +++ b/Core/API/TfaAPI.class.php @@ -84,27 +84,14 @@ namespace Core\API\TFA { $sql = $this->context->getSQL(); $password = $this->getParam("password"); if ($password) { - $res = $sql->select("password") - ->from("User") - ->whereEq("id", $currentUser->getId()) - ->execute(); - $this->success = !empty($res); - $this->lastError = $sql->getLastError(); - if (!$this->success) { - return false; - } else if (!password_verify($password, $res[0]["password"])) { + if (!password_verify($password, $currentUser->password)) { return $this->createError("Wrong password"); } } else if ($token->isConfirmed()) { // if the token is fully confirmed, require a password to remove it return $this->createError("Missing parameter: password"); } - - $res = $sql->delete("2FA") - ->whereEq("id", $token->getId()) - ->execute(); - - $this->success = $res !== false; + $this->success = $token->delete($sql) !== false; $this->lastError = $sql->getLastError(); if ($this->success && $token->isConfirmed()) { @@ -157,16 +144,11 @@ namespace Core\API\TFA { } else if (!($twoFactorToken instanceof TimeBasedTwoFactorToken)) { $twoFactorToken = new TimeBasedTwoFactorToken(generateRandomString(32, "base32")); $sql = $this->context->getSQL(); - $this->success = $sql->insert("2FA", ["type", "data"]) - ->addRow("totp", $twoFactorToken->getData()) - ->returning("id") - ->execute() !== false; + $this->success = $twoFactorToken->save($sql) !== false; $this->lastError = $sql->getLastError(); if ($this->success) { - $this->success = $sql->update("User") - ->set("2fa_id", $sql->getLastInsertId()) - ->whereEq("id", $currentUser->getId()) - ->execute() !== false; + $currentUser->setTwoFactorToken($twoFactorToken); + $this->success = $currentUser->save($sql); $this->lastError = $sql->getLastError(); } @@ -194,11 +176,12 @@ namespace Core\API\TFA { return $this->createError("Your two factor token is already confirmed."); } + if (!parent::_execute()) { + return false; + } + $sql = $this->context->getSQL(); - $this->success = $sql->update("2FA") - ->set("confirmed", true) - ->whereEq("id", $twoFactorToken->getId()) - ->execute() !== false; + $this->success = $twoFactorToken->confirm($sql) !== false; $this->lastError = $sql->getLastError(); return $this->success; } @@ -271,20 +254,15 @@ namespace Core\API\TFA { } } else { $challenge = base64_encode(generateRandomString(32, "raw")); - $res = $sql->insert("2FA", ["type", "data"]) - ->addRow("fido", $challenge) - ->returning("id") - ->execute(); - $this->success = ($res !== false); + $twoFactorToken = new KeyBasedTwoFactorToken($challenge); + $this->success = ($twoFactorToken->save($sql) !== false); $this->lastError = $sql->getLastError(); if (!$this->success) { return false; } - $this->success = $sql->update("User") - ->set("2fa_id", $sql->getLastInsertId()) - ->whereEq("id", $currentUser->getId()) - ->execute() !== false; + $currentUser->setTwoFactorToken($twoFactorToken); + $this->success = $currentUser->save($sql) !== false; $this->lastError = $sql->getLastError(); if (!$this->success) { return false; @@ -321,16 +299,7 @@ namespace Core\API\TFA { return $this->createError("Unsupported key type. Expected: -7"); } - $data = [ - "credentialID" => base64_encode($authData->getCredentialID()), - "publicKey" => $publicKey->jsonSerialize() - ]; - - $this->success = $sql->update("2FA") - ->set("data", json_encode($data)) - ->set("confirmed", true) - ->whereEq("id", $twoFactorToken->getId()) - ->execute() !== false; + $this->success = $twoFactorToken->confirmKeyBased($sql, base64_encode($authData->getCredentialID()), $publicKey) !== false; $this->lastError = $sql->getLastError(); } diff --git a/Core/Documents/Install.class.php b/Core/Documents/Install.class.php index 62f386f..e6f45b5 100644 --- a/Core/Documents/Install.class.php +++ b/Core/Documents/Install.class.php @@ -20,7 +20,6 @@ namespace Documents\Install { use Core\Configuration\Configuration; use Core\Configuration\CreateDatabase; use Core\Driver\SQL\Query\Commit; - use Core\Driver\SQL\Query\RollBack; use Core\Driver\SQL\Query\StartTransaction; use Core\Driver\SQL\SQL; use Core\Elements\Body; @@ -349,7 +348,7 @@ namespace Documents\Install { } } finally { if (!$success) { - (new RollBack($sql))->execute(); + $sql->rollback(); } } diff --git a/Core/Driver/SQL/SQL.class.php b/Core/Driver/SQL/SQL.class.php index 3d16292..f89e4b4 100644 --- a/Core/Driver/SQL/SQL.class.php +++ b/Core/Driver/SQL/SQL.class.php @@ -23,6 +23,7 @@ use Core\Driver\SQL\Expression\CurrentTimeStamp; use Core\Driver\SQL\Expression\Expression; use Core\Driver\SQL\Expression\Sum; use Core\Driver\SQL\Query\AlterTable; +use Core\Driver\SQL\Query\Commit; use Core\Driver\SQL\Query\CreateProcedure; use Core\Driver\SQL\Query\CreateTable; use Core\Driver\SQL\Query\CreateTrigger; @@ -30,7 +31,9 @@ use Core\Driver\SQL\Query\Delete; use Core\Driver\SQL\Query\Drop; use Core\Driver\SQL\Query\Insert; use Core\Driver\SQL\Query\Query; +use Core\Driver\SQL\Query\RollBack; use Core\Driver\SQL\Query\Select; +use Core\Driver\SQL\Query\StartTransaction; use Core\Driver\SQL\Query\Truncate; use Core\Driver\SQL\Query\Update; use Core\Driver\SQL\Strategy\CascadeStrategy; @@ -99,6 +102,18 @@ abstract class SQL { return new Drop($this, $table); } + public function startTransaction(): bool { + return (new StartTransaction($this))->execute(); + } + + public function commit(): bool { + return (new Commit($this))->execute(); + } + + public function rollback(): bool { + return (new RollBack($this))->execute(); + } + public function alterTable($tableName): AlterTable { return new AlterTable($this, $tableName); } diff --git a/Core/Objects/DatabaseEntity/Route.class.php b/Core/Objects/DatabaseEntity/Route.class.php index 6aa16e4..0f84a63 100644 --- a/Core/Objects/DatabaseEntity/Route.class.php +++ b/Core/Objects/DatabaseEntity/Route.class.php @@ -3,6 +3,7 @@ namespace Core\Objects\DatabaseEntity; use Core\API\Parameter\Parameter; +use Core\Driver\SQL\SQL; use Core\Objects\DatabaseEntity\Attribute\DefaultValue; use Core\Objects\DatabaseEntity\Attribute\ExtendingEnum; use Core\Objects\DatabaseEntity\Attribute\MaxLength; @@ -16,11 +17,16 @@ use Core\Objects\Router\StaticFileRoute; abstract class Route extends DatabaseEntity { const PARAMETER_PATTERN = "/^{([^:]+)(:(.*?)(\?)?)?}$/"; + + const TYPE_DYNAMIC = "dynamic"; + const TYPE_STATIC = "static"; + const TYPE_REDIRECT_PERMANENTLY = "redirect_permanently"; + const TYPE_REDIRECT_TEMPORARY = "redirect_temporary"; const ROUTE_TYPES = [ - "redirect_temporary" => RedirectRoute::class, - "redirect_permanently" => RedirectRoute::class, - "static" => StaticFileRoute::class, - "dynamic" => DocumentRoute::class + self::TYPE_REDIRECT_TEMPORARY => RedirectRoute::class, + self::TYPE_REDIRECT_PERMANENTLY => RedirectRoute::class, + self::TYPE_STATIC => StaticFileRoute::class, + self::TYPE_DYNAMIC => DocumentRoute::class ]; #[MaxLength(128)] @@ -77,6 +83,13 @@ abstract class Route extends DatabaseEntity { public abstract function call(Router $router, array $params): string; + protected function readExtra() { } + + public function postFetch(SQL $sql, array $row) { + parent::postFetch($sql, $row); + $this->readExtra(); + } + protected function getArgs(): array { return [$this->pattern, $this->exact]; } @@ -204,4 +217,28 @@ abstract class Route extends DatabaseEntity { "active" => $this->active, ]; } + + public function setActive(bool $active) { + $this->active = $active; + } + + public function getType(): string { + return $this->type; + } + + public function setPattern(string $pattern) { + $this->pattern = $pattern; + } + + public function setExtra(string $extra) { + $this->extra = $extra; + } + + public function setTarget(string $target) { + $this->target = $target; + } + + public function setExact(bool $exact) { + $this->exact = $exact; + } } \ No newline at end of file diff --git a/Core/Objects/DatabaseEntity/TwoFactorToken.class.php b/Core/Objects/DatabaseEntity/TwoFactorToken.class.php index 9297f93..6382db1 100644 --- a/Core/Objects/DatabaseEntity/TwoFactorToken.class.php +++ b/Core/Objects/DatabaseEntity/TwoFactorToken.class.php @@ -19,7 +19,7 @@ abstract class TwoFactorToken extends DatabaseEntity { #[ExtendingEnum(self::TWO_FACTOR_TOKEN_TYPES)] private string $type; private bool $confirmed; private bool $authenticated; - #[MaxLength(512)] private string $data; + #[MaxLength(512)] private ?string $data; public function __construct(string $type, ?int $id = null, bool $confirmed = false) { parent::__construct($id); @@ -27,6 +27,7 @@ abstract class TwoFactorToken extends DatabaseEntity { $this->type = $type; $this->confirmed = $confirmed; $this->authenticated = $_SESSION["2faAuthenticated"] ?? false; + $this->data = null; } public function jsonSerialize(): array { @@ -63,11 +64,12 @@ abstract class TwoFactorToken extends DatabaseEntity { return $this->confirmed; } - public function getId(): int { - return $this->id; - } - public function isAuthenticated(): bool { return $this->authenticated; } + + public function confirm(SQL $sql): bool { + $this->confirmed = true; + return $this->save($sql) !== false; + } } \ No newline at end of file diff --git a/Core/Objects/DatabaseEntity/User.class.php b/Core/Objects/DatabaseEntity/User.class.php index fd7ff0c..a4ed961 100644 --- a/Core/Objects/DatabaseEntity/User.class.php +++ b/Core/Objects/DatabaseEntity/User.class.php @@ -93,4 +93,8 @@ class User extends DatabaseEntity { $this->lastOnline = new \DateTime(); return $this->save($sql, ["last_online", "language_id"]); } + + public function setTwoFactorToken(TwoFactorToken $twoFactorToken) { + $this->twoFactorToken = $twoFactorToken; + } } \ No newline at end of file diff --git a/Core/Objects/Router/DocumentRoute.class.php b/Core/Objects/Router/DocumentRoute.class.php index 0c667c6..18a9fe6 100644 --- a/Core/Objects/Router/DocumentRoute.class.php +++ b/Core/Objects/Router/DocumentRoute.class.php @@ -24,11 +24,16 @@ class DocumentRoute extends Route { $this->extra = json_encode($args); } - public function postFetch(SQL $sql, array $row) { - parent::postFetch($sql, $row); + protected function readExtra() { + parent::readExtra(); $this->args = json_decode($this->extra); } + public function preInsert(array &$row) { + parent::preInsert($row); + $this->extra = json_encode($this->args); + } + #[Pure] private function getClassName(): string { return $this->getTarget(); } diff --git a/Core/Objects/Router/StaticFileRoute.class.php b/Core/Objects/Router/StaticFileRoute.class.php index b301ff8..80c34e7 100644 --- a/Core/Objects/Router/StaticFileRoute.class.php +++ b/Core/Objects/Router/StaticFileRoute.class.php @@ -22,11 +22,16 @@ class StaticFileRoute extends Route { $this->extra = json_encode($this->code); } - public function postFetch(SQL $sql, array $row) { - parent::postFetch($sql, $row); + protected function readExtra() { + parent::readExtra(); $this->code = json_decode($this->extra); } + public function preInsert(array &$row) { + parent::preInsert($row); + $this->extra = json_encode($this->code); + } + public function call(Router $router, array $params): string { http_response_code($this->code); $this->serveStatic($this->getAbsolutePath(), $router); diff --git a/Core/Objects/TwoFactor/KeyBasedTwoFactorToken.class.php b/Core/Objects/TwoFactor/KeyBasedTwoFactorToken.class.php index 9c522a7..a579b33 100644 --- a/Core/Objects/TwoFactor/KeyBasedTwoFactorToken.class.php +++ b/Core/Objects/TwoFactor/KeyBasedTwoFactorToken.class.php @@ -2,6 +2,7 @@ namespace Core\Objects\TwoFactor; +use Core\Driver\SQL\SQL; use Cose\Algorithm\Signature\ECDSA\ECSignature; use Core\Objects\DatabaseEntity\TwoFactorToken; @@ -13,8 +14,13 @@ class KeyBasedTwoFactorToken extends TwoFactorToken { private ?string $credentialId; private ?PublicKey $publicKey; + public function __construct(string $challenge) { + parent::__construct(self::TYPE); + $this->challenge = $challenge; + } + protected function readData(string $data) { - if ($this->isConfirmed()) { + if (!$this->isConfirmed()) { $this->challenge = base64_decode($data); $this->credentialId = null; $this->publicKey = null; @@ -27,9 +33,23 @@ class KeyBasedTwoFactorToken extends TwoFactorToken { } public function getData(): string { - return $this->challenge; + if ($this->isConfirmed()) { + return base64_encode($this->challenge); + } else { + return json_encode([ + "credentialId" => $this->credentialId, + "publicKey" => $this->publicKey->jsonSerialize() + ]); + } } + public function confirmKeyBased(SQL $sql, string $credentialId, PublicKey $publicKey): bool { + $this->credentialId = $credentialId; + $this->publicKey = $publicKey; + return parent::confirm($sql); + } + + public function getPublicKey(): ?PublicKey { return $this->publicKey; } diff --git a/Core/Objects/TwoFactor/TimeBasedTwoFactorToken.class.php b/Core/Objects/TwoFactor/TimeBasedTwoFactorToken.class.php index c99b882..560018f 100644 --- a/Core/Objects/TwoFactor/TimeBasedTwoFactorToken.class.php +++ b/Core/Objects/TwoFactor/TimeBasedTwoFactorToken.class.php @@ -5,6 +5,7 @@ namespace Core\Objects\TwoFactor; use Base32\Base32; use chillerlan\QRCode\QRCode; use chillerlan\QRCode\QROptions; +use Core\Driver\SQL\SQL; use Core\Objects\Context; use Core\Objects\DatabaseEntity\TwoFactorToken; diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 4782d5b..a3faeb7 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -7,9 +7,10 @@ RUN mkdir -p /application/core/Configuration /var/www/.gnupg && \ # YAML + dev dependencies RUN apt-get update -y && \ - apt-get install -y libyaml-dev libzip-dev libgmp-dev gnupg2 && \ + apt-get install -y libyaml-dev libzip-dev libgmp-dev libpng-dev gnupg2d && \ apt-get clean && \ - pecl install yaml && docker-php-ext-enable yaml + pecl install yaml && docker-php-ext-enable yaml && \ + docker-php-ext-install gd # Runkit (no stable release available) RUN pecl install runkit7-4.0.0a3 && docker-php-ext-enable runkit7 && \