1:  2:  3:  4:  5:  6:  7:  8:  9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 
<?php

declare(strict_types=1);

namespace Wtf\Auth\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Wtf\Root;

/**
 * Role-Based Access Control.
 */
class RBAC extends Root
{
    /**
     * Run RBAC check.
     *
     * @param ServerRequestInterface $request
     * @param ResponseInterface      $response
     * @param callable               $next
     *
     * @return ResponseInterface
     */
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface
    {
        //404 if route not found
        if (!$request->getAttribute('route')) {
            return $this->notFoundHandler->__invoke($request, $response);
        }

        // Check if allowed for current role (if exists) or for anonymous role
        if ($this->isAllowed($request, $request->getAttribute('role', '')) || $this->isAllowed($request, $this->config('auth.rbac.defaultRole', 'anonymous'))) {
            // Run next middleware if allowed
            return $next($request, $response);
        }

        // Return error if not allowed
        return $this->config('auth.rbac.errorCallback') ? $this->config('auth.rbac.errorCallback')($request, $response) : $response->withJson([
            'error' => [
                'message' => 'You are note allowed to see this page',
                'fields' => [],
            ],
            'count' => 0,
            'data' => [],
        ], 401);
    }

    /**
     * Check if request allowed for current user.
     *
     * @param ServerRequestInterface $request
     * @param string                 $role
     *
     * @return bool
     */
    protected function isAllowed(ServerRequestInterface $request, string $role): bool
    {
        $route = $request->getAttribute('route');
        // Get config string like 'routes./home.second.rbac.anonymous' to get rbac config for route with name "second" in route group "home"
        $config = 'routes.'.$route->getGroups()[0]->getPattern().'.'.\strtr('/'.$route->getName(), [$route->getGroups()[0]->getPattern() => '', '-' => '']).'.rbac.'.$role;
        if (\in_array($request->getMethod(), $this->config($config, []), true)) {
            return true;
        }

        return false;
    }
}