Skip to content

Commit

Permalink
Merge pull request pkp#10343 from touhidurabir/i10342_main
Browse files Browse the repository at this point in the history
pkp#10342 Required depending middleware attachment check
  • Loading branch information
touhidurabir authored Aug 30, 2024
2 parents 590e8d1 + db0be8d commit a1f2666
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 5 deletions.
27 changes: 23 additions & 4 deletions classes/middleware/DecodeApiTokenWithValidation.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use PKP\middleware\HasUser;
use PKP\middleware\traits\HasRequiredMiddleware;
use PKP\config\Config;
use PKP\core\PKPJwt as JWT;
use PKP\core\PKPSessionGuard;
Expand All @@ -35,6 +37,18 @@

class DecodeApiTokenWithValidation
{
use HasRequiredMiddleware;

/**
* @copydoc \PKP\middleware\traits\HasRequiredMiddleware::requiredMiddleware()
*/
public function requiredMiddleware(): array
{
return [
HasUser::class,
];
}

/**
* Decode and validate the API token with incoming api request.
*
Expand All @@ -44,15 +58,19 @@ class DecodeApiTokenWithValidation
*/
public function handle(Request $request, Closure $next)
{
// Set the default/initial user resolver
$this->setUserResolver($request);

if (!$this->hasRequiredMiddleware($request)) {
// Required middleware not attached to target routes, move to next
return $next($request);
}

$jwtToken = $this->getApiToken($request);

/* VALIDATIONS */

if (!$jwtToken) {

// Set the user resolver
$this->setUserResolver($request);

// there is nothing to decode or validate,
// upto the auth layer to determine the how to handle
return $next($request);
Expand Down Expand Up @@ -106,6 +124,7 @@ public function handle(Request $request, Closure $next)

/* ACTIONS */

// Update the user resolver
$this->setUserResolver($request, $user);

return $next($request);
Expand Down
20 changes: 19 additions & 1 deletion classes/middleware/ValidateCsrfToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,33 @@
use Closure;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use PKP\middleware\HasUser;
use PKP\middleware\traits\HasRequiredMiddleware;

class ValidateCsrfToken
{
use HasRequiredMiddleware;

/**
* @copydoc \PKP\middleware\traits\HasRequiredMiddleware::requiredMiddleware()
*/
public function requiredMiddleware(): array
{
return [
HasUser::class,
];
}

/**
* Determine and validate CSRF token
*
*/
public function handle(Request $request, Closure $next)
{
if (!$this->hasRequiredMiddleware($request)) {
// Required middleware not attached to target routes, move to next
return $next($request);
}

if($this->isApiRequest($request)) {
return $next($request);
}
Expand Down
79 changes: 79 additions & 0 deletions classes/middleware/traits/HasRequiredMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

/**
* @file classes/middleware/traits/HasRequiredMiddleware.php
*
* Copyright (c) 2024 Simon Fraser University
* Copyright (c) 2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class HasRequiredMiddleware
*
* @brief Trait to find the required middleware attachments in a middleware
*/

namespace PKP\middleware\traits;

use Exception;
use Illuminate\Http\Request;
use PKP\core\PKPBaseController;

trait HasRequiredMiddleware
{
/**
* Following constant define how the required middleware validation will be handled
*
* MIDDLEWARE_MATCH_STRICT All required middleware must be attached to target route
* MIDDLEWARE_MATCH_LOOSE At least required middleware must be attached to target route
*/
public const MIDDLEWARE_MATCH_STRICT = 1;
public const MIDDLEWARE_MATCH_LOOSE = 2;

/**
* List of required middleware for this middleware to run passing logic
*/
abstract public function requiredMiddleware(): array;

/**
* Determine if required middleware attached to target routes
*/
public function hasRequiredMiddleware(Request $request, int $matchingCriteria = self::MIDDLEWARE_MATCH_STRICT): bool
{
$requiredMiddleware = collect($this->requiredMiddleware());

if ($requiredMiddleware->count() === 0) {
throw new Exception(
sprintf(
'Empty required middleware list provided for middleware class %s',
static::class
)
);
}

$currentRoute = PKPBaseController::getRequestedRoute($request);
$routeMiddleware = collect($currentRoute?->middleware() ?? []);

$router = app('router'); /** @var \Illuminate\Routing\Router $router */
$routerMiddleware = $router->getMiddleware();

// need to replace the alias name with full class path
$routeMiddleware = $routeMiddleware->map(function (string $middleware) use($routerMiddleware): string {
// extract the middleware class or alias name if in format
// such as `has.roles:1|16|17` or `PKP\middleware\HasRoles:1|16|17`
$middleware = array_shift(array_pad(explode(":", $middleware, 2), 2, []));

if (class_exists($middleware)) {
return $middleware;
}

return $routerMiddleware[$middleware] ?? $middleware;
});

return match($matchingCriteria) {
static::MIDDLEWARE_MATCH_STRICT
=> $routeMiddleware->intersect($requiredMiddleware)->count() === $requiredMiddleware->count(),
static::MIDDLEWARE_MATCH_LOOSE
=> $routeMiddleware->intersect($requiredMiddleware)->count() > 0,
};
}
}

0 comments on commit a1f2666

Please sign in to comment.