diff --git a/src/Bundle/ChillDocStoreBundle/Controller/WebdavController.php b/src/Bundle/ChillDocStoreBundle/Controller/WebdavController.php
index d289a0131..ef9fd35aa 100644
--- a/src/Bundle/ChillDocStoreBundle/Controller/WebdavController.php
+++ b/src/Bundle/ChillDocStoreBundle/Controller/WebdavController.php
@@ -16,7 +16,6 @@ use Chill\DocStoreBundle\Dav\Response\DavResponse;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
-use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
@@ -43,7 +42,6 @@ final readonly class WebdavController
private \Twig\Environment $engine,
private StoredObjectManagerInterface $storedObjectManager,
private Security $security,
- private EntityManagerInterface $entityManager,
) {
$this->requestAnalyzer = new PropfindRequestAnalyzer();
}
@@ -194,20 +192,6 @@ final readonly class WebdavController
return $response;
}
- #[Route(path: '/dav/{access_token}/get/{uuid}/d', methods: ['PUT'])]
- public function putDocument(StoredObject $storedObject, Request $request): Response
- {
- if (!$this->security->isGranted(StoredObjectRoleEnum::EDIT->value, $storedObject)) {
- throw new AccessDeniedHttpException();
- }
-
- $this->storedObjectManager->write($storedObject, $request->getContent());
-
- $this->entityManager->flush();
-
- return new DavResponse('', Response::HTTP_NO_CONTENT);
- }
-
/**
* @return array{0: array, 1: \DateTimeInterface, 2: string, 3: int} properties, lastModified, etag, length
*/
diff --git a/src/Bundle/ChillDocStoreBundle/Controller/WebdavPutController.php b/src/Bundle/ChillDocStoreBundle/Controller/WebdavPutController.php
new file mode 100644
index 000000000..8333a0f85
--- /dev/null
+++ b/src/Bundle/ChillDocStoreBundle/Controller/WebdavPutController.php
@@ -0,0 +1,71 @@
+security->getUser();
+
+ if (null !== $token = $this->lockTokenParser->parseIfCondition($request)) {
+ // this is a renew of a token. We first perform checks
+ if (true !== $error = $this->lockManager->checkLock($storedObject, $token, $user)) {
+ $e = match ($error) {
+ LockTokenCheckResultEnum::NO_LOCK_FOUND, LockTokenCheckResultEnum::LOCK_TOKEN_DO_NOT_MATCH => new PreconditionFailedHttpException(),
+ LockTokenCheckResultEnum::LOCK_TOKEN_DO_NOT_BELONG_TO_USER => new ConflictHttpException(),
+ };
+
+ throw $e;
+ }
+ } else {
+ if ($this->lockManager->hasLock($storedObject)) {
+ throw new ConflictHttpException();
+ }
+ }
+
+ if (!$this->security->isGranted(StoredObjectRoleEnum::EDIT->value, $storedObject)) {
+ throw new AccessDeniedHttpException();
+ }
+
+ $this->storedObjectManager->write($storedObject, $request->getContent());
+
+ $this->entityManager->flush();
+
+ return new DavResponse('', Response::HTTP_NO_CONTENT);
+ }
+}
diff --git a/src/Bundle/ChillDocStoreBundle/Tests/Controller/WebdavControllerTest.php b/src/Bundle/ChillDocStoreBundle/Tests/Controller/WebdavControllerTest.php
index ec25ff5d6..c0a79f6a2 100644
--- a/src/Bundle/ChillDocStoreBundle/Tests/Controller/WebdavControllerTest.php
+++ b/src/Bundle/ChillDocStoreBundle/Tests/Controller/WebdavControllerTest.php
@@ -47,15 +47,11 @@ class WebdavControllerTest extends KernelTestCase
$storedObjectManager = new MockedStoredObjectManager();
}
- if (null === $entityManager) {
- $entityManager = $this->createMock(EntityManagerInterface::class);
- }
-
$security = $this->prophesize(Security::class);
$security->isGranted(Argument::in(['SEE_AND_EDIT', 'SEE']), Argument::type(StoredObject::class))
->willReturn(true);
- return new WebdavController($this->engine, $storedObjectManager, $security->reveal(), $entityManager);
+ return new WebdavController($this->engine, $storedObjectManager, $security->reveal());
}
private function buildDocument(): StoredObject
@@ -152,6 +148,16 @@ class WebdavControllerTest extends KernelTestCase
/dav/1234/get/716e6688-4579-4938-acf3-c4ab5856803b/d
+
+
+
+
+
+
+
+
+
+
application/vnd.oasis.opendocument.text
@@ -241,6 +247,16 @@ class WebdavControllerTest extends KernelTestCase
/dav/1234/get/716e6688-4579-4938-acf3-c4ab5856803b/d
+
+
+
+
+
+
+
+
+
+
Wed, 13 Sep 2023 14:15:00 +0200
@@ -267,6 +283,16 @@ class WebdavControllerTest extends KernelTestCase
/dav/1234/get/716e6688-4579-4938-acf3-c4ab5856803b/d
+
+
+
+
+
+
+
+
+
+
Wed, 13 Sep 2023 14:15:00 +0200
@@ -390,30 +416,6 @@ class WebdavControllerTest extends KernelTestCase
self::assertEquals('application/vnd.oasis.opendocument.text', $response->headers->get('content-type'));
self::assertEquals(5, $response->headers->get('content-length'));
}
-
- public function testPutDocument(): void
- {
- $document = $this->buildDocument();
- $entityManager = $this->createMock(EntityManagerInterface::class);
- $storedObjectManager = $this->createMock(StoredObjectManagerInterface::class);
-
- // entity manager must be flushed
- $entityManager->expects($this->once())
- ->method('flush');
-
- // object must be written by StoredObjectManager
- $storedObjectManager->expects($this->once())
- ->method('write')
- ->with($this->identicalTo($document), $this->identicalTo('1234'));
-
- $controller = $this->buildController($entityManager, $storedObjectManager);
-
- $request = new Request(content: '1234');
- $response = $controller->putDocument($document, $request);
-
- self::assertEquals(204, $response->getStatusCode());
- self::assertEquals('', $response->getContent());
- }
}
class MockedStoredObjectManager implements StoredObjectManagerInterface
diff --git a/src/Bundle/ChillDocStoreBundle/Tests/Controller/WebdavPutControllerTest.php b/src/Bundle/ChillDocStoreBundle/Tests/Controller/WebdavPutControllerTest.php
new file mode 100644
index 000000000..a58b7cabb
--- /dev/null
+++ b/src/Bundle/ChillDocStoreBundle/Tests/Controller/WebdavPutControllerTest.php
@@ -0,0 +1,236 @@
+registerVersion(type: 'application/vnd.oasis.opendocument.text')
+ ->getStoredObject();
+
+ $reflectionObject = new \ReflectionClass($object);
+ $reflectionObjectUuid = $reflectionObject->getProperty('uuid');
+
+ $reflectionObjectUuid->setValue($object, Uuid::fromString('716e6688-4579-4938-acf3-c4ab5856803b'));
+
+ return $object;
+ }
+
+ private function buildController(EntityManagerInterface $entityManager, StoredObjectManagerInterface $storedObjectManager, LockTokenParser $lockTokenParser, StoredObjectLockManager $storedObjectLockManager, Security $security): WebdavPutController
+ {
+ return new WebdavPutController($storedObjectManager, $security, $entityManager, $lockTokenParser, $storedObjectLockManager);
+ }
+
+ public function testPutDocumentHappyScenario(): void
+ {
+ $user = new User();
+ $document = $this->buildDocument();
+ $entityManager = $this->prophesize(EntityManagerInterface::class);
+
+ // entity manager must be flushed
+ $entityManager->flush()->shouldBeCalled();
+
+ // object must be written by StoredObjectManager
+ $storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class);
+ $storedObjectManager->write($document, '1234')->shouldBeCalled();
+
+ $security = $this->prophesize(Security::class);
+ $security->getUser()->willReturn($user);
+ $security->isGranted(StoredObjectRoleEnum::EDIT->value, $document)
+ ->willReturn(true)->shouldBeCalled();
+
+ $storedObjectLockManager = $this->prophesize(StoredObjectLockManager::class);
+ $storedObjectLockManager->checkLock($document, $token = 'opaquelocktoken:88f391da-2f72-11f1-bf99-67e47d3105b8', $user)
+ ->willReturn(true)->shouldBeCalled();
+ $storedObjectLockManager->hasLock($document)->willReturn(true);
+
+
+ $controller = $this->buildController(
+ $entityManager->reveal(),
+ $storedObjectManager->reveal(),
+ new LockTokenParser(),
+ $storedObjectLockManager->reveal(),
+ $security->reveal()
+ );
+
+ $request = new Request(
+ content: '1234',
+ );
+ $request->headers->set('If', '"(<'.$token.'>)"');
+ $response = $controller->putDocument($document, $request);
+
+ self::assertEquals(204, $response->getStatusCode());
+ self::assertEquals('', $response->getContent());
+ }
+
+ public function testPutDocumentNoLockFound(): void
+ {
+ $user = new User();
+ $document = $this->buildDocument();
+ $entityManager = $this->prophesize(EntityManagerInterface::class);
+ $storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class);
+ $security = $this->prophesize(Security::class);
+ $security->getUser()->willReturn($user);
+
+ $storedObjectLockManager = $this->prophesize(StoredObjectLockManager::class);
+ $storedObjectLockManager->checkLock($document, $token = 'token', $user)
+ ->willReturn(\Chill\DocStoreBundle\Service\Lock\LockTokenCheckResultEnum::NO_LOCK_FOUND);
+
+ $controller = $this->buildController(
+ $entityManager->reveal(),
+ $storedObjectManager->reveal(),
+ new LockTokenParser(),
+ $storedObjectLockManager->reveal(),
+ $security->reveal()
+ );
+
+ $request = new Request();
+ $request->headers->set('If', '"(<'.$token.'>)"');
+
+ $this->expectException(\Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException::class);
+ $controller->putDocument($document, $request);
+ }
+
+ public function testPutDocumentLockTokenDoNotMatch(): void
+ {
+ $user = new User();
+ $document = $this->buildDocument();
+ $entityManager = $this->prophesize(EntityManagerInterface::class);
+ $storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class);
+ $security = $this->prophesize(Security::class);
+ $security->getUser()->willReturn($user);
+
+ $storedObjectLockManager = $this->prophesize(StoredObjectLockManager::class);
+ $storedObjectLockManager->checkLock($document, $token = 'token', $user)
+ ->willReturn(\Chill\DocStoreBundle\Service\Lock\LockTokenCheckResultEnum::LOCK_TOKEN_DO_NOT_MATCH);
+
+ $controller = $this->buildController(
+ $entityManager->reveal(),
+ $storedObjectManager->reveal(),
+ new LockTokenParser(),
+ $storedObjectLockManager->reveal(),
+ $security->reveal()
+ );
+
+ $request = new Request();
+ $request->headers->set('If', '"(<'.$token.'>)"');
+
+ $this->expectException(\Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException::class);
+ $controller->putDocument($document, $request);
+ }
+
+ public function testPutDocumentLockTokenDoNotBelongToUser(): void
+ {
+ $user = new User();
+ $document = $this->buildDocument();
+ $entityManager = $this->prophesize(EntityManagerInterface::class);
+ $storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class);
+ $security = $this->prophesize(Security::class);
+ $security->getUser()->willReturn($user);
+
+ $storedObjectLockManager = $this->prophesize(StoredObjectLockManager::class);
+ $storedObjectLockManager->checkLock($document, $token = 'token', $user)
+ ->willReturn(\Chill\DocStoreBundle\Service\Lock\LockTokenCheckResultEnum::LOCK_TOKEN_DO_NOT_BELONG_TO_USER);
+
+ $controller = $this->buildController(
+ $entityManager->reveal(),
+ $storedObjectManager->reveal(),
+ new LockTokenParser(),
+ $storedObjectLockManager->reveal(),
+ $security->reveal()
+ );
+
+ $request = new Request();
+ $request->headers->set('If', '"(<'.$token.'>)"');
+
+ $this->expectException(\Symfony\Component\HttpKernel\Exception\ConflictHttpException::class);
+ $controller->putDocument($document, $request);
+ }
+
+ public function testPutDocumentNoLockGivenButTokenRequired(): void
+ {
+ $user = new User();
+ $document = $this->buildDocument();
+ $entityManager = $this->prophesize(EntityManagerInterface::class);
+ $storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class);
+ $security = $this->prophesize(Security::class);
+ $security->getUser()->willReturn($user);
+
+ $storedObjectLockManager = $this->prophesize(StoredObjectLockManager::class);
+ $storedObjectLockManager->hasLock($document)->willReturn(true);
+
+ $controller = $this->buildController(
+ $entityManager->reveal(),
+ $storedObjectManager->reveal(),
+ new LockTokenParser(),
+ $storedObjectLockManager->reveal(),
+ $security->reveal()
+ );
+
+ $request = new Request();
+
+ $this->expectException(\Symfony\Component\HttpKernel\Exception\ConflictHttpException::class);
+ $controller->putDocument($document, $request);
+ }
+
+ public function testPutDocumentNotGranted(): void
+ {
+ $user = new User();
+ $document = $this->buildDocument();
+ $entityManager = $this->prophesize(EntityManagerInterface::class);
+ $storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class);
+
+ $security = $this->prophesize(Security::class);
+ $security->getUser()->willReturn($user);
+ $security->isGranted(StoredObjectRoleEnum::EDIT->value, $document)
+ ->willReturn(false);
+
+ $storedObjectLockManager = $this->prophesize(StoredObjectLockManager::class);
+ $storedObjectLockManager->hasLock($document)->willReturn(false);
+
+ $controller = $this->buildController(
+ $entityManager->reveal(),
+ $storedObjectManager->reveal(),
+ new LockTokenParser(),
+ $storedObjectLockManager->reveal(),
+ $security->reveal()
+ );
+
+ $request = new Request();
+
+ $this->expectException(\Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException::class);
+ $controller->putDocument($document, $request);
+ }
+}