%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;
}
}