Skip to content

Commit 6a6131d

Browse files
committed
Use JwtContext and JwtMiddleware to validate the requests
1 parent f9e6e3e commit 6a6131d

File tree

12 files changed

+114
-80
lines changed

12 files changed

+114
-80
lines changed

composer.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@
99
"ext-json": "*",
1010
"ext-openssl": "*",
1111
"ext-curl": "*",
12-
"byjg/config": "^4.9",
13-
"byjg/anydataset-db": "^4.9",
14-
"byjg/micro-orm": "^4.9",
15-
"byjg/authuser": "^4.9",
12+
"byjg/config": "^4.9.x-dev",
13+
"byjg/anydataset-db": "^4.9.x-dev",
14+
"byjg/micro-orm": "^4.9.x-dev",
15+
"byjg/authuser": "^4.9.x-dev",
1616
"byjg/mailwrapper": "^4.9",
17-
"byjg/restserver": "^4.9",
17+
"byjg/restserver": "^4.9.x-dev",
1818
"zircote/swagger-php": "^4.6.1",
19-
"byjg/swagger-test": "^4.9",
20-
"byjg/migration": "^4.9",
21-
"byjg/php-daemonize": "^4.9",
19+
"byjg/swagger-test": "^4.9.x-dev",
20+
"byjg/migration": "^4.9.x-dev",
21+
"byjg/php-daemonize": "^4.9.x-dev",
2222
"byjg/shortid": "^4.9",
2323
"byjg/jinja-php": "^4.9"
2424
},

config/config-dev.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use ByJG\Mail\Wrapper\MailWrapperInterface;
2020
use ByJG\RestServer\HttpRequestHandler;
2121
use ByJG\RestServer\Middleware\CorsMiddleware;
22+
use ByJG\RestServer\Middleware\JwtMiddleware;
2223
use ByJG\RestServer\OutputProcessor\JsonCleanOutputProcessor;
2324
use ByJG\RestServer\Route\OpenApiRouteList;
2425
use ByJG\Util\JwtKeySecret;
@@ -118,6 +119,12 @@
118119
return preg_split('/,(?![^{}]*})/', Psr11::container()->get('CORS_SERVERS'));
119120
},
120121

122+
JwtMiddleware::class => DI::bind(JwtMiddleware::class)
123+
->withConstructorArgs([
124+
Param::get(JwtWrapper::class)
125+
])
126+
->toSingleton(),
127+
121128
CorsMiddleware::class => DI::bind(CorsMiddleware::class)
122129
->withNoConstructor()
123130
->withMethodCall("withCorsOrigins", [Param::get("CORS_SERVER_LIST")]) // Required to enable CORS
@@ -126,6 +133,7 @@
126133
->toSingleton(),
127134

128135
HttpRequestHandler::class => DI::bind(HttpRequestHandler::class)
136+
->withMethodCall('withMiddleware', [Param::get(JwtMiddleware::class)])
129137
->withMethodCall("withMiddleware", [Param::get(CorsMiddleware::class)])
130138
->toSingleton(),
131139

docs/getting_started_03_create_rest_method.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ public function putExampleCrudStatus(HttpResponse $response, HttpRequest $reques
102102
// Use one of the following methods:
103103

104104
// a. Require a user with role admin
105-
$data = JwtContext::requireRole("admin");
105+
JwtContext::requireRole($request, "admin");
106106

107107
// b. OR require any logged user
108-
$data = JwtContext::requireAuthenticated();
108+
JwtContext::requireAuthenticated($request);
109109

110110
// c. OR do nothing to make the endpoint public
111111
}

src/Repository/BaseRepository.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ public function getMapper()
3838
return $this->repository->getMapper();
3939
}
4040

41+
public function getDbDriver()
42+
{
43+
return $this->repository->getDbDriver();
44+
}
45+
46+
public function getByQuery($query)
47+
{
48+
$query->table($this->repository->getMapper()->getTable());
49+
return $this->repository->getByQuery($query);
50+
}
51+
4152
protected function prepareUuidQuery($itemId)
4253
{
4354
$result = [];

src/Rest/DummyHexRest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class DummyHexRest
6868
)]
6969
public function getDummyHex(HttpResponse $response, HttpRequest $request): void
7070
{
71-
$data = JwtContext::requireAuthenticated();
71+
JwtContext::requireAuthenticated($request);
7272

7373
$dummyHexRepo = Psr11::container()->get(DummyHexRepository::class);
7474
$id = $request->param('id');
@@ -154,7 +154,7 @@ public function getDummyHex(HttpResponse $response, HttpRequest $request): void
154154
)]
155155
public function listDummyHex(HttpResponse $response, HttpRequest $request): void
156156
{
157-
$data = JwtContext::requireAuthenticated();
157+
JwtContext::requireAuthenticated($request);
158158

159159
$repo = Psr11::container()->get(DummyHexRepository::class);
160160

@@ -227,7 +227,7 @@ public function listDummyHex(HttpResponse $response, HttpRequest $request): void
227227
)]
228228
public function postDummyHex(HttpResponse $response, HttpRequest $request): void
229229
{
230-
$data = JwtContext::requireRole(User::ROLE_ADMIN);
230+
JwtContext::requireRole($request, User::ROLE_ADMIN);
231231

232232
$payload = OpenApiContext::validateRequest($request);
233233

@@ -286,7 +286,7 @@ public function postDummyHex(HttpResponse $response, HttpRequest $request): void
286286
)]
287287
public function putDummyHex(HttpResponse $response, HttpRequest $request): void
288288
{
289-
$data = JwtContext::requireRole(User::ROLE_ADMIN);
289+
JwtContext::requireRole($request, User::ROLE_ADMIN);
290290

291291
$payload = OpenApiContext::validateRequest($request);
292292

src/Rest/DummyRest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class DummyRest
6868
)]
6969
public function getDummy(HttpResponse $response, HttpRequest $request): void
7070
{
71-
$data = JwtContext::requireAuthenticated();
71+
JwtContext::requireAuthenticated($request);
7272

7373
$dummyRepo = Psr11::container()->get(DummyRepository::class);
7474
$id = $request->param('id');
@@ -154,7 +154,7 @@ public function getDummy(HttpResponse $response, HttpRequest $request): void
154154
)]
155155
public function listDummy(HttpResponse $response, HttpRequest $request): void
156156
{
157-
$data = JwtContext::requireAuthenticated();
157+
JwtContext::requireAuthenticated($request);
158158

159159
$repo = Psr11::container()->get(DummyRepository::class);
160160

@@ -227,7 +227,7 @@ public function listDummy(HttpResponse $response, HttpRequest $request): void
227227
)]
228228
public function postDummy(HttpResponse $response, HttpRequest $request): void
229229
{
230-
$data = JwtContext::requireRole(User::ROLE_ADMIN);
230+
JwtContext::requireRole($request, User::ROLE_ADMIN);
231231

232232
$payload = OpenApiContext::validateRequest($request);
233233

@@ -286,7 +286,7 @@ public function postDummy(HttpResponse $response, HttpRequest $request): void
286286
)]
287287
public function putDummy(HttpResponse $response, HttpRequest $request): void
288288
{
289-
$data = JwtContext::requireRole(User::ROLE_ADMIN);
289+
JwtContext::requireRole($request, User::ROLE_ADMIN);
290290

291291
$payload = OpenApiContext::validateRequest($request);
292292

src/Rest/Login.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,16 @@ public function post(HttpResponse $response, HttpRequest $request)
101101
)]
102102
public function refreshToken(HttpResponse $response, HttpRequest $request)
103103
{
104-
$result = JwtContext::requireAuthenticated(null, true);
104+
JwtContext::requireAuthenticated($request);
105105

106-
$diff = ($result["exp"] - time()) / 60;
106+
$diff = ($request->param("jwt.exp") - time()) / 60;
107107

108108
if ($diff > 5) {
109109
throw new Error401Exception("You only can refresh the token 5 minutes before expire");
110110
}
111111

112112
$users = Psr11::container()->get(UsersDBDataset::class);
113-
$user = $users->getById(new HexUuidLiteral($result["data"]["userid"]));
113+
$user = $users->getById(new HexUuidLiteral(JwtContext::getUserId()));
114114

115115
$metadata = JwtContext::createUserMetadata($user);
116116

src/Rest/SampleProtected.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class SampleProtected
4444
)]
4545
public function getPing(HttpResponse $response, HttpRequest $request)
4646
{
47-
JwtContext::requireAuthenticated();
47+
JwtContext::requireAuthenticated($request);
4848

4949
$response->write([
5050
'result' => 'pong'
@@ -84,7 +84,7 @@ public function getPing(HttpResponse $response, HttpRequest $request)
8484
)]
8585
public function getPingAdm(HttpResponse $response, HttpRequest $request)
8686
{
87-
JwtContext::requireRole('admin');
87+
JwtContext::requireRole($request, 'admin');
8888

8989
$response->write([
9090
'result' => 'pongadm'

src/Util/FakeApiRequester.php

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,14 @@
99
use ByJG\Config\Exception\DependencyInjectionException;
1010
use ByJG\Config\Exception\InvalidDateException;
1111
use ByJG\Config\Exception\KeyNotFoundException;
12-
use ByJG\RestServer\Exception\ClassNotFoundException;
13-
use ByJG\RestServer\Exception\Error404Exception;
14-
use ByJG\RestServer\Exception\Error405Exception;
15-
use ByJG\RestServer\Exception\Error520Exception;
16-
use ByJG\RestServer\Exception\InvalidClassException;
12+
use ByJG\RestServer\Middleware\CorsMiddleware;
13+
use ByJG\RestServer\Middleware\JwtMiddleware;
1714
use ByJG\RestServer\MockRequestHandler;
1815
use ByJG\RestServer\Route\OpenApiRouteList;
16+
use ByJG\Util\Exception\MessageException;
1917
use ByJG\Util\MockClient;
20-
use ByJG\Util\Psr7\MessageException;
2118
use ByJG\Util\Psr7\Response;
19+
use KingPandaApi\Middleware\BlockMultiRequestsMiddleware;
2220
use Psr\Http\Message\RequestInterface;
2321
use Psr\Http\Message\ResponseInterface;
2422
use Psr\SimpleCache\InvalidArgumentException;
@@ -34,23 +32,23 @@ class FakeApiRequester extends AbstractRequester
3432
/**
3533
* @param RequestInterface $request
3634
* @return Response|ResponseInterface
37-
* @throws ClassNotFoundException
35+
* @throws ConfigException
3836
* @throws ConfigNotFoundException
3937
* @throws DependencyInjectionException
40-
* @throws Error404Exception
41-
* @throws Error405Exception
42-
* @throws Error520Exception
4338
* @throws InvalidArgumentException
44-
* @throws InvalidClassException
39+
* @throws InvalidDateException
4540
* @throws KeyNotFoundException
46-
* @throws MessageException
4741
* @throws ReflectionException
48-
* @throws ConfigException
49-
* @throws InvalidDateException
42+
* @throws MessageException
5043
*/
5144
protected function handleRequest(RequestInterface $request)
5245
{
53-
$mock = MockRequestHandler::mock(Psr11::container()->get(OpenApiRouteList::class), $request);
46+
47+
$mock = new MockRequestHandler($request);
48+
$mock->withMiddleware(Psr11::container()->get(JwtMiddleware::class));
49+
$mock->withMiddleware(Psr11::container()->get(CorsMiddleware::class));
50+
$mock->withRequestObject($request);
51+
$mock->handle(Psr11::container()->get(OpenApiRouteList::class));
5452

5553
$httpClient = new MockClient($mock->getPsr7Response());
5654
return $httpClient->sendRequest($request);

src/Util/JwtContext.php

Lines changed: 52 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,22 @@
1010
use ByJG\Config\Exception\KeyNotFoundException;
1111
use ByJG\RestServer\Exception\Error401Exception;
1212
use ByJG\RestServer\Exception\Error403Exception;
13+
use ByJG\RestServer\HttpRequest;
14+
use ByJG\RestServer\Middleware\JwtMiddleware;
1315
use ByJG\Util\JwtWrapper;
14-
use Exception;
1516
use Psr\SimpleCache\InvalidArgumentException;
17+
use ReflectionException;
1618
use RestReferenceArchitecture\Model\User;
1719
use RestReferenceArchitecture\Psr11;
1820

1921
class JwtContext
2022
{
23+
protected static ?HttpRequest $request;
24+
2125
/**
2226
* @param ?UserModel $user
2327
* @return array
2428
* @throws Error401Exception
25-
* @throws Error403Exception
2629
*/
2730
public static function createUserMetadata(?UserModel $user): array
2831
{
@@ -40,12 +43,13 @@ public static function createUserMetadata(?UserModel $user): array
4043
/**
4144
* @param array $properties
4245
* @return mixed
46+
* @throws ConfigException
4347
* @throws ConfigNotFoundException
4448
* @throws DependencyInjectionException
4549
* @throws InvalidArgumentException
46-
* @throws KeyNotFoundException
47-
* @throws ConfigException
4850
* @throws InvalidDateException
51+
* @throws KeyNotFoundException
52+
* @throws ReflectionException
4953
*/
5054
public static function createToken($properties = [])
5155
{
@@ -54,51 +58,64 @@ public static function createToken($properties = [])
5458
return $jwt->generateToken($jwtData);
5559
}
5660

57-
public static function extractToken($token = null, $fullToken = false, $throwException = false)
61+
/**
62+
* @param HttpRequest $request
63+
* @return void
64+
* @throws Error401Exception
65+
*/
66+
public static function requireAuthenticated(HttpRequest $request): void
5867
{
59-
try {
60-
$jwt = Psr11::container()->get(JwtWrapper::class);
61-
$tokenInfo = json_decode(json_encode($jwt->extractData($token)), true);
62-
if ($fullToken) {
63-
return $tokenInfo;
64-
} else {
65-
return $tokenInfo['data'];
66-
}
67-
} catch (Exception $ex) {
68-
if ($throwException) {
69-
throw new Error401Exception($ex->getMessage());
70-
} else {
71-
return false;
72-
}
68+
self::$request = $request;
69+
if ($request->param(JwtMiddleware::JWT_PARAM_PARSE_STATUS) !== JwtMiddleware::JWT_SUCCESS) {
70+
throw new Error401Exception($request->param(JwtMiddleware::JWT_PARAM_PARSE_MESSAGE));
7371
}
7472
}
7573

76-
/**
77-
* @param string|null $token
78-
* @param bool $fullToken
79-
* @return mixed
80-
* @throws Error401Exception
81-
* @throws InvalidArgumentException
82-
*/
83-
public static function requireAuthenticated($token = null, $fullToken = false)
74+
public static function parseJwt(HttpRequest $request): void
8475
{
85-
return self::extractToken($token, $fullToken, true);
76+
self::$request = $request;
8677
}
8778

8879
/**
89-
* @param $role
90-
* @param string|null $token
91-
* @return mixed
80+
* @param HttpRequest $request
81+
* @param string $role
82+
* @return void
9283
* @throws Error401Exception
9384
* @throws Error403Exception
9485
* @throws InvalidArgumentException
9586
*/
96-
public static function requireRole($role, $token = null)
87+
public static function requireRole(HttpRequest $request, string $role): void
9788
{
98-
$data = self::requireAuthenticated($token);
99-
if ($data['role'] !== $role) {
89+
self::requireAuthenticated($request);
90+
if (JwtContext::getRole() !== $role) {
10091
throw new Error403Exception('Insufficient privileges');
10192
}
102-
return $data;
10393
}
94+
95+
protected static function getRequestParam(string $value): ?string
96+
{
97+
if (isset(self::$request)) {
98+
$data = (array)self::$request->param("jwt.data");
99+
if (isset($data[$value])) {
100+
return $data[$value];
101+
}
102+
}
103+
return null;
104+
}
105+
106+
public static function getUserId(): ?string
107+
{
108+
return self::getRequestParam("userid");
109+
}
110+
111+
public static function getRole(): ?string
112+
{
113+
return self::getRequestParam("role");
114+
}
115+
116+
public static function getName(): ?string
117+
{
118+
return self::getRequestParam("name");
119+
}
120+
104121
}

0 commit comments

Comments
 (0)