Passed
Push — master ( 298e1f...cdf893 )
by Mr
02:07
created

SecureActionHandler::getValidator()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 5
ccs 0
cts 5
cp 0
crap 6
rs 10
1
<?php declare(strict_types=1);
2
/**
3
 * This file is part of the daikon-cqrs/security-interop project.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace Daikon\Security\Middleware;
10
11
use Daikon\Boot\Middleware\Action\ActionInterface;
12
use Daikon\Boot\Middleware\ActionHandler;
13
use Daikon\Interop\Assertion;
14
use Daikon\Interop\AssertionFailedException;
15
use Daikon\Interop\RuntimeException;
16
use Daikon\Security\Exception\AuthenticationException;
17
use Daikon\Security\Exception\AuthorizationException;
18
use Daikon\Security\Middleware\Action\SecureActionInterface;
19
use Exception;
20
use Middlewares\Utils\Factory;
21
use Psr\Http\Message\ResponseInterface;
22
use Psr\Http\Message\ServerRequestInterface;
23
24
final class SecureActionHandler extends ActionHandler
25
{
26
    protected function executeAction(ActionInterface $action, ServerRequestInterface $request): ResponseInterface
27
    {
28
        try {
29
            // Check action access first before running validation
30
            if ($action instanceof SecureActionInterface) {
31
                if (!$action->isAuthorized($request)) {
32
                    return Factory::createResponse(self::STATUS_FORBIDDEN);
33
                }
34
            }
35
36
            $request = $action->registerValidator($request);
37
            if ($validator = $this->getValidator($request)) {
38
                $request = $validator($request);
39
                Assertion::noContent($request->getAttribute(self::ATTR_ERRORS));
40
            }
41
42
            // Run secondary resource authorization after validation
43
            if ($action instanceof SecureActionInterface) {
44
                if (!$action->isAuthorized($request)) {
45
                    return Factory::createResponse(self::STATUS_FORBIDDEN);
46
                }
47
            }
48
49
            $request = $action($request);
50
        } catch (Exception $error) {
51
            switch (true) {
52
                case $error instanceof AssertionFailedException:
53
                    $statusCode = self::STATUS_UNPROCESSABLE_ENTITY;
54
                    break;
55
                case $error instanceof AuthenticationException:
56
                    $statusCode = self::STATUS_UNAUTHORIZED;
57
                    break;
58
                case $error instanceof AuthorizationException:
59
                    $statusCode = self::STATUS_FORBIDDEN;
60
                    break;
61
                default:
62
                    $this->logger->error($error->getMessage(), ['trace' => $error->getTrace()]);
63
                    $statusCode = self::STATUS_INTERNAL_SERVER_ERROR;
64
            }
65
            $request = $action->handleError(
66
                $request->withAttribute(
67
                    self::ATTR_STATUS_CODE,
68
                    $request->getAttribute(self::ATTR_STATUS_CODE, $statusCode)
69
                )->withAttribute(
70
                    self::ATTR_ERRORS,
71
                    $request->getAttribute(self::ATTR_ERRORS, $error)
72
                )
73
            );
74
        }
75
76
        if (!$responder = $this->getResponder($request)) {
77
            throw $error ?? new RuntimeException(
78
                sprintf("Unable to determine responder for '%s'.", get_class($action))
79
            );
80
        }
81
82
        return $responder($request);
83
    }
84
}
85