Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow user to use his own Client entity #25

Merged
merged 1 commit into from
May 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .psalm.baseline.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.7.1@cd53e047a58f71f646dd6bf45476076ab07b5d44">
<file src="src/Persistence/Mapping/Driver.php">
<ArgumentTypeCoercion occurrences="5">
<code>$metadata</code>
<code>$metadata</code>
<code>$metadata</code>
<code>$metadata</code>
<code>$metadata</code>
</ArgumentTypeCoercion>
</file>
<file src="src/Resources/config/routes.php">
<InvalidArgument occurrences="2">
<code>['league.oauth2_server.controller.authorization', 'indexAction']</code>
Expand Down
2 changes: 2 additions & 0 deletions docs/basic-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Usage:
league:oauth2-server:create-client [options] [--] [<identifier> [<secret>]]

Arguments:
name The client name
identifier The client identifier
secret The client secret

Expand Down Expand Up @@ -46,6 +47,7 @@ Options:
--redirect-uri[=REDIRECT-URI] Sets redirect uri for client. Use this option multiple times to set multiple redirect URIs. (multiple values allowed)
--grant-type[=GRANT-TYPE] Sets allowed grant type for client. Use this option multiple times to set multiple grant types. (multiple values allowed)
--scope[=SCOPE] Sets allowed scope for client. Use this option multiple times to set multiple scopes. (multiple values allowed)
--name=[=NAME] Sets name for client.
--deactivated If provided, it will deactivate the given client.
```

Expand Down
5 changes: 5 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ For implementation into Symfony projects, please see [bundle documentation](docs

# Set a custom prefix that replaces the default 'ROLE_OAUTH2_' role prefix
role_prefix: ROLE_OAUTH2_

client:
# Set a custom client class. Must be a League\Bundle\OAuth2ServerBundle\Model\Client
classname: League\Bundle\OAuth2ServerBundle\Model\Client
```

1. Enable the bundle in `config/bundles.php` by adding it to the array:
Expand Down Expand Up @@ -138,6 +142,7 @@ security:
* [Basic setup](basic-setup.md)
* [Controlling token scopes](controlling-token-scopes.md)
* [Implementing custom grant type](implementing-custom-grant-type.md)
* [Using custom client](using-custom-client.md)

## Contributing

Expand Down
37 changes: 37 additions & 0 deletions docs/using-custom-client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Using custom client

1. Create a class that extends the `\League\Bundle\OAuth2ServerBundle\Model\AbstractClient` class.

Example:

```php
<?php

declare(strict_types=1);

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use League\Bundle\OAuth2ServerBundle\Model\AbstractClient;

/**
* @ORM\Entity
*/
class Client extends AbstractClient
{
/**
* @ORM\Column(type="string")
*/
private $image;

// other properties, getters, setters, ...
}
```

2. In order to use the new client instead of `League\Bundle\OAuth2ServerBundle\Model\Client`, edit the configuration like the following:

```yaml
league_oauth2_server:
client:
classname: App\Entity\Client
```
42 changes: 25 additions & 17 deletions src/Command/CreateClientCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace League\Bundle\OAuth2ServerBundle\Command;

use League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface;
use League\Bundle\OAuth2ServerBundle\Model\Client;
use League\Bundle\OAuth2ServerBundle\Model\AbstractClient;
use League\Bundle\OAuth2ServerBundle\Model\Grant;
use League\Bundle\OAuth2ServerBundle\Model\RedirectUri;
use League\Bundle\OAuth2ServerBundle\Model\Scope;
Expand All @@ -25,11 +25,17 @@ final class CreateClientCommand extends Command
*/
private $clientManager;

public function __construct(ClientManagerInterface $clientManager)
/**
* @var string
*/
private $clientFqcn;

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

$this->clientManager = $clientManager;
$this->clientFqcn = $clientFqcn;
}

protected function configure(): void
Expand Down Expand Up @@ -57,6 +63,18 @@ protected function configure(): void
'Sets allowed scope for client. Use this option multiple times to set multiple scopes.',
[]
)
->addOption(
'public',
null,
InputOption::VALUE_NONE,
'Create a public client.'
)
->addOption(
'allow-plain-text-pkce',
null,
InputOption::VALUE_NONE,
'Create a client who is allowed to use plain challenge method for PKCE.'
)
->addArgument(
'name',
InputArgument::REQUIRED,
Expand All @@ -72,18 +90,6 @@ protected function configure(): void
InputArgument::OPTIONAL,
'The client secret'
)
->addOption(
'public',
null,
InputOption::VALUE_NONE,
'Create a public client.'
)
->addOption(
'allow-plain-text-pkce',
null,
InputOption::VALUE_NONE,
'Create a client who is allowed to use plain challenge method for PKCE.'
)
;
}

Expand All @@ -100,7 +106,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}

$this->clientManager->save($client);
$io->success('New oAuth2 client created successfully.');
$io->success('New OAuth2 client created successfully.');

$headers = ['Identifier', 'Secret'];
$rows = [
Expand All @@ -111,9 +117,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return 0;
}

private function buildClientFromInput(InputInterface $input): Client
private function buildClientFromInput(InputInterface $input): AbstractClient
{
$name = $input->getArgument('name');

/** @var string $identifier */
$identifier = $input->getArgument('identifier') ?? hash('md5', random_bytes(16));

Expand All @@ -126,7 +133,8 @@ private function buildClientFromInput(InputInterface $input): Client
/** @var string $secret */
$secret = $isPublic ? null : $input->getArgument('secret') ?? hash('sha512', random_bytes(32));

$client = new Client($name, $identifier, $secret);
/** @var AbstractClient $client */
$client = new $this->clientFqcn($name, $identifier, $secret);
mtarld marked this conversation as resolved.
Show resolved Hide resolved
$client->setActive(true);
$client->setAllowPlainTextPkce($input->getOption('allow-plain-text-pkce'));

Expand Down
12 changes: 6 additions & 6 deletions src/Command/DeleteClientCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function __construct(ClientManagerInterface $clientManager)
protected function configure(): void
{
$this
->setDescription('Deletes an oAuth2 client')
->setDescription('Deletes an OAuth2 client')
->addArgument(
'identifier',
InputArgument::REQUIRED,
Expand All @@ -42,15 +42,15 @@ protected function configure(): void
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));

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

return 1;
}

$this->clientManager->remove($client);
$io->success('Given oAuth2 client deleted successfully.');
$io->success('OAuth2 client deleted successfully.');

return 0;
}
Expand Down
7 changes: 4 additions & 3 deletions src/Command/ListClientsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use League\Bundle\OAuth2ServerBundle\Manager\ClientFilter;
use League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface;
use League\Bundle\OAuth2ServerBundle\Model\Client;
use League\Bundle\OAuth2ServerBundle\Model\AbstractClient;
use League\Bundle\OAuth2ServerBundle\Model\Grant;
use League\Bundle\OAuth2ServerBundle\Model\RedirectUri;
use League\Bundle\OAuth2ServerBundle\Model\Scope;
Expand All @@ -18,7 +18,7 @@

final class ListClientsCommand extends Command
{
private const ALLOWED_COLUMNS = ['identifier', 'secret', 'scope', 'redirect uri', 'grant type'];
private const ALLOWED_COLUMNS = ['name', 'identifier', 'secret', 'scope', 'redirect uri', 'grant type'];

protected static $defaultName = 'league:oauth2-server:list-clients';

Expand Down Expand Up @@ -112,8 +112,9 @@ private function drawTable(InputInterface $input, OutputInterface $output, array

private function getRows(array $clients, array $columns): array
{
return array_map(static function (Client $client) use ($columns): array {
return array_map(static function (AbstractClient $client) use ($columns): array {
$values = [
'name' => $client->getName(),
'identifier' => $client->getIdentifier(),
'secret' => $client->getSecret(),
'scope' => implode(', ', $client->getScopes()),
Expand Down
26 changes: 20 additions & 6 deletions src/Command/UpdateClientCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace League\Bundle\OAuth2ServerBundle\Command;

use League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface;
use League\Bundle\OAuth2ServerBundle\Model\Client;
use League\Bundle\OAuth2ServerBundle\Model\AbstractClient;
use League\Bundle\OAuth2ServerBundle\Model\Grant;
use League\Bundle\OAuth2ServerBundle\Model\RedirectUri;
use League\Bundle\OAuth2ServerBundle\Model\Scope;
Expand Down Expand Up @@ -35,7 +35,7 @@ public function __construct(ClientManagerInterface $clientManager)
protected function configure(): void
{
$this
->setDescription('Updates an oAuth2 client')
->setDescription('Updates an OAuth2 client')
->addOption(
'redirect-uri',
null,
Expand All @@ -57,6 +57,13 @@ protected function configure(): void
'Sets allowed scope for client. Use this option multiple times to set multiple scopes.',
[]
)
->addOption(
'name',
null,
InputOption::VALUE_REQUIRED,
'Sets name for client.',
[]
)
->addOption(
'deactivated',
null,
Expand All @@ -76,19 +83,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$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')));
$io->error(sprintf('OAuth2 client identified as "%s" does not exist.', $input->getArgument('identifier')));

return 1;
}

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

$io->success('OAuth2 client updated successfully.');

return 0;
}

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

Expand All @@ -99,7 +107,7 @@ private function updateClientFromInput(Client $client, InputInterface $input): C
/** @var list<string> $scopeStrings */
$scopeStrings = $input->getOption('scope');

return $client
$client
->setRedirectUris(...array_map(static function (string $redirectUri): RedirectUri {
return new RedirectUri($redirectUri);
}, $redirectUriStrings))
Expand All @@ -110,5 +118,11 @@ private function updateClientFromInput(Client $client, InputInterface $input): C
return new Scope($scope);
}, $scopeStrings))
;

if ($name = $input->getOption('name')) {
$client->setName($name);
}

return $client;
}
}
4 changes: 2 additions & 2 deletions src/Controller/AuthorizationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use League\Bundle\OAuth2ServerBundle\Event\AuthorizationRequestResolveEvent;
use League\Bundle\OAuth2ServerBundle\Event\AuthorizationRequestResolveEventFactory;
use League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface;
use League\Bundle\OAuth2ServerBundle\Model\Client;
use League\Bundle\OAuth2ServerBundle\Model\AbstractClient;
use League\Bundle\OAuth2ServerBundle\OAuth2Events;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
Expand Down Expand Up @@ -90,7 +90,7 @@ public function indexAction(Request $request): Response
$authRequest = $this->server->validateAuthorizationRequest($serverRequest);

if ('plain' === $authRequest->getCodeChallengeMethod()) {
/** @var Client $client */
/** @var AbstractClient $client */
$client = $this->clientManager->find($authRequest->getClient()->getIdentifier());
if (!$client->isPlainTextPkceAllowed()) {
throw OAuthServerException::invalidRequest('code_challenge_method', 'Plain code challenge method is not allowed for this client');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace League\Bundle\OAuth2ServerBundle\DependencyInjection\CompilerPass;

use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass;
use League\Bundle\OAuth2ServerBundle\Persistence\Mapping\Driver;
use Symfony\Component\DependencyInjection\Reference;

/**
* @author Mathias Arlaud <[email protected]>
*/
class RegisterDoctrineOrmMappingPass extends DoctrineOrmMappingsPass
{
public function __construct()
{
parent::__construct(
new Reference(Driver::class),
['League\Bundle\OAuth2ServerBundle\Model'],
['league.oauth2_server.persistence.doctrine.manager'],
'league.oauth2_server.persistence.doctrine.enabled',
['LeagueOAuth2ServerBundle' => 'League\Bundle\OAuth2ServerBundle\Model']
);
}
}
Loading