Skip to content

Commit

Permalink
Add console commands for client management (#17)
Browse files Browse the repository at this point in the history
* Add console commands for client management
* Update docs with commands info
  • Loading branch information
ajgarlag authored and alenpokos committed Feb 19, 2019
1 parent 9f5937b commit 2425b3d
Show file tree
Hide file tree
Showing 6 changed files with 495 additions and 16 deletions.
112 changes: 112 additions & 0 deletions Command/CreateClientCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

namespace Trikoder\Bundle\OAuth2Bundle\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Trikoder\Bundle\OAuth2Bundle\Manager\ClientManagerInterface;
use Trikoder\Bundle\OAuth2Bundle\Model\Client;
use Trikoder\Bundle\OAuth2Bundle\Model\Grant;
use Trikoder\Bundle\OAuth2Bundle\Model\RedirectUri;
use Trikoder\Bundle\OAuth2Bundle\Model\Scope;

final class CreateClientCommand extends Command
{
protected static $defaultName = 'trikoder:oauth2:create-client';

private $clientManager;

public function __construct(ClientManagerInterface $clientManager)
{
parent::__construct();

$this->clientManager = $clientManager;
}

protected function configure()
{
$this
->setDescription('Creates a new oAuth2 client')
->addOption(
'redirect-uri',
null,
InputOption::VALUE_OPTIONAL | 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,
'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,
'Sets allowed scope for client. Use this option multiple times to set multiple scopes.',
null
)
->addArgument(
'identifier',
InputArgument::OPTIONAL,
'The client identifier'
)
->addArgument(
'secret',
InputArgument::OPTIONAL,
'The client secret'
)
;
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$client = $this->buildClientFromInput($input);
$this->clientManager->save($client);
$io->success('New oAuth2 client created successfully.');

$headers = ['Identifier', 'Secret'];
$rows = [
[$client->getIdentifier(), $client->getSecret()],
];
$io->table($headers, $rows);

return 0;
}

private function buildClientFromInput(InputInterface $input)
{
$identifier = $input->getArgument('identifier') ?? hash('md5', random_bytes(16));
$secret = $input->getArgument('secret') ?? hash('sha512', random_bytes(32));

$client = new Client($identifier, $secret);
$client->setActive(true);

$redirectUris = array_map(
function (string $redirectUri) { return new RedirectUri($redirectUri); },
$input->getOption('redirect-uri')
);
$client->setRedirectUris(...$redirectUris);

$grants = array_map(
function (string $grant) { return new Grant($grant); },
$input->getOption('grant-type')
);
$client->setGrants(...$grants);

$scopes = array_map(
function (string $scope) { return new Scope($scope); },
$input->getOption('scope')
);
$client->setScopes(...$scopes);

return $client;
}
}
110 changes: 110 additions & 0 deletions Command/UpdateClientCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

namespace Trikoder\Bundle\OAuth2Bundle\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Trikoder\Bundle\OAuth2Bundle\Manager\ClientManagerInterface;
use Trikoder\Bundle\OAuth2Bundle\Model\Client;
use Trikoder\Bundle\OAuth2Bundle\Model\Grant;
use Trikoder\Bundle\OAuth2Bundle\Model\RedirectUri;
use Trikoder\Bundle\OAuth2Bundle\Model\Scope;

final class UpdateClientCommand extends Command
{
protected static $defaultName = 'trikoder:oauth2:update-client';

private $clientManager;

public function __construct(ClientManagerInterface $clientManager)
{
parent::__construct();

$this->clientManager = $clientManager;
}

protected function configure()
{
$this
->setDescription('Updates an oAuth2 client')
->addOption(
'redirect-uri',
null,
InputOption::VALUE_OPTIONAL | 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,
'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,
'Sets allowed scope for client. Use this option multiple times to set multiple scopes.',
null
)
->addOption(
'deactivated',
null,
InputOption::VALUE_NONE,
'If provided, it will deactivate the given client.'
)
->addArgument(
'identifier',
InputArgument::REQUIRED,
'The client ID'
)
;
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);

if (null === $client = $this->clientManager->find($input->getArgument('identifier'))) {
$io->error(sprintf('oAuth2 client identified as "%s"', $input->getArgument('identifier')));

return 1;
}

$client = $this->updateClientFromInput($client, $input);
$this->clientManager->save($client);
$io->success('Given oAuth2 client updated successfully.');

return 0;
}

private function updateClientFromInput(Client $client, InputInterface $input): Client
{
$client->setActive(!$input->getOption('deactivated'));

$redirectUris = array_map(
function (string $redirectUri) { return new RedirectUri($redirectUri); },
$input->getOption('redirect-uri')
);
$client->setRedirectUris(...$redirectUris);

$grants = array_map(
function (string $grant) { return new Grant($grant); },
$input->getOption('grant-type')
);
$client->setGrants(...$grants);

$scopes = array_map(
function (string $scope) { return new Scope($scope); },
$input->getOption('scope')
);
$client->setScopes(...$scopes);

return $client;
}
}
10 changes: 10 additions & 0 deletions Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@
<tag name="controller.service_arguments" />
</service>

<!-- Commands -->
<service id="trikoder.oauth2.command.create_client_command" class="Trikoder\Bundle\OAuth2Bundle\Command\CreateClientCommand">
<argument type="service" id="Trikoder\Bundle\OAuth2Bundle\Manager\ClientManagerInterface" />
<tag name="console.command" />
</service>
<service id="trikoder.oauth2.command.update_client_command" class="Trikoder\Bundle\OAuth2Bundle\Command\UpdateClientCommand">
<argument type="service" id="Trikoder\Bundle\OAuth2Bundle\Manager\ClientManagerInterface" />
<tag name="console.command" />
</service>

<!-- Utility services -->
<service id="trikoder.oauth2.converter.scope_converter" class="Trikoder\Bundle\OAuth2Bundle\Converter\ScopeConverter" />
</services>
Expand Down
122 changes: 122 additions & 0 deletions Tests/Acceptance/CreateClientCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php

namespace Trikoder\Bundle\OAuth2Bundle\Tests\Acceptance;

use Symfony\Component\Console\Tester\CommandTester;
use Trikoder\Bundle\OAuth2Bundle\Manager\ClientManagerInterface;
use Trikoder\Bundle\OAuth2Bundle\Model\Client;

final class CreateClientCommandTest extends AbstractAcceptanceTest
{
public function testCreateClient()
{
$command = $this->application->find('trikoder:oauth2:create-client');
$commandTester = new CommandTester($command);
$commandTester->execute([
'command' => $command->getName(),
]);

$output = $commandTester->getDisplay();
$this->assertContains('New oAuth2 client created successfully', $output);
}

public function testCreateClientWithIdentifier()
{
$command = $this->application->find('trikoder:oauth2:create-client');
$commandTester = new CommandTester($command);
$commandTester->execute([
'command' => $command->getName(),
'identifier' => 'foobar',
]);

$output = $commandTester->getDisplay();
$this->assertContains('New oAuth2 client created successfully', $output);
$this->assertContains('foobar', $output);

$client = $this->client
->getContainer()
->get(ClientManagerInterface::class)
->find('foobar');
$this->assertInstanceOf(Client::class, $client);
}

public function testCreateClientWithSecret()
{
$command = $this->application->find('trikoder:oauth2:create-client');
$commandTester = new CommandTester($command);
$commandTester->execute([
'command' => $command->getName(),
'identifier' => 'foobar',
'secret' => 'quzbaz',
]);

$output = $commandTester->getDisplay();
$this->assertContains('New oAuth2 client created successfully', $output);
$client = $this->client
->getContainer()
->get(ClientManagerInterface::class)
->find('foobar');
$this->assertInstanceOf(Client::class, $client);
$this->assertSame('quzbaz', $client->getSecret());
}

public function testCreateClientWithRedirectUris()
{
$command = $this->application->find('trikoder:oauth2:create-client');
$commandTester = new CommandTester($command);
$commandTester->execute([
'command' => $command->getName(),
'identifier' => 'foobar',
'--redirect-uri' => ['http://example.org', 'http://example.org'],
]);

$output = $commandTester->getDisplay();
$this->assertContains('New oAuth2 client created successfully', $output);
$client = $this->client
->getContainer()
->get(ClientManagerInterface::class)
->find('foobar');
$this->assertInstanceOf(Client::class, $client);
$this->assertCount(2, $client->getRedirectUris());
}

public function testCreateClientWithGrantTypes()
{
$command = $this->application->find('trikoder:oauth2:create-client');
$commandTester = new CommandTester($command);
$commandTester->execute([
'command' => $command->getName(),
'identifier' => 'foobar',
'--grant-type' => ['password', 'client_credentials'],
]);

$output = $commandTester->getDisplay();
$this->assertContains('New oAuth2 client created successfully', $output);
$client = $this->client
->getContainer()
->get(ClientManagerInterface::class)
->find('foobar');
$this->assertInstanceOf(Client::class, $client);
$this->assertCount(2, $client->getGrants());
}

public function testCreateClientWithScopes()
{
$command = $this->application->find('trikoder:oauth2:create-client');
$commandTester = new CommandTester($command);
$commandTester->execute([
'command' => $command->getName(),
'identifier' => 'foobar',
'--scope' => ['foo', 'bar'],
]);

$output = $commandTester->getDisplay();
$this->assertContains('New oAuth2 client created successfully', $output);
$client = $this->client
->getContainer()
->get(ClientManagerInterface::class)
->find('foobar');
$this->assertInstanceOf(Client::class, $client);
$this->assertCount(2, $client->getScopes());
}
}
Loading

0 comments on commit 2425b3d

Please sign in to comment.