forked from rollerworks/split-token
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSplitTokenValueHolder.php
More file actions
129 lines (110 loc) · 3.9 KB
/
SplitTokenValueHolder.php
File metadata and controls
129 lines (110 loc) · 3.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<?php
declare(strict_types=1);
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace Rollerworks\Component\SplitToken;
/**
* SplitToken keeps SplitToken information for storage.
*
* * The selector is used to identify a token, this is a unique random
* URI-safe string with a fixed length of {@see SplitToken::SELECTOR_BYTES} bytes.
*
* * The verifierHash holds a password hash of a variable
* length and is to be validated by a verifier callback.
*
* Additionally a SplitTokenValueHolder optionally holds an
* expiration timestamp and metadata to perform the operation
* or collect auditing information.
*
* The original token is not stored with this value-object.
*/
final class SplitTokenValueHolder
{
private ?string $selector = null;
private ?string $verifierHash = null;
private ?\DateTimeImmutable $expiresAt = null;
/** @var array<string, scalar> */
private ?array $metadata = [];
/** @param array<string, scalar> $metadata */
public function __construct(string $selector, string $verifierHash, ?\DateTimeImmutable $expiresAt = null, array $metadata = [])
{
$this->selector = $selector;
$this->verifierHash = $verifierHash;
$this->expiresAt = $expiresAt;
$this->metadata = $metadata;
}
public static function isEmpty(?self $valueHolder): bool
{
if ($valueHolder === null) {
return true;
}
// It's possible these values are empty when used as Embedded, because Embedded
// will always produce an object.
return $valueHolder->selector === null || $valueHolder->verifierHash === null;
}
/**
* Returns whether the current token (if any) can be replaced with the new token.
*
* This methods should only to be used to prevent setting a token when a token
* was already set, which has not expired, and the same metadata was given (strict checked!).
*
* @param array<string, scalar> $expectedMetadata
*/
public static function mayReplaceCurrentToken(?self $valueHolder, array $expectedMetadata = []): bool
{
if ($valueHolder === null || self::isEmpty($valueHolder)) {
return true;
}
if ($valueHolder->isExpired()) {
return true;
}
return $valueHolder->metadata() !== $expectedMetadata;
}
public function selector(): ?string
{
return $this->selector;
}
public function verifierHash(): ?string
{
return $this->verifierHash;
}
/** @param array<string, scalar> $metadata */
public function withMetadata(array $metadata): self
{
if (self::isEmpty($this)) {
throw new \RuntimeException('Incomplete TokenValueHolder.');
}
return new self($this->selector, $this->verifierHash, $this->expiresAt, $metadata);
}
/** @return array<string, scalar> */
public function metadata(): array
{
return $this->metadata ?? [];
}
public function isExpired(?\DateTimeImmutable $now = null): bool
{
if ($this->expiresAt === null) {
return false;
}
return $this->expiresAt->getTimestamp() < ($now ?? new \DateTimeImmutable())->getTimestamp();
}
public function expiresAt(): ?\DateTimeImmutable
{
return $this->expiresAt;
}
/**
* Compares if both objects are the same.
*
* Warning this method leaks timing information and the expiration date is ignored!
* This method should only be used to check if a new token is provided.
*/
public function equals(self $other): bool
{
return $other->selector === $this->selector
&& $other->verifierHash === $this->verifierHash
&& $other->metadata === $this->metadata;
}
}