mirror of
https://github.com/shlinkio/shlink.git
synced 2026-02-28 12:13:13 +08:00
155 lines
4.3 KiB
PHP
155 lines
4.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Shlinkio\Shlink\Rest\Service;
|
|
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
|
|
use Shlinkio\Shlink\Core\Model\Renaming;
|
|
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
|
|
use Shlinkio\Shlink\Rest\ApiKey\Repository\ApiKeyRepositoryInterface;
|
|
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
|
use Shlinkio\Shlink\Rest\Exception\ApiKeyConflictException;
|
|
use Shlinkio\Shlink\Rest\Exception\ApiKeyNotFoundException;
|
|
|
|
use function sprintf;
|
|
|
|
readonly class ApiKeyService implements ApiKeyServiceInterface
|
|
{
|
|
public function __construct(private EntityManagerInterface $em, private ApiKeyRepositoryInterface $repo)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function create(ApiKeyMeta $apiKeyMeta): ApiKey
|
|
{
|
|
return $this->em->wrapInTransaction(function () use ($apiKeyMeta) {
|
|
$apiKey = ApiKey::fromMeta($this->ensureUniqueName($apiKeyMeta));
|
|
$this->em->persist($apiKey);
|
|
|
|
return $apiKey;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Given an ApiKeyMeta object, it returns another instance ensuring the name is unique.
|
|
* - If the name was auto-generated, it continues re-trying until a unique name is resolved.
|
|
* - If the name was explicitly provided, it throws in case of name conflict.
|
|
*/
|
|
private function ensureUniqueName(ApiKeyMeta $apiKeyMeta): ApiKeyMeta
|
|
{
|
|
if (! $this->repo->nameExists($apiKeyMeta->name)) {
|
|
return $apiKeyMeta;
|
|
}
|
|
|
|
if (! $apiKeyMeta->isNameAutoGenerated) {
|
|
throw new InvalidArgumentException(
|
|
sprintf('Another API key with name "%s" already exists', $apiKeyMeta->name),
|
|
);
|
|
}
|
|
|
|
return $this->ensureUniqueName(ApiKeyMeta::fromParams(
|
|
expirationDate: $apiKeyMeta->expirationDate,
|
|
roleDefinitions: $apiKeyMeta->roleDefinitions,
|
|
));
|
|
}
|
|
|
|
public function createInitial(string $key): ApiKey|null
|
|
{
|
|
return $this->repo->createInitialApiKey($key);
|
|
}
|
|
|
|
public function check(string $key): ApiKeyCheckResult
|
|
{
|
|
$apiKey = $this->findByKey($key);
|
|
return new ApiKeyCheckResult($apiKey);
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function deleteByName(string $apiKeyName): void
|
|
{
|
|
$affectedResults = $this->repo->deleteByName($apiKeyName);
|
|
if ($affectedResults === 0) {
|
|
throw ApiKeyNotFoundException::forName($apiKeyName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function disableByName(string $apiKeyName): ApiKey
|
|
{
|
|
$apiKey = $this->repo->findOneBy(['name' => $apiKeyName]);
|
|
if ($apiKey === null) {
|
|
throw ApiKeyNotFoundException::forName($apiKeyName);
|
|
}
|
|
|
|
return $this->disableApiKey($apiKey);
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function disableByKey(string $key): ApiKey
|
|
{
|
|
$apiKey = $this->findByKey($key);
|
|
if ($apiKey === null) {
|
|
throw ApiKeyNotFoundException::forKey($key);
|
|
}
|
|
|
|
return $this->disableApiKey($apiKey);
|
|
}
|
|
|
|
private function disableApiKey(ApiKey $apiKey): ApiKey
|
|
{
|
|
$apiKey->disable();
|
|
$this->em->flush();
|
|
|
|
return $apiKey;
|
|
}
|
|
|
|
/**
|
|
* @return ApiKey[]
|
|
*/
|
|
public function listKeys(bool $enabledOnly = false): array
|
|
{
|
|
$conditions = $enabledOnly ? ['enabled' => true] : [];
|
|
return $this->repo->findBy($conditions);
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function renameApiKey(Renaming $apiKeyRenaming): ApiKey
|
|
{
|
|
$apiKey = $this->repo->findOneBy(['name' => $apiKeyRenaming->oldName]);
|
|
if ($apiKey === null) {
|
|
throw ApiKeyNotFoundException::forName($apiKeyRenaming->oldName);
|
|
}
|
|
|
|
if (! $apiKeyRenaming->nameChanged()) {
|
|
return $apiKey;
|
|
}
|
|
|
|
$this->em->wrapInTransaction(function () use ($apiKeyRenaming, $apiKey): void {
|
|
if ($this->repo->nameExists($apiKeyRenaming->newName)) {
|
|
throw ApiKeyConflictException::forName($apiKeyRenaming->newName);
|
|
}
|
|
|
|
$apiKey->name = $apiKeyRenaming->newName;
|
|
});
|
|
|
|
return $apiKey;
|
|
}
|
|
|
|
private function findByKey(string $key): ApiKey|null
|
|
{
|
|
return $this->repo->findOneBy(['key' => ApiKey::hashKey($key)]);
|
|
}
|
|
}
|