Skip to content

Commit f5c7b0d

Browse files
committed
Merge pull request #44 from php-http/authentication
Updated the authentication configuration to support credentials
2 parents 72d12d1 + 57de525 commit f5c7b0d

File tree

10 files changed

+321
-37
lines changed

10 files changed

+321
-37
lines changed

DependencyInjection/Configuration.php

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,7 @@ protected function configurePlugins(ArrayNodeDefinition $root)
120120
->arrayNode('plugins')
121121
->addDefaultsIfNotSet()
122122
->children()
123-
124-
->arrayNode('authentication')
125-
->canBeEnabled()
126-
->children()
127-
->scalarNode('authentication')
128-
->info('This must be a service id to a service implementing Http\Message\Authentication')
129-
->isRequired()
130-
->cannotBeEmpty()
131-
->end()
132-
->end()
133-
->end() // End authentication plugin
123+
->append($this->addAuthenticationPluiginNode())
134124

135125
->arrayNode('cache')
136126
->canBeEnabled()
@@ -235,4 +225,82 @@ protected function configurePlugins(ArrayNodeDefinition $root)
235225
->end()
236226
->end();
237227
}
228+
229+
/**
230+
* Add configuration for authentication plugin.
231+
*
232+
* @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition
233+
*/
234+
private function addAuthenticationPluiginNode()
235+
{
236+
$builder = new TreeBuilder();
237+
$node = $builder->root('authentication');
238+
$node
239+
->useAttributeAsKey('name')
240+
->prototype('array')
241+
->validate()
242+
->always()
243+
->then(function ($config) {
244+
switch ($config['type']) {
245+
case 'basic':
246+
$this->validateAuthenticationType(['username', 'password'], $config, 'basic');
247+
break;
248+
case 'bearer':
249+
$this->validateAuthenticationType(['token'], $config, 'bearer');
250+
break;
251+
case 'service':
252+
$this->validateAuthenticationType(['service'], $config, 'service');
253+
break;
254+
case 'wsse':
255+
$this->validateAuthenticationType(['username', 'password'], $config, 'wsse');
256+
break;
257+
}
258+
259+
return $config;
260+
})
261+
->end()
262+
->children()
263+
->enumNode('type')
264+
->values(['basic', 'bearer', 'wsse', 'service'])
265+
->isRequired()
266+
->cannotBeEmpty()
267+
->end()
268+
->scalarNode('username')->end()
269+
->scalarNode('password')->end()
270+
->scalarNode('token')->end()
271+
->scalarNode('service')->end()
272+
->end()
273+
->end()
274+
->end(); // End authentication plugin
275+
276+
return $node;
277+
}
278+
279+
/**
280+
* Validate that the configuration fragment has the specified keys and none other.
281+
*
282+
* @param array $expected Fields that must exist
283+
* @param array $actual Actual configuration hashmap
284+
* @param string $authName Name of authentication method for error messages
285+
*
286+
* @throws InvalidConfigurationException If $actual does not have exactly the keys specified in $expected (plus 'type')
287+
*/
288+
private function validateAuthenticationType(array $expected, array $actual, $authName)
289+
{
290+
unset($actual['type']);
291+
$actual = array_keys($actual);
292+
sort($actual);
293+
sort($expected);
294+
295+
if ($expected === $actual) {
296+
return;
297+
}
298+
299+
throw new InvalidConfigurationException(sprintf(
300+
'Authentication "%s" requires %s but got %s',
301+
$authName,
302+
implode(', ', $expected),
303+
implode(', ', $actual)
304+
));
305+
}
238306
}

DependencyInjection/HttplugExtension.php

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
namespace Http\HttplugBundle\DependencyInjection;
44

5+
use Http\Client\Plugin\AuthenticationPlugin;
56
use Http\Client\Plugin\PluginClient;
67
use Http\HttplugBundle\ClientFactory\DummyClient;
8+
use Http\Message\Authentication\BasicAuth;
9+
use Http\Message\Authentication\Bearer;
10+
use Http\Message\Authentication\Wsse;
711
use Symfony\Component\Config\FileLocator;
812
use Symfony\Component\DependencyInjection\ContainerBuilder;
913
use Symfony\Component\DependencyInjection\Definition;
@@ -109,6 +113,11 @@ private function configureClients(ContainerBuilder $container, array $config)
109113
*/
110114
private function configurePlugins(ContainerBuilder $container, array $config)
111115
{
116+
if (!empty($config['authentication'])) {
117+
$this->configureAuthentication($container, $config['authentication']);
118+
}
119+
unset($config['authentication']);
120+
112121
foreach ($config as $name => $pluginConfig) {
113122
$pluginId = 'httplug.plugin.'.$name;
114123
if ($pluginConfig['enabled']) {
@@ -128,9 +137,6 @@ private function configurePlugins(ContainerBuilder $container, array $config)
128137
private function configurePluginByName($name, Definition $definition, array $config)
129138
{
130139
switch ($name) {
131-
case 'authentication':
132-
$definition->replaceArgument(0, new Reference($config['authentication']));
133-
break;
134140
case 'cache':
135141
$definition
136142
->replaceArgument(0, new Reference($config['cache_pool']))
@@ -165,4 +171,40 @@ private function configurePluginByName($name, Definition $definition, array $con
165171
break;
166172
}
167173
}
174+
175+
/**
176+
* @param ContainerBuilder $container
177+
* @param Definition $parent
178+
* @param array $config
179+
*/
180+
private function configureAuthentication(ContainerBuilder $container, array $config)
181+
{
182+
foreach ($config as $name => $values) {
183+
$authServiceKey = sprintf('httplug.plugin.authentication.%s.auth', $name);
184+
switch ($values['type']) {
185+
case 'bearer':
186+
$container->register($authServiceKey, Bearer::class)
187+
->addArgument($values['token']);
188+
break;
189+
case 'basic':
190+
$container->register($authServiceKey, BasicAuth::class)
191+
->addArgument($values['username'])
192+
->addArgument($values['password']);
193+
break;
194+
case 'wsse':
195+
$container->register($authServiceKey, Wsse::class)
196+
->addArgument($values['username'])
197+
->addArgument($values['password']);
198+
break;
199+
case 'service':
200+
$authServiceKey = $values['service'];
201+
break;
202+
default:
203+
throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
204+
}
205+
206+
$container->register('httplug.plugin.authentication.'.$name, AuthenticationPlugin::class)
207+
->addArgument(new Reference($authServiceKey));
208+
}
209+
}
168210
}

README.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ For information how to write applications with the services provided by this bun
5252
| httplug.client.[name] | This is your Httpclient that you have configured. With the configuration below the name would be `acme_client`.
5353
| httplug.client | This is the first client configured or a client named `default`.
5454
| httplug.plugin.content_length <br> httplug.plugin.decoder<br> httplug.plugin.error<br> httplug.plugin.logger<br> httplug.plugin.redirect<br> httplug.plugin.retry<br> httplug.plugin.stopwatch | These are plugins that are enabled by default. These services are not public and may only be used when configure HttpClients or other services.
55-
| httplug.plugin.authentication <br> httplug.plugin.cache<br> httplug.plugin.cookie<br> httplug.plugin.history | These are plugins that are disabled by default. They need to be configured before they can be used. These services are not public and may only be used when configure HttpClients or other services.
55+
| httplug.plugin.cache<br> httplug.plugin.cookie<br> httplug.plugin.history | These are plugins that are disabled by default. They need to be configured before they can be used. These services are not public and may only be used when configure HttpClients or other services.
5656

5757
\* *These services are always an alias to another service. You can specify your own service or leave the default, which is the same name with `.default` appended. The default services in turn use the service discovery mechanism to provide the best available implementation. You can specify a class for each of the default services to use instead of discovery, as long as those classes can be instantiated without arguments.*
5858

@@ -146,6 +146,34 @@ httpug:
146146
base_uri: 'http://google.se/'
147147
```
148148
149+
#### Authentication
150+
151+
```yaml
152+
// config.yml
153+
httpug:
154+
plugins:
155+
authentication:
156+
my_basic:
157+
type: 'basic'
158+
username: 'my_username'
159+
password: 'p4ssw0rd'
160+
my_wsse:
161+
type: 'wsse'
162+
username: 'my_username'
163+
password: 'p4ssw0rd'
164+
my_brearer:
165+
type: 'bearer'
166+
token: 'authentication_token_hash'
167+
my_service:
168+
type: 'service'
169+
service: 'my_authentication_service'
170+
171+
clients:
172+
acme:
173+
factory: 'httplug.factory.guzzle6'
174+
plugins: ['httplug.plugin.authentication.my_wsse']
175+
```
176+
149177
150178
### Use for Reusable Bundles
151179

Resources/config/plugins.xml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
55

66
<services>
7-
<service id="httplug.plugin.authentication" class="Http\Client\Plugin\AuthenticationPlugin" public="false">
8-
<argument />
9-
</service>
107
<service id="httplug.plugin.cache" class="Http\Client\Plugin\CachePlugin" public="false">
118
<argument />
129
<argument />

Tests/Resources/Fixtures/config/full.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,59 @@
1313
'uri_factory' => 'Http\Message\UriFactory\GuzzleUriFactory',
1414
'stream_factory' => 'Http\Message\StreamFactory\GuzzleStreamFactory',
1515
],
16+
'toolbar' => [
17+
'enabled' => true,
18+
'formatter' => 'my_toolbar_formatter',
19+
],
20+
'plugins' => [
21+
'authentication' => [
22+
'my_basic' => [
23+
'type' => 'basic',
24+
'username' => 'foo',
25+
'password' => 'bar',
26+
],
27+
'my_wsse' => [
28+
'type' => 'wsse',
29+
'username' => 'foo',
30+
'password' => 'bar',
31+
],
32+
'my_bearer' => [
33+
'type' => 'bearer',
34+
'token' => 'foo',
35+
],
36+
'my_service' => [
37+
'type' => 'service',
38+
'service' => 'my_auth_service',
39+
],
40+
],
41+
'cache' => [
42+
'cache_pool' => 'my_cache_pool',
43+
'stream_factory' => 'my_other_stream_factory',
44+
'config' => [
45+
'default_ttl' => 42,
46+
'respect_cache_headers' => false,
47+
],
48+
],
49+
'cookie' => [
50+
'cookie_jar' => 'my_cookie_jar',
51+
],
52+
'decoder' => [
53+
'enabled' => false,
54+
],
55+
'history' => [
56+
'journal' => 'my_journal',
57+
],
58+
'logger' => [
59+
'enabled' => false,
60+
],
61+
'redirect' => [
62+
'enabled' => false,
63+
],
64+
'retry' => [
65+
'enabled' => false,
66+
],
67+
'stopwatch' => [
68+
'enabled' => false,
69+
],
70+
],
1671
]);

Tests/Resources/Fixtures/config/full.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,25 @@
1414
<uri-factory>Http\Message\UriFactory\GuzzleUriFactory</uri-factory>
1515
<stream-factory>Http\Message\StreamFactory\GuzzleStreamFactory</stream-factory>
1616
</classes>
17+
<toolbar enabled="true" formatter="my_toolbar_formatter"/>
18+
<plugins>
19+
<authentication>
20+
<my_basic type="basic" username="foo" password="bar"/>
21+
<my_wsse type="wsse" username="foo" password="bar"/>
22+
<my_bearer type="bearer" token="foo"/>
23+
<my_service type="service" service="my_auth_service"/>
24+
</authentication>
25+
<cache cache-pool="my_cache_pool" stream-factory="my_other_stream_factory">
26+
<config default-ttl="42" respect-cache-headers="false"/>
27+
</cache>
28+
<cookie cookie-jar="my_cookie_jar"/>
29+
<decoder enabled="false"/>
30+
<history journal="my_journal"/>
31+
<logger enabled="false"/>
32+
<redirect enabled="false"/>
33+
<retry enabled="false"/>
34+
<stopwatch enabled="false"/>
35+
</plugins>
1736
</config>
1837

1938
</container>

Tests/Resources/Fixtures/config/full.yml

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,43 @@ httplug:
88
client: Http\Adapter\Guzzle6\Client
99
message_factory: Http\Message\MessageFactory\GuzzleMessageFactory
1010
uri_factory: Http\Message\UriFactory\GuzzleUriFactory
11-
stream_factory: Http\Message\StreamFactory\GuzzleStreamFactory
11+
stream_factory: Http\Message\StreamFactory\GuzzleStreamFactory
12+
toolbar:
13+
enabled: true
14+
formatter: my_toolbar_formatter
15+
plugins:
16+
authentication:
17+
my_basic:
18+
type: basic
19+
username: foo
20+
password: bar
21+
my_wsse:
22+
type: wsse
23+
username: foo
24+
password: bar
25+
my_bearer:
26+
type: bearer
27+
token: foo
28+
my_service:
29+
type: service
30+
service: my_auth_service
31+
cache:
32+
cache_pool: my_cache_pool
33+
stream_factory: my_other_stream_factory
34+
config:
35+
default_ttl: 42
36+
respect_cache_headers: false
37+
cookie:
38+
cookie_jar: my_cookie_jar
39+
decoder:
40+
enabled: false
41+
history:
42+
journal: my_journal
43+
logger:
44+
enabled: false
45+
redirect:
46+
enabled: false
47+
retry:
48+
enabled: false
49+
stopwatch:
50+
enabled: false
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
httplug:
2+
plugins:
3+
authentication:
4+
my_auth:
5+
type: service
6+
service: foobar
7+
username: user
8+
password: pass

0 commit comments

Comments
 (0)