%PDF- %PDF-
Direktori : /www/varak.net/nextcloud.varak.net/apps_old/apps/passwords/lib/Services/ |
Current File : //www/varak.net/nextcloud.varak.net/apps_old/apps/passwords/lib/Services/SessionService.php |
<?php /* * @copyright 2023 Passwords App * * @author Marius David Wieschollek * @license AGPL-3.0 * * This file is part of the Passwords App * created by Marius David Wieschollek. */ namespace OCA\Passwords\Services; use Exception; use OCA\Passwords\Db\Session; use OCA\Passwords\Db\SessionMapper; use OCA\Passwords\Encryption\Object\SimpleEncryption; use OCA\Passwords\Helper\Settings\UserSettingsHelper; use OCA\Passwords\Helper\Uuid\UuidHelper; use OCP\AppFramework\Db\DoesNotExistException; use OCP\IRequest; use OCP\ISession; use Throwable; /** * Class SessionService * * @package OCA\Passwords\Services\Object */ class SessionService { const VALUE_USER_SECRET = 'userSecret'; const API_SESSION_HEADER = 'X-API-SESSION'; const API_SESSION_COOKIE = 'nc_passwords'; const SESSION_KEY_ID = 'nc_pw_session'; const SESSION_KEY_PASSPHRASE = 'nc_pw_passphrase'; /** * @var array */ protected array $data = []; /** * @var array */ protected array $shadowVars = []; /** * @var Session|null */ private ?Session $session = null; /** * @var bool */ protected bool $modified = false; /** * @var string|null */ private ?string $encryptedId = null; /** * @var string|null */ private ?string $passphrase = null; /** * SessionService constructor. * * @param IRequest $request * @param ISession $userSession * @param SessionMapper $mapper * @param UuidHelper $uuidHelper * @param LoggingService $logger * @param EnvironmentService $environment * @param SimpleEncryption $encryption * @param UserSettingsHelper $userSettings */ public function __construct( protected IRequest $request, protected ISession $userSession, protected SessionMapper $mapper, protected UuidHelper $uuidHelper, protected LoggingService $logger, protected EnvironmentService $environment, protected SimpleEncryption $encryption, protected UserSettingsHelper $userSettings ) { } /** * @return bool */ public function isModified(): bool { return $this->modified; } /** * @return string|null */ public function getEncryptedId(): ?string { if($this->encryptedId !== null) { return $this->encryptedId; } if($this->session !== null) { $id = $this->session->getUuid().':'.$this->getPassphrase(); try { return $this->encryption->encrypt($id); } catch(Exception $e) { } } return null; } /** * @param string $key * @param null $default * * @return mixed */ public function get(string $key, $default = null) { return $this->has($key) ? $this->data[ $key ]:$default; } /** * @param string $key * @param $value */ public function set(string $key, $value): void { if($this->session === null) $this->load(); $this->modified = true; $this->data[ $key ] = $value; } /** * @param string $key * * @return bool */ public function has(string $key): bool { if($this->session === null) $this->load(); return isset($this->data[ $key ]); } /** * @param string $key */ public function unset(string $key): void { if($this->has($key)) { unset($this->data[ $key ]); $this->modified = true; } } /** * @param $name */ public function addShadow($name): void { if($this->session === null) $this->load(); if(!in_array($name, $this->shadowVars)) { $this->shadowVars[] = $name; $this->modified = true; } } /** * @param $name */ public function removeShadow($name): void { if($this->session === null) $this->load(); if(in_array($name, $this->shadowVars)) { $key = array_search($name, $this->shadowVars); unset($this->shadowVars[ $key ]); $this->modified = true; } } /** * @return bool */ public function isAuthorized(): bool { if($this->session === null) $this->load(); return $this->session->getAuthorized(); } /** * */ public function authorizeSession(): void { if($this->session === null) $this->load(); $this->session->setAuthorized(true); $this->modified = true; } /** * * @throws \OCP\DB\Exception */ public function save(): void { if($this->session === null || (!$this->modified && empty($this->session->getId()))) return; if($this->modified) { $this->encryptSessionData($this->data); $shadowData = []; foreach($this->shadowVars as $name) { $shadowData[ $name ] = $this->userSession->get($name); } $this->encryptSessionData($shadowData, 'shadowData'); } if(empty($this->session->getId())) { $this->session = $this->mapper->insert($this->session); } else { $this->session->setUpdated(time()); $this->session = $this->mapper->update($this->session); } $this->modified = false; if($this->environment->getLoginType() === EnvironmentService::LOGIN_SESSION || $this->environment->getLoginType() === EnvironmentService::LOGIN_PASSWORD) { $this->userSession->set(self::SESSION_KEY_ID, $this->session->getUuid()); $this->userSession->set(self::SESSION_KEY_PASSPHRASE, $this->getPassphrase()); } } /** * @return void * @throws \OCP\DB\Exception */ public function delete(): void { if(!empty($this->session->getId())) { $this->mapper->delete($this->session); } $this->data = []; $this->shadowVars = []; $this->session = $this->create(); $this->encryptedId = null; } /** * */ public function load(): void { if($this->session !== null) return; [$sessionId, $passphrase] = $this->getSessionIdFromRequest(); if(!empty($sessionId)) { try { /** @var Session $session */ $session = $this->mapper->findByUuid($sessionId); if($this->environment->getUserId() !== $session->getUserId() || $this->environment->getLoginType() !== $session->getLoginType()) { $this->mapper->delete($session); $this->logger->error(['Unauthorized session access by %s on %s', $this->environment->getUserId(), $session->getUserId()]); } else if(time() > $session->getUpdated() + $this->userSettings->get('session/lifetime')) { $this->mapper->delete($session); $this->logger->info(['Cancelled expired session %s for %s', $session->getUuid(), $session->getUserId()]); } else { $this->session = $session; $this->passphrase = $passphrase; $this->data = $this->decryptSessionData(); $shadow = $this->decryptSessionData('shadowData'); foreach($shadow as $name => $value) { $this->userSession->set($name, $value); } $this->shadowVars = array_keys($shadow); return; } } catch(DoesNotExistException $e) { $this->logger->info(['Attempt to access expired or nonexistent session %s by %s', $sessionId, $this->environment->getUserId()]); $this->passphrase = null; } catch(Throwable $e) { $this->logger->logException($e); $this->passphrase = null; } } $this->session = $this->create(); } /** * @param array $data * @param string $property */ protected function encryptSessionData(array $data, string $property = 'data'): void { try { $value = $this->encryption->encrypt(json_encode($data), $this->getPassphrase()); $this->session->setProperty($property, $value); } catch(Exception $e) { $this->session->setProperty($property, '[]'); } } /** * @param string $property * * @return array|mixed */ protected function decryptSessionData(string $property = 'data'): mixed { try { return json_decode( $this->encryption->decrypt( $this->session->getProperty($property), $this->passphrase ), true); } catch(Exception $e) { return []; } } /** * @return Session */ protected function create(): Session { $model = new Session(); $model->setLoginType($this->environment->getLoginType()); $model->setUserId($this->environment->getUserId()); $model->setClient($this->environment->getClient()); $model->setUuid($this->uuidHelper->generateUuid()); $model->setAuthorized(false); $model->setDeleted(false); $model->setCreated(time()); $model->setUpdated(time()); $this->modified = true; $this->encryptedId = null; $this->passphrase = null; return $model; } /** * @return string[]|null */ protected function getSessionIdFromRequest(): ?array { $encryptedId = null; if($this->request->getCookie(SessionService::API_SESSION_COOKIE)) { $encryptedId = $this->request->getCookie(SessionService::API_SESSION_COOKIE); } else if(!empty($this->request->getHeader(self::API_SESSION_HEADER))) { $encryptedId = $this->request->getHeader(self::API_SESSION_HEADER); } else if($this->userSession->exists(self::SESSION_KEY_ID)) { return [ $this->userSession->get(self::SESSION_KEY_ID), $this->userSession->get(self::SESSION_KEY_PASSPHRASE) ]; } try { if(!empty($encryptedId)) { $sessionId = $this->encryption->decrypt($encryptedId); if(!empty($sessionId) && str_contains($sessionId, ':')) { $this->encryptedId = $encryptedId; return explode(':', $sessionId, 2); } } } catch(Exception $e) { } return null; } /** * @return string */ protected function getPassphrase(): string { if($this->passphrase === null) { $this->passphrase = $this->uuidHelper->generateUuid(); } return $this->passphrase; } }