Passed
Push — master ( 8b5b59...a17876 )
by Mr
02:27
created

SecureActionHandler   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 55
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 35
c 4
b 0
f 0
dl 0
loc 55
ccs 0
cts 46
cp 0
rs 10
wmc 11

1 Method

Rating   Name   Duplication   Size   Complexity  
C execute() 0 53 11
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 Daikon\Validize\Validation\ValidatorDefinition;
20
use Daikon\Validize\ValueObject\Severity;
21
use Exception;
22
use Middlewares\Utils\Factory;
23
use Psr\Http\Message\ResponseInterface;
24
use Psr\Http\Message\ServerRequestInterface;
25
26
final class SecureActionHandler extends ActionHandler
27
{
28
    protected function execute(ActionInterface $action, ServerRequestInterface $request): ResponseInterface
29
    {
30
        try {
31
            // Check action access first before running validation
32
            if ($action instanceof SecureActionInterface) {
33
                if (!$action->isAuthorized($request)) {
34
                    return Factory::createResponse(self::STATUS_FORBIDDEN);
35
                }
36
            }
37
38
            if ($validator = $action->getValidator($request)) {
39
                $validatorDefinition = (new ValidatorDefinition('$', Severity::critical()))->withArgument($request);
40
                $request = $request->withAttribute(self::PAYLOAD, $validator($validatorDefinition));
41
                Assertion::noContent($request->getAttribute(self::ERRORS));
42
            }
43
44
            // Run secondary resource authorization after validation
45
            if ($action instanceof SecureActionInterface) {
46
                if (!$action->isAuthorized($request)) {
47
                    return Factory::createResponse(self::STATUS_FORBIDDEN);
48
                }
49
            }
50
51
            $request = $action($request);
52
        } catch (Exception $error) {
53
            switch (true) {
54
                case $error instanceof AssertionFailedException:
55
                    $statusCode = self::STATUS_UNPROCESSABLE_ENTITY;
56
                    break;
57
                case $error instanceof AuthenticationException:
58
                    $statusCode = self::STATUS_UNAUTHORIZED;
59
                    break;
60
                case $error instanceof AuthorizationException:
61
                    $statusCode = self::STATUS_FORBIDDEN;
62
                    break;
63
                default:
64
                    $this->logger->error($error->getMessage(), ['trace' => $error->getTrace()]);
65
                    $statusCode = self::STATUS_INTERNAL_SERVER_ERROR;
66
            }
67
            $request = $action->handleError(
68
                $request
69
                    ->withAttribute(self::STATUS_CODE, $request->getAttribute(self::STATUS_CODE, $statusCode))
70
                    ->withAttribute(self::ERRORS, $request->getAttribute(self::ERRORS, $error))
71
            );
72
        }
73
74
        if (!$responder = $this->resolveResponder($request)) {
75
            throw $error ?? new RuntimeException(
76
                sprintf("Unable to determine responder for '%s'.", get_class($action))
77
            );
78
        }
79
80
        return $responder->handle($request);
81
    }
82
}
83