Passed
Push — master ( 72a710...bee29d )
by Peter
03:15
created

Api::getUserByUserId()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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