diff --git a/module/CLI/config/dependencies.config.php b/module/CLI/config/dependencies.config.php
index 313d0022..3c9d74ce 100644
--- a/module/CLI/config/dependencies.config.php
+++ b/module/CLI/config/dependencies.config.php
@@ -8,7 +8,6 @@ use Doctrine\DBAL\Connection;
use GeoIp2\Database\Reader;
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use Laminas\ServiceManager\Factory\InvokableFactory;
-use Shlinkio\Shlink\CLI\Util\GeolocationDbUpdater;
use Shlinkio\Shlink\Common\Doctrine\NoDbNameConnectionFactory;
use Shlinkio\Shlink\Core\Domain\DomainService;
use Shlinkio\Shlink\Core\Service;
@@ -32,7 +31,8 @@ return [
SymfonyCli\Helper\ProcessHelper::class => ProcessHelperFactory::class,
PhpExecutableFinder::class => InvokableFactory::class,
- GeolocationDbUpdater::class => ConfigAbstractFactory::class,
+ Util\GeolocationDbUpdater::class => ConfigAbstractFactory::class,
+ ApiKey\RoleResolver::class => ConfigAbstractFactory::class,
Command\ShortUrl\GenerateShortUrlCommand::class => ConfigAbstractFactory::class,
Command\ShortUrl\ResolveUrlCommand::class => ConfigAbstractFactory::class,
@@ -59,7 +59,8 @@ return [
],
ConfigAbstractFactory::class => [
- GeolocationDbUpdater::class => [DbUpdater::class, Reader::class, LOCAL_LOCK_FACTORY],
+ Util\GeolocationDbUpdater::class => [DbUpdater::class, Reader::class, LOCAL_LOCK_FACTORY],
+ ApiKey\RoleResolver::class => [DomainService::class],
Command\ShortUrl\GenerateShortUrlCommand::class => [
Service\UrlShortener::class,
@@ -75,10 +76,10 @@ return [
Visit\VisitLocator::class,
IpLocationResolverInterface::class,
LockFactory::class,
- GeolocationDbUpdater::class,
+ Util\GeolocationDbUpdater::class,
],
- Command\Api\GenerateKeyCommand::class => [ApiKeyService::class],
+ Command\Api\GenerateKeyCommand::class => [ApiKeyService::class, ApiKey\RoleResolver::class],
Command\Api\DisableKeyCommand::class => [ApiKeyService::class],
Command\Api\ListKeysCommand::class => [ApiKeyService::class],
diff --git a/module/CLI/src/ApiKey/RoleResolver.php b/module/CLI/src/ApiKey/RoleResolver.php
new file mode 100644
index 00000000..d007697a
--- /dev/null
+++ b/module/CLI/src/ApiKey/RoleResolver.php
@@ -0,0 +1,36 @@
+domainService = $domainService;
+ }
+
+ public function determineRoles(InputInterface $input): array
+ {
+ $domainAuthority = $input->getOption('domain-only');
+ $author = $input->getOption('author-only');
+
+ $roleDefinitions = [];
+ if ($author) {
+ $roleDefinitions[] = RoleDefinition::forAuthoredShortUrls();
+ }
+ if ($domainAuthority !== null) {
+ $domain = $this->domainService->getOrCreate($domainAuthority);
+ $roleDefinitions[] = RoleDefinition::forDomain($domain->getId());
+ }
+
+ return $roleDefinitions;
+ }
+}
diff --git a/module/CLI/src/ApiKey/RoleResolverInterface.php b/module/CLI/src/ApiKey/RoleResolverInterface.php
new file mode 100644
index 00000000..98d50483
--- /dev/null
+++ b/module/CLI/src/ApiKey/RoleResolverInterface.php
@@ -0,0 +1,19 @@
+%command.name% generates a new valid API key.
+
+ %command.full_name%
+
+ You can optionally set its expiration date with --expirationDate or -e:
+
+ %command.full_name% --expirationDate 2020-01-01
+
+ You can also set roles to the API key:
+
+ * Can interact with short URLs created with this API key: %command.full_name% --author-only
+ * Can interact with short URLs for one domain only: %command.full_name% --domain-only=example.com
+ * Both: %command.full_name% --author-only --domain-only=example.com
+ HELP;
private ApiKeyServiceInterface $apiKeyService;
+ private RoleResolverInterface $roleResolver;
- public function __construct(ApiKeyServiceInterface $apiKeyService)
+ public function __construct(ApiKeyServiceInterface $apiKeyService, RoleResolverInterface $roleResolver)
{
- $this->apiKeyService = $apiKeyService;
parent::__construct();
+ $this->apiKeyService = $apiKeyService;
+ $this->roleResolver = $roleResolver;
}
protected function configure(): void
@@ -37,15 +56,33 @@ class GenerateKeyCommand extends Command
'e',
InputOption::VALUE_REQUIRED,
'The date in which the API key should expire. Use any valid PHP format.',
- );
+ )
+ ->addOption(
+ RoleResolverInterface::AUTHOR_ONLY_PARAM,
+ 'a',
+ InputOption::VALUE_NONE,
+ sprintf('Adds the "%s" role to the new API key.', Role::AUTHORED_SHORT_URLS),
+ )
+ ->addOption(
+ RoleResolverInterface::DOMAIN_ONLY_PARAM,
+ 'd',
+ InputOption::VALUE_REQUIRED,
+ sprintf('Adds the "%s" role to the new API key, with the domain provided.', Role::DOMAIN_SPECIFIC),
+ )
+ ->setHelp(self::HELP);
}
protected function execute(InputInterface $input, OutputInterface $output): ?int
{
$expirationDate = $input->getOption('expirationDate');
- $apiKey = $this->apiKeyService->create(isset($expirationDate) ? Chronos::parse($expirationDate) : null);
+ $apiKey = $this->apiKeyService->create(
+ isset($expirationDate) ? Chronos::parse($expirationDate) : null,
+ ...$this->roleResolver->determineRoles($input),
+ );
+
+ // TODO Print permissions that have been set
+ (new SymfonyStyle($input, $output))->success(sprintf('Generated API key: "%s"', $apiKey->toString()));
- (new SymfonyStyle($input, $output))->success(sprintf('Generated API key: "%s"', $apiKey));
return ExitCodes::EXIT_SUCCESS;
}
}
diff --git a/module/CLI/test/Command/Api/GenerateKeyCommandTest.php b/module/CLI/test/Command/Api/GenerateKeyCommandTest.php
index 7ff87a3f..744fb482 100644
--- a/module/CLI/test/Command/Api/GenerateKeyCommandTest.php
+++ b/module/CLI/test/Command/Api/GenerateKeyCommandTest.php
@@ -9,10 +9,12 @@ use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
+use Shlinkio\Shlink\CLI\ApiKey\RoleResolverInterface;
use Shlinkio\Shlink\CLI\Command\Api\GenerateKeyCommand;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface;
use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Tester\CommandTester;
class GenerateKeyCommandTest extends TestCase
@@ -21,11 +23,15 @@ class GenerateKeyCommandTest extends TestCase
private CommandTester $commandTester;
private ObjectProphecy $apiKeyService;
+ private ObjectProphecy $roleResolver;
public function setUp(): void
{
$this->apiKeyService = $this->prophesize(ApiKeyServiceInterface::class);
- $command = new GenerateKeyCommand($this->apiKeyService->reveal());
+ $this->roleResolver = $this->prophesize(RoleResolverInterface::class);
+ $this->roleResolver->determineRoles(Argument::type(InputInterface::class))->willReturn([]);
+
+ $command = new GenerateKeyCommand($this->apiKeyService->reveal(), $this->roleResolver->reveal());
$app = new Application();
$app->add($command);
$this->commandTester = new CommandTester($command);