Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
RequestHandler
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
4 / 4
22
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 process
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
5
 resolveRequestHandler
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
9
 checkPermissions
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
7
1<?php
2
3declare(strict_types=1);
4
5namespace Engelsystem\Middleware;
6
7use Engelsystem\Application;
8use Engelsystem\Controllers\BaseController;
9use Engelsystem\Helpers\Authenticator;
10use Engelsystem\Http\Exceptions\HttpForbidden;
11use Illuminate\Support\Str;
12use Psr\Http\Message\ResponseInterface;
13use Psr\Http\Message\ServerRequestInterface;
14use Psr\Http\Server\MiddlewareInterface;
15use Psr\Http\Server\RequestHandlerInterface;
16
17class RequestHandler implements MiddlewareInterface
18{
19    use ResolvesMiddlewareTrait;
20
21    public function __construct(protected Application $container)
22    {
23    }
24
25    /**
26     * Process an incoming server request and return a response, optionally delegating
27     * response creation to a handler.
28     * Implements basic permission checking if the controller supports it.
29     */
30    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
31    {
32        $requestHandler = $request->getAttribute('route-request-handler');
33        $this->container->instance(ServerRequestInterface::class, $request);
34        $this->container->instance('request', $request);
35
36        /** @var CallableHandler|MiddlewareInterface|RequestHandlerInterface $requestHandler */
37        $requestHandler = $this->resolveRequestHandler($requestHandler);
38
39        if ($requestHandler instanceof CallableHandler) {
40            $callable = $requestHandler->getCallable();
41
42            if (is_array($callable) && $callable[0] instanceof BaseController) {
43                $this->checkPermissions($callable[0], $callable[1]);
44            }
45        }
46
47        if ($requestHandler instanceof MiddlewareInterface) {
48            return $requestHandler->process($request, $handler);
49        }
50
51        /**
52         * Is RequestHandlerInterface
53         * @see RequestHandlerInterface
54         */
55        return $requestHandler->handle($request);
56    }
57
58    /**
59     * Resolve the given class
60     */
61    protected function resolveRequestHandler(
62        string|callable|MiddlewareInterface|RequestHandlerInterface $handler
63    ): MiddlewareInterface|RequestHandlerInterface {
64        if (is_string($handler) && mb_strpos($handler, '@') !== false) {
65            list($class, $method) = explode('@', $handler, 2);
66            if (!class_exists($class) && !$this->container->has($class)) {
67                $class = sprintf('Engelsystem\\Controllers\\%s', $class);
68            }
69
70            $handler = [$class, $method];
71        }
72
73        if (
74            is_array($handler)
75            && is_string($handler[0])
76            && (
77                class_exists($handler[0])
78                || $this->container->has($handler[0])
79            )
80        ) {
81            $handler[0] = $this->container->make($handler[0]);
82        }
83
84        return $this->resolveMiddleware($handler);
85    }
86
87    /**
88     * Check required page permissions
89     */
90    protected function checkPermissions(BaseController $controller, string $method): bool
91    {
92        /** @var Authenticator $auth */
93        $auth = $this->container->get('auth');
94        $permissions = $controller->getPermissions();
95
96        // Merge action permissions
97        if (isset($permissions[$method])) {
98            $permissions = array_merge($permissions, (array) $permissions[$method]);
99        }
100
101        foreach ($permissions as $key => $permission) {
102            // Skip all action permission entries
103            if (!is_int($key)) {
104                continue;
105            }
106
107            foreach ((array) $permission as $value) {
108                if (
109                    Str::contains($value, '||')
110                        ? !$auth->canAny(explode('||', $value))
111                        : !$auth->can($permission)
112                ) {
113                    throw new HttpForbidden();
114                }
115            }
116        }
117
118        return true;
119    }
120}