generated from Nodarx/template
feat: implement initial read only access
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace KTXM\ServiceWopi\Controllers;
|
||||
|
||||
use KTXC\Context\IdentityContextInterface;
|
||||
use KTXC\Context\TenantContextInterface;
|
||||
use KTXC\Http\Request\Request;
|
||||
use KTXC\Http\Response\JsonResponse;
|
||||
use KTXF\Controller\ControllerAbstract;
|
||||
use KTXF\Routing\Attributes\AuthenticatedRoute;
|
||||
use KTXM\ServiceWopi\Host\HostException;
|
||||
use KTXM\ServiceWopi\Host\WopiService;
|
||||
|
||||
class OfficeController extends ControllerAbstract
|
||||
{
|
||||
public function __construct(private readonly TenantContextInterface $tenant, private readonly IdentityContextInterface $identity, private readonly WopiService $wopi) {}
|
||||
|
||||
#[AuthenticatedRoute('/capabilities', name: 'wopi.capabilities', methods: ['GET'], permissions: ['service_wopi.view'])]
|
||||
public function capabilities(): JsonResponse
|
||||
{
|
||||
return $this->respond(fn () => $this->wopi->capabilities($this->tenant->requireIdentifier(), $this->identity->requireIdentifier()));
|
||||
}
|
||||
|
||||
#[AuthenticatedRoute('/sessions', name: 'wopi.sessions', methods: ['POST'], permissions: ['service_wopi.view'])]
|
||||
public function launch(Request $request): JsonResponse
|
||||
{
|
||||
return $this->respond(function () use ($request): array {
|
||||
$tenant = $this->tenant->requireIdentifier();
|
||||
$config = $this->wopi->configuration($tenant);
|
||||
// Require same-origin browser JSON POST in addition to framework authentication.
|
||||
if ($request->headers->get('Origin') !== $config->origin->origin
|
||||
|| !str_starts_with(strtolower($request->headers->get('Content-Type', '')), 'application/json')) {
|
||||
throw new HostException('Invalid launch origin or content type.', 403);
|
||||
}
|
||||
$data = json_decode($request->getContent(), true, 32, JSON_THROW_ON_ERROR);
|
||||
$resource = $data['resource'] ?? null;
|
||||
if (!is_string($resource) || $resource === '' || strlen($resource) > 2048) {
|
||||
throw new HostException('A document resource is required.');
|
||||
}
|
||||
return $this->wopi->launch($tenant, $this->identity->requireIdentifier(), $resource);
|
||||
});
|
||||
}
|
||||
|
||||
private function respond(callable $operation): JsonResponse
|
||||
{
|
||||
try { return new JsonResponse($operation(), 200, ['Cache-Control' => 'no-store']); }
|
||||
catch (HostException $error) { return new JsonResponse(['message' => $error->getMessage()], $error->status, ['Cache-Control' => 'no-store']); }
|
||||
catch (\JsonException) { return new JsonResponse(['message' => 'Invalid JSON request.'], 400, ['Cache-Control' => 'no-store']); }
|
||||
catch (\Throwable) { return new JsonResponse(['message' => 'Office viewing is temporarily unavailable.'], 503, ['Cache-Control' => 'no-store']); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace KTXM\ServiceWopi\Controllers;
|
||||
|
||||
use KTXC\Context\TenantContextInterface;
|
||||
use KTXC\Http\Request\Request;
|
||||
use KTXC\Http\Response\JsonResponse;
|
||||
use KTXC\Http\Response\Response;
|
||||
use KTXF\Controller\ControllerAbstract;
|
||||
use KTXF\Routing\Attributes\AnonymousPrefixRoute;
|
||||
use KTXF\Routing\Attributes\AnonymousRoute;
|
||||
use KTXM\ServiceWopi\Host\HostException;
|
||||
use KTXM\ServiceWopi\Host\WopiService;
|
||||
|
||||
class WopiController extends ControllerAbstract
|
||||
{
|
||||
public function __construct(private readonly TenantContextInterface $tenant, private readonly WopiService $wopi) {}
|
||||
|
||||
private const HEADERS = ['Cache-Control' => 'no-store', 'X-Content-Type-Options' => 'nosniff'];
|
||||
|
||||
#[AnonymousPrefixRoute('/wopi/files/', name: 'wopi.unsupported', methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'])]
|
||||
public function unsupported(Request $request): Response
|
||||
{
|
||||
return $request->getMethod() === 'GET'
|
||||
? new Response('', 404, self::HEADERS)
|
||||
: new Response('', 405, self::HEADERS + ['Allow' => 'GET']);
|
||||
}
|
||||
|
||||
#[AnonymousRoute('/wopi/files/{id}', name: 'wopi.retrieve_meta')]
|
||||
public function retrieveMeta(Request $request, string $id): Response
|
||||
{
|
||||
return $this->respond($request, $id, static function (array $session, array $file, array $headers): Response {
|
||||
return new JsonResponse([
|
||||
'BaseFileName' => $file['label'], 'Size' => $file['size'], 'Version' => $file['version'],
|
||||
'OwnerId' => $session['user'], 'UserId' => $session['user'],
|
||||
'ReadOnly' => true, 'UserCanWrite' => false, 'SupportsUpdate' => false,
|
||||
'SupportsLocks' => false, 'UserCanNotWriteRelative' => true,
|
||||
], 200, $headers);
|
||||
});
|
||||
}
|
||||
|
||||
#[AnonymousRoute('/wopi/files/{id}/contents', name: 'wopi.retrieve_contents')]
|
||||
public function retrieveContents(Request $request, string $id): Response
|
||||
{
|
||||
return $this->respond($request, $id, static fn (array $session, array $file, array $headers): Response =>
|
||||
new Response($file['content'], 200, $headers + ['Content-Type' => 'application/octet-stream', 'Content-Length' => (string) $file['size']])
|
||||
);
|
||||
}
|
||||
|
||||
private function respond(Request $request, string $id, callable $response): Response
|
||||
{
|
||||
$headers = self::HEADERS;
|
||||
if ($request->getMethod() !== 'GET') { return $this->unsupported($request); }
|
||||
if (!preg_match('/^[a-f0-9]{32}$/D', $id)) { return new Response('', 404, $headers); }
|
||||
try {
|
||||
if (!$this->tenant->enabled()) { throw new HostException('Document access is unavailable.', 404); }
|
||||
['session' => $session, 'file' => $file] = $this->wopi->access($this->tenant->requireIdentifier(), $id, $request);
|
||||
$headers['X-WOPI-ItemVersion'] = $file['version'];
|
||||
return $response($session, $file, $headers);
|
||||
} catch (HostException $error) {
|
||||
return new JsonResponse(['message' => $error->getMessage()], $error->status, $headers);
|
||||
} catch (\Throwable) {
|
||||
// Never log the request, query string, file contents, or access token.
|
||||
return new JsonResponse(['message' => 'Document access is unavailable.'], 503, $headers);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user