Completed
Push — develop ( 40c835...a2fd7c )
by Neomerx
06:52 queued 05:10
created

BasePassportServerIntegration::getContainer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php namespace Limoncello\Passport\Integration;
2
3
/**
4
 * Copyright 2015-2018 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use Doctrine\DBAL\Connection;
20
use Exception;
21
use Limoncello\Contracts\Settings\SettingsProviderInterface;
22
use Limoncello\OAuthServer\Contracts\ClientInterface;
23
use Limoncello\Passport\Contracts\Entities\DatabaseSchemaInterface;
24
use Limoncello\Passport\Contracts\Entities\TokenInterface;
25
use Limoncello\Passport\Contracts\PassportServerIntegrationInterface;
26
use Limoncello\Passport\Entities\Client;
27
use Limoncello\Passport\Entities\DatabaseSchema;
28
use Limoncello\Passport\Package\PassportSettings as C;
29
use Psr\Container\ContainerInterface;
30
use Psr\Http\Message\ResponseInterface;
31
use Psr\Http\Message\UriInterface;
32
use Zend\Diactoros\Response\RedirectResponse;
33
use Zend\Diactoros\Uri;
34
35
/**
36
 * @package Limoncello\Passport
37
 *
38
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
39
 */
40
abstract class BasePassportServerIntegration implements PassportServerIntegrationInterface
41
{
42
    /** Approval parameter */
43
    const SCOPE_APPROVAL_TYPE = 'type';
44
45
    /** Approval parameter */
46
    const SCOPE_APPROVAL_CLIENT_ID = 'client_id';
47
48
    /** Approval parameter */
49
    const SCOPE_APPROVAL_CLIENT_NAME = 'client_name';
50
51
    /** Approval parameter */
52
    const SCOPE_APPROVAL_REDIRECT_URI = 'redirect_uri';
53
54
    /** Approval parameter */
55
    const SCOPE_APPROVAL_IS_SCOPE_MODIFIED = 'is_scope_modified';
56
57
    /** Approval parameter */
58
    const SCOPE_APPROVAL_SCOPE = 'scope';
59
60
    /** Approval parameter */
61
    const SCOPE_APPROVAL_STATE = 'state';
62
63
    /**
64
     * @var ContainerInterface
65
     */
66
    private $container;
67
68
    /**
69
     * @var array
70
     */
71
    private $settings;
72
73
    /**
74
     * @var string
75
     */
76
    private $defaultClientId;
77
78
    /**
79
     * @var Connection
80
     */
81
    private $connection;
82
83
    /**
84
     * @var DatabaseSchemaInterface
85
     */
86
    private $databaseSchema;
87
88
    /**
89
     * @var string
90
     */
91
    private $approvalUriString;
92
93
    /**
94
     * @var string
95
     */
96
    private $errorUriString;
97
98
    /**
99
     * @var int
100
     */
101
    private $codeExpiration;
102
103
    /**
104
     * @var int
105
     */
106
    private $tokenExpiration;
107
    /**
108
     * @var bool
109
     */
110
    private $isRenewRefreshValue;
111
112
    /**
113
     * @var callable|null
114
     */
115
    private $customPropProvider;
116
117
    /**
118
     * @param ContainerInterface $container
119
     */
120 23
    public function __construct(ContainerInterface $container)
121
    {
122 23
        $this->container = $container;
123 23
        $this->settings  = $container->get(SettingsProviderInterface::class)->get(C::class);
124
125
        /** @var Connection $connection */
126 23
        $connection = $container->get(Connection::class);
127
128
        /** @var callable|null $customPropProvider */
129 23
        $customPropProvider = $this->settings[C::KEY_TOKEN_CUSTOM_PROPERTIES_PROVIDER] ?? null;
130 23
        $wrapper            = $customPropProvider !== null ?
131
            function (TokenInterface $token) use ($container, $customPropProvider): array {
132 5
                return call_user_func($customPropProvider, $container, $token);
133 23
            } : null;
134
135 23
        $this->defaultClientId     = $this->settings[C::KEY_DEFAULT_CLIENT_ID];
136 23
        $this->connection          = $connection;
137 23
        $this->approvalUriString   = $this->settings[C::KEY_APPROVAL_URI_STRING];
138 23
        $this->errorUriString      = $this->settings[C::KEY_ERROR_URI_STRING];
139 23
        $this->codeExpiration      = $this->settings[C::KEY_CODE_EXPIRATION_TIME_IN_SECONDS] ?? 600;
140 23
        $this->tokenExpiration     = $this->settings[C::KEY_TOKEN_EXPIRATION_TIME_IN_SECONDS] ?? 3600;
141 23
        $this->isRenewRefreshValue = $this->settings[C::KEY_RENEW_REFRESH_VALUE_ON_TOKEN_REFRESH] ?? false;
142 23
        $this->customPropProvider  = $wrapper;
143
    }
144
145
    /**
146
     * @inheritdoc
147
     */
148 7
    public function validateUserId(string $userName, string $password)
149
    {
150 7
        $validator    = $this->settings[C::KEY_USER_CREDENTIALS_VALIDATOR];
151 7
        $nullOrUserId = call_user_func($validator, $this->getContainer(), $userName, $password);
152
153 7
        return $nullOrUserId;
154
    }
155
156
    /**
157
     * @inheritdoc
158
     */
159 6
    public function verifyAllowedUserScope(int $userIdentity, array $scope = null): ?array
160
    {
161 6
        $validator   = $this->settings[C::KEY_USER_SCOPE_VALIDATOR];
162 6
        $nullOrScope = call_user_func($validator, $this->getContainer(), $userIdentity, $scope);
163
164 6
        return $nullOrScope;
165
    }
166
167
    /**
168
     * @inheritdoc
169
     */
170 4
    public function getDefaultClientIdentifier(): string
171
    {
172 4
        return $this->defaultClientId;
173
    }
174
175
    /**
176
     * @inheritdoc
177
     *
178
     * @throws Exception
179
     */
180 1
    public function generateCodeValue(TokenInterface $token): string
181
    {
182 1
        $codeValue = bin2hex(random_bytes(16)) . uniqid();
183
184 1
        assert(is_string($codeValue) === true && empty($codeValue) === false);
185
186 1
        return $codeValue;
187
    }
188
189
    /**
190
     * @inheritdoc
191
     *
192
     * @throws Exception
193
     */
194 6
    public function generateTokenValues(TokenInterface $token): array
195
    {
196 6
        $tokenValue     = bin2hex(random_bytes(16)) . uniqid();
197 6
        $tokenType      = 'bearer';
198 6
        $tokenExpiresIn = $this->getTokenExpirationPeriod();
199 6
        $refreshValue   = bin2hex(random_bytes(16)) . uniqid();
200
201 6
        assert(is_string($tokenValue) === true && empty($tokenValue) === false);
202 6
        assert(is_string($tokenType) === true && empty($tokenType) === false);
203 6
        assert(is_int($tokenExpiresIn) === true && $tokenExpiresIn > 0);
204 6
        assert($refreshValue === null || (is_string($refreshValue) === true && empty($refreshValue) === false));
205
206 6
        return [$tokenValue, $tokenType, $tokenExpiresIn, $refreshValue];
207
    }
208
209
    /**
210
     * @inheritdoc
211
     */
212 1
    public function getCodeExpirationPeriod(): int
213
    {
214 1
        return $this->codeExpiration;
215
    }
216
217
    /**
218
     * @inheritdoc
219
     */
220 6
    public function getTokenExpirationPeriod(): int
221
    {
222 6
        return $this->tokenExpiration;
223
    }
224
225
    /**
226
     * @inheritdoc
227
     */
228 2
    public function isRenewRefreshValue(): bool
229
    {
230 2
        return $this->isRenewRefreshValue;
231
    }
232
233
    /**
234
     * @inheritdoc
235
     */
236 5
    public function createInvalidClientAndRedirectUriErrorResponse(): ResponseInterface
237
    {
238 5
        return new RedirectResponse($this->getErrorUriString());
239
    }
240
241
    /** @noinspection PhpTooManyParametersInspection
242
     * @inheritdoc
243
     *
244
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
245
     */
246 5
    public function createAskResourceOwnerForApprovalResponse(
247
        string $type,
248
        ClientInterface $client,
249
        string $redirectUri = null,
250
        bool $isScopeModified = false,
251
        array $scopeList = null,
252
        string $state = null,
253
        array $extraParameters = []
254
    ): ResponseInterface {
255
        /** @var Client $client */
256 5
        assert($client instanceof Client);
257
258
        // TODO think if we can receive objects instead of individual properties
259 5
        $scopeList = empty($scopeList) === true ? null : implode(' ', $scopeList);
260 5
        $filtered  = array_filter([
261 5
            self::SCOPE_APPROVAL_TYPE              => $type,
262 5
            self::SCOPE_APPROVAL_CLIENT_ID         => $client->getIdentifier(),
263 5
            self::SCOPE_APPROVAL_CLIENT_NAME       => $client->getName(),
264 5
            self::SCOPE_APPROVAL_REDIRECT_URI      => $redirectUri,
265 5
            self::SCOPE_APPROVAL_IS_SCOPE_MODIFIED => $isScopeModified,
266 5
            self::SCOPE_APPROVAL_SCOPE             => $scopeList,
267 5
            self::SCOPE_APPROVAL_STATE             => $state,
268
        ], function ($value) {
269 5
            return $value !== null;
270 5
        });
271
272 5
        return new RedirectResponse($this->createRedirectUri($this->getApprovalUriString(), $filtered));
273
    }
274
275
    /**
276
     * @inheritdoc
277
     */
278 1
    public function verifyClientCredentials(ClientInterface $client, string $credentials): bool
279
    {
280
        /** @var \Limoncello\Passport\Contracts\Entities\ClientInterface $client */
281 1
        assert($client instanceof \Limoncello\Passport\Contracts\Entities\ClientInterface);
282
283 1
        return password_verify($credentials, $client->getCredentials());
284
    }
285
286
    /**
287
     * @inheritdoc
288
     */
289 5
    public function getBodyTokenExtraParameters(TokenInterface $token): array
290
    {
291 5
        return $this->customPropProvider !== null ? call_user_func($this->customPropProvider, $token) : [];
292
    }
293
294
    /**
295
     * @param string $uri
296
     * @param array  $data
297
     *
298
     * @return UriInterface
299
     */
300 5
    protected function createRedirectUri(string $uri, array $data): UriInterface
301
    {
302 5
        $query  = http_build_query($data, '', '&', PHP_QUERY_RFC3986);
303 5
        $result = (new Uri($uri))->withQuery($query);
304
305 5
        return $result;
306
    }
307
308
    /**
309
     * @return ContainerInterface
310
     */
311 7
    protected function getContainer(): ContainerInterface
312
    {
313 7
        return $this->container;
314
    }
315
316
    /**
317
     * @return Connection
318
     */
319 19
    protected function getConnection(): Connection
320
    {
321 19
        return $this->connection;
322
    }
323
324
    /**
325
     * @return DatabaseSchemaInterface
326
     */
327 19
    protected function getDatabaseSchema(): DatabaseSchemaInterface
328
    {
329 19
        if ($this->databaseSchema === null) {
330 19
            $this->databaseSchema = new DatabaseSchema();
331
        }
332
333 19
        return $this->databaseSchema;
334
    }
335
336
    /**
337
     * @return string
338
     */
339 5
    protected function getApprovalUriString(): string
340
    {
341 5
        return $this->approvalUriString;
342
    }
343
344
    /**
345
     * @return string
346
     */
347 5
    protected function getErrorUriString(): string
348
    {
349 5
        return $this->errorUriString;
350
    }
351
}
352