180 lines
5.2 KiB
PHP
180 lines
5.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
namespace KTXM\ProviderJmapc\Stores;
|
|
|
|
use KTXC\Db\DataStore;
|
|
use KTXF\Security\Crypto;
|
|
use KTXF\Utile\UUID;
|
|
use KTXM\ProviderJmapc\Providers\Mail\Service;
|
|
|
|
/**
|
|
* JMAP Service Store
|
|
*
|
|
* Shared by Mail, Calendar, and Contacts providers.
|
|
*/
|
|
class ServiceStore
|
|
{
|
|
protected const COLLECTION_NAME = 'provider_jmapc_services';
|
|
|
|
public function __construct(
|
|
protected readonly DataStore $dataStore,
|
|
protected readonly Crypto $crypto,
|
|
) {}
|
|
|
|
/**
|
|
* List services for a tenant and user, optionally filtered by service IDs
|
|
*/
|
|
public function list(string $tenantId, string $userId, ?array $filter = null): array
|
|
{
|
|
$filterCondition = [
|
|
'tid' => $tenantId,
|
|
'uid' => $userId,
|
|
];
|
|
|
|
if ($filter !== null && !empty($filter)) {
|
|
$filterCondition['sid'] = ['$in' => $filter];
|
|
}
|
|
|
|
$cursor = $this->dataStore->selectCollection(self::COLLECTION_NAME)->find($filterCondition);
|
|
|
|
$list = [];
|
|
foreach ($cursor as $entry) {
|
|
|
|
if (isset($entry['identity']['secret'])) {
|
|
$entry['identity']['secret'] = $this->crypto->decrypt($entry['identity']['secret']);
|
|
}
|
|
|
|
$list[$entry['sid']] = $entry;
|
|
}
|
|
return $list;
|
|
}
|
|
|
|
/**
|
|
* Check existence of services by IDs for a tenant and user
|
|
*/
|
|
public function extant(string $tenantId, string $userId, array $identifiers): array
|
|
{
|
|
if (empty($identifiers)) {
|
|
return [];
|
|
}
|
|
|
|
$cursor = $this->dataStore->selectCollection(self::COLLECTION_NAME)->find(
|
|
[
|
|
'tid' => $tenantId,
|
|
'uid' => $userId,
|
|
'sid' => ['$in' => array_map('strval', $identifiers)]
|
|
],
|
|
['projection' => ['sid' => 1]]
|
|
);
|
|
|
|
$existingIds = [];
|
|
foreach ($cursor as $document) {
|
|
$existingIds[] = $document['sid'];
|
|
}
|
|
|
|
// Build result map: all identifiers default to false, existing ones set to true
|
|
$result = [];
|
|
foreach ($identifiers as $id) {
|
|
$result[(string) $id] = in_array((string) $id, $existingIds, true);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Retrieve a single service by ID
|
|
*/
|
|
public function fetch(string $tenantId, string $userId, string|int $serviceId): ?Service
|
|
{
|
|
$document = $this->dataStore->selectCollection(self::COLLECTION_NAME)->findOne([
|
|
'tid' => $tenantId,
|
|
'uid' => $userId,
|
|
'sid' => (string)$serviceId,
|
|
]);
|
|
|
|
if (!$document) {
|
|
return null;
|
|
}
|
|
|
|
if (isset($document['identity']['secret'])) {
|
|
$document['identity']['secret'] = $this->crypto->decrypt($document['identity']['secret']);
|
|
}
|
|
|
|
return (new Service())->fromStore($document);
|
|
}
|
|
|
|
/**
|
|
* Create a new service
|
|
*/
|
|
public function create(string $tenantId, string $userId, Service $service): Service
|
|
{
|
|
$document = $service->toStore();
|
|
|
|
// prepare document for insertion
|
|
$document['tid'] = $tenantId;
|
|
$document['uid'] = $userId;
|
|
$document['sid'] = UUID::v4();
|
|
$document['createdOn'] = new \MongoDB\BSON\UTCDateTime();
|
|
$document['modifiedOn'] = new \MongoDB\BSON\UTCDateTime();
|
|
if (isset($document['identity']['secret'])) {
|
|
$document['identity']['secret'] = $this->crypto->encrypt($document['identity']['secret']);
|
|
}
|
|
|
|
$result = $this->dataStore->selectCollection(self::COLLECTION_NAME)->insertOne($document);
|
|
|
|
return (new Service())->fromStore($document);
|
|
}
|
|
|
|
/**
|
|
* Modify an existing service
|
|
*/
|
|
public function modify(string $tenantId, string $userId, Service $service): Service
|
|
{
|
|
$serviceId = $service->id();
|
|
if (empty($serviceId)) {
|
|
throw new \InvalidArgumentException('Service ID is required for update');
|
|
}
|
|
|
|
// prepare document for modification
|
|
$document = $service->toStore();
|
|
$document['modifiedOn'] = new \MongoDB\BSON\UTCDateTime();
|
|
if (isset($document['identity']['secret'])) {
|
|
$document['identity']['secret'] = $this->crypto->encrypt($document['identity']['secret']);
|
|
}
|
|
unset($document['sid'], $document['tid'], $document['uid'], $document['createdOn']);
|
|
|
|
$this->dataStore->selectCollection(self::COLLECTION_NAME)->updateOne(
|
|
[
|
|
'tid' => $tenantId,
|
|
'uid' => $userId,
|
|
'sid' => (string)$serviceId,
|
|
],
|
|
['$set' => $document]
|
|
);
|
|
|
|
return (new Service())->fromStore($document);
|
|
}
|
|
|
|
/**
|
|
* Delete a service
|
|
*/
|
|
public function delete(string $tenantId, string $userId, string|int $serviceId): bool
|
|
{
|
|
$result = $this->dataStore->selectCollection(self::COLLECTION_NAME)->deleteOne([
|
|
'tid' => $tenantId,
|
|
'uid' => $userId,
|
|
'sid' => (string)$serviceId,
|
|
]);
|
|
|
|
return $result->getDeletedCount() > 0;
|
|
}
|
|
|
|
}
|