mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
Applied API role specs to short URL creation
This commit is contained in:
@@ -8,6 +8,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
|||||||
use Shlinkio\Shlink\Core\Domain\Model\DomainItem;
|
use Shlinkio\Shlink\Core\Domain\Model\DomainItem;
|
||||||
use Shlinkio\Shlink\Core\Domain\Repository\DomainRepositoryInterface;
|
use Shlinkio\Shlink\Core\Domain\Repository\DomainRepositoryInterface;
|
||||||
use Shlinkio\Shlink\Core\Entity\Domain;
|
use Shlinkio\Shlink\Core\Entity\Domain;
|
||||||
|
use Shlinkio\Shlink\Core\Exception\DomainNotFoundException;
|
||||||
use Shlinkio\Shlink\Rest\ApiKey\Role;
|
use Shlinkio\Shlink\Rest\ApiKey\Role;
|
||||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||||
|
|
||||||
@@ -43,4 +44,15 @@ class DomainService implements DomainServiceInterface
|
|||||||
...$mappedDomains,
|
...$mappedDomains,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDomain(string $domainId): Domain
|
||||||
|
{
|
||||||
|
/** @var Domain|null $domain */
|
||||||
|
$domain = $this->em->find(Domain::class, $domainId);
|
||||||
|
if ($domain === null) {
|
||||||
|
throw DomainNotFoundException::fromId($domainId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $domain;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace Shlinkio\Shlink\Core\Domain;
|
namespace Shlinkio\Shlink\Core\Domain;
|
||||||
|
|
||||||
use Shlinkio\Shlink\Core\Domain\Model\DomainItem;
|
use Shlinkio\Shlink\Core\Domain\Model\DomainItem;
|
||||||
|
use Shlinkio\Shlink\Core\Entity\Domain;
|
||||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||||
|
|
||||||
interface DomainServiceInterface
|
interface DomainServiceInterface
|
||||||
@@ -13,4 +14,6 @@ interface DomainServiceInterface
|
|||||||
* @return DomainItem[]
|
* @return DomainItem[]
|
||||||
*/
|
*/
|
||||||
public function listDomains(?ApiKey $apiKey = null): array;
|
public function listDomains(?ApiKey $apiKey = null): array;
|
||||||
|
|
||||||
|
public function getDomain(string $domainId): Domain;
|
||||||
}
|
}
|
||||||
|
|||||||
32
module/Core/src/Exception/DomainNotFoundException.php
Normal file
32
module/Core/src/Exception/DomainNotFoundException.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Shlinkio\Shlink\Core\Exception;
|
||||||
|
|
||||||
|
use Fig\Http\Message\StatusCodeInterface;
|
||||||
|
use Mezzio\ProblemDetails\Exception\CommonProblemDetailsExceptionTrait;
|
||||||
|
use Mezzio\ProblemDetails\Exception\ProblemDetailsExceptionInterface;
|
||||||
|
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
|
class DomainNotFoundException extends DomainException implements ProblemDetailsExceptionInterface
|
||||||
|
{
|
||||||
|
use CommonProblemDetailsExceptionTrait;
|
||||||
|
|
||||||
|
private const TITLE = 'Domain not found';
|
||||||
|
private const TYPE = 'DOMAIN_NOT_FOUND';
|
||||||
|
|
||||||
|
public static function fromId(string $id): self
|
||||||
|
{
|
||||||
|
$e = new self(sprintf('Domain with id "%s" could not be found', $id));
|
||||||
|
|
||||||
|
$e->detail = $e->getMessage();
|
||||||
|
$e->title = self::TITLE;
|
||||||
|
$e->type = self::TYPE;
|
||||||
|
$e->status = StatusCodeInterface::STATUS_NOT_FOUND;
|
||||||
|
$e->additional = ['id' => $id];
|
||||||
|
|
||||||
|
return $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,10 +10,10 @@ use Happyr\DoctrineSpecification\Spec;
|
|||||||
|
|
||||||
class BelongsToDomain extends BaseSpecification
|
class BelongsToDomain extends BaseSpecification
|
||||||
{
|
{
|
||||||
private int $domainId;
|
private string $domainId;
|
||||||
private string $dqlAlias;
|
private string $dqlAlias;
|
||||||
|
|
||||||
public function __construct(int $domainId, ?string $dqlAlias = null)
|
public function __construct(string $domainId, ?string $dqlAlias = null)
|
||||||
{
|
{
|
||||||
$this->domainId = $domainId;
|
$this->domainId = $domainId;
|
||||||
$this->dqlAlias = $dqlAlias ?? 's';
|
$this->dqlAlias = $dqlAlias ?? 's';
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ use Happyr\DoctrineSpecification\Specification\Specification;
|
|||||||
|
|
||||||
class BelongsToDomainInlined implements Specification
|
class BelongsToDomainInlined implements Specification
|
||||||
{
|
{
|
||||||
private int $domainId;
|
private string $domainId;
|
||||||
|
|
||||||
public function __construct(int $domainId)
|
public function __construct(string $domainId)
|
||||||
{
|
{
|
||||||
$this->domainId = $domainId;
|
$this->domainId = $domainId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use Shlinkio\Shlink\Core\Domain\DomainService;
|
|||||||
use Shlinkio\Shlink\Core\Domain\Model\DomainItem;
|
use Shlinkio\Shlink\Core\Domain\Model\DomainItem;
|
||||||
use Shlinkio\Shlink\Core\Domain\Repository\DomainRepositoryInterface;
|
use Shlinkio\Shlink\Core\Domain\Repository\DomainRepositoryInterface;
|
||||||
use Shlinkio\Shlink\Core\Entity\Domain;
|
use Shlinkio\Shlink\Core\Entity\Domain;
|
||||||
|
use Shlinkio\Shlink\Core\Exception\DomainNotFoundException;
|
||||||
|
|
||||||
class DomainServiceTest extends TestCase
|
class DomainServiceTest extends TestCase
|
||||||
{
|
{
|
||||||
@@ -54,4 +55,27 @@ class DomainServiceTest extends TestCase
|
|||||||
[$default, new DomainItem('foo.com', false), new DomainItem('bar.com', false)],
|
[$default, new DomainItem('foo.com', false), new DomainItem('bar.com', false)],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function getDomainThrowsExceptionWhenDomainIsNotFound(): void
|
||||||
|
{
|
||||||
|
$find = $this->em->find(Domain::class, '123')->willReturn(null);
|
||||||
|
|
||||||
|
$this->expectException(DomainNotFoundException::class);
|
||||||
|
$find->shouldBeCalledOnce();
|
||||||
|
|
||||||
|
$this->domainService->getDomain('123');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function getDomainReturnsEntityWhenFound(): void
|
||||||
|
{
|
||||||
|
$domain = new Domain('');
|
||||||
|
$find = $this->em->find(Domain::class, '123')->willReturn($domain);
|
||||||
|
|
||||||
|
$result = $this->domainService->getDomain('123');
|
||||||
|
|
||||||
|
self::assertSame($domain, $result);
|
||||||
|
$find->shouldHaveBeenCalledOnce();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
module/Core/test/Exception/DomainNotFoundExceptionTest.php
Normal file
28
module/Core/test/Exception/DomainNotFoundExceptionTest.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace ShlinkioTest\Shlink\Core\Exception;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Shlinkio\Shlink\Core\Exception\DomainNotFoundException;
|
||||||
|
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
|
class DomainNotFoundExceptionTest extends TestCase
|
||||||
|
{
|
||||||
|
/** @test */
|
||||||
|
public function properlyCreatesExceptionFromNotFoundTag(): void
|
||||||
|
{
|
||||||
|
$id = '123';
|
||||||
|
$expectedMessage = sprintf('Domain with id "%s" could not be found', $id);
|
||||||
|
$e = DomainNotFoundException::fromId($id);
|
||||||
|
|
||||||
|
self::assertEquals($expectedMessage, $e->getMessage());
|
||||||
|
self::assertEquals($expectedMessage, $e->getDetail());
|
||||||
|
self::assertEquals('Domain not found', $e->getTitle());
|
||||||
|
self::assertEquals('DOMAIN_NOT_FOUND', $e->getType());
|
||||||
|
self::assertEquals(['id' => $id], $e->getAdditionalData());
|
||||||
|
self::assertEquals(404, $e->getStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace ShlinkioTest\Shlink\Rest\Exception;
|
namespace ShlinkioTest\Shlink\Core\Exception;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
|
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ return [
|
|||||||
Middleware\ShortUrl\CreateShortUrlContentNegotiationMiddleware::class => InvokableFactory::class,
|
Middleware\ShortUrl\CreateShortUrlContentNegotiationMiddleware::class => InvokableFactory::class,
|
||||||
Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class => ConfigAbstractFactory::class,
|
Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class => ConfigAbstractFactory::class,
|
||||||
Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class => ConfigAbstractFactory::class,
|
Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class => ConfigAbstractFactory::class,
|
||||||
|
Middleware\ShortUrl\OverrideDomainMiddleware::class => ConfigAbstractFactory::class,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -81,6 +82,7 @@ return [
|
|||||||
Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class => [
|
Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class => [
|
||||||
'config.url_shortener.default_short_codes_length',
|
'config.url_shortener.default_short_codes_length',
|
||||||
],
|
],
|
||||||
|
Middleware\ShortUrl\OverrideDomainMiddleware::class => [DomainService::class],
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ namespace Shlinkio\Shlink\Rest;
|
|||||||
|
|
||||||
$contentNegotiationMiddleware = Middleware\ShortUrl\CreateShortUrlContentNegotiationMiddleware::class;
|
$contentNegotiationMiddleware = Middleware\ShortUrl\CreateShortUrlContentNegotiationMiddleware::class;
|
||||||
$dropDomainMiddleware = Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class;
|
$dropDomainMiddleware = Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class;
|
||||||
|
$overrideDomainMiddleware = Middleware\ShortUrl\OverrideDomainMiddleware::class;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
@@ -16,9 +17,13 @@ return [
|
|||||||
Action\ShortUrl\CreateShortUrlAction::getRouteDef([
|
Action\ShortUrl\CreateShortUrlAction::getRouteDef([
|
||||||
$contentNegotiationMiddleware,
|
$contentNegotiationMiddleware,
|
||||||
$dropDomainMiddleware,
|
$dropDomainMiddleware,
|
||||||
|
$overrideDomainMiddleware,
|
||||||
Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class,
|
Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class,
|
||||||
]),
|
]),
|
||||||
Action\ShortUrl\SingleStepCreateShortUrlAction::getRouteDef([$contentNegotiationMiddleware]),
|
Action\ShortUrl\SingleStepCreateShortUrlAction::getRouteDef([
|
||||||
|
$contentNegotiationMiddleware,
|
||||||
|
$overrideDomainMiddleware,
|
||||||
|
]),
|
||||||
Action\ShortUrl\EditShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
Action\ShortUrl\EditShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
||||||
Action\ShortUrl\DeleteShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
Action\ShortUrl\DeleteShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
||||||
Action\ShortUrl\ResolveShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
Action\ShortUrl\ResolveShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ class SingleStepCreateShortUrlAction extends AbstractCreateShortUrlAction
|
|||||||
|
|
||||||
return new CreateShortUrlData($longUrl, [], ShortUrlMeta::fromRawData([
|
return new CreateShortUrlData($longUrl, [], ShortUrlMeta::fromRawData([
|
||||||
ShortUrlMetaInputFilter::API_KEY => $apiKeyResult->apiKey(),
|
ShortUrlMetaInputFilter::API_KEY => $apiKeyResult->apiKey(),
|
||||||
|
// This will usually be null, unless this API key enforces one specific domain
|
||||||
|
ShortUrlMetaInputFilter::DOMAIN => $request->getAttribute(ShortUrlMetaInputFilter::DOMAIN),
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,15 @@ class Role
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($role->name() === self::DOMAIN_SPECIFIC) {
|
if ($role->name() === self::DOMAIN_SPECIFIC) {
|
||||||
$domainId = $role->meta()['domain_id'] ?? -1;
|
$domainId = self::domainIdFromMeta($role->meta());
|
||||||
return $inlined ? new BelongsToDomainInlined($domainId) : new BelongsToDomain($domainId);
|
return $inlined ? new BelongsToDomainInlined($domainId) : new BelongsToDomain($domainId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Spec::andX();
|
return Spec::andX();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function domainIdFromMeta(array $meta): string
|
||||||
|
{
|
||||||
|
return $meta['domain_id'] ?? '-1';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,4 +83,11 @@ class ApiKey extends AbstractEntity
|
|||||||
{
|
{
|
||||||
return $this->roles->exists(fn ($key, ApiKeyRole $role) => $role->name() === $roleName);
|
return $this->roles->exists(fn ($key, ApiKeyRole $role) => $role->name() === $roleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRoleMeta(string $roleName): array
|
||||||
|
{
|
||||||
|
/** @var ApiKeyRole|false $role */
|
||||||
|
$role = $this->roles->filter(fn (ApiKeyRole $role) => $role->name() === $roleName)->first();
|
||||||
|
return ! $role ? [] : $role->meta();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Shlinkio\Shlink\Rest\Middleware\ShortUrl;
|
||||||
|
|
||||||
|
use Fig\Http\Message\RequestMethodInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\Server\MiddlewareInterface;
|
||||||
|
use Psr\Http\Server\RequestHandlerInterface;
|
||||||
|
use Shlinkio\Shlink\Core\Domain\DomainServiceInterface;
|
||||||
|
use Shlinkio\Shlink\Core\Validation\ShortUrlMetaInputFilter;
|
||||||
|
use Shlinkio\Shlink\Rest\ApiKey\Role;
|
||||||
|
use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware;
|
||||||
|
|
||||||
|
class OverrideDomainMiddleware implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
private DomainServiceInterface $domainService;
|
||||||
|
|
||||||
|
public function __construct(DomainServiceInterface $domainService)
|
||||||
|
{
|
||||||
|
$this->domainService = $domainService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||||
|
{
|
||||||
|
$apiKey = AuthenticationMiddleware::apiKeyFromRequest($request);
|
||||||
|
if (! $apiKey->hasRole(Role::DOMAIN_SPECIFIC)) {
|
||||||
|
return $handler->handle($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestMethod = $request->getMethod();
|
||||||
|
$domainId = Role::domainIdFromMeta($apiKey->getRoleMeta(Role::DOMAIN_SPECIFIC));
|
||||||
|
$domain = $this->domainService->getDomain($domainId);
|
||||||
|
|
||||||
|
if ($requestMethod === RequestMethodInterface::METHOD_POST) {
|
||||||
|
$payload = $request->getParsedBody();
|
||||||
|
$payload[ShortUrlMetaInputFilter::DOMAIN] = $domain->getAuthority();
|
||||||
|
|
||||||
|
return $handler->handle($request->withParsedBody($payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $handler->handle($request->withAttribute(ShortUrlMetaInputFilter::DOMAIN, $domain->getAuthority()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace ShlinkioTest\Shlink\Rest\Middleware\ShortUrl;
|
||||||
|
|
||||||
|
use Laminas\Diactoros\Response;
|
||||||
|
use Laminas\Diactoros\ServerRequestFactory;
|
||||||
|
use PHPUnit\Framework\Assert;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\Server\RequestHandlerInterface;
|
||||||
|
use Shlinkio\Shlink\Core\Domain\DomainServiceInterface;
|
||||||
|
use Shlinkio\Shlink\Core\Entity\Domain;
|
||||||
|
use Shlinkio\Shlink\Core\Validation\ShortUrlMetaInputFilter;
|
||||||
|
use Shlinkio\Shlink\Rest\ApiKey\Role;
|
||||||
|
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||||
|
use Shlinkio\Shlink\Rest\Middleware\ShortUrl\OverrideDomainMiddleware;
|
||||||
|
|
||||||
|
class OverrideDomainMiddlewareTest extends TestCase
|
||||||
|
{
|
||||||
|
use ProphecyTrait;
|
||||||
|
|
||||||
|
private OverrideDomainMiddleware $middleware;
|
||||||
|
private ObjectProphecy $domainService;
|
||||||
|
private ObjectProphecy $apiKey;
|
||||||
|
private ObjectProphecy $handler;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->apiKey = $this->prophesize(ApiKey::class);
|
||||||
|
$this->handler = $this->prophesize(RequestHandlerInterface::class);
|
||||||
|
|
||||||
|
$this->domainService = $this->prophesize(DomainServiceInterface::class);
|
||||||
|
$this->middleware = new OverrideDomainMiddleware($this->domainService->reveal());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function nextMiddlewareIsCalledWhenApiKeyDoesNotHaveProperRole(): void
|
||||||
|
{
|
||||||
|
$request = $this->requestWithApiKey();
|
||||||
|
$response = new Response();
|
||||||
|
$hasRole = $this->apiKey->hasRole(Role::DOMAIN_SPECIFIC)->willReturn(false);
|
||||||
|
$handle = $this->handler->handle($request)->willReturn($response);
|
||||||
|
$getDomain = $this->domainService->getDomain(Argument::cetera());
|
||||||
|
|
||||||
|
$result = $this->middleware->process($request, $this->handler->reveal());
|
||||||
|
|
||||||
|
self::assertSame($response, $result);
|
||||||
|
$hasRole->shouldHaveBeenCalledOnce();
|
||||||
|
$handle->shouldHaveBeenCalledOnce();
|
||||||
|
$getDomain->shouldNotHaveBeenCalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @dataProvider provideBodies
|
||||||
|
*/
|
||||||
|
public function overwritesRequestBodyWhenMethodIsPost(Domain $domain, array $body, array $expectedBody): void
|
||||||
|
{
|
||||||
|
$request = $this->requestWithApiKey()->withMethod('POST')->withParsedBody($body);
|
||||||
|
$hasRole = $this->apiKey->hasRole(Role::DOMAIN_SPECIFIC)->willReturn(true);
|
||||||
|
$getRoleMeta = $this->apiKey->getRoleMeta(Role::DOMAIN_SPECIFIC)->willReturn(['domain_id' => '123']);
|
||||||
|
$getDomain = $this->domainService->getDomain('123')->willReturn($domain);
|
||||||
|
$handle = $this->handler->handle(Argument::that(
|
||||||
|
function (ServerRequestInterface $req) use ($expectedBody): bool {
|
||||||
|
Assert::assertEquals($req->getParsedBody(), $expectedBody);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
))->willReturn(new Response());
|
||||||
|
|
||||||
|
$this->middleware->process($request, $this->handler->reveal());
|
||||||
|
|
||||||
|
$hasRole->shouldHaveBeenCalledOnce();
|
||||||
|
$getRoleMeta->shouldHaveBeenCalledOnce();
|
||||||
|
$getDomain->shouldHaveBeenCalledOnce();
|
||||||
|
$handle->shouldHaveBeenCalledOnce();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideBodies(): iterable
|
||||||
|
{
|
||||||
|
yield 'no domain provided' => [new Domain('foo.com'), [], [ShortUrlMetaInputFilter::DOMAIN => 'foo.com']];
|
||||||
|
yield 'other domain provided' => [
|
||||||
|
new Domain('bar.com'),
|
||||||
|
[ShortUrlMetaInputFilter::DOMAIN => 'foo.com'],
|
||||||
|
[ShortUrlMetaInputFilter::DOMAIN => 'bar.com'],
|
||||||
|
];
|
||||||
|
yield 'same domain provided' => [
|
||||||
|
new Domain('baz.com'),
|
||||||
|
[ShortUrlMetaInputFilter::DOMAIN => 'baz.com'],
|
||||||
|
[ShortUrlMetaInputFilter::DOMAIN => 'baz.com'],
|
||||||
|
];
|
||||||
|
yield 'more body params' => [
|
||||||
|
new Domain('doma.in'),
|
||||||
|
[ShortUrlMetaInputFilter::DOMAIN => 'baz.com', 'something' => 'else', 'foo' => 123],
|
||||||
|
[ShortUrlMetaInputFilter::DOMAIN => 'doma.in', 'something' => 'else', 'foo' => 123],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @dataProvider provideMethods
|
||||||
|
*/
|
||||||
|
public function setsRequestAttributeWhenMethodIsNotPost(string $method): void
|
||||||
|
{
|
||||||
|
$domain = new Domain('something.com');
|
||||||
|
$request = $this->requestWithApiKey()->withMethod($method);
|
||||||
|
$hasRole = $this->apiKey->hasRole(Role::DOMAIN_SPECIFIC)->willReturn(true);
|
||||||
|
$getRoleMeta = $this->apiKey->getRoleMeta(Role::DOMAIN_SPECIFIC)->willReturn(['domain_id' => '123']);
|
||||||
|
$getDomain = $this->domainService->getDomain('123')->willReturn($domain);
|
||||||
|
$handle = $this->handler->handle(Argument::that(
|
||||||
|
function (ServerRequestInterface $req): bool {
|
||||||
|
Assert::assertEquals($req->getAttribute(ShortUrlMetaInputFilter::DOMAIN), 'something.com');
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
))->willReturn(new Response());
|
||||||
|
|
||||||
|
$this->middleware->process($request, $this->handler->reveal());
|
||||||
|
|
||||||
|
$hasRole->shouldHaveBeenCalledOnce();
|
||||||
|
$getRoleMeta->shouldHaveBeenCalledOnce();
|
||||||
|
$getDomain->shouldHaveBeenCalledOnce();
|
||||||
|
$handle->shouldHaveBeenCalledOnce();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideMethods(): iterable
|
||||||
|
{
|
||||||
|
yield 'GET' => ['GET'];
|
||||||
|
yield 'PUT' => ['PUT'];
|
||||||
|
yield 'PATCH' => ['PATCH'];
|
||||||
|
yield 'DELETE' => ['DELETE'];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function requestWithApiKey(): ServerRequestInterface
|
||||||
|
{
|
||||||
|
return ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, $this->apiKey->reveal());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user