BaseToken::isExpired()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Platine OAuth2
5
 *
6
 * Platine OAuth2 is a library that implements the OAuth2 specification
7
 *
8
 * This content is released under the MIT License (MIT)
9
 *
10
 * Copyright (c) 2020 Platine OAuth2
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a copy
13
 * of this software and associated documentation files (the "Software"), to deal
14
 * in the Software without restriction, including without limitation the rights
15
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
 * copies of the Software, and to permit persons to whom the Software is
17
 * furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included in all
20
 * copies or substantial portions of the Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
 * SOFTWARE.
29
 */
30
31
declare(strict_types=1);
32
33
namespace Platine\OAuth2\Entity;
34
35
use DateMalformedStringException;
0 ignored issues
show
Bug introduced by
The type DateMalformedStringException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
36
use DateTime;
37
use DateTimeInterface;
38
39
/**
40
 * Provide basic functionality for both access tokens, refresh tokens and authorization codes
41
 * Please note that scopes are stored as a string instead using
42
 * associations to scope entities, mainly for performance reasons and to avoid useless database calls.
43
 *
44
 * @class BaseToken
45
 * @package Platine\OAuth2\Entity
46
 */
47
abstract class BaseToken
48
{
49
    /**
50
     * The token value
51
     * @var string
52
     */
53
    protected string $token;
54
55
    /**
56
     * The client to use
57
     * @var Client|null
58
     */
59
    protected ?Client $client = null;
60
61
    /**
62
     * The token owner
63
     * @var TokenOwnerInterface|null
64
     */
65
    protected ?TokenOwnerInterface $owner = null;
66
67
    /**
68
     * The token expires at
69
     * @var DateTimeInterface|null
70
     */
71
    protected ?DateTimeInterface $expireAt = null;
72
73
    /**
74
     * The scopes associated with the token
75
     * @var array<string>
76
     */
77
    protected array $scopes = [];
78
79
    /**
80
     * Can not rewrite the constructor in child classes
81
     */
82
    final public function __construct()
83
    {
84
    }
85
86
    /**
87
     * Create token using given data
88
     * @param array<string, mixed> $data
89
     * @return self
90
     */
91
    public static function hydrate(array $data): self
92
    {
93
        $token = new static();
94
        $token->token = $data['token'];
95
        $token->owner = $data['owner'];
96
        $token->client = $data['client'];
97
        $token->scopes = (array) $data['scopes'];
98
        $token->expireAt = $data['expires_at'];
99
100
        return $token;
101
    }
102
103
    /**
104
     * Return the token owner
105
     * @return TokenOwnerInterface|null
106
     */
107
    public function getOwner(): ?TokenOwnerInterface
108
    {
109
        return $this->owner;
110
    }
111
112
    /**
113
     * Return the client
114
     * @return Client|null
115
     */
116
    public function getClient(): ?Client
117
    {
118
        return $this->client;
119
    }
120
121
    /**
122
     * Return the token value
123
     * @return string
124
     */
125
    public function getToken(): string
126
    {
127
        return $this->token;
128
    }
129
130
    /**
131
     * Return the expires at
132
     * @return DateTimeInterface|null
133
     */
134
    public function getExpireAt(): ?DateTimeInterface
135
    {
136
        return $this->expireAt ? clone $this->expireAt : null;
137
    }
138
139
140
    /**
141
     * Return the token expires in (seconds)
142
     * (if expired, will return a negative value)
143
     * @return int
144
     */
145
    public function getExpiresIn(): int
146
    {
147
        if ($this->expireAt === null) {
148
            return 0;
149
        }
150
151
        return $this->expireAt->getTimestamp() - (new DateTime())->getTimestamp();
152
    }
153
154
    /**
155
     * Whether the token is expired
156
     * @return bool
157
     */
158
    public function isExpired(): bool
159
    {
160
        if ($this->expireAt === null) {
161
            return true;
162
        }
163
164
        return $this->expireAt->getTimestamp() <= (new DateTime())->getTimestamp();
165
    }
166
167
    /**
168
     * Return the scopes
169
     *
170
     * @return array<string>
171
     */
172
    public function getScopes(): array
173
    {
174
        return $this->scopes;
175
    }
176
177
    /**
178
     * Match the scopes of the token with the one provided in the parameter
179
     * @param string|array<string> $scopes
180
     * @return bool
181
     */
182
    public function matchScopes(string|array $scopes): bool
183
    {
184
        if (is_string($scopes)) {
0 ignored issues
show
introduced by
The condition is_string($scopes) is always false.
Loading history...
185
            $scopes = explode(' ', $scopes);
186
        }
187
        $diff = array_diff($scopes, $this->scopes);
188
189
        return count($diff) === 0;
190
    }
191
192
    /**
193
     * Check if the token is valid, according to the
194
     * given scope(s) and expiration dates
195
     * @param string|array<string> $scopes
196
     * @return bool
197
     */
198
    public function isValid(string|array $scopes): bool
199
    {
200
        if ($this->isExpired()) {
201
            return false;
202
        }
203
204
        if (!empty($scopes) && $this->matchScopes($scopes) === false) {
205
            return false;
206
        }
207
208
        return true;
209
    }
210
211
    /**
212
     * Create new token
213
     * @param int $ttl
214
     * @param TokenOwnerInterface|null $owner
215
     * @param Client|null $client
216
     * @param array<string>|Scope[]|null $scopes
217
     * @return self
218
     */
219
    protected static function createNew(
220
        int $ttl,
221
        ?TokenOwnerInterface $owner = null,
222
        ?Client $client = null,
223
        ?array $scopes = null
224
    ): self {
225
        if (is_array($scopes)) {
226
            $scopes = array_map(fn($scope) => (string) $scope, $scopes);
227
        }
228
229
        $token = new static();
230
        $token->token = bin2hex(random_bytes(20));
231
        $token->owner = $owner;
232
        $token->client = $client;
233
        $token->scopes = $scopes ?? [];
234
235
        $expireAt = null;
236
        if ($ttl > 0) {
237
            // Since PHP >= 8.3 DateTime::modify() now throws
238
            // DateMalformedStringException if an invalid string is passed.
239
            if (PHP_VERSION_ID >= 80300) {
240
                try {
241
                    $expireAt = (new DateTime())->modify(sprintf('%+d seconds', $ttl));
242
                } catch (DateMalformedStringException $ex) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
243
                }
244
            } else {
245
                $res = (new DateTime())->modify(sprintf('%+d seconds', $ttl));
246
                if ($res !== false) {
247
                    $expireAt = $res;
248
                }
249
            }
250
        }
251
252
        $token->expireAt = $expireAt;
253
254
        return $token;
255
    }
256
}
257