Updated services required to initialize API keys with roles

This commit is contained in:
Alejandro Celaya 2021-01-10 20:05:14 +01:00
parent 95e51665b1
commit c9ff2b3834
7 changed files with 70 additions and 9 deletions

View file

@ -30,7 +30,7 @@
"laminas/laminas-diactoros": "^2.1.3",
"laminas/laminas-inputfilter": "^2.10",
"laminas/laminas-paginator": "^2.8",
"laminas/laminas-servicemanager": "^3.4",
"laminas/laminas-servicemanager": "^3.6",
"laminas/laminas-stdlib": "^3.2",
"lcobucci/jwt": "^4.0",
"league/uri": "^6.2",

View file

@ -45,6 +45,9 @@ class DomainService implements DomainServiceInterface
];
}
/**
* @throws DomainNotFoundException
*/
public function getDomain(string $domainId): Domain
{
/** @var Domain|null $domain */
@ -55,4 +58,16 @@ class DomainService implements DomainServiceInterface
return $domain;
}
public function getOrCreate(string $authority): Domain
{
$repo = $this->em->getRepository(Domain::class);
/** @var Domain|null $domain */
$domain = $repo->findOneBy(['authority' => $authority]) ?? new Domain($authority);
$this->em->persist($domain);
$this->em->flush();
return $domain;
}
}

View file

@ -6,6 +6,7 @@ namespace Shlinkio\Shlink\Core\Domain;
use Shlinkio\Shlink\Core\Domain\Model\DomainItem;
use Shlinkio\Shlink\Core\Entity\Domain;
use Shlinkio\Shlink\Core\Exception\DomainNotFoundException;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
interface DomainServiceInterface
@ -15,5 +16,10 @@ interface DomainServiceInterface
*/
public function listDomains(?ApiKey $apiKey = null): array;
/**
* @throws DomainNotFoundException
*/
public function getDomain(string $domainId): Domain;
public function getOrCreate(string $authority): Domain;
}

View file

@ -6,6 +6,7 @@ namespace ShlinkioTest\Shlink\Core\Domain;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Domain\DomainService;
@ -111,4 +112,33 @@ class DomainServiceTest extends TestCase
self::assertSame($domain, $result);
$find->shouldHaveBeenCalledOnce();
}
/**
* @test
* @dataProvider provideFoundDomains
*/
public function getOrCreateAlwaysPersistsDomain(?Domain $foundDomain): void
{
$authority = 'example.com';
$repo = $this->prophesize(DomainRepositoryInterface::class);
$repo->findOneBy(['authority' => $authority])->willReturn($foundDomain);
$getRepo = $this->em->getRepository(Domain::class)->willReturn($repo->reveal());
$persist = $this->em->persist($foundDomain !== null ? $foundDomain : Argument::type(Domain::class));
$flush = $this->em->flush();
$result = $this->domainService->getOrCreate($authority);
if ($foundDomain !== null) {
self::assertSame($result, $foundDomain);
}
$getRepo->shouldHaveBeenCalledOnce();
$persist->shouldHaveBeenCalledOnce();
$flush->shouldHaveBeenCalledOnce();
}
public function provideFoundDomains(): iterable
{
yield 'domain not found' => [null];
yield 'domain found' => [new Domain('')];
}
}

View file

@ -7,6 +7,7 @@ namespace Shlinkio\Shlink\Rest\Service;
use Cake\Chronos\Chronos;
use Doctrine\ORM\EntityManagerInterface;
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
use function sprintf;
@ -20,9 +21,13 @@ class ApiKeyService implements ApiKeyServiceInterface
$this->em = $em;
}
public function create(?Chronos $expirationDate = null): ApiKey
public function create(?Chronos $expirationDate = null, RoleDefinition ...$roleDefinitions): ApiKey
{
$key = new ApiKey($expirationDate);
foreach ($roleDefinitions as $definition) {
$key->registerRole($definition);
}
$this->em->persist($key);
$this->em->flush();
@ -31,7 +36,6 @@ class ApiKeyService implements ApiKeyServiceInterface
public function check(string $key): ApiKeyCheckResult
{
/** @var ApiKey|null $apiKey */
$apiKey = $this->getByKey($key);
return new ApiKeyCheckResult($apiKey);
}
@ -41,7 +45,6 @@ class ApiKeyService implements ApiKeyServiceInterface
*/
public function disable(string $key): ApiKey
{
/** @var ApiKey|null $apiKey */
$apiKey = $this->getByKey($key);
if ($apiKey === null) {
throw new InvalidArgumentException(sprintf('API key "%s" does not exist and can\'t be disabled', $key));

View file

@ -6,11 +6,12 @@ namespace Shlinkio\Shlink\Rest\Service;
use Cake\Chronos\Chronos;
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
interface ApiKeyServiceInterface
{
public function create(?Chronos $expirationDate = null): ApiKey;
public function create(?Chronos $expirationDate = null, RoleDefinition ...$roleDefinitions): ApiKey;
public function check(string $key): ApiKeyCheckResult;

View file

@ -12,6 +12,7 @@ use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\Rest\Service\ApiKeyService;
@ -31,21 +32,26 @@ class ApiKeyServiceTest extends TestCase
/**
* @test
* @dataProvider provideCreationDate
* @param RoleDefinition[] $roles
*/
public function apiKeyIsProperlyCreated(?Chronos $date): void
public function apiKeyIsProperlyCreated(?Chronos $date, array $roles): void
{
$this->em->flush()->shouldBeCalledOnce();
$this->em->persist(Argument::type(ApiKey::class))->shouldBeCalledOnce();
$key = $this->service->create($date);
$key = $this->service->create($date, ...$roles);
self::assertEquals($date, $key->getExpirationDate());
foreach ($roles as $roleDefinition) {
self::assertTrue($key->hasRole($roleDefinition->roleName()));
}
}
public function provideCreationDate(): iterable
{
yield 'no expiration date' => [null];
yield 'expiration date' => [Chronos::parse('2030-01-01')];
yield 'no expiration date' => [null, []];
yield 'expiration date' => [Chronos::parse('2030-01-01'), []];
yield 'roles' => [null, [RoleDefinition::forDomain('123'), RoleDefinition::forAuthoredShortUrls()]];
}
/**