2022-06-20 19:52:31 +02:00
|
|
|
<?php
|
|
|
|
|
2022-11-18 18:06:46 +01:00
|
|
|
namespace Core\Objects\DatabaseEntity;
|
2022-06-20 19:52:31 +02:00
|
|
|
|
|
|
|
use DateTime;
|
|
|
|
use Exception;
|
|
|
|
use Firebase\JWT\JWT;
|
2022-11-18 18:06:46 +01:00
|
|
|
use Core\Objects\Context;
|
|
|
|
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
|
|
|
use Core\Objects\DatabaseEntity\Attribute\Json;
|
|
|
|
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
|
|
|
use Core\Objects\DatabaseEntity\Attribute\Transient;
|
2022-11-20 17:13:53 +01:00
|
|
|
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
2022-06-20 19:52:31 +02:00
|
|
|
|
|
|
|
class Session extends DatabaseEntity {
|
|
|
|
|
|
|
|
# in minutes
|
|
|
|
const DURATION = 60 * 60 * 24 * 14;
|
|
|
|
#[Transient] private Context $context;
|
|
|
|
|
|
|
|
private User $user;
|
|
|
|
private DateTime $expires;
|
|
|
|
#[MaxLength(45)] private string $ipAddress;
|
|
|
|
#[DefaultValue(true)] private bool $active;
|
|
|
|
#[MaxLength(64)] private ?string $os;
|
|
|
|
#[MaxLength(64)] private ?string $browser;
|
|
|
|
#[DefaultValue(true)] public bool $stayLoggedIn;
|
|
|
|
#[MaxLength(16)] private string $csrfToken;
|
|
|
|
#[Json] private mixed $data;
|
|
|
|
|
|
|
|
public function __construct(Context $context, User $user, ?string $csrfToken = null) {
|
|
|
|
parent::__construct();
|
|
|
|
$this->context = $context;
|
|
|
|
$this->user = $user;
|
|
|
|
$this->stayLoggedIn = false;
|
|
|
|
$this->csrfToken = $csrfToken ?? generateRandomString(16);
|
|
|
|
$this->expires = (new DateTime())->modify(sprintf("+%d second", Session::DURATION));
|
|
|
|
$this->active = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function init(Context $context, int $userId, int $sessionId): ?Session {
|
|
|
|
$session = Session::find($context->getSQL(), $sessionId, true, true);
|
|
|
|
if (!$session || !$session->active || $session->user->getId() !== $userId) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-03-05 15:30:06 +01:00
|
|
|
if (is_array($session->data)) {
|
|
|
|
foreach ($session->data as $key => $value) {
|
|
|
|
$_SESSION[$key] = $value;
|
|
|
|
if ($key === "2faAuthenticated" && $value === true) {
|
|
|
|
$tfaToken = $session->getUser()->getTwoFactorToken();
|
|
|
|
$tfaToken?->authenticate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-20 19:52:31 +02:00
|
|
|
$session->context = $context;
|
|
|
|
return $session;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getUser(): User {
|
|
|
|
return $this->user;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function updateMetaData() {
|
|
|
|
$this->expires = (new \DateTime())->modify(sprintf("+%d minutes", Session::DURATION));
|
|
|
|
$this->ipAddress = $this->context->isCLI() ? "127.0.0.1" : $_SERVER['REMOTE_ADDR'];
|
|
|
|
try {
|
|
|
|
$userAgent = @get_browser($_SERVER['HTTP_USER_AGENT'], true);
|
|
|
|
$this->os = $userAgent['platform'] ?? "Unknown";
|
|
|
|
$this->browser = $userAgent['parent'] ?? "Unknown";
|
|
|
|
} catch (Exception $ex) {
|
|
|
|
$this->os = "Unknown";
|
|
|
|
$this->browser = "Unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setData(array $data) {
|
2023-03-05 15:30:06 +01:00
|
|
|
$this->data = $data;
|
2022-06-20 19:52:31 +02:00
|
|
|
foreach ($data as $key => $value) {
|
|
|
|
$_SESSION[$key] = $value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getCookie(): string {
|
|
|
|
$this->updateMetaData();
|
|
|
|
$settings = $this->context->getSettings();
|
|
|
|
$token = ['userId' => $this->user->getId(), 'sessionId' => $this->getId()];
|
2022-10-23 21:26:27 +02:00
|
|
|
$jwtPublicKey = $settings->getJwtPublicKey();
|
|
|
|
return JWT::encode($token, $jwtPublicKey->getKeyMaterial(), $jwtPublicKey->getAlgorithm());
|
2022-06-20 19:52:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function sendCookie(string $domain) {
|
|
|
|
$sessionCookie = $this->getCookie();
|
|
|
|
$secure = strcmp(getProtocol(), "https") === 0;
|
|
|
|
setcookie('session', $sessionCookie, $this->getExpiresTime(), "/", $domain, $secure, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getExpiresTime(): int {
|
|
|
|
return ($this->stayLoggedIn ? $this->expires->getTimestamp() : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getExpiresSeconds(): int {
|
|
|
|
return ($this->stayLoggedIn ? $this->expires->getTimestamp() - time() : -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function destroy(): bool {
|
|
|
|
session_destroy();
|
|
|
|
$this->active = false;
|
2022-11-29 14:17:11 +01:00
|
|
|
return $this->save($this->context->getSQL(), ["active"]);
|
2022-06-20 19:52:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function update(): bool {
|
|
|
|
$this->updateMetaData();
|
|
|
|
|
|
|
|
$this->expires = (new DateTime())->modify(sprintf("+%d second", Session::DURATION));
|
|
|
|
$this->data = json_encode($_SESSION ?? []);
|
|
|
|
|
|
|
|
$sql = $this->context->getSQL();
|
|
|
|
return $this->user->update($sql) &&
|
2023-03-05 15:30:06 +01:00
|
|
|
$this->save($sql, ["expires", "data", "os", "browser"]);
|
2022-06-20 19:52:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getCsrfToken(): string {
|
|
|
|
return $this->csrfToken;
|
|
|
|
}
|
|
|
|
}
|