Skip to content

Commit

Permalink
Merge pull request #5 from gregurco/validation_and_tests
Browse files Browse the repository at this point in the history
Validation and tests
  • Loading branch information
gregurco authored Nov 9, 2017
2 parents 033cae7 + 230a6c2 commit 086e2ab
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 33 deletions.
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ new EightPoints\Bundle\GuzzleBundle\EightPointsGuzzleBundle([
```

### Basic configuration
#### With password grant type
#### With default grant type (client)
``` yaml
# app/config/config.yml

Expand All @@ -59,13 +59,12 @@ eight_points_guzzle:
oauth2:
base_uri: "https://example.com"
token_url: "/oauth/token"
username: "[email protected]"
password: "pa55w0rd"
client_id: "test-client-id"
client_secret: "test-client-secret" # optional
scope: "administration"
```
#### With client credentials grant type
#### With password grant type
``` yaml
# app/config/config.yml

Expand All @@ -82,9 +81,10 @@ eight_points_guzzle:
base_uri: "https://example.com"
token_url: "/oauth/token"
client_id: "test-client-id"
client_secret: "test-client-secret" # optional
username: "johndoe"
password: "A3ddj3w"
scope: "administration"
grant_type: "Sainsburys\\Guzzle\\Oauth2\\GrantType\\ClientCredentials"
grant_type: "Sainsburys\\Guzzle\\Oauth2\\GrantType\\PasswordCredentials"
```
#### With client credentials in body
Expand All @@ -104,9 +104,7 @@ eight_points_guzzle:
base_uri: "https://example.com"
token_url: "/oauth/token"
client_id: "test-client-id"
client_secret: "test-client-secret" # optional
scope: "administration"
grant_type: "Sainsburys\\Guzzle\\Oauth2\\GrantType\\ClientCredentials"
auth_location: "body"
```
Expand All @@ -122,8 +120,9 @@ eight_points_guzzle:
| password | The resource owner password | for PasswordCredentials grant type | A3ddj3w |
| auth_location | The place where to put client_id and client_secret in auth request. <br/>Default: headers. Allowed values: body, headers. | no | body |
| resource | The App ID URI of the web API (secured resource) | no | https://service.contoso.com/ |
| private_key | Path to private key | for JwtBearer grant type | `"%kernel.root_dir%/path/to/private.key"` |
| scope | One or more scope values indicating which parts of the user's account you wish to access | no | administration |
| grant_type | Grant type class path. Class should implement GrantTypeInterface. <br/> Default: `Sainsburys\\Guzzle\\Oauth2\\GrantType\\PasswordCredentials` | no | `Sainsburys\\Guzzle\\Oauth2\\GrantType\\ClientCredentials` |
| grant_type | Grant type class path. Class should implement GrantTypeInterface. <br/> Default: `Sainsburys\\Guzzle\\Oauth2\\GrantType\\ClientCredentials` | no | `Sainsburys\\Guzzle\\Oauth2\\GrantType\\PasswordCredentials`<br/>`Sainsburys\\Guzzle\\Oauth2\\GrantType\\AuthorizationCode`<br/>`Sainsburys\\Guzzle\\Oauth2\\GrantType\\JwtBearer` |

See more information about middleware [here][3].

Expand Down
55 changes: 49 additions & 6 deletions src/GuzzleBundleOAuth2Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@

use Gregurco\Bundle\GuzzleBundleOAuth2Plugin\DependencyInjection\GuzzleBundleOAuth2Extension;
use EightPoints\Bundle\GuzzleBundle\EightPointsGuzzleBundlePlugin;
use Sainsburys\Guzzle\Oauth2\GrantType\ClientCredentials;
use Sainsburys\Guzzle\Oauth2\GrantType\GrantTypeBase;
use Sainsburys\Guzzle\Oauth2\GrantType\GrantTypeInterface;
use Sainsburys\Guzzle\Oauth2\GrantType\JwtBearer;
use Sainsburys\Guzzle\Oauth2\GrantType\PasswordCredentials;
use Sainsburys\Guzzle\Oauth2\GrantType\RefreshToken;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
Expand Down Expand Up @@ -40,14 +43,25 @@ public function loadForClient(array $config, ContainerBuilder $container, string
$middlewareConfig = [
PasswordCredentials::CONFIG_USERNAME => $config['username'],
PasswordCredentials::CONFIG_PASSWORD => $config['password'],
PasswordCredentials::CONFIG_CLIENT_ID => $config['client_id'],
PasswordCredentials::CONFIG_CLIENT_SECRET => $config['client_secret'],
PasswordCredentials::CONFIG_TOKEN_URL => $config['token_url'],
PasswordCredentials::CONFIG_AUTH_LOCATION => $config['auth_location'],
PasswordCredentials::CONFIG_RESOURCE => $config['resource'],
GrantTypeBase::CONFIG_CLIENT_ID => $config['client_id'],
GrantTypeBase::CONFIG_CLIENT_SECRET => $config['client_secret'],
GrantTypeBase::CONFIG_TOKEN_URL => $config['token_url'],
GrantTypeBase::CONFIG_AUTH_LOCATION => $config['auth_location'],
GrantTypeBase::CONFIG_RESOURCE => $config['resource'],
JwtBearer::CONFIG_PRIVATE_KEY => null,
'scope' => $config['scope'],
];

if ($config['private_key']) {
// Define Client
$privateKeyDefinitionName = sprintf('guzzle_bundle_oauth2_plugin.private_key.%s', $clientName);
$privateKeyDefinition = new Definition(\SplFileObject::class);
$privateKeyDefinition->addArgument($config['private_key']);
$container->setDefinition($privateKeyDefinitionName, $privateKeyDefinition);

$middlewareConfig[JwtBearer::CONFIG_PRIVATE_KEY] = new Reference($privateKeyDefinitionName);
}

// Define Client
$oauthClientDefinitionName = sprintf('guzzle_bundle_oauth2_plugin.client.%s', $clientName);
$oauthClientDefinition = new Definition(Client::class);
Expand Down Expand Up @@ -93,6 +107,34 @@ public function addConfiguration(ArrayNodeDefinition $pluginNode)
{
$pluginNode
->canBeEnabled()
->validate()
->ifTrue(function (array $config) {
return $config['enabled'] === true && empty($config['base_uri']);
})
->thenInvalid('base_uri is required')
->end()
->validate()
->ifTrue(function (array $config) {
return $config['enabled'] === true && empty($config['client_id']);
})
->thenInvalid('client_id is required')
->end()
->validate()
->ifTrue(function (array $config) {
return $config['enabled'] === true &&
$config['grant_type'] === PasswordCredentials::class &&
(empty($config['username']) || empty($config['password']));
})
->thenInvalid('username and password are required')
->end()
->validate()
->ifTrue(function (array $config) {
return $config['enabled'] === true &&
$config['grant_type'] === JwtBearer::class &&
empty($config['private_key']);
})
->thenInvalid('private_key is required')
->end()
->children()
->scalarNode('base_uri')->defaultNull()->end()
->scalarNode('username')->defaultNull()->end()
Expand All @@ -102,6 +144,7 @@ public function addConfiguration(ArrayNodeDefinition $pluginNode)
->scalarNode('token_url')->defaultNull()->end()
->scalarNode('scope')->defaultNull()->end()
->scalarNode('resource')->defaultNull()->end()
->scalarNode('private_key')->defaultNull()->end()
->scalarNode('auth_location')
->defaultValue('headers')
->validate()
Expand All @@ -110,7 +153,7 @@ public function addConfiguration(ArrayNodeDefinition $pluginNode)
->end()
->end()
->scalarNode('grant_type')
->defaultValue(PasswordCredentials::class)
->defaultValue(ClientCredentials::class)
->validate()
->ifTrue(function ($v) {
return !is_subclass_of($v, GrantTypeInterface::class);
Expand Down
148 changes: 130 additions & 18 deletions tests/GuzzleBundleOAuth2PluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use EightPoints\Bundle\GuzzleBundle\EightPointsGuzzleBundlePlugin;
use Gregurco\Bundle\GuzzleBundleOAuth2Plugin\GuzzleBundleOAuth2Plugin;
use Sainsburys\Guzzle\Oauth2\GrantType\ClientCredentials;
use Sainsburys\Guzzle\Oauth2\GrantType\GrantTypeInterface;
use Sainsburys\Guzzle\Oauth2\GrantType\JwtBearer;
use Sainsburys\Guzzle\Oauth2\GrantType\PasswordCredentials;
use Sainsburys\Guzzle\Oauth2\GrantType\RefreshToken;
use Sainsburys\Guzzle\Oauth2\Middleware\OAuthMiddleware;
Expand Down Expand Up @@ -56,8 +58,9 @@ public function testAddConfiguration()
'token_url' => null,
'scope' => null,
'resource' => null,
'private_key' => null,
'auth_location' => 'headers',
'grant_type' => PasswordCredentials::class,
'grant_type' => ClientCredentials::class,
],
$node->getDefaultValue()
);
Expand Down Expand Up @@ -91,14 +94,15 @@ public function testLoadForClient()
'enabled' => true,
'base_uri' => 'https://example.com',
'token_url' => '/oauth/token',
'username' => '[email protected]',
'password' => 'pa55w0rd',
'username' => null,
'password' => null,
'client_id' => 'test-client-id',
'client_secret' => '',
'scope' => 'administration',
'resource' => null,
'private_key' => null,
'auth_location' => 'headers',
'grant_type' => PasswordCredentials::class,
'grant_type' => ClientCredentials::class,
],
$container, 'api_payment', $handler
);
Expand All @@ -110,6 +114,41 @@ public function testLoadForClient()
$this->assertCount(3, $clientMiddlewareDefinition->getArguments());
}

public function testLoadForClientWithPrivateKey()
{
$handler = new Definition();
$container = new ContainerBuilder();

$this->plugin->loadForClient(
[
'enabled' => true,
'base_uri' => 'https://example.com',
'token_url' => '/oauth/token',
'username' => null,
'password' => null,
'client_id' => 'test-client-id',
'client_secret' => '',
'scope' => 'administration',
'resource' => null,
'private_key' => '/path/to/private.key',
'auth_location' => 'headers',
'grant_type' => JwtBearer::class,
],
$container, 'api_payment', $handler
);

$this->assertTrue($container->hasDefinition('guzzle_bundle_oauth2_plugin.middleware.api_payment'));
$this->assertCount(2, $handler->getMethodCalls());

$clientMiddlewareDefinition = $container->getDefinition('guzzle_bundle_oauth2_plugin.middleware.api_payment');
$this->assertCount(3, $clientMiddlewareDefinition->getArguments());

$this->assertTrue($container->hasDefinition('guzzle_bundle_oauth2_plugin.private_key.api_payment'));
$clientMiddlewareDefinition = $container->getDefinition('guzzle_bundle_oauth2_plugin.private_key.api_payment');
$this->assertCount(1, $clientMiddlewareDefinition->getArguments());
$this->assertEquals('/path/to/private.key', $clientMiddlewareDefinition->getArgument(0));
}

/**
* @dataProvider provideValidConfigurationData
*
Expand Down Expand Up @@ -141,29 +180,45 @@ public function testAddConfigurationWithData(array $pluginConfiguration)
public function provideValidConfigurationData() : array
{
return [
'config is empty' => [[]],
'plugin is disabled' => [[
'enabled' => false,
]],
'plugin is enabled' => [[
'enabled' => true,
'base_uri' => 'https://example.com',
'client_id' => 's6BhdRkqt3',
]],
'PasswordCredentials in grant_type' => [[
'base_uri' => 'https://example.com',
'client_id' => 's6BhdRkqt3',
'username' => 'johndoe',
'password' => 'A3ddj3w',
'grant_type' => PasswordCredentials::class,
]],
'ClientCredentials in grant_type' => [[
'grant_type' => ClientCredentials::class,
]],
'JwtBearer in grant_type' => [[
'base_uri' => 'https://example.com',
'client_id' => 's6BhdRkqt3',
'grant_type' => ClientCredentials::class,
]],
'RefreshToken in grant_type' => [[
'base_uri' => 'https://example.com',
'client_id' => 's6BhdRkqt3',
'grant_type' => RefreshToken::class,
]],
'JwtBearer in grant_type' => [[
'base_uri' => 'https://example.com',
'client_id' => 's6BhdRkqt3',
'private_key' => '/path/to/private/key',
'grant_type' => JwtBearer::class,
]],
'headers in auth_location' => [[
'base_uri' => 'https://example.com',
'client_id' => 's6BhdRkqt3',
'auth_location' => 'headers',
]],
'body in auth_location' => [[
'base_uri' => 'https://example.com',
'client_id' => 's6BhdRkqt3',
'auth_location' => 'body',
]],
];
Expand All @@ -173,10 +228,12 @@ public function provideValidConfigurationData() : array
* @dataProvider provideInvalidConfigurationData
*
* @param array $pluginConfiguration
* @param string $message
*/
public function testAddConfigurationWithInvalidData(array $pluginConfiguration)
public function testAddConfigurationWithInvalidData(array $pluginConfiguration, string $message)
{
$this->expectException(InvalidConfigurationException::class);
$this->expectExceptionMessage($message);

$config = [
'eight_points_guzzle' => [
Expand All @@ -200,15 +257,70 @@ public function testAddConfigurationWithInvalidData(array $pluginConfiguration)
public function provideInvalidConfigurationData() : array
{
return [
'invalid type in grant_type' => [[
'grant_type' => true,
]],
'invalid class in grant_type' => [[
'grant_type' => \stdClass::class,
]],
'invalid grant_type' => [[
'auth_location' => 'somewhere',
]],
'without base_uri' => [
'config' => [
'enabled' => true,
'client_id' => 's6BhdRkqt3',
],
'exception message' => 'base_uri is required',
],
'without client_id' => [
'config' => [
'enabled' => true,
'base_uri' => 'https://example.com',
],
'exception message' => 'client_id is required',
],
'invalid type in grant_type' => [
'config' => [
'base_uri' => 'https://example.com',
'client_id' => 's6BhdRkqt3',
'grant_type' => true,
],
'exception message' => sprintf('Use instance of %s in grant_type', GrantTypeInterface::class),
],
'invalid class in grant_type' => [
'config' => [
'base_uri' => 'https://example.com',
'client_id' => 's6BhdRkqt3',
'grant_type' => \stdClass::class,
],
'exception message' => sprintf('Use instance of %s in grant_type', GrantTypeInterface::class),
],
'invalid auth_location' => [
'config' => [
'base_uri' => 'https://example.com',
'client_id' => 's6BhdRkqt3',
'auth_location' => 'somewhere',
],
'exception message' => 'Invalid auth_location "somewhere". Allowed values: headers, body.',
],
'PasswordCredentials grant type without username' => [
'config' => [
'base_uri' => 'https://example.com',
'client_id' => 's6BhdRkqt3',
'password' => 'A3ddj3w',
'grant_type' => PasswordCredentials::class,
],
'exception message' => 'username and password are required',
],
'PasswordCredentials grant type without password' => [
'config' => [
'base_uri' => 'https://example.com',
'client_id' => 's6BhdRkqt3',
'username' => 'johndoe',
'grant_type' => PasswordCredentials::class,
],
'exception message' => 'username and password are required',
],
'JwtBearer grant type without private_key' => [
'config' => [
'base_uri' => 'https://example.com',
'client_id' => 's6BhdRkqt3',
'grant_type' => JwtBearer::class,
],
'exception message' => 'private_key is required',
],
];
}
}

0 comments on commit 086e2ab

Please sign in to comment.