TokenRepository::readByValue()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 2
crap 2
1
<?php declare(strict_types=1);
2
3
namespace Limoncello\Passport\Adaptors\Generic;
4
5
/**
6
 * Copyright 2015-2019 [email protected]
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
use Doctrine\DBAL\Connection;
22
use Doctrine\DBAL\DBALException as DBALEx;
23
use Doctrine\DBAL\Query\QueryBuilder;
24
use Limoncello\Passport\Contracts\Entities\DatabaseSchemaInterface;
25
use Limoncello\Passport\Contracts\Entities\TokenInterface;
26
use Limoncello\Passport\Exceptions\RepositoryException;
27
use PDO;
28
use function assert;
29
use function is_numeric;
30
31
/**
32
 * @package Limoncello\Passport
33
 */
34
class TokenRepository extends \Limoncello\Passport\Repositories\TokenRepository
35
{
36
    /**
37
     * @var string
38
     */
39
    private $modelClass;
40
41
    /**
42
     * @param Connection              $connection
43
     * @param DatabaseSchemaInterface $databaseSchema
44
     * @param string                  $modelClass
45
     */
46 23
    public function __construct(
47
        Connection $connection,
48
        DatabaseSchemaInterface $databaseSchema,
49
        string $modelClass = Token::class
50
    ) {
51 23
        $this->setConnection($connection)->setDatabaseSchema($databaseSchema);
52 23
        $this->modelClass = $modelClass;
53
    }
54
55
    /**
56
     * @inheritdoc
57
     */
58 4
    public function read(int $identifier): ?TokenInterface
59
    {
60 4
        $token = parent::read($identifier);
61
62 3
        if ($token !== null) {
63 3
            $this->addScope($token);
64
        }
65
66 3
        return $token;
67
    }
68
69
    /**
70
     * @inheritdoc
71
     */
72 3
    public function readByCode(string $code, int $expirationInSeconds): ?TokenInterface
73
    {
74 3
        $token = parent::readByCode($code, $expirationInSeconds);
75 2
        if ($token !== null) {
76 2
            $this->addScope($token);
77
        }
78
79 2
        return $token;
80
    }
81
82
    /**
83
     * @inheritdoc
84
     */
85 4
    public function readByValue(string $tokenValue, int $expirationInSeconds): ?TokenInterface
86
    {
87 4
        $token = parent::readByValue($tokenValue, $expirationInSeconds);
88 4
        if ($token !== null) {
89 4
            $this->addScope($token);
90
        }
91
92 4
        return $token;
93
    }
94
95
    /**
96
     * @inheritdoc
97
     */
98 4
    public function readByRefresh(string $refreshValue, int $expirationInSeconds): ?TokenInterface
99
    {
100 4
        $token = parent::readByRefresh($refreshValue, $expirationInSeconds);
101 4
        if ($token !== null) {
102 3
            $this->addScope($token);
103
        }
104
105 4
        return $token;
106
    }
107
108
    /**
109
     * @inheritdoc
110
     *
111
     * @throws RepositoryException
112
     */
113 2
    public function readByUser(int $userId, int $expirationInSeconds, int $limit = null): array
114
    {
115
        try {
116
            /** @var TokenInterface[] $tokens */
117 2
            $tokens = parent::readByUser($userId, $expirationInSeconds, $limit);
118
119
            // select scope identifiers for tokens
120 1
            if (empty($tokens) === false) {
121 1
                $schema        = $this->getDatabaseSchema();
122 1
                $tokenIdColumn = $schema->getTokensScopesTokenIdentityColumn();
123 1
                $scopeIdColumn = $schema->getTokensScopesScopeIdentityColumn();
124
125 1
                $connection = $this->getConnection();
126 1
                $query      = $connection->createQueryBuilder();
127
128 1
                $tokenIds = array_keys($tokens);
129
                $query
130 1
                    ->select([$tokenIdColumn, $scopeIdColumn])
131 1
                    ->from($schema->getTokensScopesTable())
132 1
                    ->where($query->expr()->in($tokenIdColumn, $tokenIds))
133 1
                    ->orderBy($tokenIdColumn);
134
135 1
                $statement = $query->execute();
136 1
                $statement->setFetchMode(PDO::FETCH_ASSOC);
137 1
                $tokenScopePairs = $statement->fetchAll();
138
139 1
                $curTokenId = null;
140 1
                $curScopes  = null;
141
                // set selected scopes to tokens
142 1
                foreach ($tokenScopePairs as $pair) {
143 1
                    $tokenId = $pair[$tokenIdColumn];
144 1
                    $scopeId = $pair[$scopeIdColumn];
145
146 1
                    if ($curTokenId !== $tokenId) {
147 1
                        $assignScopes = $curTokenId !== null && empty($curScopes) === false;
148 1
                        $assignScopes ? $tokens[$curTokenId]->setScopeIdentifiers($curScopes) : null;
149 1
                        $curTokenId = $tokenId;
150 1
                        $curScopes  = [$scopeId];
151
152 1
                        continue;
153
                    }
154
155 1
                    $curScopes[] = $scopeId;
156
                }
157 1
                $curTokenId === null || empty($curScopes) === true ?:
158 1
                    $tokens[$curTokenId]->setScopeIdentifiers($curScopes);
159
            }
160
161 1
            return $tokens;
162 1
        } /** @noinspection PhpRedundantCatchClauseInspection */ catch (RepositoryException | DBALEx $exception) {
163 1
            $message = 'Token reading failed.';
164 1
            throw new RepositoryException($message, 0, $exception);
165
        }
166
    }
167
168
    /**
169
     * @inheritdoc
170
     *
171
     * @throws RepositoryException
172
     */
173 2
    public function readPassport(string $tokenValue, int $expirationInSeconds): ?array
174
    {
175
        try {
176 2
            $statement = $this->createPassportDataQuery($tokenValue, $expirationInSeconds)->execute();
177 1
            $statement->setFetchMode(PDO::FETCH_ASSOC);
178 1
            $data   = $statement->fetch();
179 1
            $result = null;
180 1
            if ($data !== false) {
181 1
                $schema  = $this->getDatabaseSchema();
182 1
                $tokenId = $data[$schema->getTokensIdentityColumn()];
183 1
                assert(is_numeric($tokenId));
184
185 1
                $scopes                                     = $this->readScopeIdentifiers((int)$tokenId);
186 1
                $data[$schema->getTokensViewScopesColumn()] = $scopes;
187 1
                $result                                     = $data;
188
            }
189
190 1
            return $result;
191 1
        } /** @noinspection PhpRedundantCatchClauseInspection */ catch (DBALEx $exception) {
192 1
            $message = 'Passport reading failed';
193 1
            throw new RepositoryException($message, 0, $exception);
194
        }
195
    }
196
197
    /**
198
     * @inheritdoc
199
     */
200 10
    protected function getClassName(): string
201
    {
202 10
        return $this->modelClass;
203
    }
204
205
    /**
206
     * @inheritdoc
207
     */
208 14
    protected function getTableNameForReading(): string
209
    {
210 14
        return $this->getTableNameForWriting();
211
    }
212
213
    /**
214
     * @param string $tokenValue
215
     * @param int    $expirationInSeconds
216
     *
217
     * @return QueryBuilder
218
     */
219 2
    private function createPassportDataQuery(
220
        string $tokenValue,
221
        int $expirationInSeconds
222
    ): QueryBuilder {
223 2
        $schema = $this->getDatabaseSchema();
224 2
        $query  = $this->createEnabledTokenByColumnWithExpirationCheckQuery(
225 2
            $tokenValue,
226 2
            $schema->getTokensValueColumn(),
227 2
            $expirationInSeconds,
228 2
            $schema->getTokensValueCreatedAtColumn()
229
        );
230
231 2
        $connection       = $query->getConnection();
232 2
        $tokensTableAlias = $this->getTableNameForReading();
233 2
        $usersTable       = $connection->quoteIdentifier($usersTableAlias = $schema->getUsersTable());
234 2
        $usersFk          = $connection->quoteIdentifier($schema->getTokensUserIdentityColumn());
235 2
        $usersPk          = $connection->quoteIdentifier($schema->getUsersIdentityColumn());
236 2
        $query->innerJoin(
237 2
            $tokensTableAlias,
238 2
            $usersTable,
239 2
            $usersTableAlias,
240 2
            "$tokensTableAlias.$usersFk = $usersTableAlias.$usersPk"
241
        );
242
243 2
        return $query;
244
    }
245
246
    /**
247
     * @param TokenInterface $token
248
     *
249
     * @return void
250
     *
251
     * @throws RepositoryException
252
     */
253 10
    private function addScope(TokenInterface $token)
254
    {
255 10
        $token->setScopeIdentifiers($this->readScopeIdentifiers($token->getIdentifier()));
256
    }
257
}
258