| 148 |
lars |
1 |
<?php
|
|
|
2 |
|
|
|
3 |
/**
|
|
|
4 |
* This file is part of the ramsey/uuid library
|
|
|
5 |
*
|
|
|
6 |
* For the full copyright and license information, please view the LICENSE
|
|
|
7 |
* file that was distributed with this source code.
|
|
|
8 |
*
|
|
|
9 |
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
|
|
|
10 |
* @license http://opensource.org/licenses/MIT MIT
|
|
|
11 |
*/
|
|
|
12 |
|
|
|
13 |
declare(strict_types=1);
|
|
|
14 |
|
|
|
15 |
namespace Ramsey\Uuid\Generator;
|
|
|
16 |
|
|
|
17 |
use Ramsey\Uuid\Converter\NumberConverterInterface;
|
|
|
18 |
use Ramsey\Uuid\Exception\DceSecurityException;
|
|
|
19 |
use Ramsey\Uuid\Provider\DceSecurityProviderInterface;
|
|
|
20 |
use Ramsey\Uuid\Type\Hexadecimal;
|
|
|
21 |
use Ramsey\Uuid\Type\Integer as IntegerObject;
|
|
|
22 |
use Ramsey\Uuid\Uuid;
|
|
|
23 |
|
|
|
24 |
use function hex2bin;
|
|
|
25 |
use function in_array;
|
|
|
26 |
use function pack;
|
|
|
27 |
use function str_pad;
|
|
|
28 |
use function strlen;
|
|
|
29 |
use function substr_replace;
|
|
|
30 |
|
|
|
31 |
use const STR_PAD_LEFT;
|
|
|
32 |
|
|
|
33 |
/**
|
|
|
34 |
* DceSecurityGenerator generates strings of binary data based on a local
|
|
|
35 |
* domain, local identifier, node ID, clock sequence, and the current time
|
|
|
36 |
*/
|
|
|
37 |
class DceSecurityGenerator implements DceSecurityGeneratorInterface
|
|
|
38 |
{
|
|
|
39 |
private const DOMAINS = [
|
|
|
40 |
Uuid::DCE_DOMAIN_PERSON,
|
|
|
41 |
Uuid::DCE_DOMAIN_GROUP,
|
|
|
42 |
Uuid::DCE_DOMAIN_ORG,
|
|
|
43 |
];
|
|
|
44 |
|
|
|
45 |
/**
|
|
|
46 |
* Upper bounds for the clock sequence in DCE Security UUIDs.
|
|
|
47 |
*/
|
|
|
48 |
private const CLOCK_SEQ_HIGH = 63;
|
|
|
49 |
|
|
|
50 |
/**
|
|
|
51 |
* Lower bounds for the clock sequence in DCE Security UUIDs.
|
|
|
52 |
*/
|
|
|
53 |
private const CLOCK_SEQ_LOW = 0;
|
|
|
54 |
|
|
|
55 |
public function __construct(
|
|
|
56 |
private NumberConverterInterface $numberConverter,
|
|
|
57 |
private TimeGeneratorInterface $timeGenerator,
|
|
|
58 |
private DceSecurityProviderInterface $dceSecurityProvider
|
|
|
59 |
) {
|
|
|
60 |
}
|
|
|
61 |
|
|
|
62 |
public function generate(
|
|
|
63 |
int $localDomain,
|
|
|
64 |
?IntegerObject $localIdentifier = null,
|
|
|
65 |
?Hexadecimal $node = null,
|
|
|
66 |
?int $clockSeq = null
|
|
|
67 |
): string {
|
|
|
68 |
if (!in_array($localDomain, self::DOMAINS)) {
|
|
|
69 |
throw new DceSecurityException(
|
|
|
70 |
'Local domain must be a valid DCE Security domain'
|
|
|
71 |
);
|
|
|
72 |
}
|
|
|
73 |
|
|
|
74 |
if ($localIdentifier && $localIdentifier->isNegative()) {
|
|
|
75 |
throw new DceSecurityException(
|
|
|
76 |
'Local identifier out of bounds; it must be a value between 0 and 4294967295'
|
|
|
77 |
);
|
|
|
78 |
}
|
|
|
79 |
|
|
|
80 |
if ($clockSeq > self::CLOCK_SEQ_HIGH || $clockSeq < self::CLOCK_SEQ_LOW) {
|
|
|
81 |
throw new DceSecurityException(
|
|
|
82 |
'Clock sequence out of bounds; it must be a value between 0 and 63'
|
|
|
83 |
);
|
|
|
84 |
}
|
|
|
85 |
|
|
|
86 |
switch ($localDomain) {
|
|
|
87 |
case Uuid::DCE_DOMAIN_ORG:
|
|
|
88 |
if ($localIdentifier === null) {
|
|
|
89 |
throw new DceSecurityException(
|
|
|
90 |
'A local identifier must be provided for the org domain'
|
|
|
91 |
);
|
|
|
92 |
}
|
|
|
93 |
|
|
|
94 |
break;
|
|
|
95 |
case Uuid::DCE_DOMAIN_PERSON:
|
|
|
96 |
if ($localIdentifier === null) {
|
|
|
97 |
$localIdentifier = $this->dceSecurityProvider->getUid();
|
|
|
98 |
}
|
|
|
99 |
|
|
|
100 |
break;
|
|
|
101 |
case Uuid::DCE_DOMAIN_GROUP:
|
|
|
102 |
default:
|
|
|
103 |
if ($localIdentifier === null) {
|
|
|
104 |
$localIdentifier = $this->dceSecurityProvider->getGid();
|
|
|
105 |
}
|
|
|
106 |
|
|
|
107 |
break;
|
|
|
108 |
}
|
|
|
109 |
|
|
|
110 |
$identifierHex = $this->numberConverter->toHex($localIdentifier->toString());
|
|
|
111 |
|
|
|
112 |
// The maximum value for the local identifier is 0xffffffff, or
|
|
|
113 |
// 4294967295. This is 8 hexadecimal digits, so if the length of
|
|
|
114 |
// hexadecimal digits is greater than 8, we know the value is greater
|
|
|
115 |
// than 0xffffffff.
|
|
|
116 |
if (strlen($identifierHex) > 8) {
|
|
|
117 |
throw new DceSecurityException(
|
|
|
118 |
'Local identifier out of bounds; it must be a value between 0 and 4294967295'
|
|
|
119 |
);
|
|
|
120 |
}
|
|
|
121 |
|
|
|
122 |
$domainByte = pack('n', $localDomain)[1];
|
|
|
123 |
$identifierBytes = (string) hex2bin(str_pad($identifierHex, 8, '0', STR_PAD_LEFT));
|
|
|
124 |
|
|
|
125 |
if ($node instanceof Hexadecimal) {
|
|
|
126 |
$node = $node->toString();
|
|
|
127 |
}
|
|
|
128 |
|
|
|
129 |
// Shift the clock sequence 8 bits to the left, so it matches 0x3f00.
|
|
|
130 |
if ($clockSeq !== null) {
|
|
|
131 |
$clockSeq = $clockSeq << 8;
|
|
|
132 |
}
|
|
|
133 |
|
|
|
134 |
$bytes = $this->timeGenerator->generate($node, $clockSeq);
|
|
|
135 |
|
|
|
136 |
// Replace bytes in the time-based UUID with DCE Security values.
|
|
|
137 |
$bytes = substr_replace($bytes, $identifierBytes, 0, 4);
|
|
|
138 |
|
|
|
139 |
return substr_replace($bytes, $domainByte, 9, 1);
|
|
|
140 |
}
|
|
|
141 |
}
|