Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
RequestHandler
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
4 / 4
24
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%
13 / 13
100.00% covered (success)
100.00%
1 / 1
6
 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%
16 / 16
100.00% covered (success)
100.00%
1 / 1
8
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 (
43                is_array($callable)
44                && $callable[0] instanceof BaseController
45                && !$this->checkPermissions($request, $callable[0], $callable[1])
46            ) {
47                throw new HttpForbidden();
48            }
49        }
50
51        if ($requestHandler instanceof MiddlewareInterface) {
52            return $requestHandler->process($request, $handler);
53        }
54
55        /**
56         * Is RequestHandlerInterface
57         * @see RequestHandlerInterface
58         */
59        return $requestHandler->handle($request);
60    }
61
62    /**
63     * Resolve the given class
64     */
65    protected function resolveRequestHandler(
66        string|callable|MiddlewareInterface|RequestHandlerInterface $handler
67    ): MiddlewareInterface|RequestHandlerInterface {
68        if (is_string($handler) && mb_strpos($handler, '@') !== false) {
69            list($class, $method) = explode('@', $handler, 2);
70            if (!class_exists($class) && !$this->container->has($class)) {
71                $class = sprintf('Engelsystem\\Controllers\\%s', $class);
72            }
73
74            $handler = [$class, $method];
75        }
76
77        if (
78            is_array($handler)
79            && is_string($handler[0])
80            && (
81                class_exists($handler[0])
82                || $this->container->has($handler[0])
83            )
84        ) {
85            $handler[0] = $this->container->make($handler[0]);
86        }
87
88        return $this->resolveMiddleware($handler);
89    }
90
91    /**
92     * Check required page permissions
93     */
94    protected function checkPermissions(
95        ServerRequestInterface $request,
96        BaseController $controller,
97        string $method
98    ): bool {
99        $hasPermission = $controller->hasPermission($request, $method);
100        if (!is_null($hasPermission)) {
101            return $hasPermission;
102        }
103
104        /** @var Authenticator $auth */
105        $auth = $this->container->get('auth');
106        $permissions = $controller->getPermissions();
107
108        // Merge action permissions
109        if (isset($permissions[$method])) {
110            $permissions = array_merge($permissions, (array) $permissions[$method]);
111        }
112
113        foreach ($permissions as $key => $permission) {
114            // Skip all action permission entries
115            if (!is_int($key)) {
116                continue;
117            }
118
119            foreach ((array) $permission as $value) {
120                if (
121                    Str::contains($value, '||')
122                        ? !$auth->canAny(explode('||', $value))
123                        : !$auth->can($permission)
124                ) {
125                    return false;
126                }
127            }
128        }
129
130        return true;
131    }
132}