From dc08286a72406ede01aebe28167eff53c7d6b283 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 3 Jan 2021 13:33:07 +0100 Subject: [PATCH] Applied API role specs to single short URL resolution --- .../src/Repository/ShortUrlRepository.php | 20 +++++++++++-------- .../ShortUrlRepositoryInterface.php | 2 +- .../src/Service/ShortUrl/ShortUrlResolver.php | 9 +++++++-- .../ShortUrl/ShortUrlResolverInterface.php | 3 ++- .../Service/ShortUrl/ShortUrlResolverTest.php | 4 ++-- .../Action/ShortUrl/ResolveShortUrlAction.php | 6 +++++- .../ShortUrl/ResolveShortUrlActionTest.php | 7 +++++-- 7 files changed, 34 insertions(+), 17 deletions(-) diff --git a/module/Core/src/Repository/ShortUrlRepository.php b/module/Core/src/Repository/ShortUrlRepository.php index 363d3290..b4a2ec6e 100644 --- a/module/Core/src/Repository/ShortUrlRepository.php +++ b/module/Core/src/Repository/ShortUrlRepository.php @@ -90,10 +90,10 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU } private function createListQueryBuilder( - ?string $searchTerm = null, - array $tags = [], - ?DateRange $dateRange = null, - ?Specification $spec = null + ?string $searchTerm, + array $tags, + ?DateRange $dateRange, + ?Specification $spec ): QueryBuilder { $qb = $this->getEntityManager()->createQueryBuilder(); $qb->from(ShortUrl::class, 's') @@ -171,9 +171,9 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU return $query->getOneOrNullResult(); } - public function findOne(string $shortCode, ?string $domain = null): ?ShortUrl + public function findOne(string $shortCode, ?string $domain = null, ?Specification $spec = null): ?ShortUrl { - $qb = $this->createFindOneQueryBuilder($shortCode, $domain); + $qb = $this->createFindOneQueryBuilder($shortCode, $domain, $spec); $qb->select('s'); return $qb->getQuery()->getOneOrNullResult(); @@ -181,13 +181,13 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU public function shortCodeIsInUse(string $slug, ?string $domain = null): bool { - $qb = $this->createFindOneQueryBuilder($slug, $domain); + $qb = $this->createFindOneQueryBuilder($slug, $domain, null); $qb->select('COUNT(DISTINCT s.id)'); return ((int) $qb->getQuery()->getSingleScalarResult()) > 0; } - private function createFindOneQueryBuilder(string $slug, ?string $domain = null): QueryBuilder + private function createFindOneQueryBuilder(string $slug, ?string $domain, ?Specification $spec): QueryBuilder { $qb = $this->getEntityManager()->createQueryBuilder(); $qb->from(ShortUrl::class, 's') @@ -198,6 +198,10 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU $this->whereDomainIs($qb, $domain); + if ($spec !== null) { + $this->applySpecification($qb, $spec, 's'); + } + return $qb; } diff --git a/module/Core/src/Repository/ShortUrlRepositoryInterface.php b/module/Core/src/Repository/ShortUrlRepositoryInterface.php index 98bfe778..fee546fe 100644 --- a/module/Core/src/Repository/ShortUrlRepositoryInterface.php +++ b/module/Core/src/Repository/ShortUrlRepositoryInterface.php @@ -34,7 +34,7 @@ interface ShortUrlRepositoryInterface extends ObjectRepository, EntitySpecificat public function findOneWithDomainFallback(string $shortCode, ?string $domain = null): ?ShortUrl; - public function findOne(string $shortCode, ?string $domain = null): ?ShortUrl; + public function findOne(string $shortCode, ?string $domain = null, ?Specification $spec = null): ?ShortUrl; public function shortCodeIsInUse(string $slug, ?string $domain): bool; diff --git a/module/Core/src/Service/ShortUrl/ShortUrlResolver.php b/module/Core/src/Service/ShortUrl/ShortUrlResolver.php index 414a3446..6e03114c 100644 --- a/module/Core/src/Service/ShortUrl/ShortUrlResolver.php +++ b/module/Core/src/Service/ShortUrl/ShortUrlResolver.php @@ -9,6 +9,7 @@ use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\Repository\ShortUrlRepository; +use Shlinkio\Shlink\Rest\Entity\ApiKey; class ShortUrlResolver implements ShortUrlResolverInterface { @@ -22,11 +23,15 @@ class ShortUrlResolver implements ShortUrlResolverInterface /** * @throws ShortUrlNotFoundException */ - public function resolveShortUrl(ShortUrlIdentifier $identifier): ShortUrl + public function resolveShortUrl(ShortUrlIdentifier $identifier, ?ApiKey $apiKey = null): ShortUrl { /** @var ShortUrlRepository $shortUrlRepo */ $shortUrlRepo = $this->em->getRepository(ShortUrl::class); - $shortUrl = $shortUrlRepo->findOne($identifier->shortCode(), $identifier->domain()); + $shortUrl = $shortUrlRepo->findOne( + $identifier->shortCode(), + $identifier->domain(), + $apiKey !== null ? $apiKey->spec() : null, + ); if ($shortUrl === null) { throw ShortUrlNotFoundException::fromNotFound($identifier); } diff --git a/module/Core/src/Service/ShortUrl/ShortUrlResolverInterface.php b/module/Core/src/Service/ShortUrl/ShortUrlResolverInterface.php index a3a7c115..daa66e43 100644 --- a/module/Core/src/Service/ShortUrl/ShortUrlResolverInterface.php +++ b/module/Core/src/Service/ShortUrl/ShortUrlResolverInterface.php @@ -7,13 +7,14 @@ namespace Shlinkio\Shlink\Core\Service\ShortUrl; use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; +use Shlinkio\Shlink\Rest\Entity\ApiKey; interface ShortUrlResolverInterface { /** * @throws ShortUrlNotFoundException */ - public function resolveShortUrl(ShortUrlIdentifier $identifier): ShortUrl; + public function resolveShortUrl(ShortUrlIdentifier $identifier, ?ApiKey $apiKey = null): ShortUrl; /** * @throws ShortUrlNotFoundException diff --git a/module/Core/test/Service/ShortUrl/ShortUrlResolverTest.php b/module/Core/test/Service/ShortUrl/ShortUrlResolverTest.php index 3566b285..e9ff7a51 100644 --- a/module/Core/test/Service/ShortUrl/ShortUrlResolverTest.php +++ b/module/Core/test/Service/ShortUrl/ShortUrlResolverTest.php @@ -42,7 +42,7 @@ class ShortUrlResolverTest extends TestCase $shortCode = $shortUrl->getShortCode(); $repo = $this->prophesize(ShortUrlRepositoryInterface::class); - $findOne = $repo->findOne($shortCode, null)->willReturn($shortUrl); + $findOne = $repo->findOne($shortCode, null, null)->willReturn($shortUrl); $getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); $result = $this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode)); @@ -58,7 +58,7 @@ class ShortUrlResolverTest extends TestCase $shortCode = 'abc123'; $repo = $this->prophesize(ShortUrlRepositoryInterface::class); - $findOne = $repo->findOne($shortCode, null)->willReturn(null); + $findOne = $repo->findOne($shortCode, null, null)->willReturn(null); $getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); $this->expectException(ShortUrlNotFoundException::class); diff --git a/module/Rest/src/Action/ShortUrl/ResolveShortUrlAction.php b/module/Rest/src/Action/ShortUrl/ResolveShortUrlAction.php index 9c2cb3e4..99e58fee 100644 --- a/module/Rest/src/Action/ShortUrl/ResolveShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/ResolveShortUrlAction.php @@ -11,6 +11,7 @@ use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface; use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer; use Shlinkio\Shlink\Rest\Action\AbstractRestAction; +use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware; class ResolveShortUrlAction extends AbstractRestAction { @@ -29,7 +30,10 @@ class ResolveShortUrlAction extends AbstractRestAction public function handle(Request $request): Response { $transformer = new ShortUrlDataTransformer($this->domainConfig); - $url = $this->urlResolver->resolveShortUrl(ShortUrlIdentifier::fromApiRequest($request)); + $url = $this->urlResolver->resolveShortUrl( + ShortUrlIdentifier::fromApiRequest($request), + AuthenticationMiddleware::apiKeyFromRequest($request), + ); return new JsonResponse($transformer->transform($url)); } diff --git a/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php index d61f0f64..f4c49a60 100644 --- a/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php @@ -12,6 +12,7 @@ use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface; use Shlinkio\Shlink\Rest\Action\ShortUrl\ResolveShortUrlAction; +use Shlinkio\Shlink\Rest\Entity\ApiKey; use function strpos; @@ -32,12 +33,14 @@ class ResolveShortUrlActionTest extends TestCase public function correctShortCodeReturnsSuccess(): void { $shortCode = 'abc123'; - $this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode))->willReturn( + $apiKey = new ApiKey(); + $this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode), $apiKey)->willReturn( new ShortUrl('http://domain.com/foo/bar'), )->shouldBeCalledOnce(); - $request = (new ServerRequest())->withAttribute('shortCode', $shortCode); + $request = (new ServerRequest())->withAttribute('shortCode', $shortCode)->withAttribute(ApiKey::class, $apiKey); $response = $this->action->handle($request); + self::assertEquals(200, $response->getStatusCode()); self::assertTrue(strpos($response->getBody()->getContents(), 'http://domain.com/foo/bar') > 0); }