Refactor backend for getting signed url

This commit is contained in:
2024-08-28 18:00:20 +02:00
parent 7ab52ff09e
commit 00cc3b7806
7 changed files with 345 additions and 135 deletions

View File

@@ -15,7 +15,7 @@ use Chill\DocStoreBundle\AsyncUpload\SignedUrl;
use Chill\DocStoreBundle\AsyncUpload\SignedUrlPost;
use Chill\DocStoreBundle\AsyncUpload\TempUrlGeneratorInterface;
use Chill\DocStoreBundle\Controller\AsyncUploadController;
use Chill\DocStoreBundle\Security\Authorization\AsyncUploadVoter;
use Chill\DocStoreBundle\Entity\StoredObject;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
@@ -34,46 +34,165 @@ class AsyncUploadControllerTest extends TestCase
{
use ProphecyTrait;
public function testGenerateWhenUserIsNotGranted(): void
public function testGetSignedUrlPost(): void
{
$this->expectException(AccessDeniedHttpException::class);
$controller = $this->buildAsyncUploadController(false);
$storedObject = new StoredObject();
$security = $this->prophesize(Security::class);
$security->isGranted('SEE_AND_EDIT', $storedObject)->willReturn(true)->shouldBeCalled();
$controller->getSignedUrl('POST', new Request());
}
$controller = new AsyncUploadController(
$this->buildTempUrlGenerator(),
$this->buildSerializer(),
$security->reveal(),
new NullLogger(),
);
public function testGeneratePost(): void
{
$controller = $this->buildAsyncUploadController(true);
$actual = $controller->getSignedUrlPost(new Request(query: ['expires_delay' => 10, 'submit_delay' => 1800]), $storedObject);
$actual = $controller->getSignedUrl('POST', new Request());
$decodedActual = json_decode($actual->getContent(), true, JSON_THROW_ON_ERROR, JSON_THROW_ON_ERROR);
self::assertArrayHasKey('method', $decodedActual);
self::assertEquals('POST', $decodedActual['method']);
}
public function testGenerateGet(): void
public function testGetSignedUrlGetSimpleScenarioHappy(): void
{
$controller = $this->buildAsyncUploadController(true);
$storedObject = new StoredObject();
$storedObject->registerVersion();
$security = $this->prophesize(Security::class);
$security->isGranted('SEE', $storedObject)->willReturn(true)->shouldBeCalled();
$controller = new AsyncUploadController(
$this->buildTempUrlGenerator(),
$this->buildSerializer(),
$security->reveal(),
new NullLogger(),
);
$actual = $controller->getSignedUrlGet(new Request(query: ['expires_delay' => 10, 'submit_delay' => 1800]), $storedObject, 'get');
$actual = $controller->getSignedUrl('GET', new Request(['object_name' => 'abc']));
$decodedActual = json_decode($actual->getContent(), true, JSON_THROW_ON_ERROR, JSON_THROW_ON_ERROR);
self::assertArrayHasKey('method', $decodedActual);
self::assertEquals('GET', $decodedActual['method']);
}
private function buildAsyncUploadController(
bool $isGranted,
): AsyncUploadController {
$tempUrlGenerator = new class () implements TempUrlGeneratorInterface {
public function generatePost(?int $expire_delay = null, ?int $submit_delay = null, int $max_file_count = 1): SignedUrlPost
public function testGetSignedUrlGetSimpleScenarioNotAuthorized(): void
{
$this->expectException(AccessDeniedHttpException::class);
$storedObject = new StoredObject();
$storedObject->registerVersion();
$security = $this->prophesize(Security::class);
$security->isGranted('SEE', $storedObject)->willReturn(false)->shouldBeCalled();
$controller = new AsyncUploadController(
$this->buildTempUrlGenerator(),
$this->buildSerializer(),
$security->reveal(),
new NullLogger(),
);
$controller->getSignedUrlGet(new Request(query: ['expires_delay' => 10, 'submit_delay' => 1800]), $storedObject, 'get');
}
public function testGetSignedUrlGetForSpecificVersionOfTheStoredObjectStoredInDatabase(): void
{
$storedObject = new StoredObject();
$version = $storedObject->registerVersion();
// we add a version to be sure that the we do not get the last one
$storedObject->registerVersion();
$security = $this->prophesize(Security::class);
$security->isGranted('SEE', $storedObject)->willReturn(true)->shouldBeCalled();
$controller = new AsyncUploadController(
$this->buildTempUrlGenerator(),
$this->buildSerializer(),
$security->reveal(),
new NullLogger(),
);
$actual = $controller->getSignedUrlGet(
new Request(query: ['expires_delay' => 10, 'submit_delay' => 1800, 'version' => $version->getFilename()]),
$storedObject,
'get'
);
$decodedActual = json_decode($actual->getContent(), true, JSON_THROW_ON_ERROR, JSON_THROW_ON_ERROR);
self::assertArrayHasKey('method', $decodedActual);
self::assertEquals('GET', $decodedActual['method']);
self::assertArrayHasKey('object_name', $decodedActual);
self::assertEquals($version->getFilename(), $decodedActual['object_name']);
}
public function testGetSignedUrlGetForSpecificVersionOfTheStoredObjectNotYetStoredInDatabase(): void
{
$storedObject = new StoredObject();
$storedObject->registerVersion();
// we generate a valid name
$version = $storedObject->getPrefix().'/some-version';
$security = $this->prophesize(Security::class);
$security->isGranted('SEE', $storedObject)->willReturn(true)->shouldBeCalled();
$controller = new AsyncUploadController(
$this->buildTempUrlGenerator(),
$this->buildSerializer(),
$security->reveal(),
new NullLogger(),
);
$actual = $controller->getSignedUrlGet(
new Request(query: ['expires_delay' => 10, 'submit_delay' => 1800, 'version' => $version]),
$storedObject,
'get'
);
$decodedActual = json_decode($actual->getContent(), true, JSON_THROW_ON_ERROR, JSON_THROW_ON_ERROR);
self::assertArrayHasKey('method', $decodedActual);
self::assertEquals('GET', $decodedActual['method']);
self::assertArrayHasKey('object_name', $decodedActual);
self::assertEquals($version, $decodedActual['object_name']);
}
public function testGetSignedUrlGetForSpecificVersionNotBelongingToTheStoreObject(): void
{
$this->expectException(AccessDeniedHttpException::class);
$storedObject = new StoredObject();
$storedObject->registerVersion();
// we generate a random version
$version = 'something/else';
$security = $this->prophesize(Security::class);
$security->isGranted('SEE', $storedObject)->willReturn(true)->shouldBeCalled();
$controller = new AsyncUploadController(
$this->buildTempUrlGenerator(),
$this->buildSerializer(),
$security->reveal(),
new NullLogger(),
);
$controller->getSignedUrlGet(
new Request(query: ['expires_delay' => 10, 'submit_delay' => 1800, 'version' => $version]),
$storedObject,
'get'
);
}
public function buildTempUrlGenerator(): TempUrlGeneratorInterface
{
return new class () implements TempUrlGeneratorInterface {
public function generatePost(?int $expire_delay = null, ?int $submit_delay = null, int $max_file_count = 1, ?string $object_name = null): SignedUrlPost
{
return new SignedUrlPost(
'https://object.store.example',
new \DateTimeImmutable('1 hour'),
'abc',
$object_name ?? 'abc',
150,
1,
1800,
@@ -86,27 +205,25 @@ class AsyncUploadControllerTest extends TestCase
public function generate(string $method, string $object_name, ?int $expire_delay = null): SignedUrl
{
return new SignedUrl(
$method,
strtoupper($method),
'https://object.store.example',
new \DateTimeImmutable('1 hour'),
$object_name
);
}
};
}
private function buildSerializer(): SerializerInterface
{
$serializer = $this->prophesize(SerializerInterface::class);
$serializer->serialize(Argument::type(SignedUrl::class), 'json', Argument::type('array'))
->will(fn (array $args): string => json_encode(['method' => $args[0]->method], JSON_THROW_ON_ERROR, 3));
->will(fn (array $args): string => json_encode(
['method' => $args[0]->method, 'object_name' => $args[0]->object_name],
JSON_THROW_ON_ERROR,
3
));
$security = $this->prophesize(Security::class);
$security->isGranted(AsyncUploadVoter::GENERATE_SIGNATURE, Argument::type(SignedUrl::class))
->willReturn($isGranted);
return new AsyncUploadController(
$tempUrlGenerator,
$serializer->reveal(),
$security->reveal(),
new NullLogger()
);
return $serializer->reveal();
}
}