mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
Created new env var to programatically provide an initial API key
This commit is contained in:
parent
e6ee4ceae2
commit
f5138385be
@ -8,8 +8,8 @@ return [
|
||||
|
||||
'debug' => false,
|
||||
|
||||
// Disabling config cache for cli, ensures it's never used for openswoole and also that console commands don't
|
||||
// generate a cache file that's then used by non-openswoole web executions
|
||||
// Disabling config cache for cli, ensures it's never used for openswoole/RoadRunner, and also that console
|
||||
// commands don't generate a cache file that's then used by php-fpm web executions
|
||||
ConfigAggregator::ENABLE_CACHE => PHP_SAPI !== 'cli',
|
||||
|
||||
];
|
||||
|
@ -47,6 +47,7 @@ enum EnvVars: string
|
||||
case PORT = 'PORT';
|
||||
case TASK_WORKER_NUM = 'TASK_WORKER_NUM';
|
||||
case WEB_WORKER_NUM = 'WEB_WORKER_NUM';
|
||||
case INITIAL_API_KEY = 'INITIAL_API_KEY';
|
||||
case ANONYMIZE_REMOTE_ADDR = 'ANONYMIZE_REMOTE_ADDR';
|
||||
case TRACK_ORPHAN_VISITS = 'TRACK_ORPHAN_VISITS';
|
||||
case DISABLE_TRACK_PARAM = 'DISABLE_TRACK_PARAM';
|
||||
|
@ -15,7 +15,8 @@ use function Shlinkio\Shlink\Core\determineTableName;
|
||||
return static function (ClassMetadata $metadata, array $emConfig): void {
|
||||
$builder = new ClassMetadataBuilder($metadata);
|
||||
|
||||
$builder->setTable(determineTableName('api_keys', $emConfig));
|
||||
$builder->setTable(determineTableName('api_keys', $emConfig))
|
||||
->setCustomRepositoryClass(ApiKey\Repository\ApiKeyRepository::class);
|
||||
|
||||
$builder->createField('id', Types::BIGINT)
|
||||
->makePrimaryKey()
|
||||
|
24
module/Rest/config/initial-api-key.config.php
Normal file
24
module/Rest/config/initial-api-key.config.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Rest;
|
||||
|
||||
use Mezzio\Application;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use const PHP_SAPI;
|
||||
|
||||
return [
|
||||
|
||||
'initial_api_key' => PHP_SAPI !== 'cli' ? null : EnvVars::INITIAL_API_KEY->loadFromEnv(),
|
||||
|
||||
'dependencies' => [
|
||||
'delegators' => [
|
||||
Application::class => [
|
||||
ApiKey\InitialApiKeyDelegator::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
31
module/Rest/src/ApiKey/InitialApiKeyDelegator.php
Normal file
31
module/Rest/src/ApiKey/InitialApiKeyDelegator.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?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 ($initialApiKey !== null) {
|
||||
$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);
|
||||
}
|
||||
}
|
29
module/Rest/src/ApiKey/Repository/ApiKeyRepository.php
Normal file
29
module/Rest/src/ApiKey/Repository/ApiKeyRepository.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Rest\ApiKey\Repository;
|
||||
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
class ApiKeyRepository extends EntitySpecificationRepository implements ApiKeyRepositoryInterface
|
||||
{
|
||||
public function createInitialApiKey(string $apiKey): void
|
||||
{
|
||||
$this->getEntityManager()->wrapInTransaction(function () use ($apiKey): void {
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
$amountOfApiKeys = $qb->select('COUNT(a.id)')
|
||||
->from(ApiKey::class, 'a')
|
||||
->getQuery()
|
||||
->setLockMode(LockMode::PESSIMISTIC_WRITE)
|
||||
->getSingleScalarResult();
|
||||
|
||||
if ($amountOfApiKeys === 0) {
|
||||
$this->getEntityManager()->persist(ApiKey::fromKey($apiKey));
|
||||
$this->getEntityManager()->flush();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Rest\ApiKey\Repository;
|
||||
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterface;
|
||||
|
||||
interface ApiKeyRepositoryInterface extends ObjectRepository, EntitySpecificationRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Will create provided API key only if there's no API keys yet
|
||||
*/
|
||||
public function createInitialApiKey(string $apiKey): void;
|
||||
}
|
@ -23,16 +23,14 @@ class ApiKey extends AbstractEntity
|
||||
private bool $enabled;
|
||||
/** @var Collection|ApiKeyRole[] */
|
||||
private Collection $roles;
|
||||
private ?string $name;
|
||||
private ?string $name = null;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function __construct(?string $name = null, ?Chronos $expirationDate = null)
|
||||
private function __construct(?string $key = null)
|
||||
{
|
||||
$this->key = Uuid::uuid4()->toString();
|
||||
$this->expirationDate = $expirationDate;
|
||||
$this->name = $name;
|
||||
$this->key = $key ?? Uuid::uuid4()->toString();
|
||||
$this->enabled = true;
|
||||
$this->roles = new ArrayCollection();
|
||||
}
|
||||
@ -44,7 +42,10 @@ class ApiKey extends AbstractEntity
|
||||
|
||||
public static function fromMeta(ApiKeyMeta $meta): self
|
||||
{
|
||||
$apiKey = new self($meta->name, $meta->expirationDate);
|
||||
$apiKey = self::create();
|
||||
$apiKey->name = $meta->name;
|
||||
$apiKey->expirationDate = $meta->expirationDate;
|
||||
|
||||
foreach ($meta->roleDefinitions as $roleDefinition) {
|
||||
$apiKey->registerRole($roleDefinition);
|
||||
}
|
||||
@ -52,6 +53,11 @@ class ApiKey extends AbstractEntity
|
||||
return $apiKey;
|
||||
}
|
||||
|
||||
public static function fromKey(string $key): self
|
||||
{
|
||||
return new self($key);
|
||||
}
|
||||
|
||||
public function getExpirationDate(): ?Chronos
|
||||
{
|
||||
return $this->expirationDate;
|
||||
|
@ -22,10 +22,11 @@ class ConfigProviderTest extends TestCase
|
||||
{
|
||||
$config = ($this->configProvider)();
|
||||
|
||||
self::assertCount(4, $config);
|
||||
self::assertCount(5, $config);
|
||||
self::assertArrayHasKey('dependencies', $config);
|
||||
self::assertArrayHasKey('auth', $config);
|
||||
self::assertArrayHasKey('entity_manager', $config);
|
||||
self::assertArrayHasKey('initial_api_key', $config);
|
||||
self::assertArrayHasKey(ConfigAbstractFactory::class, $config);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user