mirror of
https://github.com/shlinkio/shlink.git
synced 2026-03-06 23:33:13 +08:00
New CLI command to create the initial API key idempotently
This commit is contained in:
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Rest\ApiKey;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Mezzio\Application;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Shlinkio\Shlink\Rest\ApiKey\Repository\ApiKeyRepositoryInterface;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
class InitialApiKeyDelegator
|
||||
{
|
||||
public function __invoke(ContainerInterface $container, string $serviceName, callable $callback): Application
|
||||
{
|
||||
$initialApiKey = $container->get('config')['initial_api_key'] ?? null;
|
||||
if (! empty($initialApiKey)) {
|
||||
$this->createInitialApiKey($initialApiKey, $container);
|
||||
}
|
||||
|
||||
return $callback();
|
||||
}
|
||||
|
||||
private function createInitialApiKey(string $initialApiKey, ContainerInterface $container): void
|
||||
{
|
||||
/** @var ApiKeyRepositoryInterface $repo */
|
||||
$repo = $container->get(EntityManager::class)->getRepository(ApiKey::class);
|
||||
$repo->createInitialApiKey($initialApiKey);
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,13 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
class ApiKeyRepository extends EntitySpecificationRepository implements ApiKeyRepositoryInterface
|
||||
{
|
||||
public function createInitialApiKey(string $apiKey): void
|
||||
/**
|
||||
* Will create provided API key with admin permissions, only if there's no other API keys yet
|
||||
*/
|
||||
public function createInitialApiKey(string $apiKey): ?ApiKey
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$em->wrapInTransaction(function () use ($apiKey, $em): void {
|
||||
return $em->wrapInTransaction(function () use ($apiKey, $em): ?ApiKey {
|
||||
// Ideally this would be a SELECT COUNT(...), but MsSQL and Postgres do not allow locking on aggregates
|
||||
// Because of that we check if at least one result exists
|
||||
$firstResult = $em->createQueryBuilder()->select('a.id')
|
||||
@@ -24,10 +27,16 @@ class ApiKeyRepository extends EntitySpecificationRepository implements ApiKeyRe
|
||||
->setLockMode(LockMode::PESSIMISTIC_WRITE)
|
||||
->getOneOrNullResult();
|
||||
|
||||
if ($firstResult === null) {
|
||||
$em->persist(ApiKey::fromMeta(ApiKeyMeta::fromParams(key: $apiKey)));
|
||||
$em->flush();
|
||||
// Do not create an initial API key if other keys already exist
|
||||
if ($firstResult !== null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$new = ApiKey::fromMeta(ApiKeyMeta::fromParams(key: $apiKey));
|
||||
$em->persist($new);
|
||||
$em->flush();
|
||||
|
||||
return $new;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@ namespace Shlinkio\Shlink\Rest\ApiKey\Repository;
|
||||
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterface;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
interface ApiKeyRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Will create provided API key only if there's no API keys yet
|
||||
*/
|
||||
public function createInitialApiKey(string $apiKey): void;
|
||||
public function createInitialApiKey(string $apiKey): ?ApiKey;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Shlinkio\Shlink\Rest\Service;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
|
||||
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
|
||||
use Shlinkio\Shlink\Rest\ApiKey\Repository\ApiKeyRepositoryInterface;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
use function sprintf;
|
||||
@@ -27,6 +28,13 @@ class ApiKeyService implements ApiKeyServiceInterface
|
||||
return $apiKey;
|
||||
}
|
||||
|
||||
public function createInitial(string $key): ?ApiKey
|
||||
{
|
||||
/** @var ApiKeyRepositoryInterface $repo */
|
||||
$repo = $this->em->getRepository(ApiKey::class);
|
||||
return $repo->createInitialApiKey($key);
|
||||
}
|
||||
|
||||
public function check(string $key): ApiKeyCheckResult
|
||||
{
|
||||
$apiKey = $this->getByKey($key);
|
||||
|
||||
@@ -12,6 +12,8 @@ interface ApiKeyServiceInterface
|
||||
{
|
||||
public function create(ApiKeyMeta $apiKeyMeta): ApiKey;
|
||||
|
||||
public function createInitial(string $key): ?ApiKey;
|
||||
|
||||
public function check(string $key): ApiKeyCheckResult;
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user