Completed
Push — v3.0.0-dev ( c7f980...54069c )
by Hilmi Erdem
28:15
created

Token::cipherText()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 2
cp 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
/*
4
 * @copyright 2018 Hilmi Erdem KEREN
5
 * @license MIT
6
 */
7
8
namespace Erdemkeren\TemporaryAccess;
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
    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
        $now = $this->getNow();
52
53
        if (null === $authenticableId) {
54
            throw new \LogicException(
55
                'The unique identifier of token owner shall not be null.'
56
            );
57
        }
58
59
        $this->attributes['authenticable_id'] = $authenticableId;
60
        $this->attributes['plain_text'] = $plainText;
61
        $this->attributes['cipher_text'] = $cipherText;
62
        $this->attributes['created_at'] = $createdAt ?: $now;
63
        $this->attributes['updated_at'] = $updatedAt ?: $now;
64
        $this->attributes['expiry_time'] = null === $expiryTime ? $this->getDefaultExpiryTime() : $expiryTime;
65
    }
66
67
    /**
68
     * Convert the token to string.
69
     *
70
     * @return string
71
     */
72
    public function __toString(): string
73
    {
74
        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
    public function authenticableId()
84
    {
85
        return $this->attributes['authenticable_id'];
86
    }
87
88
    /**
89
     * Get the token as cipher text.
90
     *
91
     * @return string
92
     */
93
    public function cipherText(): string
94
    {
95
        return $this->attributes['cipher_text'];
96
    }
97
98
    /**
99
     * Get the token as plain text.
100
     *
101
     * @return null|string
102
     */
103
    public function plainText(): ?string
104
    {
105
        return $this->attributes['plain_text'];
106
    }
107
108
    /**
109
     * Get the date token created.
110
     *
111
     * @return Carbon
112
     */
113
    public function createdAt(): Carbon
114
    {
115
        return clone $this->attributes['created_at'];
116
    }
117
118
    /**
119
     * Get the last update date of the token.
120
     *
121
     * @return Carbon
122
     */
123
    public function updatedAt(): Carbon
124
    {
125
        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
    public function expiryTime(): int
134
    {
135
        return $this->attributes['expiry_time'];
136
    }
137
138
    /**
139
     * Get the date time the token will expire.
140
     *
141
     * @return Carbon
142
     */
143
    public function expiresAt(): Carbon
144
    {
145
        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
    public function timeLeft(): int
154
    {
155
        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
    public function expired(): bool
164
    {
165
        return $this->timeLeft() <= 0;
166
    }
167
168
    /**
169
     * Alias for invalidate.
170
     */
171
    public function revoke(): void
172
    {
173
        $this->invalidate();
174
    }
175
176
    /**
177
     * Invalidate the token.
178
     */
179
    public function invalidate(): void
180
    {
181
        $this->attributes['expiry_time'] = 0;
182
183
        $this->persist();
184
    }
185
186
    /**
187
     * Extend the validity of the token.
188
     *
189
     * @param null|int $seconds
190
     *
191
     * @return bool
192
     */
193
    public function extend(?int $seconds = null): bool
194
    {
195
        $seconds = null === $seconds ? $this->getDefaultExpiryTime() : $seconds;
196
197
        $this->attributes['expiry_time'] += $seconds;
198
199
        return $this->persist();
200
    }
201
202
    /**
203
     * Refresh the token.
204
     *
205
     * @return bool
206
     */
207
    public function refresh(): bool
208
    {
209
        return $this->extend(
210
            $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
    public static function create(
224
        $authenticableId,
225
        string $cipherText,
226
        ?string $plainText = null
227
    ): TokenInterface {
228
        $token = new self($authenticableId, $cipherText, $plainText);
229
230
        $token->persist();
231
232
        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
    public static function retrieveByAttributes(array $attributes): ?TokenInterface
243
    {
244
        $query = DB::table(self::getTable());
245
246
        foreach ($attributes as $key => $value) {
247
            $query->where($key, $value);
248
        }
249
250
        if (!$entity = $query->first()) {
251
            return null;
252
        }
253
254
        return new static(
255
            $entity->authenticable_id,
256
            $entity->cipher_text,
257
            null,
258
            $entity->expiry_time,
259
            new Carbon($entity->created_at),
260
            new Carbon($entity->updated_at)
261
        );
262
    }
263
264
    /**
265
     * Convert the token to a token notification.
266
     *
267
     * @return Notification
268
     */
269
    public function toNotification(): Notification
270
    {
271
        return new TokenNotification($this);
272
    }
273
274
    /**
275
     * Persist the token in the storage.
276
     *
277
     * @return bool
278
     */
279
    protected function persist(): bool
280
    {
281
        $this->attributes['created_at'] = $this->attributes['created_at'] ?: $this->getNow();
282
        $this->attributes['updated_at'] = $this->getNow();
283
284
        $attributes = $this->attributes;
285
286
        if (array_key_exists('plain_text', $attributes)) {
287
            unset($attributes['plain_text']);
288
        }
289
290
        try {
291
            DB::beginTransaction();
292
293
            DB::table(self::getTable())->updateOrInsert([
294
                'authenticable_id' => $this->authenticableId(),
295
                'cipher_text'      => $this->cipherText(),
296
            ], $attributes);
297
298
            DB::commit();
299
        } catch (\Exception $e) {
300
            DB::rollBack();
301
302
            throw new \RuntimeException(
303
                'Something went wrong while saving the access token.',
304
                0,
305
                $e
306
            );
307
        }
308
309
        return true;
310
    }
311
312
    /**
313
     * Get the date time at the moment.
314
     *
315
     * @return Carbon
316
     */
317
    private function getNow(): Carbon
318
    {
319
        return Carbon::now();
320
    }
321
322
    /**
323
     * Get the name of the table token will be persisted.
324
     *
325
     * @return string
326
     */
327
    private static function getTable(): string
328
    {
329
        return config('temporary_access.table');
330
    }
331
332
    /**
333
     * Get the default expiry time in seconds.
334
     *
335
     * @return int
336
     */
337
    private function getDefaultExpiryTime(): int
338
    {
339
        return config('temporary_access.expires') * 60;
340
    }
341
}
342