Passed
Push — master ( ced451...069cb4 )
by Hilmi Erdem
04:47 queued 45s
created

Token::getNow()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
/*
4
 * @copyright 2018 Hilmi Erdem KEREN
5
 * @license MIT
6
 */
7
8
namespace Erdemkeren\Otp;
9
10
use Carbon\Carbon;
11
use Illuminate\Support\Facades\DB;
12
use Illuminate\Notifications\Notification;
13
14
/**
15
 * Class Token.
16
 */
17
class Token implements TokenInterface
18
{
19
    /**
20
     * The attributes of the token.
21
     *
22
     * @var array
23
     */
24
    public $attributes = [
25
        'authenticable_id' => null,
26
        'plain_text'       => null,
27
        'expiry_time'      => null,
28
        'cipher_text'      => null,
29
        'created_at'       => null,
30
        'updated_at'       => null,
31
    ];
32
33
    /**
34
     * Token constructor.
35
     *
36
     * @param int|mixed|string $authenticableId
37
     * @param string           $cipherText
38
     * @param null|string      $plainText
39
     * @param null|int         $expiryTime
40
     * @param null|Carbon      $createdAt
41
     * @param null|Carbon      $updatedAt
42
     */
43 20
    public function __construct(
44
        $authenticableId,
45
        string $cipherText,
46
        ?string $plainText = null,
47
        ?int    $expiryTime = null,
48
        ?Carbon $createdAt = null,
49
        ?Carbon $updatedAt = null
50
    ) {
51 20
        $now = $this->getNow();
52
53 20
        if (null === $authenticableId) {
54 1
            throw new \LogicException(
55 1
                'The unique identifier of token owner shall not be null.'
56
            );
57
        }
58
59 20
        $this->attributes['authenticable_id'] = $authenticableId;
60 20
        $this->attributes['plain_text'] = $plainText;
61 20
        $this->attributes['cipher_text'] = $cipherText;
62 20
        $this->attributes['created_at'] = $createdAt ?: $now;
63 20
        $this->attributes['updated_at'] = $updatedAt ?: $now;
64 20
        $this->attributes['expiry_time'] = null === $expiryTime ? $this->getDefaultExpiryTime() : $expiryTime;
65 20
    }
66
67
    /**
68
     * Convert the token to string.
69
     *
70
     * @return string
71
     */
72 2
    public function __toString(): string
73
    {
74 2
        return $this->cipherText();
75
    }
76
77
    /**
78
     * Get the unique identifier of the authenticable
79
     * who owns the token.
80
     *
81
     * @return mixed
82
     */
83 7
    public function authenticableId()
84
    {
85 7
        return $this->attributes['authenticable_id'];
86
    }
87
88
    /**
89
     * Get the token as cipher text.
90
     *
91
     * @return string
92
     */
93 8
    public function cipherText(): string
94
    {
95 8
        return $this->attributes['cipher_text'];
96
    }
97
98
    /**
99
     * Get the token as plain text.
100
     *
101
     * @return null|string
102
     */
103 1
    public function plainText(): ?string
104
    {
105 1
        return $this->attributes['plain_text'];
106
    }
107
108
    /**
109
     * Get the date token created.
110
     *
111
     * @return Carbon
112
     */
113 4
    public function createdAt(): Carbon
114
    {
115 4
        return clone $this->attributes['created_at'];
116
    }
117
118
    /**
119
     * Get the last update date of the token.
120
     *
121
     * @return Carbon
122
     */
123 2
    public function updatedAt(): Carbon
124
    {
125 2
        return clone $this->attributes['updated_at'];
126
    }
127
128
    /**
129
     * Get the expiry time of the token in seconds.
130
     *
131
     * @return int
132
     */
133 8
    public function expiryTime(): int
134
    {
135 8
        return $this->attributes['expiry_time'];
136
    }
137
138
    /**
139
     * Get the date time the token will expire.
140
     *
141
     * @return Carbon
142
     */
143 3
    public function expiresAt(): Carbon
144
    {
145 3
        return (clone $this->createdAt())->addSeconds($this->expiryTime());
146
    }
147
148
    /**
149
     * Get the validity time left for the token.
150
     *
151
     * @return int
152
     */
153 2
    public function timeLeft(): int
154
    {
155 2
        return $this->getNow()->diffInSeconds($this->expiresAt(), false);
156
    }
157
158
    /**
159
     * Determine if the token is expired or not.
160
     *
161
     * @return bool
162
     */
163 1
    public function expired(): bool
164
    {
165 1
        return $this->timeLeft() <= 0;
166
    }
167
168
    /**
169
     * Alias for invalidate.
170
     */
171 1
    public function revoke(): void
172
    {
173 1
        $this->invalidate();
174 1
    }
175
176
    /**
177
     * Invalidate the token.
178
     */
179 2
    public function invalidate(): void
180
    {
181 2
        $this->attributes['expiry_time'] = 0;
182
183 2
        $this->persist();
184 2
    }
185
186
    /**
187
     * Extend the validity of the token.
188
     *
189
     * @param null|int $seconds
190
     *
191
     * @return bool
192
     */
193 2
    public function extend(?int $seconds = null): bool
194
    {
195 2
        $seconds = null === $seconds ? $this->getDefaultExpiryTime() : $seconds;
196
197 2
        $this->attributes['expiry_time'] += $seconds;
198
199 2
        return $this->persist();
200
    }
201
202
    /**
203
     * Refresh the token.
204
     *
205
     * @return bool
206
     */
207 1
    public function refresh(): bool
208
    {
209 1
        return $this->extend(
210 1
            $this->getNow()->diffInSeconds($this->updatedAt())
211
        );
212
    }
213
214
    /**
215
     * Create a new token.
216
     *
217
     * @param $authenticableId
218
     * @param string      $cipherText
219
     * @param null|string $plainText
220
     *
221
     * @return TokenInterface
222
     */
223 2
    public static function create(
224
        $authenticableId,
225
        string $cipherText,
226
        ?string $plainText = null
227
    ): TokenInterface {
228 2
        $token = new self($authenticableId, $cipherText, $plainText);
229
230 2
        $token->persist();
231
232 1
        return $token;
233
    }
234
235
    /**
236
     * Retrieve a token by the given attributes from the storage.
237
     *
238
     * @param array $attributes
239
     *
240
     * @return null|TokenInterface
241
     */
242 2
    public static function retrieveByAttributes(array $attributes): ?TokenInterface
243
    {
244 2
        $query = DB::table(self::getTable());
245
246 2
        foreach ($attributes as $key => $value) {
247 1
            $query->where($key, $value);
248
        }
249
250 2
        if (! $entity = $query->first()) {
251 1
            return null;
252
        }
253
254 1
        return new static(
255 1
            $entity->authenticable_id,
256 1
            $entity->cipher_text,
257 1
            null,
258 1
            $entity->expiry_time,
259 1
            new Carbon($entity->created_at),
260 1
            new Carbon($entity->updated_at)
261
        );
262
    }
263
264
    /**
265
     * Convert the token to a token notification.
266
     *
267
     * @return Notification
268
     */
269 1
    public function toNotification(): Notification
270
    {
271 1
        return new TokenNotification($this);
272
    }
273
274
    /**
275
     * Persist the token in the storage.
276
     *
277
     * @return bool
278
     */
279 6
    protected function persist(): bool
280
    {
281 6
        $this->attributes['created_at'] = $this->attributes['created_at']->toDateTimeString();
282 6
        $this->attributes['updated_at'] = $this->getNow()->toDateTimeString();
283
284 6
        $attributes = $this->attributes;
285
286 6
        if (array_key_exists('plain_text', $attributes)) {
287 6
            unset($attributes['plain_text']);
288
        }
289
290
        try {
291 6
            DB::beginTransaction();
292
293 6
            DB::table(self::getTable())->updateOrInsert([
294 6
                'authenticable_id' => $this->authenticableId(),
295 6
                'cipher_text'      => $this->cipherText(),
296 6
            ], $attributes);
297
298 5
            DB::commit();
299 1
        } catch (\Exception $e) {
300 1
            DB::rollBack();
301
302 1
            throw new \RuntimeException(
303 1
                'Something went wrong while saving the access token.',
304 1
                0,
305 1
                $e
306
            );
307
        }
308
309 5
        return true;
310
    }
311
312
    /**
313
     * Get the date time at the moment.
314
     *
315
     * @return Carbon
316
     */
317 20
    private function getNow(): Carbon
318
    {
319 20
        return Carbon::now();
320
    }
321
322
    /**
323
     * Get the name of the table token will be persisted.
324
     *
325
     * @return string
326
     */
327 8
    private static function getTable(): string
328
    {
329 8
        return config('otp.table');
330
    }
331
332
    /**
333
     * Get the default expiry time in seconds.
334
     *
335
     * @return int
336
     */
337 2
    private function getDefaultExpiryTime(): int
338
    {
339 2
        return config('otp.expires') * 60;
340
    }
341
}
342