Skip to content

Commit f8c1209

Browse files
committed
Merge pull request #12 from K-Phoen/key-extractor
API keys can be extracted from different sources
2 parents 9c4dd1b + 9496714 commit f8c1209

7 files changed

Lines changed: 156 additions & 5 deletions

File tree

src/Uecode/Bundle/ApiKeyBundle/DependencyInjection/Configuration.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@ public function getConfigTreeBuilder()
1515
$treeBuilder = new TreeBuilder();
1616
$rootNode = $treeBuilder->root('uecode_api_key');
1717

18+
$rootNode
19+
->children()
20+
->scalarNode('delivery')
21+
->defaultValue('query')
22+
->validate()
23+
->ifNotInArray(array('query', 'header'))
24+
->thenInvalid('Unknown authentication delivery type "%s".')
25+
->end()
26+
->end()
27+
->scalarNode('parameter_name')
28+
->defaultValue('api_key')
29+
->end()
30+
->end()
31+
;
32+
1833
return $treeBuilder;
1934
}
2035
}

src/Uecode/Bundle/ApiKeyBundle/DependencyInjection/UecodeApiKeyExtension.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ public function load(array $configs, ContainerBuilder $container)
2222
new FileLocator(__DIR__.'/../Resources/config')
2323
);
2424
$loader->load('services.yml');
25+
26+
$this->defineKeyExtractor($config, $container);
27+
}
28+
29+
private function defineKeyExtractor(array $config, ContainerBuilder $container)
30+
{
31+
$container->setParameter('uecode.api_key.parameter_name', $config['parameter_name']);
32+
$container->setAlias('uecode.api_key.extractor', 'uecode.api_key.extractor.'.$config['delivery']);
2533
}
2634
}
2735

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Uecode\Bundle\ApiKeyBundle\Extractor;
4+
5+
use Symfony\Component\HttpFoundation\Request;
6+
7+
/**
8+
* Extracts API keys from request headers.
9+
*
10+
* @author Kévin Gomez <contact@kevingomez.fr>
11+
*/
12+
class HeaderExtractor implements KeyExtractor
13+
{
14+
private $parameterName;
15+
16+
/**
17+
* @param string $parameterName The name of the URL parameter containing the API key.
18+
*/
19+
public function __construct($parameterName)
20+
{
21+
$this->parameterName = $parameterName;
22+
}
23+
24+
/**
25+
* {@inheritDoc}
26+
*/
27+
public function hasKey(Request $request)
28+
{
29+
return $request->headers->has($this->parameterName);
30+
}
31+
32+
/**
33+
* {@inheritDoc}
34+
*/
35+
public function extractKey(Request $request)
36+
{
37+
return $request->headers->get($this->parameterName);
38+
}
39+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Uecode\Bundle\ApiKeyBundle\Extractor;
4+
5+
use Symfony\Component\HttpFoundation\Request;
6+
7+
/**
8+
* @author Kévin Gomez <contact@kevingomez.fr>
9+
*/
10+
interface KeyExtractor
11+
{
12+
/**
13+
* Tells if the given requests carries an API key.
14+
*
15+
* @param Request $request
16+
*
17+
* @return bool
18+
*/
19+
function hasKey(Request $request);
20+
21+
/**
22+
* Extract the API key from thhe given request
23+
*
24+
* @param Request $request
25+
*
26+
* @return string
27+
*/
28+
function extractKey(Request $request);
29+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Uecode\Bundle\ApiKeyBundle\Extractor;
4+
5+
use Symfony\Component\HttpFoundation\Request;
6+
7+
/**
8+
* Extracts API keys from a request query string.
9+
*
10+
* @author Kévin Gomez <contact@kevingomez.fr>
11+
*/
12+
class QueryExtractor implements KeyExtractor
13+
{
14+
private $parameterName;
15+
16+
/**
17+
* @param string $parameterName The name of the URL parameter containing the API key.
18+
*/
19+
public function __construct($parameterName)
20+
{
21+
$this->parameterName = $parameterName;
22+
}
23+
24+
/**
25+
* {@inheritDoc}
26+
*/
27+
public function hasKey(Request $request)
28+
{
29+
return $request->query->has($this->parameterName);
30+
}
31+
32+
/**
33+
* {@inheritDoc}
34+
*/
35+
public function extractKey(Request $request)
36+
{
37+
return $request->query->get($this->parameterName);
38+
}
39+
}

src/Uecode/Bundle/ApiKeyBundle/Resources/config/services.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ parameters:
33
uecode.api_key.provider.api_key.class: Uecode\Bundle\ApiKeyBundle\Security\Authentication\Provider\ApiKeyProvider
44
uecode.api_key.listener.api_key.class: Uecode\Bundle\ApiKeyBundle\Security\Firewall\ApiKeyListener
55

6+
uecode.api_key.extractor.query.class: Uecode\Bundle\ApiKeyBundle\Extractor\QueryExtractor
7+
uecode.api_key.extractor.header.class: Uecode\Bundle\ApiKeyBundle\Extractor\HeaderExtractor
8+
69
services:
710
uecode.api_key.provider.user_provider:
811
class: %uecode.api_key.provider.user_provider.class%
@@ -12,5 +15,13 @@ services:
1215
arguments: [""]
1316
uecode.api_key.listener.api_key:
1417
class: %uecode.api_key.listener.api_key.class%
15-
arguments: [@security.context, @security.authentication.manager]
18+
arguments: [@security.context, @security.authentication.manager, @uecode.api_key.extractor]
1619

20+
uecode.api_key.extractor.query:
21+
class: %uecode.api_key.extractor.query.class%
22+
arguments: [%uecode.api_key.parameter_name%]
23+
public: false
24+
uecode.api_key.extractor.header:
25+
class: %uecode.api_key.extractor.header.class%
26+
arguments: [%uecode.api_key.parameter_name%]
27+
public: false

src/Uecode/Bundle/ApiKeyBundle/Security/Firewall/ApiKeyListener.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Symfony\Component\Security\Core\SecurityContextInterface;
1010
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
1111
use Uecode\Bundle\ApiKeyBundle\Security\Authentication\Token\ApiKeyUserToken;
12+
use Uecode\Bundle\ApiKeyBundle\Extractor\KeyExtractor;
1213

1314
/**
1415
* @author Aaron Scherer <aequasi@gmail.com>
@@ -25,10 +26,16 @@ class ApiKeyListener implements ListenerInterface
2526
*/
2627
protected $authenticationManager;
2728

28-
public function __construct(SecurityContextInterface $context, AuthenticationManagerInterface $manager)
29+
/**
30+
* @var KeyExtractor
31+
*/
32+
protected $keyExtractor;
33+
34+
public function __construct(SecurityContextInterface $context, AuthenticationManagerInterface $manager, KeyExtractor $keyExtractor)
2935
{
3036
$this->securityContext = $context;
3137
$this->authenticationManager = $manager;
38+
$this->keyExtractor = $keyExtractor;
3239
}
3340

3441
/**
@@ -39,15 +46,18 @@ public function __construct(SecurityContextInterface $context, AuthenticationMan
3946
public function handle(GetResponseEvent $event)
4047
{
4148
$request = $event->getRequest();
42-
if (!$request->query->has('api_key')) {
49+
50+
if (!$this->keyExtractor->hasKey($request)) {
4351
$response = new Response();
4452
$response->setStatusCode(401);
4553
$event->setResponse($response);
4654
return ;
4755
}
4856

57+
$apiKey = $this->keyExtractor->extractKey($request);
58+
4959
$token = new ApiKeyUserToken();
50-
$token->setApiKey($request->query->get('api_key'));
60+
$token->setApiKey($apiKey);
5161

5262
try {
5363
$authToken = $this->authenticationManager->authenticate($token);
@@ -56,7 +66,7 @@ public function handle(GetResponseEvent $event)
5666
return;
5767
} catch (AuthenticationException $failed) {
5868
$token = $this->securityContext->getToken();
59-
if ($token instanceof ApiKeyUserToken && $token->getCredentials() == $request->query->get('apiKey')) {
69+
if ($token instanceof ApiKeyUserToken && $token->getCredentials() == $apiKey) {
6070
$this->securityContext->setToken(null);
6171
}
6272

0 commit comments

Comments
 (0)