diff --git a/Command/CreateClientCommand.php b/Command/CreateClientCommand.php
index 5d4b8208..fbbcad6f 100644
--- a/Command/CreateClientCommand.php
+++ b/Command/CreateClientCommand.php
@@ -34,23 +34,23 @@ protected function configure()
->addOption(
'redirect-uri',
null,
- InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Sets redirect uri for client. Use this option multiple times to set multiple redirect URIs.',
- null
+ []
)
->addOption(
'grant-type',
null,
- InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Sets allowed grant type for client. Use this option multiple times to set multiple grant types.',
- null
+ []
)
->addOption(
'scope',
null,
- InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Sets allowed scope for client. Use this option multiple times to set multiple scopes.',
- null
+ []
)
->addArgument(
'identifier',
@@ -81,7 +81,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
return 0;
}
- private function buildClientFromInput(InputInterface $input)
+ private function buildClientFromInput(InputInterface $input): Client
{
$identifier = $input->getArgument('identifier') ?? hash('md5', random_bytes(16));
$secret = $input->getArgument('secret') ?? hash('sha512', random_bytes(32));
@@ -90,19 +90,19 @@ private function buildClientFromInput(InputInterface $input)
$client->setActive(true);
$redirectUris = array_map(
- function (string $redirectUri) { return new RedirectUri($redirectUri); },
+ function (string $redirectUri): RedirectUri { return new RedirectUri($redirectUri); },
$input->getOption('redirect-uri')
);
$client->setRedirectUris(...$redirectUris);
$grants = array_map(
- function (string $grant) { return new Grant($grant); },
+ function (string $grant): Grant { return new Grant($grant); },
$input->getOption('grant-type')
);
$client->setGrants(...$grants);
$scopes = array_map(
- function (string $scope) { return new Scope($scope); },
+ function (string $scope): Scope { return new Scope($scope); },
$input->getOption('scope')
);
$client->setScopes(...$scopes);
diff --git a/Command/DeleteClientCommand.php b/Command/DeleteClientCommand.php
new file mode 100644
index 00000000..257ff773
--- /dev/null
+++ b/Command/DeleteClientCommand.php
@@ -0,0 +1,50 @@
+clientManager = $clientManager;
+ }
+
+ protected function configure(): void
+ {
+ $this
+ ->setDescription('Deletes an oAuth2 client')
+ ->addArgument(
+ 'identifier',
+ InputArgument::REQUIRED,
+ 'The client ID'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $io = new SymfonyStyle($input, $output);
+ $identifier = $input->getArgument('identifier');
+ $client = $this->clientManager->find($identifier);
+ if (null === $client) {
+ $io->error(sprintf('oAuth2 client identified as "%s" does not exist', $identifier));
+
+ return 1;
+ }
+ $this->clientManager->remove($client);
+ $io->success('Given oAuth2 client deleted successfully.');
+
+ return 0;
+ }
+}
diff --git a/Command/ListClientsCommand.php b/Command/ListClientsCommand.php
new file mode 100644
index 00000000..ae45b1dc
--- /dev/null
+++ b/Command/ListClientsCommand.php
@@ -0,0 +1,127 @@
+clientManager = $clientManager;
+ }
+
+ protected function configure(): void
+ {
+ $this
+ ->setDescription('Lists existing oAuth2 clients')
+ ->addOption(
+ 'columns',
+ null,
+ InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
+ 'Determine which columns are shown. Can be used multiple times to specify multiple columns.',
+ self::ALLOWED_COLUMNS
+ )
+ ->addOption(
+ 'redirect-uri',
+ null,
+ InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
+ 'Finds by redirect uri for client. Use this option multiple times to filter by multiple redirect URIs.',
+ []
+ )
+ ->addOption(
+ 'grant-type',
+ null,
+ InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
+ 'Finds by allowed grant type for client. Use this option multiple times to filter by multiple grant types.',
+ []
+ )
+ ->addOption(
+ 'scope',
+ null,
+ InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
+ 'Finds by allowed scope for client. Use this option multiple times to find by multiple scopes.',
+ []
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $criteria = $this->getFindByCriteria($input);
+ $clients = $this->clientManager->list($criteria);
+ $this->drawTable($input, $output, $clients);
+
+ return 0;
+ }
+
+ private function getFindByCriteria(InputInterface $input): ClientFilter
+ {
+ return
+ ClientFilter
+ ::create()
+ ->addGrantCriteria(...array_map(function (string $grant): Grant {
+ return new Grant($grant);
+ }, $input->getOption('grant-type')))
+ ->addRedirectUriCriteria(...array_map(function (string $redirectUri): RedirectUri {
+ return new RedirectUri($redirectUri);
+ }, $input->getOption('redirect-uri')))
+ ->addScopeCriteria(...array_map(function (string $scope): Scope {
+ return new Scope($scope);
+ }, $input->getOption('scope')))
+ ;
+ }
+
+ private function drawTable(InputInterface $input, OutputInterface $output, array $clients): void
+ {
+ $io = new SymfonyStyle($input, $output);
+ $columns = $this->getColumns($input);
+ $rows = $this->getRows($clients, $columns);
+ $io->table($columns, $rows);
+ }
+
+ private function getRows(array $clients, array $columns): array
+ {
+ return array_map(function (Client $client) use ($columns): array {
+ $values = [
+ 'identifier' => $client->getIdentifier(),
+ 'secret' => $client->getSecret(),
+ 'scope' => implode(', ', $client->getScopes()),
+ 'redirect uri' => implode(', ', $client->getRedirectUris()),
+ 'grant type' => implode(', ', $client->getGrants()),
+ ];
+
+ return array_map(function (string $column) use ($values): string {
+ return $values[$column];
+ }, $columns);
+ }, $clients);
+ }
+
+ private function getColumns(InputInterface $input): array
+ {
+ $allowedColumns = self::ALLOWED_COLUMNS;
+
+ $requestedColumns = $input->getOption('columns');
+ $requestedColumns = array_map(function (string $column): string {
+ return strtolower(trim($column));
+ }, $requestedColumns);
+
+ return array_intersect($requestedColumns, $allowedColumns);
+ }
+}
diff --git a/Command/UpdateClientCommand.php b/Command/UpdateClientCommand.php
index fa9908ca..d6f8e278 100644
--- a/Command/UpdateClientCommand.php
+++ b/Command/UpdateClientCommand.php
@@ -27,30 +27,30 @@ public function __construct(ClientManagerInterface $clientManager)
$this->clientManager = $clientManager;
}
- protected function configure()
+ protected function configure(): void
{
$this
->setDescription('Updates an oAuth2 client')
->addOption(
'redirect-uri',
null,
- InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Sets redirect uri for client. Use this option multiple times to set multiple redirect URIs.',
- null
+ []
)
->addOption(
'grant-type',
null,
- InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Sets allowed grant type for client. Use this option multiple times to set multiple grant types.',
- null
+ []
)
->addOption(
'scope',
null,
- InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Sets allowed scope for client. Use this option multiple times to set multiple scopes.',
- null
+ []
)
->addOption(
'deactivated',
@@ -66,7 +66,7 @@ protected function configure()
;
}
- protected function execute(InputInterface $input, OutputInterface $output)
+ protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
@@ -88,19 +88,19 @@ private function updateClientFromInput(Client $client, InputInterface $input): C
$client->setActive(!$input->getOption('deactivated'));
$redirectUris = array_map(
- function (string $redirectUri) { return new RedirectUri($redirectUri); },
+ function (string $redirectUri): RedirectUri { return new RedirectUri($redirectUri); },
$input->getOption('redirect-uri')
);
$client->setRedirectUris(...$redirectUris);
$grants = array_map(
- function (string $grant) { return new Grant($grant); },
+ function (string $grant): Grant { return new Grant($grant); },
$input->getOption('grant-type')
);
$client->setGrants(...$grants);
$scopes = array_map(
- function (string $scope) { return new Scope($scope); },
+ function (string $scope): Scope { return new Scope($scope); },
$input->getOption('scope')
);
$client->setScopes(...$scopes);
diff --git a/Manager/ClientFilter.php b/Manager/ClientFilter.php
new file mode 100644
index 00000000..e1ca217e
--- /dev/null
+++ b/Manager/ClientFilter.php
@@ -0,0 +1,86 @@
+addCriteria($this->grants, ...$grants);
+ }
+
+ public function addRedirectUriCriteria(RedirectUri ...$redirectUris): self
+ {
+ return $this->addCriteria($this->redirectUris, ...$redirectUris);
+ }
+
+ public function addScopeCriteria(Scope ...$scopes): self
+ {
+ return $this->addCriteria($this->scopes, ...$scopes);
+ }
+
+ private function addCriteria(&$field, ...$values): self
+ {
+ if (0 === \count($values)) {
+ return $this;
+ }
+
+ $field = array_merge($field, $values);
+
+ return $this;
+ }
+
+ /**
+ * @return Grant[]
+ */
+ public function getGrants(): array
+ {
+ return $this->grants;
+ }
+
+ /**
+ * @return RedirectUri[]
+ */
+ public function getRedirectUris(): array
+ {
+ return $this->redirectUris;
+ }
+
+ /**
+ * @return Scope[]
+ */
+ public function getScopes(): array
+ {
+ return $this->scopes;
+ }
+
+ public function hasFilters(): bool
+ {
+ return
+ !empty($this->grants)
+ || !empty($this->redirectUris)
+ || !empty($this->scopes);
+ }
+}
diff --git a/Manager/ClientManagerInterface.php b/Manager/ClientManagerInterface.php
index cb12f490..4560e45e 100644
--- a/Manager/ClientManagerInterface.php
+++ b/Manager/ClientManagerInterface.php
@@ -9,4 +9,11 @@ interface ClientManagerInterface
public function find(string $identifier): ?Client;
public function save(Client $client): void;
+
+ public function remove(Client $client): void;
+
+ /**
+ * @return Client[]
+ */
+ public function list(?ClientFilter $clientFilter): array;
}
diff --git a/Manager/Doctrine/ClientManager.php b/Manager/Doctrine/ClientManager.php
index 19d16add..70e13d20 100644
--- a/Manager/Doctrine/ClientManager.php
+++ b/Manager/Doctrine/ClientManager.php
@@ -3,6 +3,7 @@
namespace Trikoder\Bundle\OAuth2Bundle\Manager\Doctrine;
use Doctrine\ORM\EntityManagerInterface;
+use Trikoder\Bundle\OAuth2Bundle\Manager\ClientFilter;
use Trikoder\Bundle\OAuth2Bundle\Manager\ClientManagerInterface;
use Trikoder\Bundle\OAuth2Bundle\Model\Client;
@@ -34,4 +35,50 @@ public function save(Client $client): void
$this->entityManager->persist($client);
$this->entityManager->flush();
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove(Client $client): void
+ {
+ $this->entityManager->remove($client);
+ $this->entityManager->flush();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function list(?ClientFilter $clientFilter): array
+ {
+ $repository = $this->entityManager->getRepository(Client::class);
+ $criteria = self::filterToCriteria($clientFilter);
+
+ return $repository->findBy($criteria);
+ }
+
+ private static function filterToCriteria(?ClientFilter $clientFilter): array
+ {
+ if (null === $clientFilter || false === $clientFilter->hasFilters()) {
+ return [];
+ }
+
+ $criteria = [];
+
+ $grants = $clientFilter->getGrants();
+ if ($grants) {
+ $criteria['grants'] = $grants;
+ }
+
+ $redirectUris = $clientFilter->getRedirectUris();
+ if ($redirectUris) {
+ $criteria['redirect_uris'] = $redirectUris;
+ }
+
+ $scopes = $clientFilter->getScopes();
+ if ($scopes) {
+ $criteria['scopes'] = $scopes;
+ }
+
+ return $criteria;
+ }
}
diff --git a/Manager/InMemory/ClientManager.php b/Manager/InMemory/ClientManager.php
index 4d6d765c..70e85cd6 100644
--- a/Manager/InMemory/ClientManager.php
+++ b/Manager/InMemory/ClientManager.php
@@ -2,6 +2,7 @@
namespace Trikoder\Bundle\OAuth2Bundle\Manager\InMemory;
+use Trikoder\Bundle\OAuth2Bundle\Manager\ClientFilter;
use Trikoder\Bundle\OAuth2Bundle\Manager\ClientManagerInterface;
use Trikoder\Bundle\OAuth2Bundle\Model\Client;
@@ -27,4 +28,44 @@ public function save(Client $client): void
{
$this->clients[$client->getIdentifier()] = $client;
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove(Client $client): void
+ {
+ unset($this->clients[$client->getIdentifier()]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function list(?ClientFilter $clientFilter): array
+ {
+ if (!$clientFilter || !$clientFilter->hasFilters()) {
+ return $this->clients;
+ }
+
+ return array_filter($this->clients, function (Client $client) use ($clientFilter) {
+ $grantsPassed = self::passesFilter($client->getGrants(), $clientFilter->getGrants());
+ $scopesPassed = self::passesFilter($client->getScopes(), $clientFilter->getScopes());
+ $redirectUrisPassed = self::passesFilter($client->getRedirectUris(), $clientFilter->getRedirectUris());
+
+ return $grantsPassed && $scopesPassed && $redirectUrisPassed;
+ });
+ }
+
+ private static function passesFilter(array $clientValues, array $filterValues): bool
+ {
+ if (empty($filterValues)) {
+ return true;
+ }
+
+ $clientValues = array_map('strval', $clientValues);
+ $filterValues = array_map('strval', $filterValues);
+
+ $valuesPassed = array_intersect($filterValues, $clientValues);
+
+ return \count($valuesPassed) > 0;
+ }
}
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
index edfc68a3..7e50064c 100644
--- a/Resources/config/services.xml
+++ b/Resources/config/services.xml
@@ -82,6 +82,14 @@