feat: implement initial read only access

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-09-07 21:58:35 -04:00
parent 661e0e1213
commit 92e85aed47
17 changed files with 863 additions and 4 deletions
+57
View File
@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace KTXM\ServiceWopi\Host;
use KTXC\Service\TenantService;
use KTXC\Stores\UserAccountsStore;
use KTXF\Resource\Identifier\EntityIdentifier;
use KTXF\Resource\Identifier\ResourceIdentifier;
use KTXM\DocumentsManager\Manager;
use KTXM\ProviderLocalDocuments\Providers\Personal\PersonalService;
class Documents
{
public function __construct(private readonly Manager $manager, private readonly UserAccountsStore $users, private readonly TenantService $tenants) {}
public function authorize(string $tenant, string $user): void
{
$account = $this->users->fetchByIdentifier($tenant, $user);
if (!$this->tenants->fetchById($tenant)?->getEnabled() || !($account['enabled'] ?? false)) {
throw new HostException('Document access is unavailable.', 404);
}
$roles = (array) ($account['roles'] ?? []);
$permissions = (array) ($account['permissions'] ?? []);
if (!array_intersect(['admin', 'system.admin'], $roles)
&& !array_intersect(['*', 'service_wopi.*', 'service_wopi.view'], $permissions)) {
throw new HostException('Document access is unavailable.', 404);
}
}
/** @return array{label: string, content: string, version: string, size: int} */
public function read(string $tenant, string $user, string $resource, int $maxBytes): array
{
$this->authorize($tenant, $user);
$id = ResourceIdentifier::fromString($resource);
if (!$id instanceof EntityIdentifier || $id->provider() !== 'default' || $id->service() !== 'personal') {
throw new HostException('This storage provider is not available for office viewing.', 404);
}
$service = $this->manager->serviceFetch($tenant, $user, $id->provider(), $id->service());
if (!$service instanceof PersonalService || !$service->getEnabled()) {
throw new HostException('Document access is unavailable.', 404);
}
$entity = $this->manager->entityFetchBulk($tenant, $user, $id)[$resource] ?? null;
if ($entity === null) { throw new HostException('Document access is unavailable.', 404); }
if ($entity->getProperties()->size() > $maxBytes) { throw new HostException('Document exceeds the office viewing size limit.', 413); }
$stream = $service->entityReadStream($id);
if (!is_resource($stream)) { throw new HostException('Document access is unavailable.', 404); }
try {
$content = stream_get_contents($stream, $maxBytes + 1);
} finally {
fclose($stream);
}
if ($content === false) { throw new HostException('Could not read the document.', 503); }
if (strlen($content) > $maxBytes) { throw new HostException('Document exceeds the office viewing size limit.', 413); }
$label = $entity->getProperties()->getLabel();
return ['label' => $label, 'content' => $content, 'version' => hash('sha256', $label . "\0" . $content), 'size' => strlen($content)];
}
}