mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
Added roles info to api key generation and api key list
This commit is contained in:
parent
c49a0ca040
commit
9e9d213f20
@ -28,7 +28,7 @@ class RoleResolver implements RoleResolverInterface
|
|||||||
}
|
}
|
||||||
if ($domainAuthority !== null) {
|
if ($domainAuthority !== null) {
|
||||||
$domain = $this->domainService->getOrCreate($domainAuthority);
|
$domain = $this->domainService->getOrCreate($domainAuthority);
|
||||||
$roleDefinitions[] = RoleDefinition::forDomain($domain->getId());
|
$roleDefinitions[] = RoleDefinition::forDomain($domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $roleDefinitions;
|
return $roleDefinitions;
|
||||||
|
@ -6,6 +6,7 @@ namespace Shlinkio\Shlink\CLI\Command\Api;
|
|||||||
|
|
||||||
use Shlinkio\Shlink\CLI\Util\ExitCodes;
|
use Shlinkio\Shlink\CLI\Util\ExitCodes;
|
||||||
use Shlinkio\Shlink\CLI\Util\ShlinkTable;
|
use Shlinkio\Shlink\CLI\Util\ShlinkTable;
|
||||||
|
use Shlinkio\Shlink\Rest\ApiKey\Role;
|
||||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||||
use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface;
|
use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
@ -14,7 +15,8 @@ use Symfony\Component\Console\Input\InputOption;
|
|||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
use function array_filter;
|
use function array_filter;
|
||||||
use function array_map;
|
use function Functional\map;
|
||||||
|
use function implode;
|
||||||
use function sprintf;
|
use function sprintf;
|
||||||
|
|
||||||
class ListKeysCommand extends Command
|
class ListKeysCommand extends Command
|
||||||
@ -50,7 +52,7 @@ class ListKeysCommand extends Command
|
|||||||
{
|
{
|
||||||
$enabledOnly = $input->getOption('enabledOnly');
|
$enabledOnly = $input->getOption('enabledOnly');
|
||||||
|
|
||||||
$rows = array_map(function (ApiKey $apiKey) use ($enabledOnly) {
|
$rows = map($this->apiKeyService->listKeys($enabledOnly), function (ApiKey $apiKey) use ($enabledOnly) {
|
||||||
$expiration = $apiKey->getExpirationDate();
|
$expiration = $apiKey->getExpirationDate();
|
||||||
$messagePattern = $this->determineMessagePattern($apiKey);
|
$messagePattern = $this->determineMessagePattern($apiKey);
|
||||||
|
|
||||||
@ -60,13 +62,21 @@ class ListKeysCommand extends Command
|
|||||||
$rowData[] = sprintf($messagePattern, $this->getEnabledSymbol($apiKey));
|
$rowData[] = sprintf($messagePattern, $this->getEnabledSymbol($apiKey));
|
||||||
}
|
}
|
||||||
$rowData[] = $expiration !== null ? $expiration->toAtomString() : '-';
|
$rowData[] = $expiration !== null ? $expiration->toAtomString() : '-';
|
||||||
|
$rowData[] = $apiKey->isAdmin() ? '-' : implode("\n", $apiKey->mapRoles(
|
||||||
|
fn (string $roleName, array $meta) =>
|
||||||
|
empty($meta)
|
||||||
|
? Role::toFriendlyName($roleName)
|
||||||
|
: sprintf('%s: %s', Role::toFriendlyName($roleName), Role::domainAuthorityFromMeta($meta)),
|
||||||
|
));
|
||||||
|
|
||||||
return $rowData;
|
return $rowData;
|
||||||
}, $this->apiKeyService->listKeys($enabledOnly));
|
});
|
||||||
|
|
||||||
ShlinkTable::fromOutput($output)->render(array_filter([
|
ShlinkTable::fromOutput($output)->render(array_filter([
|
||||||
'Key',
|
'Key',
|
||||||
! $enabledOnly ? 'Is enabled' : null,
|
! $enabledOnly ? 'Is enabled' : null,
|
||||||
'Expiration date',
|
'Expiration date',
|
||||||
|
'Roles',
|
||||||
]), $rows);
|
]), $rows);
|
||||||
return ExitCodes::EXIT_SUCCESS;
|
return ExitCodes::EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -80,8 +90,6 @@ class ListKeysCommand extends Command
|
|||||||
return $apiKey->isExpired() ? self::WARNING_STRING_PATTERN : self::SUCCESS_STRING_PATTERN;
|
return $apiKey->isExpired() ? self::WARNING_STRING_PATTERN : self::SUCCESS_STRING_PATTERN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
private function getEnabledSymbol(ApiKey $apiKey): string
|
private function getEnabledSymbol(ApiKey $apiKey): string
|
||||||
{
|
{
|
||||||
return ! $apiKey->isEnabled() || $apiKey->isExpired() ? '---' : '+++';
|
return ! $apiKey->isEnabled() || $apiKey->isExpired() ? '---' : '+++';
|
||||||
|
@ -47,6 +47,7 @@ class RoleResolverTest extends TestCase
|
|||||||
|
|
||||||
public function provideRoles(): iterable
|
public function provideRoles(): iterable
|
||||||
{
|
{
|
||||||
|
$domain = (new Domain('example.com'))->setId('1');
|
||||||
$buildInput = function (array $definition): InputInterface {
|
$buildInput = function (array $definition): InputInterface {
|
||||||
$input = $this->prophesize(InputInterface::class);
|
$input = $this->prophesize(InputInterface::class);
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ class RoleResolverTest extends TestCase
|
|||||||
];
|
];
|
||||||
yield 'domain role only' => [
|
yield 'domain role only' => [
|
||||||
$buildInput([RoleResolver::DOMAIN_ONLY_PARAM => 'example.com', RoleResolver::AUTHOR_ONLY_PARAM => false]),
|
$buildInput([RoleResolver::DOMAIN_ONLY_PARAM => 'example.com', RoleResolver::AUTHOR_ONLY_PARAM => false]),
|
||||||
[RoleDefinition::forDomain('1')],
|
[RoleDefinition::forDomain($domain)],
|
||||||
1,
|
1,
|
||||||
];
|
];
|
||||||
yield 'author role only' => [
|
yield 'author role only' => [
|
||||||
@ -74,7 +75,7 @@ class RoleResolverTest extends TestCase
|
|||||||
];
|
];
|
||||||
yield 'both roles' => [
|
yield 'both roles' => [
|
||||||
$buildInput([RoleResolver::DOMAIN_ONLY_PARAM => 'example.com', RoleResolver::AUTHOR_ONLY_PARAM => true]),
|
$buildInput([RoleResolver::DOMAIN_ONLY_PARAM => 'example.com', RoleResolver::AUTHOR_ONLY_PARAM => true]),
|
||||||
[RoleDefinition::forAuthoredShortUrls(), RoleDefinition::forDomain('1')],
|
[RoleDefinition::forAuthoredShortUrls(), RoleDefinition::forDomain($domain)],
|
||||||
1,
|
1,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -72,12 +72,12 @@ class DomainRepositoryTest extends DatabaseTestCase
|
|||||||
|
|
||||||
$this->getEntityManager()->flush();
|
$this->getEntityManager()->flush();
|
||||||
|
|
||||||
$authorAndDomainApiKey->registerRole(RoleDefinition::forDomain($fooDomain->getId()));
|
$authorAndDomainApiKey->registerRole(RoleDefinition::forDomain($fooDomain));
|
||||||
|
|
||||||
$fooDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($fooDomain->getId()));
|
$fooDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($fooDomain));
|
||||||
$this->getEntityManager()->persist($fooDomainApiKey);
|
$this->getEntityManager()->persist($fooDomainApiKey);
|
||||||
|
|
||||||
$barDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($barDomain->getId()));
|
$barDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($barDomain));
|
||||||
$this->getEntityManager()->persist($fooDomainApiKey);
|
$this->getEntityManager()->persist($fooDomainApiKey);
|
||||||
|
|
||||||
$this->getEntityManager()->flush();
|
$this->getEntityManager()->flush();
|
||||||
|
@ -335,9 +335,9 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
|||||||
$this->getEntityManager()->persist($apiKey);
|
$this->getEntityManager()->persist($apiKey);
|
||||||
$otherApiKey = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls());
|
$otherApiKey = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls());
|
||||||
$this->getEntityManager()->persist($otherApiKey);
|
$this->getEntityManager()->persist($otherApiKey);
|
||||||
$wrongDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($wrongDomain->getId()));
|
$wrongDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($wrongDomain));
|
||||||
$this->getEntityManager()->persist($wrongDomainApiKey);
|
$this->getEntityManager()->persist($wrongDomainApiKey);
|
||||||
$rightDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($rightDomain->getId()));
|
$rightDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($rightDomain));
|
||||||
$this->getEntityManager()->persist($rightDomainApiKey);
|
$this->getEntityManager()->persist($rightDomainApiKey);
|
||||||
|
|
||||||
$shortUrl = new ShortUrl('foo', ShortUrlMeta::fromRawData(
|
$shortUrl = new ShortUrl('foo', ShortUrlMeta::fromRawData(
|
||||||
|
@ -114,7 +114,7 @@ class TagRepositoryTest extends DatabaseTestCase
|
|||||||
|
|
||||||
$authorApiKey = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls());
|
$authorApiKey = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls());
|
||||||
$this->getEntityManager()->persist($authorApiKey);
|
$this->getEntityManager()->persist($authorApiKey);
|
||||||
$domainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($domain->getId()));
|
$domainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($domain));
|
||||||
$this->getEntityManager()->persist($domainApiKey);
|
$this->getEntityManager()->persist($domainApiKey);
|
||||||
|
|
||||||
$names = ['foo', 'bar', 'baz', 'another'];
|
$names = ['foo', 'bar', 'baz', 'another'];
|
||||||
|
@ -221,7 +221,7 @@ class VisitRepositoryTest extends DatabaseTestCase
|
|||||||
$this->getEntityManager()->persist($shortUrl3);
|
$this->getEntityManager()->persist($shortUrl3);
|
||||||
$this->createVisitsForShortUrl($shortUrl3, 7);
|
$this->createVisitsForShortUrl($shortUrl3, 7);
|
||||||
|
|
||||||
$domainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($domain->getId()));
|
$domainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($domain));
|
||||||
$this->getEntityManager()->persist($domainApiKey);
|
$this->getEntityManager()->persist($domainApiKey);
|
||||||
|
|
||||||
$this->getEntityManager()->flush();
|
$this->getEntityManager()->flush();
|
||||||
|
@ -51,7 +51,7 @@ class DomainServiceTest extends TestCase
|
|||||||
{
|
{
|
||||||
$default = new DomainItem('default.com', true);
|
$default = new DomainItem('default.com', true);
|
||||||
$adminApiKey = new ApiKey();
|
$adminApiKey = new ApiKey();
|
||||||
$domainSpecificApiKey = ApiKey::withRoles(RoleDefinition::forDomain('123'));
|
$domainSpecificApiKey = ApiKey::withRoles(RoleDefinition::forDomain((new Domain(''))->setId('123')));
|
||||||
|
|
||||||
yield 'empty list without API key' => [[], [$default], null];
|
yield 'empty list without API key' => [[], [$default], null];
|
||||||
yield 'one item without API key' => [
|
yield 'one item without API key' => [
|
||||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Shlinkio\Shlink\Rest\ApiKey\Model;
|
namespace Shlinkio\Shlink\Rest\ApiKey\Model;
|
||||||
|
|
||||||
|
use Shlinkio\Shlink\Core\Entity\Domain;
|
||||||
use Shlinkio\Shlink\Rest\ApiKey\Role;
|
use Shlinkio\Shlink\Rest\ApiKey\Role;
|
||||||
|
|
||||||
final class RoleDefinition
|
final class RoleDefinition
|
||||||
@ -22,9 +23,12 @@ final class RoleDefinition
|
|||||||
return new self(Role::AUTHORED_SHORT_URLS, []);
|
return new self(Role::AUTHORED_SHORT_URLS, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function forDomain(string $domainId): self
|
public static function forDomain(Domain $domain): self
|
||||||
{
|
{
|
||||||
return new self(Role::DOMAIN_SPECIFIC, ['domain_id' => $domainId]);
|
return new self(
|
||||||
|
Role::DOMAIN_SPECIFIC,
|
||||||
|
['domain_id' => $domain->getId(), 'authority' => $domain->getAuthority()],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function roleName(): string
|
public function roleName(): string
|
||||||
|
@ -16,6 +16,10 @@ class Role
|
|||||||
{
|
{
|
||||||
public const AUTHORED_SHORT_URLS = 'AUTHORED_SHORT_URLS';
|
public const AUTHORED_SHORT_URLS = 'AUTHORED_SHORT_URLS';
|
||||||
public const DOMAIN_SPECIFIC = 'DOMAIN_SPECIFIC';
|
public const DOMAIN_SPECIFIC = 'DOMAIN_SPECIFIC';
|
||||||
|
private const ROLE_FRIENDLY_NAMES = [
|
||||||
|
self::AUTHORED_SHORT_URLS => 'Author only',
|
||||||
|
self::DOMAIN_SPECIFIC => 'Domain only',
|
||||||
|
];
|
||||||
|
|
||||||
public static function toSpec(ApiKeyRole $role, bool $inlined): Specification
|
public static function toSpec(ApiKeyRole $role, bool $inlined): Specification
|
||||||
{
|
{
|
||||||
@ -35,4 +39,14 @@ class Role
|
|||||||
{
|
{
|
||||||
return $meta['domain_id'] ?? '-1';
|
return $meta['domain_id'] ?? '-1';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function domainAuthorityFromMeta(array $meta): string
|
||||||
|
{
|
||||||
|
return $meta['authority'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function toFriendlyName(string $roleName): string
|
||||||
|
{
|
||||||
|
return self::ROLE_FRIENDLY_NAMES[$roleName] ?? '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ class ApiKeyFixture extends AbstractFixture implements DependentFixtureInterface
|
|||||||
/** @var Domain $exampleDomain */
|
/** @var Domain $exampleDomain */
|
||||||
$exampleDomain = $this->getReference('example_domain');
|
$exampleDomain = $this->getReference('example_domain');
|
||||||
$domainApiKey = $this->buildApiKey('domain_api_key', true);
|
$domainApiKey = $this->buildApiKey('domain_api_key', true);
|
||||||
$domainApiKey->registerRole(RoleDefinition::forDomain($exampleDomain->getId()));
|
$domainApiKey->registerRole(RoleDefinition::forDomain($exampleDomain));
|
||||||
$manager->persist($domainApiKey);
|
$manager->persist($domainApiKey);
|
||||||
|
|
||||||
$manager->flush();
|
$manager->flush();
|
||||||
|
@ -12,6 +12,7 @@ use Prophecy\Argument;
|
|||||||
use Prophecy\PhpUnit\ProphecyTrait;
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
|
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
|
||||||
|
use Shlinkio\Shlink\Core\Entity\Domain;
|
||||||
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
|
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
|
||||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||||
use Shlinkio\Shlink\Rest\Service\ApiKeyService;
|
use Shlinkio\Shlink\Rest\Service\ApiKeyService;
|
||||||
@ -51,7 +52,10 @@ class ApiKeyServiceTest extends TestCase
|
|||||||
{
|
{
|
||||||
yield 'no expiration date' => [null, []];
|
yield 'no expiration date' => [null, []];
|
||||||
yield 'expiration date' => [Chronos::parse('2030-01-01'), []];
|
yield 'expiration date' => [Chronos::parse('2030-01-01'), []];
|
||||||
yield 'roles' => [null, [RoleDefinition::forDomain('123'), RoleDefinition::forAuthoredShortUrls()]];
|
yield 'roles' => [null, [
|
||||||
|
RoleDefinition::forDomain((new Domain(''))->setId('123')),
|
||||||
|
RoleDefinition::forAuthoredShortUrls(),
|
||||||
|
]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user