Api::handle()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 17
c 2
b 0
f 0
dl 0
loc 28
rs 9.3888
cc 5
nc 5
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AbterPhp\Admin\Http\Middleware;
6
7
use AbterPhp\Admin\Domain\Entities\User;
8
use AbterPhp\Admin\Orm\UserRepo;
9
use AbterPhp\Admin\Psr7\RequestConverter;
10
use Closure;
11
use Exception;
12
use League\OAuth2\Server\Exception\OAuthServerException;
13
use League\OAuth2\Server\ResourceServer;
14
use Opulence\Http\Requests\Request;
15
use Opulence\Http\Responses\Response;
16
use Opulence\Orm\OrmException;
17
use Opulence\Routing\Middleware\IMiddleware;
18
use Psr\Http\Message\ServerRequestInterface;
19
use Psr\Log\LoggerInterface;
20
21
class Api implements IMiddleware
22
{
23
    public const ATTRIBUTE_USER_ID   = 'oauth_user_id';
24
    public const ATTRIBUTE_CLIENT_ID = 'oauth_client_id';
25
26
    public const HEADER_USER_ID       = 'xxx-user-id';
27
    public const HEADER_USER_USERNAME = 'xxx-user-username';
28
29
    protected ResourceServer $resourceServer;
30
31
    protected RequestConverter $requestConverter;
32
33
    protected UserRepo $userRepo;
34
35
    protected LoggerInterface $logger;
36
37
    protected string $problemBaseUrl;
38
39
    /**
40
     * Api constructor.
41
     *
42
     * @param ResourceServer   $resourceServer
43
     * @param RequestConverter $requestConverter
44
     * @param UserRepo         $userRepo
45
     * @param LoggerInterface  $logger
46
     * @param string           $problemBaseUrl
47
     */
48
    public function __construct(
49
        ResourceServer $resourceServer,
50
        RequestConverter $requestConverter,
51
        UserRepo $userRepo,
52
        LoggerInterface $logger,
53
        string $problemBaseUrl
54
    ) {
55
        $this->resourceServer = $resourceServer;
56
57
        $this->requestConverter = $requestConverter;
58
        $this->userRepo         = $userRepo;
59
        $this->logger           = $logger;
60
        $this->problemBaseUrl   = $problemBaseUrl;
61
    }
62
63
    // TODO: Check error response formats
64
    // $next consists of the next middleware in the pipeline
65
    public function handle(Request $request, Closure $next): Response
66
    {
67
        $psr7Request = $this->requestConverter->toPsr($request);
68
69
        try {
70
            $psr7Request = $this->resourceServer->validateAuthenticatedRequest($psr7Request);
71
        } catch (OAuthServerException $e) {
72
            return $this->createResponse($e);
73
        } catch (Exception $e) {
74
            return $this->createResponse(new OAuthServerException($e->getMessage(), 0, 'unknown_error', 500));
75
        }
76
77
        try {
78
            $user = $this->getUser($psr7Request);
79
            if (null === $user) {
80
                throw new Exception('Unexpected user retrieval error');
81
            }
82
        } catch (Exception $e) {
83
            return $this->createResponse(new OAuthServerException($e->getMessage(), 0, 'unknown_error', 500));
84
        }
85
86
        // This is a workaround as Opulence request doesn't have a straight-forward way of storing internal data
87
        $headers = $request->getHeaders();
88
89
        $headers[static::HEADER_USER_ID]       = $user->getId();
90
        $headers[static::HEADER_USER_USERNAME] = $user->getUsername();
91
92
        return $next($request);
93
    }
94
95
    /**
96
     * @param OAuthServerException $e
97
     *
98
     * @return Response
99
     */
100
    protected function createResponse(OAuthServerException $e): Response
101
    {
102
        $status  = $e->getHttpStatusCode();
103
        $content = [
104
            'type'   => sprintf('%srequest-authentication-failure', $this->problemBaseUrl),
105
            'title'  => 'Access Denied',
106
            'status' => $status,
107
            'detail' => $e->getMessage(),
108
        ];
109
110
        $response = new Response();
111
        $response->setStatusCode($status);
112
        $response->setContent(json_encode($content));
113
114
        return $response;
115
    }
116
117
    /**
118
     * @param ServerRequestInterface $psr7Request
119
     *
120
     * @return User|null
121
     * @throws OrmException
122
     */
123
    protected function getUser(ServerRequestInterface $psr7Request): ?User
124
    {
125
        $user = $this->getUserByUserId($psr7Request);
126
        if ($user) {
127
            return $user;
128
        }
129
130
        return $this->getUserByClientId($psr7Request);
131
    }
132
133
    /**
134
     * @param ServerRequestInterface $psr7Request
135
     *
136
     * @return User|null
137
     * @throws OrmException
138
     */
139
    protected function getUserByUserId(ServerRequestInterface $psr7Request): ?User
140
    {
141
        $userId = $psr7Request->getAttribute(static::ATTRIBUTE_USER_ID);
142
        if (!$userId) {
143
            return null;
144
        }
145
146
        return $this->userRepo->getById($userId);
147
    }
148
149
    /**
150
     * @param ServerRequestInterface $psr7Request
151
     *
152
     * @return User|null
153
     * @throws OrmException
154
     */
155
    protected function getUserByClientId(ServerRequestInterface $psr7Request): ?User
156
    {
157
        $clientId = $psr7Request->getAttribute(static::ATTRIBUTE_CLIENT_ID);
158
        if (!$clientId) {
159
            return null;
160
        }
161
162
        return $this->userRepo->getByClientId($clientId);
163
    }
164
}
165