1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * For the full copyright and license information, please view |
||||
5 | * the LICENSE file that was distributed with this source code. |
||||
6 | */ |
||||
7 | |||||
8 | declare(strict_types=1); |
||||
9 | |||||
10 | namespace ChampsLibres\WopiBundle\Service; |
||||
11 | |||||
12 | use ChampsLibres\WopiBundle\Contracts\AuthorizationManagerInterface; |
||||
13 | use ChampsLibres\WopiBundle\Contracts\UserManagerInterface; |
||||
14 | use ChampsLibres\WopiBundle\Service\Wopi\PutFile; |
||||
15 | use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface; |
||||
16 | use ChampsLibres\WopiLib\Contract\Service\WopiInterface; |
||||
17 | use DateTimeInterface; |
||||
18 | |||||
19 | use Psr\Http\Message\RequestInterface; |
||||
20 | use Psr\Http\Message\ResponseFactoryInterface; |
||||
21 | |||||
22 | use Psr\Http\Message\ResponseInterface; |
||||
23 | use Psr\Http\Message\StreamFactoryInterface; |
||||
24 | use Psr\Http\Message\UriFactoryInterface; |
||||
25 | use Psr\Log\LoggerInterface; |
||||
26 | use Symfony\Component\HttpFoundation\Response; |
||||
27 | |||||
28 | use Symfony\Component\Routing\RouterInterface; |
||||
29 | |||||
30 | use const PATHINFO_EXTENSION; |
||||
31 | use const PATHINFO_FILENAME; |
||||
32 | |||||
33 | final class Wopi implements WopiInterface |
||||
34 | { |
||||
35 | private const LOG_PREFIX = '[wopi][Wopi] '; |
||||
36 | |||||
37 | private AuthorizationManagerInterface $authorizationManager; |
||||
38 | |||||
39 | private DocumentManagerInterface $documentManager; |
||||
40 | |||||
41 | private LoggerInterface $logger; |
||||
42 | |||||
43 | private PutFile $putFileExecutor; |
||||
44 | |||||
45 | private ResponseFactoryInterface $responseFactory; |
||||
46 | |||||
47 | private RouterInterface $router; |
||||
48 | |||||
49 | private StreamFactoryInterface $streamFactory; |
||||
50 | |||||
51 | private UriFactoryInterface $uriFactory; |
||||
52 | |||||
53 | private UserManagerInterface $userManager; |
||||
54 | |||||
55 | public function __construct( |
||||
56 | AuthorizationManagerInterface $authorizationManager, |
||||
57 | DocumentManagerInterface $documentManager, |
||||
58 | LoggerInterface $logger, |
||||
59 | ResponseFactoryInterface $responseFactory, |
||||
60 | RouterInterface $router, |
||||
61 | StreamFactoryInterface $streamFactory, |
||||
62 | UriFactoryInterface $uriFactory, |
||||
63 | UserManagerInterface $userManager, |
||||
64 | PutFile $putFile |
||||
65 | ) { |
||||
66 | $this->authorizationManager = $authorizationManager; |
||||
67 | $this->documentManager = $documentManager; |
||||
68 | $this->logger = $logger; |
||||
69 | $this->responseFactory = $responseFactory; |
||||
70 | $this->streamFactory = $streamFactory; |
||||
71 | $this->router = $router; |
||||
72 | $this->uriFactory = $uriFactory; |
||||
73 | $this->userManager = $userManager; |
||||
74 | $this->putFileExecutor = $putFile; |
||||
75 | } |
||||
76 | |||||
77 | /** |
||||
78 | * @param array<string, string|boolean|int|null> $overrideProperties |
||||
79 | */ |
||||
80 | public function checkFileInfo(string $fileId, string $accessToken, RequestInterface $request, array $overrideProperties = []): ResponseInterface |
||||
81 | { |
||||
82 | $userIdentifier = $this->userManager->getUserId($accessToken, $fileId, $request); |
||||
83 | |||||
84 | if (null === $userIdentifier && false === $this->userManager->isAnonymousUser($accessToken, $fileId, $request)) { |
||||
85 | $this->logger->error(self::LOG_PREFIX . 'user not found nor anonymous'); |
||||
86 | |||||
87 | return $this->responseFactory |
||||
88 | ->createResponse(404) |
||||
89 | ->withBody($this->streamFactory->createStream((string) json_encode(['message' => 'user not found nor anonymous']))); |
||||
90 | } |
||||
91 | |||||
92 | $document = $this->documentManager->findByDocumentId($fileId); |
||||
93 | |||||
94 | if (null === $document) { |
||||
95 | return $this->makeDocumentNotFoundResponse($fileId); |
||||
96 | } |
||||
97 | |||||
98 | if (!$this->authorizationManager->userCanRead($accessToken, $document, $request)) { |
||||
99 | $this->logger->info(self::LOG_PREFIX . 'user is not allowed to read document', ['fileId' => $fileId, 'userIdentifier' => $userIdentifier]); |
||||
100 | |||||
101 | return $this->responseFactory->createResponse(401)->withBody($this->streamFactory->createStream((string) json_encode([ |
||||
102 | 'message' => 'user is not allowed to see this document', |
||||
103 | ]))); |
||||
104 | } |
||||
105 | |||||
106 | $properties = [ |
||||
107 | 'BaseFileName' => $this->documentManager->getBasename($document), |
||||
108 | 'OwnerId' => 'Symfony', |
||||
109 | 'Size' => $this->documentManager->getSize($document), |
||||
110 | 'UserId' => $userIdentifier, |
||||
111 | 'ReadOnly' => !$this->authorizationManager->userCanWrite($accessToken, $document, $request), |
||||
112 | 'RestrictedWebViewOnly' => $this->authorizationManager->isRestrictedWebViewOnly($accessToken, $document, $request), |
||||
113 | 'UserCanAttend' => $this->authorizationManager->userCanAttend($accessToken, $document, $request), |
||||
114 | 'UserCanPresent' => $this->authorizationManager->userCanPresent($accessToken, $document, $request), |
||||
115 | 'UserCanRename' => $this->authorizationManager->userCanRename($accessToken, $document, $request), |
||||
116 | 'UserCanWrite' => $this->authorizationManager->userCanWrite($accessToken, $document, $request), |
||||
117 | 'UserCanNotWriteRelative' => $this->authorizationManager->userCannotWriteRelative($accessToken, $document, $request), |
||||
118 | 'SupportsUserInfo' => false, |
||||
119 | 'SupportsDeleteFile' => true, |
||||
120 | 'SupportsLocks' => true, |
||||
121 | 'SupportsGetLock' => true, |
||||
122 | 'SupportsExtendedLockLength' => true, |
||||
123 | 'SupportsUpdate' => true, |
||||
124 | 'SupportsRename' => true, |
||||
125 | 'SupportsFolders' => false, |
||||
126 | 'UserFriendlyName' => $this->userManager->getUserFriendlyName($accessToken, $fileId, $request), |
||||
127 | 'DisablePrint' => false, |
||||
128 | 'AllowExternalMarketplace' => false, |
||||
129 | 'SupportedShareUrlTypes' => [ |
||||
130 | 'ReadOnly', |
||||
131 | ], |
||||
132 | 'SHA256' => $this->documentManager->getSha256($document), |
||||
133 | 'LastModifiedTime' => $this->documentManager->getLastModifiedDate($document) |
||||
134 | ->format(DateTimeInterface::ATOM), |
||||
135 | ]; |
||||
136 | |||||
137 | return $this |
||||
138 | ->responseFactory |
||||
139 | ->createResponse() |
||||
140 | ->withHeader('Content-Type', 'application/json') |
||||
141 | ->withBody($this->streamFactory->createStream((string) json_encode(array_merge($properties, $overrideProperties)))); |
||||
142 | } |
||||
143 | |||||
144 | public function deleteFile(string $fileId, string $accessToken, RequestInterface $request): ResponseInterface |
||||
145 | { |
||||
146 | $document = $this->documentManager->findByDocumentId($fileId); |
||||
147 | |||||
148 | if (null === $document) { |
||||
149 | return $this->makeDocumentNotFoundResponse($fileId); |
||||
150 | } |
||||
151 | |||||
152 | if ( |
||||
153 | false === $this->authorizationManager->userCanDelete($accessToken, $document, $request) |
||||
154 | || false === $this->authorizationManager->userCanWrite($accessToken, $document, $request) |
||||
155 | ) { |
||||
156 | $this->logger->info( |
||||
157 | self::LOG_PREFIX . 'user is not authorized to delete file', |
||||
158 | ['fileId' => $fileId, 'userId' => $this->userManager->getUserId($accessToken, $fileId, $request)] |
||||
159 | ); |
||||
160 | |||||
161 | return $this->responseFactory |
||||
162 | ->createResponse(401); |
||||
163 | } |
||||
164 | |||||
165 | $this->documentManager->remove($document); |
||||
166 | |||||
167 | return $this |
||||
168 | ->responseFactory |
||||
169 | ->createResponse(200); |
||||
170 | } |
||||
171 | |||||
172 | public function enumerateAncestors( |
||||
173 | string $fileId, |
||||
174 | string $accessToken, |
||||
175 | RequestInterface $request |
||||
176 | ): ResponseInterface { |
||||
177 | return $this |
||||
178 | ->responseFactory |
||||
179 | ->createResponse(501); |
||||
180 | } |
||||
181 | |||||
182 | public function getFile( |
||||
183 | string $fileId, |
||||
184 | string $accessToken, |
||||
185 | RequestInterface $request |
||||
186 | ): ResponseInterface { |
||||
187 | $document = $this->documentManager->findByDocumentId($fileId); |
||||
188 | |||||
189 | if (null === $document) { |
||||
190 | return $this->makeDocumentNotFoundResponse($fileId); |
||||
191 | } |
||||
192 | |||||
193 | if (!$this->authorizationManager->userCanRead($accessToken, $document, $request)) { |
||||
194 | $userIdentifier = $this->userManager->getUserId($accessToken, $fileId, $request); |
||||
195 | $this->logger->info(self::LOG_PREFIX . 'user is not allowed to read document', ['fileId' => $fileId, 'userIdentifier' => $userIdentifier]); |
||||
196 | |||||
197 | return $this->responseFactory->createResponse(401)->withBody($this->streamFactory->createStream((string) json_encode([ |
||||
198 | 'message' => 'user is not allowed to see this document', |
||||
199 | ]))); |
||||
200 | } |
||||
201 | |||||
202 | $revision = $this->documentManager->getVersion($document); |
||||
203 | $content = $this->documentManager->read($document); |
||||
204 | |||||
205 | return $this |
||||
206 | ->responseFactory |
||||
207 | ->createResponse() |
||||
208 | ->withHeader( |
||||
209 | WopiInterface::HEADER_ITEM_VERSION, |
||||
210 | sprintf('v%s', $revision) |
||||
211 | ) |
||||
212 | ->withHeader( |
||||
213 | 'Content-Type', |
||||
214 | 'application/octet-stream', |
||||
215 | ) |
||||
216 | ->withHeader( |
||||
217 | 'Content-Length', |
||||
218 | (string) $this->documentManager->getSize($document) |
||||
219 | ) |
||||
220 | ->withHeader( |
||||
221 | 'Content-Disposition', |
||||
222 | sprintf('attachment; filename=%s', $this->documentManager->getBasename($document)) |
||||
223 | ) |
||||
224 | ->withBody($content); |
||||
225 | } |
||||
226 | |||||
227 | public function getLock(string $fileId, string $accessToken, RequestInterface $request): ResponseInterface |
||||
228 | { |
||||
229 | $document = $this->documentManager->findByDocumentId($fileId); |
||||
230 | |||||
231 | if (null === $document) { |
||||
232 | return $this->makeDocumentNotFoundResponse($fileId); |
||||
233 | } |
||||
234 | |||||
235 | if (!$this->authorizationManager->isTokenValid($accessToken, $document, $request)) { |
||||
236 | $this->logger->info(self::LOG_PREFIX . 'invalid access token', ['fileId' => $fileId]); |
||||
237 | |||||
238 | return $this->responseFactory->createResponse(401)->withBody($this->streamFactory->createStream((string) json_encode([ |
||||
239 | 'message' => 'invalid access token', |
||||
240 | ]))); |
||||
241 | } |
||||
242 | |||||
243 | if ($this->documentManager->hasLock($document)) { |
||||
244 | return $this |
||||
245 | ->responseFactory |
||||
246 | ->createResponse() |
||||
247 | ->withHeader(WopiInterface::HEADER_LOCK, $this->documentManager->getLock($document)); |
||||
248 | } |
||||
249 | |||||
250 | return $this |
||||
251 | ->responseFactory |
||||
252 | ->createResponse(404) |
||||
253 | ->withHeader(WopiInterface::HEADER_LOCK, ''); |
||||
254 | } |
||||
255 | |||||
256 | public function getShareUrl(string $fileId, string $accessToken, RequestInterface $request): ResponseInterface |
||||
257 | { |
||||
258 | return $this |
||||
259 | ->responseFactory |
||||
260 | ->createResponse(501); |
||||
261 | } |
||||
262 | |||||
263 | public function lock( |
||||
264 | string $fileId, |
||||
265 | string $accessToken, |
||||
266 | string $xWopiLock, |
||||
267 | RequestInterface $request |
||||
268 | ): ResponseInterface { |
||||
269 | $document = $this->documentManager->findByDocumentId($fileId); |
||||
270 | |||||
271 | if (null === $document) { |
||||
272 | return $this->makeDocumentNotFoundResponse($fileId); |
||||
273 | } |
||||
274 | |||||
275 | if (!$this->authorizationManager->isTokenValid($accessToken, $document, $request)) { |
||||
276 | $this->logger->info(self::LOG_PREFIX . 'invalid access token', ['fileId' => $fileId]); |
||||
277 | |||||
278 | return $this->responseFactory->createResponse(401)->withBody($this->streamFactory->createStream((string) json_encode([ |
||||
279 | 'message' => 'invalid access token', |
||||
280 | ]))); |
||||
281 | } |
||||
282 | |||||
283 | $version = $this->documentManager->getVersion($document); |
||||
284 | |||||
285 | if ($this->documentManager->hasLock($document)) { |
||||
286 | if ($xWopiLock === $currentLock = $this->documentManager->getLock($document)) { |
||||
287 | return $this->refreshLock($fileId, $accessToken, $xWopiLock, $request); |
||||
288 | } |
||||
289 | |||||
290 | return $this |
||||
291 | ->responseFactory |
||||
292 | ->createResponse(409) |
||||
293 | ->withHeader(WopiInterface::HEADER_LOCK, $currentLock) |
||||
294 | ->withHeader( |
||||
295 | WopiInterface::HEADER_ITEM_VERSION, |
||||
296 | sprintf('v%s', $version) |
||||
297 | ); |
||||
298 | } |
||||
299 | |||||
300 | $this->documentManager->lock($document, $xWopiLock); |
||||
301 | |||||
302 | return $this |
||||
303 | ->responseFactory |
||||
304 | ->createResponse() |
||||
305 | ->withHeader( |
||||
306 | WopiInterface::HEADER_ITEM_VERSION, |
||||
307 | sprintf('v%s', $version) |
||||
308 | ); |
||||
309 | } |
||||
310 | |||||
311 | public function putFile( |
||||
312 | string $fileId, |
||||
313 | string $accessToken, |
||||
314 | string $xWopiLock, |
||||
315 | string $xWopiEditors, |
||||
316 | RequestInterface $request |
||||
317 | ): ResponseInterface { |
||||
318 | return ($this->putFileExecutor)($fileId, $accessToken, $xWopiLock, $xWopiEditors, $request); |
||||
319 | } |
||||
320 | |||||
321 | public function putRelativeFile( |
||||
322 | string $fileId, |
||||
323 | string $accessToken, |
||||
324 | ?string $suggestedTarget, |
||||
325 | ?string $relativeTarget, |
||||
326 | bool $overwriteRelativeTarget, |
||||
327 | int $size, |
||||
328 | RequestInterface $request |
||||
329 | ): ResponseInterface { |
||||
330 | if ((null === $suggestedTarget) && (null === $relativeTarget)) { |
||||
331 | return $this |
||||
332 | ->responseFactory |
||||
333 | ->createResponse(400) |
||||
334 | ->withBody($this->streamFactory->createStream((string) json_encode([ |
||||
335 | 'message' => 'target is null', |
||||
336 | ]))); |
||||
337 | } |
||||
338 | |||||
339 | if (null !== $suggestedTarget) { |
||||
340 | // If it starts with a dot... |
||||
341 | if (0 === strpos($suggestedTarget, '.', 0)) { |
||||
342 | $document = $this->documentManager->findByDocumentId($fileId); |
||||
343 | |||||
344 | if (null === $document) { |
||||
345 | return $this->makeDocumentNotFoundResponse($fileId); |
||||
346 | } |
||||
347 | $filename = pathinfo($this->documentManager->getBasename($document), PATHINFO_EXTENSION | PATHINFO_FILENAME); |
||||
348 | |||||
349 | $suggestedTarget = sprintf('%s%s', $filename, $suggestedTarget); |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
350 | } |
||||
351 | |||||
352 | $target = $suggestedTarget; |
||||
353 | } else { |
||||
354 | $document = $this->documentManager->findByDocumentFilename($relativeTarget); |
||||
0 ignored issues
–
show
It seems like
$relativeTarget can also be of type null ; however, parameter $documentFilename of ChampsLibres\WopiLib\Con...indByDocumentFilename() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
355 | |||||
356 | /** |
||||
357 | * If a file with the specified name already exists, |
||||
358 | * the host must respond with a 409 Conflict, |
||||
359 | * unless the X-WOPI-OverwriteRelativeTarget request header is set to true. |
||||
360 | * |
||||
361 | * When responding with a 409 Conflict for this reason, |
||||
362 | * the host may include an X-WOPI-ValidRelativeTarget specifying a file name that is valid. |
||||
363 | * |
||||
364 | * If the X-WOPI-OverwriteRelativeTarget request header is set to true |
||||
365 | * and a file with the specified name already exists and is locked, |
||||
366 | * the host must respond with a 409 Conflict and include an |
||||
367 | * X-WOPI-Lock response header containing the value of the current lock on the file. |
||||
368 | */ |
||||
369 | if (null !== $document) { |
||||
370 | if (false === $overwriteRelativeTarget) { |
||||
371 | $extension = pathinfo($this->documentManager->getBasename($document), PATHINFO_EXTENSION); |
||||
372 | |||||
373 | return $this |
||||
374 | ->responseFactory |
||||
375 | ->createResponse(409) |
||||
376 | ->withHeader('Content-Type', 'application/json') |
||||
377 | ->withHeader( |
||||
378 | WopiInterface::HEADER_VALID_RELATIVE_TARGET, |
||||
379 | sprintf('%s.%s', uniqid(), $extension) |
||||
380 | ); |
||||
381 | } |
||||
382 | |||||
383 | if ($this->documentManager->hasLock($document)) { |
||||
384 | return $this |
||||
385 | ->responseFactory |
||||
386 | ->createResponse(409) |
||||
387 | ->withHeader(WopiInterface::HEADER_LOCK, $this->documentManager->getLock($document)); |
||||
388 | } |
||||
389 | } |
||||
390 | |||||
391 | $target = $relativeTarget; |
||||
392 | } |
||||
393 | |||||
394 | /** @var array{filename: string, extension: string} $pathInfo */ |
||||
395 | $pathInfo = pathinfo($target, PATHINFO_EXTENSION | PATHINFO_FILENAME); |
||||
0 ignored issues
–
show
It seems like
$target can also be of type null ; however, parameter $path of pathinfo() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
396 | |||||
397 | $new = $this->documentManager->create([ |
||||
398 | 'basename' => $target, |
||||
399 | 'name' => $pathInfo['filename'], |
||||
400 | 'extension' => $pathInfo['extension'], |
||||
401 | 'content' => (string) $request->getBody(), |
||||
402 | 'size' => $request->getHeaderLine(WopiInterface::HEADER_SIZE), |
||||
403 | ]); |
||||
404 | |||||
405 | $this->documentManager->write($new); |
||||
406 | |||||
407 | $uri = $this |
||||
408 | ->uriFactory |
||||
409 | ->createUri( |
||||
410 | $this |
||||
411 | ->router |
||||
412 | ->generate( |
||||
413 | 'checkFileInfo', |
||||
414 | [ |
||||
415 | 'fileId' => $this->documentManager->getDocumentId($new), |
||||
416 | ], |
||||
417 | RouterInterface::ABSOLUTE_URL |
||||
418 | ) |
||||
419 | ) |
||||
420 | ->withQuery(http_build_query([ |
||||
421 | 'access_token' => $accessToken, |
||||
422 | ])); |
||||
423 | |||||
424 | $properties = [ |
||||
425 | 'Name' => $this->documentManager->getBasename($new), |
||||
426 | 'Url' => (string) $uri, |
||||
427 | 'HostEditUrl' => $this->documentManager->getDocumentId($new), |
||||
428 | 'HostViewUrl' => $this->documentManager->getDocumentId($new), |
||||
429 | ]; |
||||
430 | |||||
431 | return $this |
||||
432 | ->responseFactory |
||||
433 | ->createResponse() |
||||
434 | ->withHeader('Content-Type', 'application/json') |
||||
435 | ->withBody($this->streamFactory->createStream((string) json_encode($properties))); |
||||
436 | } |
||||
437 | |||||
438 | public function putUserInfo(string $fileId, string $accessToken, RequestInterface $request): ResponseInterface |
||||
439 | { |
||||
440 | $this->logger->warning(self::LOG_PREFIX . 'user info called, but not implemented'); |
||||
441 | |||||
442 | return $this->responseFactory->createResponse(501) |
||||
443 | ->withBody($this->streamFactory->createStream((string) json_encode([ |
||||
444 | 'message' => 'User info not implemented', |
||||
445 | ]))); |
||||
446 | } |
||||
447 | |||||
448 | public function refreshLock( |
||||
449 | string $fileId, |
||||
450 | string $accessToken, |
||||
451 | string $xWopiLock, |
||||
452 | RequestInterface $request |
||||
453 | ): ResponseInterface { |
||||
454 | // note: the validation of access token is done inside unlock and lock methods |
||||
455 | $this->unlock($fileId, $accessToken, $xWopiLock, $request); |
||||
456 | |||||
457 | return $this->lock($fileId, $accessToken, $xWopiLock, $request); |
||||
458 | } |
||||
459 | |||||
460 | public function renameFile( |
||||
461 | string $fileId, |
||||
462 | string $accessToken, |
||||
463 | string $xWopiLock, |
||||
464 | string $xWopiRequestedName, |
||||
465 | RequestInterface $request |
||||
466 | ): ResponseInterface { |
||||
467 | $document = $this->documentManager->findByDocumentId($fileId); |
||||
468 | |||||
469 | if (null === $document) { |
||||
470 | return $this->makeDocumentNotFoundResponse($fileId); |
||||
471 | } |
||||
472 | |||||
473 | if (!$this->authorizationManager->userCanRename($accessToken, $document, $request)) { |
||||
474 | $userIdentifier = $this->userManager->getUserId($accessToken, $fileId, $request); |
||||
475 | $this->logger->info(self::LOG_PREFIX . 'user is not allowed to rename', ['fileId' => $fileId, 'userIdentifier' => $userIdentifier]); |
||||
476 | |||||
477 | return $this->responseFactory->createResponse(401)->withBody($this->streamFactory->createStream((string) json_encode([ |
||||
478 | 'message' => 'user is not allowed to rename', |
||||
479 | ]))); |
||||
480 | } |
||||
481 | |||||
482 | if ($this->documentManager->hasLock($document)) { |
||||
483 | if ($xWopiLock !== $currentLock = $this->documentManager->getLock($document)) { |
||||
484 | return $this |
||||
485 | ->responseFactory |
||||
486 | ->createResponse(409) |
||||
487 | ->withHeader(WopiInterface::HEADER_LOCK, $currentLock); |
||||
488 | } |
||||
489 | } |
||||
490 | |||||
491 | $this->documentManager->rename($document, $xWopiRequestedName); |
||||
492 | |||||
493 | $data = [ |
||||
494 | 'Name' => $xWopiRequestedName, |
||||
495 | ]; |
||||
496 | |||||
497 | return $this |
||||
498 | ->responseFactory |
||||
499 | ->createResponse(200) |
||||
500 | ->withHeader('Content-Type', 'application/json') |
||||
501 | ->withBody( |
||||
502 | $this->streamFactory->createStream((string) json_encode($data)) |
||||
503 | ); |
||||
504 | } |
||||
505 | |||||
506 | public function unlock( |
||||
507 | string $fileId, |
||||
508 | string $accessToken, |
||||
509 | string $xWopiLock, |
||||
510 | RequestInterface $request |
||||
511 | ): ResponseInterface { |
||||
512 | $document = $this->documentManager->findByDocumentId($fileId); |
||||
513 | |||||
514 | if (null === $document) { |
||||
515 | return $this->makeDocumentNotFoundResponse($fileId); |
||||
516 | } |
||||
517 | |||||
518 | if (!$this->authorizationManager->isTokenValid($accessToken, $document, $request)) { |
||||
519 | $this->logger->info(self::LOG_PREFIX . 'invalid access token', ['fileId' => $fileId]); |
||||
520 | |||||
521 | return $this->responseFactory->createResponse(401)->withBody($this->streamFactory->createStream((string) json_encode([ |
||||
522 | 'message' => 'invalid access token', |
||||
523 | ]))); |
||||
524 | } |
||||
525 | |||||
526 | $version = $this->documentManager->getVersion($document); |
||||
527 | |||||
528 | if (!$this->documentManager->hasLock($document)) { |
||||
529 | return $this |
||||
530 | ->responseFactory |
||||
531 | ->createResponse(409) |
||||
532 | ->withHeader(WopiInterface::HEADER_LOCK, ''); |
||||
533 | } |
||||
534 | |||||
535 | $currentLock = $this->documentManager->getLock($document); |
||||
536 | |||||
537 | if ($currentLock !== $xWopiLock) { |
||||
538 | return $this |
||||
539 | ->responseFactory |
||||
540 | ->createResponse(409) |
||||
541 | ->withHeader(WopiInterface::HEADER_LOCK, $currentLock); |
||||
542 | } |
||||
543 | |||||
544 | $this->documentManager->deleteLock($document); |
||||
545 | |||||
546 | return $this |
||||
547 | ->responseFactory |
||||
548 | ->createResponse() |
||||
549 | ->withHeader(WopiInterface::HEADER_LOCK, '') |
||||
550 | ->withHeader( |
||||
551 | WopiInterface::HEADER_ITEM_VERSION, |
||||
552 | sprintf('v%s', $version) |
||||
553 | ); |
||||
554 | } |
||||
555 | |||||
556 | public function unlockAndRelock( |
||||
557 | string $fileId, |
||||
558 | string $accessToken, |
||||
559 | string $xWopiLock, |
||||
560 | string $xWopiOldLock, |
||||
561 | RequestInterface $request |
||||
562 | ): ResponseInterface { |
||||
563 | $this->unlock($fileId, $accessToken, $xWopiOldLock, $request); |
||||
564 | |||||
565 | return $this->lock($fileId, $accessToken, $xWopiLock, $request); |
||||
566 | } |
||||
567 | |||||
568 | private function makeDocumentNotFoundResponse(string $fileId): ResponseInterface |
||||
569 | { |
||||
570 | $this->logger->error(self::LOG_PREFIX . 'Document not found', ['fileId' => $fileId]); |
||||
571 | |||||
572 | return $this->responseFactory->createResponse(404) |
||||
573 | ->withBody($this->streamFactory->createStream((string) json_encode([ |
||||
574 | 'message' => "Document with id {$fileId} not found", |
||||
575 | ]))); |
||||
576 | } |
||||
577 | } |
||||
578 |