Skip to content

Commit b905dd3

Browse files
committed
Added middleware
1 parent e451185 commit b905dd3

File tree

4 files changed

+161
-4
lines changed

4 files changed

+161
-4
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace ApiSkeletons\Laravel\Doctrine\ApiKey\Http\Middleware;
4+
5+
use ApiSkeletons\Laravel\Doctrine\ApiKey\Service\ApiKeyService;
6+
use Illuminate\Http\Request;
7+
8+
class AuthorizeApiKey
9+
{
10+
private ApiKeyService $apiKeyService;
11+
12+
public function __construct(ApiKeyService $apiKeyService)
13+
{
14+
$this->apiKeyService = $apiKeyService;
15+
}
16+
17+
/**
18+
* Handle request
19+
*
20+
* @param Request $request
21+
* @param Closure $next
22+
* @return mixed
23+
*/
24+
public function handle(Request $request, \Closure $next, string $scope = null)
25+
{
26+
$header = $request->header('Authorization');
27+
// Remove Bearer from key prefix
28+
$key = substr($header, 7);
29+
30+
$apiKey = $this->apiKeyService->isActive($key);
31+
if ($apiKey) {
32+
if ($scope) {
33+
// If a scope is passed then verify it exists for the key
34+
if ($this->apiKeyService->hasScope($key, $scope)) {
35+
$this->apiKeyService->logAccessEvent($request, $apiKey);
36+
37+
return $next($request);
38+
}
39+
} else {
40+
$this->apiKeyService->logAccessEvent($request, $apiKey);
41+
42+
return $next($request);
43+
}
44+
}
45+
46+
return response([
47+
'errors' => [[
48+
'message' => 'Unauthorized'
49+
]]
50+
], 401);
51+
}
52+
}
53+

src/Service/ApiKeyService.php

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

33
namespace ApiSkeletons\Laravel\Doctrine\ApiKey\Service;
44

5+
use ApiSkeletons\Laravel\Doctrine\ApiKey\Entity\AccessEvent;
56
use ApiSkeletons\Laravel\Doctrine\ApiKey\Entity\ApiKey;
67
use ApiSkeletons\Laravel\Doctrine\ApiKey\Entity\Scope;
78
use Doctrine\ORM\EntityManager;
89
use Doctrine\ORM\Mapping\Driver\XmlDriver;
10+
use Illuminate\Http\Request;
911

1012
class ApiKeyService
1113
{
@@ -35,18 +37,18 @@ public function init(EntityManager $entityManager): self|bool
3537
return $this;
3638
}
3739

38-
public function isActive(string $key): bool
40+
public function isActive(string $key): ApiKey|bool
3941
{
4042
$apiKey = $this->entityManager->getRepository(ApiKey::class)
4143
->findOneBy([
4244
'key' => $key,
4345
]);
4446

45-
if (! $apiKey) {
47+
if (! $apiKey || ! $apiKey->getIsActive()) {
4648
return false;
4749
}
4850

49-
return $apiKey->getIsActive();
51+
return $apiKey;
5052
}
5153

5254
public function hasScope(string $key, string $scopeName): bool
@@ -76,4 +78,22 @@ public function hasScope(string $key, string $scopeName): bool
7678
return $found;
7779
}
7880

81+
/**
82+
* Log an access event
83+
*
84+
* @param Request $request
85+
* @param ApiKey $apiKey
86+
*/
87+
public function logAccessEvent(Request $request, ApiKey $apiKey)
88+
{
89+
$event = (new AccessEvent())
90+
->setCreatedAt(new \DateTime())
91+
->setApiKey($apiKey)
92+
->setIpAddress($request->ip())
93+
->setUrl($request->fullUrl())
94+
;
95+
96+
$this->getEntityManager()->persist($event);
97+
$this->getEntityManager()->flush();
98+
}
7999
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
namespace ApiSkeletonsTest\Laravel\Doctrine\ApiKey\Feature\Http\Middleware;
4+
5+
use ApiSkeletons\Laravel\Doctrine\ApiKey\Entity\ApiKey;
6+
use ApiSkeletons\Laravel\Doctrine\ApiKey\Entity\Scope;
7+
use ApiSkeletons\Laravel\Doctrine\ApiKey\Http\Middleware\AuthorizeApiKey;
8+
use ApiSkeletons\Laravel\Doctrine\ApiKey\Service\ApiKeyService;
9+
use ApiSkeletonsTest\Laravel\Doctrine\ApiKey\TestCase;
10+
use Illuminate\Http\Request;
11+
12+
final class AuthorizeApiKeyTest extends TestCase
13+
{
14+
public function testApiKeyAuthorizesRequest(): void
15+
{
16+
$entityManager = $this->createDatabase(app('em'));
17+
$apiKeyRepository = $entityManager->getRepository(ApiKey::class);
18+
19+
$apiKey = $apiKeyRepository->generate('testing');
20+
$entityManager->flush();
21+
22+
$request = Request::create('/apiresource', 'GET');
23+
$request->headers->set('Authorization', 'Bearer ' . $apiKey->getKey());
24+
25+
$middleware = new AuthorizeApiKey(app(ApiKeyService::class));
26+
27+
$response = $middleware->handle($request, function() {});
28+
$this->assertNull($response);
29+
}
30+
31+
public function testApiKeyDoesNotAuthorizeRequest(): void
32+
{
33+
$entityManager = $this->createDatabase(app('em'));
34+
35+
$request = Request::create('/apiresource', 'GET');
36+
37+
$middleware = new AuthorizeApiKey(app(ApiKeyService::class));
38+
39+
$response = $middleware->handle($request, function() {});
40+
$this->assertNotNull($response);
41+
}
42+
43+
public function testApiKeyAuthorizesRequestWithScope(): void
44+
{
45+
$entityManager = $this->createDatabase(app('em'));
46+
$apiKeyRepository = $entityManager->getRepository(ApiKey::class);
47+
$scopeRepository = $entityManager->getRepository(Scope::class);
48+
49+
$apiKey = $apiKeyRepository->generate('testing');
50+
$scope = $scopeRepository->generate('scopetest');
51+
$entityManager->flush();
52+
53+
$apiKeyRepository->addScope($apiKey, $scope);
54+
$entityManager->flush();
55+
56+
$request = Request::create('/apiresource', 'GET');
57+
$request->headers->set('Authorization', 'Bearer ' . $apiKey->getKey());
58+
59+
$middleware = new AuthorizeApiKey(app(ApiKeyService::class));
60+
61+
$response = $middleware->handle($request, function() {}, $scope->getName());
62+
$this->assertNull($response);
63+
}
64+
65+
public function testApiKeyDoesNotAuthorizeRequestWithInvalidScope(): void
66+
{
67+
$entityManager = $this->createDatabase(app('em'));
68+
$apiKeyRepository = $entityManager->getRepository(ApiKey::class);
69+
$scopeRepository = $entityManager->getRepository(Scope::class);
70+
71+
$apiKey = $apiKeyRepository->generate('testing');
72+
$scope = $scopeRepository->generate('scopetest');
73+
$entityManager->flush();
74+
75+
$request = Request::create('/apiresource', 'GET');
76+
$request->headers->set('Authorization', 'Bearer ' . $apiKey->getKey());
77+
78+
$middleware = new AuthorizeApiKey(app(ApiKeyService::class));
79+
80+
$response = $middleware->handle($request, function() {}, $scope->getName());
81+
$this->assertNotNull($response);
82+
}
83+
}

test/Feature/Service/ApiKeyServiceTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public function testIsActive(): void
3838
$apiKey = $apiKeyRepository->generate('testing');
3939
$entityManager->flush();
4040

41-
$this->assertTrue($apiKeyService->isActive($apiKey->getKey()));
41+
$apiKey = $apiKeyService->isActive($apiKey->getKey());
42+
$this->assertInstanceOf(ApiKey::class, $apiKey);
4243
}
4344

4445
public function testIsActiveReturnsFalseForInvalidKey(): void

0 commit comments

Comments
 (0)