mirror of
https://github.com/ramsey/uuid.git
synced 2026-06-14 15:56:48 +03:00
Support generation of version 2 (DCE Security) UUIDs
This commit is contained in:
@@ -36,11 +36,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
UUIDs or creating UUIDs from existing strings, bytes, or integers, if the UUID
|
||||
is an RFC 4122 variant, one of these instances will be returned:
|
||||
* `Rfc4122\UuidV1`
|
||||
* `Rfc4122\UuidV2`
|
||||
* `Rfc4122\UuidV3`
|
||||
* `Rfc4122\UuidV4`
|
||||
* `Rfc4122\UuidV5`
|
||||
* `Rfc4122\NilUuid`
|
||||
* Add `Rfc4122\UuidBuilder` to build RFC 4122 variant UUIDs. This replaces the
|
||||
existing `Builder\DefaultUuidBuilder`, which is now deprecated.
|
||||
* Add ability to generate version 2 (DCE Security) UUIDs.
|
||||
* Add classes to represent GUIDs and nonstandard (non-RFC 4122 variant) UUIDs:
|
||||
* `Guid\Guid`
|
||||
* `Nonstandard\Uuid`.
|
||||
@@ -68,6 +71,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
returns an instance of `\DateTimeImmutable` instead of `\DateTime`.
|
||||
* Add `getFields()` method to `UuidInterface`.
|
||||
* Add `getValidator()` method to `UuidFactoryInterface`.
|
||||
* Add `uuid2()` method to `UuidFactoryInterface`.
|
||||
* Add `convertTime()` method to `Converter\TimeConverterInterface`.
|
||||
* Add `getTime()` method to `Provider\TimeProviderInterface`.
|
||||
* Change `Uuid::getFields()` to return an instance of `Fields\FieldsInterface`.
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"mockery/mockery": "^1.3",
|
||||
"moontoast/math": "^1.1",
|
||||
"paragonie/random-lib": "^2",
|
||||
"php-mock/php-mock-mockery": "^1.3",
|
||||
"php-mock/php-mock-phpunit": "^2.5",
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpdoc-parser": "0.4.1",
|
||||
|
||||
+11
-4
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<files psalm-version="3.8.2@90d6b73fd8062432030ef39b7b6694b3902daa31">
|
||||
<files psalm-version="3.8.3@389af1bfc739bfdff3f9e3dc7bd6499aee51a831">
|
||||
<file src="src/Builder/DegradedUuidBuilder.php">
|
||||
<DeprecatedClass occurrences="3">
|
||||
<code>new DegradedTimeConverter()</code>
|
||||
@@ -44,11 +44,18 @@
|
||||
<code>uuid_parse($uuid)</code>
|
||||
</MixedReturnStatement>
|
||||
</file>
|
||||
<file src="src/Provider/Dce/SystemDceSecurityProvider.php">
|
||||
<ForbiddenCode occurrences="5">
|
||||
<code>shell_exec('id -u')</code>
|
||||
<code>shell_exec('id -g')</code>
|
||||
<code>shell_exec('whoami /user /fo csv /nh')</code>
|
||||
<code>shell_exec('net user %username% | findstr /b /i "Local Group Memberships"')</code>
|
||||
<code>shell_exec('wmic group get name,sid | findstr /b /i ' . escapeshellarg($firstGroup))</code>
|
||||
</ForbiddenCode>
|
||||
</file>
|
||||
<file src="src/Provider/Node/SystemNodeProvider.php">
|
||||
<MixedArgument occurrences="4">
|
||||
<MixedArgument occurrences="2">
|
||||
<code>$node</code>
|
||||
<code>constant('PHP_OS')</code>
|
||||
<code>constant('PHP_OS')</code>
|
||||
<code>$macs</code>
|
||||
</MixedArgument>
|
||||
<MixedAssignment occurrences="2">
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the ramsey/uuid library
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ramsey\Uuid\Exception;
|
||||
|
||||
use RuntimeException as PhpRuntimeException;
|
||||
|
||||
/**
|
||||
* Thrown to indicate an exception occurred while dealing with DCE Security
|
||||
* (version 2) UUIDs
|
||||
*/
|
||||
class DceSecurityException extends PhpRuntimeException
|
||||
{
|
||||
}
|
||||
@@ -24,6 +24,8 @@ use Ramsey\Uuid\Converter\NumberConverterInterface;
|
||||
use Ramsey\Uuid\Converter\Time\GenericTimeConverter;
|
||||
use Ramsey\Uuid\Converter\Time\PhpTimeConverter;
|
||||
use Ramsey\Uuid\Converter\TimeConverterInterface;
|
||||
use Ramsey\Uuid\Generator\DceSecurityGenerator;
|
||||
use Ramsey\Uuid\Generator\DceSecurityGeneratorInterface;
|
||||
use Ramsey\Uuid\Generator\PeclUuidTimeGenerator;
|
||||
use Ramsey\Uuid\Generator\RandomGeneratorFactory;
|
||||
use Ramsey\Uuid\Generator\RandomGeneratorInterface;
|
||||
@@ -33,6 +35,8 @@ use Ramsey\Uuid\Guid\GuidBuilder;
|
||||
use Ramsey\Uuid\Math\BrickMathCalculator;
|
||||
use Ramsey\Uuid\Math\CalculatorInterface;
|
||||
use Ramsey\Uuid\Nonstandard\UuidBuilder as NonstandardUuidBuilder;
|
||||
use Ramsey\Uuid\Provider\Dce\SystemDceSecurityProvider;
|
||||
use Ramsey\Uuid\Provider\DceSecurityProviderInterface;
|
||||
use Ramsey\Uuid\Provider\Node\FallbackNodeProvider;
|
||||
use Ramsey\Uuid\Provider\Node\RandomNodeProvider;
|
||||
use Ramsey\Uuid\Provider\Node\SystemNodeProvider;
|
||||
@@ -81,6 +85,11 @@ class FeatureSet
|
||||
*/
|
||||
private $codec;
|
||||
|
||||
/**
|
||||
* @var DceSecurityGeneratorInterface
|
||||
*/
|
||||
private $dceSecurityGenerator;
|
||||
|
||||
/**
|
||||
* @var NodeProviderInterface
|
||||
*/
|
||||
@@ -145,6 +154,7 @@ class FeatureSet
|
||||
$this->nodeProvider = $this->buildNodeProvider();
|
||||
$this->randomGenerator = $this->buildRandomGenerator();
|
||||
$this->setTimeProvider(new SystemTimeProvider());
|
||||
$this->setDceSecurityProvider(new SystemDceSecurityProvider());
|
||||
$this->validator = new GenericValidator();
|
||||
}
|
||||
|
||||
@@ -172,6 +182,14 @@ class FeatureSet
|
||||
return $this->codec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DCE Security generator configured for this environment
|
||||
*/
|
||||
public function getDceSecurityGenerator(): DceSecurityGeneratorInterface
|
||||
{
|
||||
return $this->dceSecurityGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node provider configured for this environment
|
||||
*/
|
||||
@@ -222,6 +240,14 @@ class FeatureSet
|
||||
$this->timeConverter = $this->buildTimeConverter($calculator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the DCE Security provider to use in this environment
|
||||
*/
|
||||
public function setDceSecurityProvider(DceSecurityProviderInterface $dceSecurityProvider): void
|
||||
{
|
||||
$this->dceSecurityGenerator = $this->buildDceSecurityGenerator($dceSecurityProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time provider to use in this environment
|
||||
*/
|
||||
@@ -252,6 +278,19 @@ class FeatureSet
|
||||
return new StringCodec($this->builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a DCE Security generator configured for this environment
|
||||
*/
|
||||
private function buildDceSecurityGenerator(
|
||||
DceSecurityProviderInterface $dceSecurityProvider
|
||||
): DceSecurityGeneratorInterface {
|
||||
return new DceSecurityGenerator(
|
||||
$this->numberConverter,
|
||||
$this->timeGenerator,
|
||||
$dceSecurityProvider
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a node provider configured for this environment
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the ramsey/uuid library
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ramsey\Uuid\Generator;
|
||||
|
||||
use Ramsey\Uuid\Converter\NumberConverterInterface;
|
||||
use Ramsey\Uuid\Exception\InvalidArgumentException;
|
||||
use Ramsey\Uuid\Provider\DceSecurityProviderInterface;
|
||||
use Ramsey\Uuid\Type\Hexadecimal;
|
||||
use Ramsey\Uuid\Type\IntegerValue;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
/**
|
||||
* DceSecurityGenerator generates strings of binary data based on a local
|
||||
* domain, local identifier, node ID, clock sequence, and the current time
|
||||
*/
|
||||
class DceSecurityGenerator implements DceSecurityGeneratorInterface
|
||||
{
|
||||
private const DOMAINS = [
|
||||
Uuid::DCE_DOMAIN_PERSON,
|
||||
Uuid::DCE_DOMAIN_GROUP,
|
||||
Uuid::DCE_DOMAIN_ORG,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var NumberConverterInterface
|
||||
*/
|
||||
private $numberConverter;
|
||||
|
||||
/**
|
||||
* @var TimeGeneratorInterface
|
||||
*/
|
||||
private $timeGenerator;
|
||||
|
||||
/**
|
||||
* @var DceSecurityProviderInterface
|
||||
*/
|
||||
private $dceSecurityProvider;
|
||||
|
||||
public function __construct(
|
||||
NumberConverterInterface $numberConverter,
|
||||
TimeGeneratorInterface $timeGenerator,
|
||||
DceSecurityProviderInterface $dceSecurityProvider
|
||||
) {
|
||||
$this->numberConverter = $numberConverter;
|
||||
$this->timeGenerator = $timeGenerator;
|
||||
$this->dceSecurityProvider = $dceSecurityProvider;
|
||||
}
|
||||
|
||||
public function generate(
|
||||
int $localDomain,
|
||||
?IntegerValue $localIdentifier = null,
|
||||
?Hexadecimal $node = null,
|
||||
?int $clockSeq = null
|
||||
): string {
|
||||
if (!in_array($localDomain, self::DOMAINS)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Local domain must be a valid DCE Security domain'
|
||||
);
|
||||
}
|
||||
|
||||
switch ($localDomain) {
|
||||
case Uuid::DCE_DOMAIN_ORG:
|
||||
if ($localIdentifier === null) {
|
||||
throw new InvalidArgumentException(
|
||||
'A local identifier must be provided for the org domain'
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
case Uuid::DCE_DOMAIN_PERSON:
|
||||
if ($localIdentifier === null) {
|
||||
$localIdentifier = $this->dceSecurityProvider->getUid();
|
||||
}
|
||||
|
||||
break;
|
||||
case Uuid::DCE_DOMAIN_GROUP:
|
||||
default:
|
||||
if ($localIdentifier === null) {
|
||||
$localIdentifier = $this->dceSecurityProvider->getGid();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$domainByte = pack('n', $localDomain)[1];
|
||||
$identifierBytes = hex2bin(str_pad(
|
||||
$this->numberConverter->toHex($localIdentifier->toString()),
|
||||
8,
|
||||
'0',
|
||||
STR_PAD_LEFT
|
||||
));
|
||||
|
||||
if ($node instanceof Hexadecimal) {
|
||||
$node = $node->toString();
|
||||
}
|
||||
|
||||
/** @var string $bytes */
|
||||
$bytes = $this->timeGenerator->generate($node, $clockSeq);
|
||||
|
||||
// Replace bytes in the time-based UUID with DCE Security values.
|
||||
$bytes = substr_replace($bytes, $identifierBytes, 0, 4);
|
||||
$bytes = substr_replace($bytes, $domainByte, 9, 1);
|
||||
|
||||
return $bytes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the ramsey/uuid library
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ramsey\Uuid\Generator;
|
||||
|
||||
use Ramsey\Uuid\Rfc4122\UuidV2;
|
||||
use Ramsey\Uuid\Type\Hexadecimal;
|
||||
use Ramsey\Uuid\Type\IntegerValue;
|
||||
|
||||
/**
|
||||
* A DCE Security generator generates strings of binary data based on a local
|
||||
* domain, local identifier, node ID, clock sequence, and the current time
|
||||
*
|
||||
* @see UuidV2
|
||||
*/
|
||||
interface DceSecurityGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* Generate a binary string from a local domain, local identifier, node ID,
|
||||
* clock sequence, and current time
|
||||
*
|
||||
* @param int $localDomain The local domain to use when generating bytes,
|
||||
* according to DCE Security
|
||||
* @param IntegerValue|null $localIdentifier The local identifier for the
|
||||
* given domain; this may be a UID or GID on POSIX systems, if the local
|
||||
* domain is person or group, or it may be a site-defined identifier
|
||||
* if the local domain is org
|
||||
* @param Hexadecimal|null $node A 48-bit number representing the hardware
|
||||
* address
|
||||
* @param int|null $clockSeq A 14-bit number used to help avoid duplicates
|
||||
* that could arise when the clock is set backwards in time or if the
|
||||
* node ID changes
|
||||
*
|
||||
* @return string A binary string
|
||||
*/
|
||||
public function generate(
|
||||
int $localDomain,
|
||||
?IntegerValue $localIdentifier = null,
|
||||
?Hexadecimal $node = null,
|
||||
?int $clockSeq = null
|
||||
): string;
|
||||
}
|
||||
@@ -23,11 +23,12 @@ interface TimeGeneratorInterface
|
||||
/**
|
||||
* Generate a binary string from a node ID, clock sequence, and current time
|
||||
*
|
||||
* @param int|string $node A 48-bit number representing the hardware address;
|
||||
* this number may be represented as an integer or a hexadecimal string
|
||||
* @param int $clockSeq A 14-bit number used to help avoid duplicates that
|
||||
* could arise when the clock is set backwards in time or if the node ID
|
||||
* changes
|
||||
* @param int|string|null $node A 48-bit number representing the hardware
|
||||
* address; this number may be represented as an integer or a
|
||||
* hexadecimal string
|
||||
* @param int|null $clockSeq A 14-bit number used to help avoid duplicates
|
||||
* that could arise when the clock is set backwards in time or if the
|
||||
* node ID changes
|
||||
*
|
||||
* @return string A binary string
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the ramsey/uuid library
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ramsey\Uuid\Provider\Dce;
|
||||
|
||||
use Ramsey\Uuid\Exception\DceSecurityException;
|
||||
use Ramsey\Uuid\Provider\DceSecurityProviderInterface;
|
||||
use Ramsey\Uuid\Type\IntegerValue;
|
||||
|
||||
/**
|
||||
* SystemDceSecurityProvider retrieves the user or group identifiers from the system
|
||||
*/
|
||||
class SystemDceSecurityProvider implements DceSecurityProviderInterface
|
||||
{
|
||||
/**
|
||||
* @throws DceSecurityException if unable to get a user identifier
|
||||
*
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getUid(): IntegerValue
|
||||
{
|
||||
static $uid = null;
|
||||
|
||||
if ($uid instanceof IntegerValue) {
|
||||
return $uid;
|
||||
}
|
||||
|
||||
if ($uid === null) {
|
||||
$uid = $this->getSystemUid();
|
||||
}
|
||||
|
||||
if ($uid === '') {
|
||||
throw new DceSecurityException(
|
||||
'Unable to get a user identifier using the system DCE '
|
||||
. 'Security provider; please provide a custom identifier or '
|
||||
. 'use a different provider'
|
||||
);
|
||||
}
|
||||
|
||||
$uid = new IntegerValue($uid);
|
||||
|
||||
return $uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DceSecurityException if unable to get a group identifier
|
||||
*
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getGid(): IntegerValue
|
||||
{
|
||||
static $gid = null;
|
||||
|
||||
if ($gid instanceof IntegerValue) {
|
||||
return $gid;
|
||||
}
|
||||
|
||||
if ($gid === null) {
|
||||
$gid = $this->getSystemGid();
|
||||
}
|
||||
|
||||
if ($gid === '') {
|
||||
throw new DceSecurityException(
|
||||
'Unable to get a group identifier using the system DCE '
|
||||
. 'Security provider; please provide a custom identifier or '
|
||||
. 'use a different provider'
|
||||
);
|
||||
}
|
||||
|
||||
$gid = new IntegerValue($gid);
|
||||
|
||||
return $gid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the UID from the system
|
||||
*/
|
||||
private function getSystemUid(): string
|
||||
{
|
||||
if (!$this->hasShellExec()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
switch ($this->getOs()) {
|
||||
case 'WIN':
|
||||
return $this->getWindowsUid();
|
||||
case 'DAR':
|
||||
case 'FRE':
|
||||
case 'LIN':
|
||||
default:
|
||||
return trim((string) shell_exec('id -u'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the GID from the system
|
||||
*/
|
||||
private function getSystemGid(): string
|
||||
{
|
||||
if (!$this->hasShellExec()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
switch ($this->getOs()) {
|
||||
case 'WIN':
|
||||
return $this->getWindowsGid();
|
||||
case 'DAR':
|
||||
case 'FRE':
|
||||
case 'LIN':
|
||||
default:
|
||||
return trim((string) shell_exec('id -g'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if shell_exec() is available for use
|
||||
*/
|
||||
private function hasShellExec(): bool
|
||||
{
|
||||
$disabledFunctions = strtolower((string) ini_get('disable_functions'));
|
||||
|
||||
return strpos($disabledFunctions, 'shell_exec') === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the PHP_OS string
|
||||
*/
|
||||
private function getOs(): string
|
||||
{
|
||||
return strtoupper(substr(constant('PHP_OS'), 0, 3));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user identifier for a user on a Windows system
|
||||
*
|
||||
* Windows does not have the same concept as an effective POSIX UID for the
|
||||
* running script. Instead, each user is uniquely identified by an SID
|
||||
* (security identifier). The SID includes three 32-bit unsigned integers
|
||||
* that make up a unique domain identifier, followed by an RID (relative
|
||||
* identifier) that we will use as the UID. The primary caveat is that this
|
||||
* UID may not be unique to the system, since it is, instead, unique to the
|
||||
* domain.
|
||||
*
|
||||
* @link https://www.lifewire.com/what-is-an-sid-number-2626005 What Is an SID Number?
|
||||
* @link https://bit.ly/30vE7NM Well-known SID Structures
|
||||
* @link https://bit.ly/2FWcYKJ Well-known security identifiers in Windows operating systems
|
||||
* @link https://www.windows-commandline.com/get-sid-of-user/ Get SID of user
|
||||
*/
|
||||
private function getWindowsUid(): string
|
||||
{
|
||||
$response = shell_exec('whoami /user /fo csv /nh');
|
||||
|
||||
if ($response === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/** @var string $sid */
|
||||
$sid = str_getcsv(trim($response))[1] ?? '';
|
||||
|
||||
if (($lastHyphen = strrpos($sid, '-')) === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return trim(substr($sid, $lastHyphen + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group identifier for a user on a Windows system
|
||||
*
|
||||
* Since Windows does not have the same concept as an effective POSIX GID
|
||||
* for the running script, we will get the local group memberships for the
|
||||
* user running the script. Then, we will get the SID (security identifier)
|
||||
* for the first group. that appears in that list. Finally, we will return
|
||||
* the RID (relative identifier) for the group and use that as the GID.
|
||||
*
|
||||
* @link https://www.windows-commandline.com/list-of-user-groups-command-line/ List of user groups command line
|
||||
*/
|
||||
private function getWindowsGid(): string
|
||||
{
|
||||
$response = shell_exec('net user %username% | findstr /b /i "Local Group Memberships"');
|
||||
|
||||
if ($response === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/** @var string[] $userGroups */
|
||||
$userGroups = preg_split('/\s{2,}/', $response, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
$firstGroup = trim($userGroups[1] ?? '', "* \t\n\r\0\x0B");
|
||||
|
||||
if ($firstGroup === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$response = shell_exec('wmic group get name,sid | findstr /b /i ' . escapeshellarg($firstGroup));
|
||||
|
||||
if ($response === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/** @var string[] $userGroup */
|
||||
$userGroup = preg_split('/\s{2,}/', $response, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
$sid = $userGroup[1] ?? '';
|
||||
|
||||
if (($lastHyphen = strrpos($sid, '-')) === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return trim((string) substr($sid, $lastHyphen + 1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the ramsey/uuid library
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ramsey\Uuid\Provider;
|
||||
|
||||
use Ramsey\Uuid\Rfc4122\UuidV2;
|
||||
use Ramsey\Uuid\Type\IntegerValue;
|
||||
|
||||
/**
|
||||
* A DCE provider provides access to local domain identifiers for version 2,
|
||||
* DCE Security, UUIDs
|
||||
*
|
||||
* @see UuidV2
|
||||
*/
|
||||
interface DceSecurityProviderInterface
|
||||
{
|
||||
/**
|
||||
* Returns a user identifier for the system
|
||||
*
|
||||
* @link https://en.wikipedia.org/wiki/User_identifier User identifier
|
||||
*/
|
||||
public function getUid(): IntegerValue;
|
||||
|
||||
/**
|
||||
* Returns a group identifier for the system
|
||||
*
|
||||
* @link https://en.wikipedia.org/wiki/Group_identifier Group identifier
|
||||
*/
|
||||
public function getGid(): IntegerValue;
|
||||
}
|
||||
@@ -21,7 +21,6 @@ use Ramsey\Uuid\Converter\TimeConverterInterface;
|
||||
use Ramsey\Uuid\Exception\UnableToBuildUuidException;
|
||||
use Ramsey\Uuid\Exception\UnsupportedOperationException;
|
||||
use Ramsey\Uuid\Rfc4122\UuidInterface as Rfc4122UuidInterface;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
use Throwable;
|
||||
|
||||
@@ -79,7 +78,7 @@ class UuidBuilder implements UuidBuilderInterface
|
||||
case 1:
|
||||
return new UuidV1($fields, $this->numberConverter, $codec, $this->timeConverter);
|
||||
case 2:
|
||||
return new Uuid($fields, $this->numberConverter, $codec, $this->timeConverter);
|
||||
return new UuidV2($fields, $this->numberConverter, $codec, $this->timeConverter);
|
||||
case 3:
|
||||
return new UuidV3($fields, $this->numberConverter, $codec, $this->timeConverter);
|
||||
case 4:
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the ramsey/uuid library
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ramsey\Uuid\Rfc4122;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
/**
|
||||
* DCE Security version, or version 2, UUIDs include local domain identifier,
|
||||
* local ID for the specified domain, and node values that are combined into a
|
||||
* 128-bit unsigned integer
|
||||
*
|
||||
* @link https://publications.opengroup.org/c311 DCE 1.1: Authentication and Security Services
|
||||
* @link https://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01 DCE 1.1, §5.2.1.1
|
||||
* @link https://pubs.opengroup.org/onlinepubs/9696989899/chap11.htm#tagcjh_14_05_01_01 DCE 1.1, §11.5.1.1
|
||||
* @link https://github.com/google/uuid Go package for UUIDs based on RFC 4122 and DCE 1.1: Auth and Security Services
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
final class UuidV2 extends Uuid implements UuidInterface
|
||||
{
|
||||
}
|
||||
@@ -143,6 +143,27 @@ class Uuid implements UuidInterface
|
||||
*/
|
||||
public const UUID_TYPE_HASH_SHA1 = 5;
|
||||
|
||||
/**
|
||||
* DCE Security principal domain
|
||||
*
|
||||
* @link https://pubs.opengroup.org/onlinepubs/9696989899/chap11.htm#tagcjh_14_05_01_01 DCE 1.1, §11.5.1.1
|
||||
*/
|
||||
public const DCE_DOMAIN_PERSON = 0;
|
||||
|
||||
/**
|
||||
* DCE Security group domain
|
||||
*
|
||||
* @link https://pubs.opengroup.org/onlinepubs/9696989899/chap11.htm#tagcjh_14_05_01_01 DCE 1.1, §11.5.1.1
|
||||
*/
|
||||
public const DCE_DOMAIN_GROUP = 1;
|
||||
|
||||
/**
|
||||
* DCE Security organization domain
|
||||
*
|
||||
* @link https://pubs.opengroup.org/onlinepubs/9696989899/chap11.htm#tagcjh_14_05_01_01 DCE 1.1, §11.5.1.1
|
||||
*/
|
||||
public const DCE_DOMAIN_ORG = 2;
|
||||
|
||||
/**
|
||||
* @var UuidFactoryInterface|null
|
||||
*/
|
||||
|
||||
@@ -17,9 +17,12 @@ namespace Ramsey\Uuid;
|
||||
use Ramsey\Uuid\Builder\UuidBuilderInterface;
|
||||
use Ramsey\Uuid\Codec\CodecInterface;
|
||||
use Ramsey\Uuid\Converter\NumberConverterInterface;
|
||||
use Ramsey\Uuid\Generator\DceSecurityGeneratorInterface;
|
||||
use Ramsey\Uuid\Generator\RandomGeneratorInterface;
|
||||
use Ramsey\Uuid\Generator\TimeGeneratorInterface;
|
||||
use Ramsey\Uuid\Provider\NodeProviderInterface;
|
||||
use Ramsey\Uuid\Type\Hexadecimal;
|
||||
use Ramsey\Uuid\Type\IntegerValue;
|
||||
use Ramsey\Uuid\Validator\ValidatorInterface;
|
||||
|
||||
class UuidFactory implements UuidFactoryInterface
|
||||
@@ -29,6 +32,11 @@ class UuidFactory implements UuidFactoryInterface
|
||||
*/
|
||||
private $codec;
|
||||
|
||||
/**
|
||||
* @var DceSecurityGeneratorInterface
|
||||
*/
|
||||
private $dceSecurityGenerator;
|
||||
|
||||
/**
|
||||
* @var NodeProviderInterface
|
||||
*/
|
||||
@@ -67,6 +75,7 @@ class UuidFactory implements UuidFactoryInterface
|
||||
$features = $features ?: new FeatureSet();
|
||||
|
||||
$this->codec = $features->getCodec();
|
||||
$this->dceSecurityGenerator = $features->getDceSecurityGenerator();
|
||||
$this->nodeProvider = $features->getNodeProvider();
|
||||
$this->numberConverter = $features->getNumberConverter();
|
||||
$this->randomGenerator = $features->getRandomGenerator();
|
||||
@@ -236,6 +245,24 @@ class UuidFactory implements UuidFactoryInterface
|
||||
return $this->uuidFromHashedName($hex, 1);
|
||||
}
|
||||
|
||||
public function uuid2(
|
||||
int $localDomain,
|
||||
?IntegerValue $localIdentifier,
|
||||
?Hexadecimal $node = null,
|
||||
?int $clockSeq = null
|
||||
): UuidInterface {
|
||||
$bytes = $this->dceSecurityGenerator->generate(
|
||||
$localDomain,
|
||||
$localIdentifier,
|
||||
$node,
|
||||
$clockSeq
|
||||
);
|
||||
|
||||
$hex = bin2hex($bytes);
|
||||
|
||||
return $this->uuidFromHashedName($hex, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
@@ -14,6 +14,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Ramsey\Uuid;
|
||||
|
||||
use Ramsey\Uuid\Type\Hexadecimal;
|
||||
use Ramsey\Uuid\Type\IntegerValue;
|
||||
use Ramsey\Uuid\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
@@ -44,6 +46,29 @@ interface UuidFactoryInterface
|
||||
*/
|
||||
public function uuid1($node = null, ?int $clockSeq = null): UuidInterface;
|
||||
|
||||
/**
|
||||
* Returns a version 2 (DCE Security) UUID from a local domain, local
|
||||
* identifier, host ID, clock sequence, and the current time
|
||||
*
|
||||
* @param int $localDomain The local domain to use when generating bytes,
|
||||
* according to DCE Security
|
||||
* @param IntegerValue|null $localIdentifier The local identifier for the
|
||||
* given domain; this may be a UID or GID on POSIX systems, if the local
|
||||
* domain is person or group, or it may be a site-defined identifier
|
||||
* if the local domain is org
|
||||
* @param Hexadecimal|null $node A 48-bit number representing the hardware
|
||||
* address
|
||||
* @param int|null $clockSeq A 14-bit number used to help avoid duplicates
|
||||
* that could arise when the clock is set backwards in time or if the
|
||||
* node ID changes
|
||||
*/
|
||||
public function uuid2(
|
||||
int $localDomain,
|
||||
?IntegerValue $localIdentifier,
|
||||
?Hexadecimal $node = null,
|
||||
?int $clockSeq = null
|
||||
): UuidInterface;
|
||||
|
||||
/**
|
||||
* Returns a version 3 (name-based) UUID based on the MD5 hash of a
|
||||
* namespace ID and a name
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ramsey\Uuid\Test\Generator;
|
||||
|
||||
use Mockery;
|
||||
use Ramsey\Uuid\Converter\Number\GenericNumberConverter;
|
||||
use Ramsey\Uuid\Converter\NumberConverterInterface;
|
||||
use Ramsey\Uuid\Converter\Time\GenericTimeConverter;
|
||||
use Ramsey\Uuid\Exception\InvalidArgumentException;
|
||||
use Ramsey\Uuid\Generator\DceSecurityGenerator;
|
||||
use Ramsey\Uuid\Generator\DefaultTimeGenerator;
|
||||
use Ramsey\Uuid\Generator\TimeGeneratorInterface;
|
||||
use Ramsey\Uuid\Math\BrickMathCalculator;
|
||||
use Ramsey\Uuid\Provider\DceSecurityProviderInterface;
|
||||
use Ramsey\Uuid\Provider\NodeProviderInterface;
|
||||
use Ramsey\Uuid\Provider\Time\FixedTimeProvider;
|
||||
use Ramsey\Uuid\Test\TestCase;
|
||||
use Ramsey\Uuid\Type\Hexadecimal;
|
||||
use Ramsey\Uuid\Type\IntegerValue;
|
||||
use Ramsey\Uuid\Type\Time;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
class DceSecurityGeneratorTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param mixed $uid
|
||||
* @param mixed $gid
|
||||
* @param mixed $seconds
|
||||
* @param mixed $microseconds
|
||||
*
|
||||
* @dataProvider provideValuesForDceSecurityGenerator
|
||||
*/
|
||||
public function testGenerateBytesReplacesBytesWithDceValues(
|
||||
$uid,
|
||||
$gid,
|
||||
string $node,
|
||||
$seconds,
|
||||
$microseconds,
|
||||
int $providedDomain,
|
||||
?IntegerValue $providedId,
|
||||
?Hexadecimal $providedNode,
|
||||
?int $providedClockSeq,
|
||||
string $expectedId,
|
||||
string $expectedDomain,
|
||||
string $expectedNode,
|
||||
string $expectedTimeMidHi
|
||||
): void {
|
||||
$dceSecurityProvider = Mockery::mock(DceSecurityProviderInterface::class, [
|
||||
'getUid' => new IntegerValue($uid),
|
||||
'getGid' => new IntegerValue($gid),
|
||||
]);
|
||||
|
||||
$nodeProvider = Mockery::mock(NodeProviderInterface::class, [
|
||||
'getNode' => $node,
|
||||
]);
|
||||
|
||||
$timeProvider = new FixedTimeProvider(new Time($seconds, $microseconds));
|
||||
|
||||
$calculator = new BrickMathCalculator();
|
||||
$numberConverter = new GenericNumberConverter($calculator);
|
||||
$timeConverter = new GenericTimeConverter($calculator);
|
||||
$timeGenerator = new DefaultTimeGenerator($nodeProvider, $timeConverter, $timeProvider);
|
||||
|
||||
$dceSecurityGenerator = new DceSecurityGenerator($numberConverter, $timeGenerator, $dceSecurityProvider);
|
||||
|
||||
$bytes = $dceSecurityGenerator->generate($providedDomain, $providedId, $providedNode, $providedClockSeq);
|
||||
|
||||
$this->assertSame($expectedId, bin2hex(substr($bytes, 0, 4)));
|
||||
$this->assertSame($expectedDomain, bin2hex(substr($bytes, 9, 1)));
|
||||
$this->assertSame($expectedNode, bin2hex(substr($bytes, 10)));
|
||||
$this->assertSame($expectedTimeMidHi, bin2hex(substr($bytes, 4, 4)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification
|
||||
*/
|
||||
public function provideValuesForDceSecurityGenerator(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'uid' => '1001',
|
||||
'gid' => '2001',
|
||||
'node' => '001122334455',
|
||||
'seconds' => 1579132397,
|
||||
'microseconds' => 500000,
|
||||
'providedDomain' => Uuid::DCE_DOMAIN_PERSON,
|
||||
'providedId' => null,
|
||||
'providedNode' => null,
|
||||
'providedClockSeq' => null,
|
||||
'expectedId' => '000003e9',
|
||||
'expectedDomain' => '00',
|
||||
'expectedNode' => '001122334455',
|
||||
'expectedTimeMidHi' => '37f211ea',
|
||||
],
|
||||
[
|
||||
'uid' => '1001',
|
||||
'gid' => '2001',
|
||||
'node' => '001122334455',
|
||||
'seconds' => 1579132397,
|
||||
'microseconds' => 500000,
|
||||
'providedDomain' => Uuid::DCE_DOMAIN_GROUP,
|
||||
'providedId' => null,
|
||||
'providedNode' => null,
|
||||
'providedClockSeq' => null,
|
||||
'expectedId' => '000007d1',
|
||||
'expectedDomain' => '01',
|
||||
'expectedNode' => '001122334455',
|
||||
'expectedTimeMidHi' => '37f211ea',
|
||||
],
|
||||
[
|
||||
'uid' => 0,
|
||||
'gid' => 0,
|
||||
'node' => '001122334455',
|
||||
'seconds' => 1579132397,
|
||||
'microseconds' => 500000,
|
||||
'providedDomain' => Uuid::DCE_DOMAIN_ORG,
|
||||
'providedId' => new IntegerValue('4294967295'),
|
||||
'providedNode' => null,
|
||||
'providedClockSeq' => null,
|
||||
'expectedId' => 'ffffffff',
|
||||
'expectedDomain' => '02',
|
||||
'expectedNode' => '001122334455',
|
||||
'expectedTimeMidHi' => '37f211ea',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testGenerateThrowsExceptionForInvalidDomain(): void
|
||||
{
|
||||
$numberConverter = Mockery::mock(NumberConverterInterface::class);
|
||||
$timeGenerator = Mockery::mock(TimeGeneratorInterface::class);
|
||||
$dceSecurityProvider = Mockery::mock(DceSecurityProviderInterface::class);
|
||||
|
||||
$generator = new DceSecurityGenerator($numberConverter, $timeGenerator, $dceSecurityProvider);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Local domain must be a valid DCE Security domain');
|
||||
|
||||
$generator->generate(42);
|
||||
}
|
||||
|
||||
public function testGenerateThrowsExceptionForOrgWithoutIdentifier(): void
|
||||
{
|
||||
$numberConverter = Mockery::mock(NumberConverterInterface::class);
|
||||
$timeGenerator = Mockery::mock(TimeGeneratorInterface::class);
|
||||
$dceSecurityProvider = Mockery::mock(DceSecurityProviderInterface::class);
|
||||
|
||||
$generator = new DceSecurityGenerator($numberConverter, $timeGenerator, $dceSecurityProvider);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('A local identifier must be provided for the org domain');
|
||||
|
||||
$generator->generate(Uuid::DCE_DOMAIN_ORG);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,502 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ramsey\Uuid\Test\Provider\Dce;
|
||||
|
||||
use Ramsey\Uuid\Exception\DceSecurityException;
|
||||
use Ramsey\Uuid\Provider\Dce\SystemDceSecurityProvider;
|
||||
use Ramsey\Uuid\Test\TestCase;
|
||||
use Ramsey\Uuid\Type\IntegerValue;
|
||||
use phpmock\mockery\PHPMockery;
|
||||
|
||||
class SystemDceSecurityProviderTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testGetUidThrowsExceptionIfShellExecDisabled(): void
|
||||
{
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'ini_get'
|
||||
)->with('disable_functions')->once()->andReturn('foo bar shell_exec baz');
|
||||
|
||||
$provider = new SystemDceSecurityProvider();
|
||||
|
||||
// Test that we catch the exception multiple times, but the ini_get()
|
||||
// function is called only once.
|
||||
$caughtException = 0;
|
||||
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
try {
|
||||
$provider->getUid();
|
||||
} catch (DceSecurityException $e) {
|
||||
$caughtException++;
|
||||
|
||||
$this->assertSame(
|
||||
'Unable to get a user identifier using the system DCE '
|
||||
. 'Security provider; please provide a custom identifier or '
|
||||
. 'use a different provider',
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertSame(5, $caughtException);
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testGetUidForPosixThrowsExceptionIfShellExecReturnsNull(): void
|
||||
{
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'ini_get'
|
||||
)->with('disable_functions')->once()->andReturn('nothing');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'constant'
|
||||
)->with('PHP_OS')->once()->andReturn('Linux');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'shell_exec'
|
||||
)->with('id -u')->once()->andReturnNull();
|
||||
|
||||
$provider = new SystemDceSecurityProvider();
|
||||
|
||||
$this->expectException(DceSecurityException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'Unable to get a user identifier using the system DCE '
|
||||
. 'Security provider; please provide a custom identifier or '
|
||||
. 'use a different provider'
|
||||
);
|
||||
|
||||
$provider->getUid();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
* @dataProvider provideWindowsBadValues
|
||||
*/
|
||||
public function testGetUidForWindowsThrowsExceptionIfShellExecForWhoAmIReturnsBadValues($value): void
|
||||
{
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'ini_get'
|
||||
)->with('disable_functions')->once()->andReturn('nothing');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'constant'
|
||||
)->with('PHP_OS')->once()->andReturn('Windows_NT');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'shell_exec'
|
||||
)->with('whoami /user /fo csv /nh')->once()->andReturn($value);
|
||||
|
||||
$provider = new SystemDceSecurityProvider();
|
||||
|
||||
$this->expectException(DceSecurityException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'Unable to get a user identifier using the system DCE '
|
||||
. 'Security provider; please provide a custom identifier or '
|
||||
. 'use a different provider'
|
||||
);
|
||||
|
||||
$provider->getUid();
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
* @dataProvider provideWindowsGoodWhoAmIValues
|
||||
*/
|
||||
public function testGetUidForWindowsWhenShellExecForWhoAmIReturnsGoodValues(
|
||||
string $value,
|
||||
string $expectedId
|
||||
): void {
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'ini_get'
|
||||
)->with('disable_functions')->once()->andReturn('nothing');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'constant'
|
||||
)->with('PHP_OS')->once()->andReturn('Windows_NT');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'shell_exec'
|
||||
)->with('whoami /user /fo csv /nh')->once()->andReturn($value);
|
||||
|
||||
$provider = new SystemDceSecurityProvider();
|
||||
|
||||
$uid = $provider->getUid();
|
||||
|
||||
$this->assertInstanceOf(IntegerValue::class, $uid);
|
||||
$this->assertSame($expectedId, $uid->toString());
|
||||
$this->assertSame($uid, $provider->getUid());
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification
|
||||
*/
|
||||
public function provideWindowsGoodWhoAmIValues(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'value' => '"Melilot Sackville","S-1-5-21-7375663-6890924511-1272660413-2944159"',
|
||||
'expectedId' => '2944159',
|
||||
],
|
||||
[
|
||||
'value' => '"Brutus Sandheaver","S-1-3-12-1234525106-3567804255-30012867-1437"',
|
||||
'expectedId' => '1437',
|
||||
],
|
||||
[
|
||||
'value' => '"Cora Rumble","S-345"',
|
||||
'expectedId' => '345',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
* @dataProvider providePosixTestValues
|
||||
*/
|
||||
public function testGetUidForPosixSystems(string $os, string $id): void
|
||||
{
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'ini_get'
|
||||
)->with('disable_functions')->once()->andReturn('nothing');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'constant'
|
||||
)->with('PHP_OS')->once()->andReturn($os);
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'shell_exec'
|
||||
)->with('id -u')->once()->andReturn($id);
|
||||
|
||||
$provider = new SystemDceSecurityProvider();
|
||||
|
||||
$uid = $provider->getUid();
|
||||
|
||||
$this->assertInstanceOf(IntegerValue::class, $uid);
|
||||
$this->assertSame($id, $uid->toString());
|
||||
$this->assertSame($uid, $provider->getUid());
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testGetGidThrowsExceptionIfShellExecDisabled(): void
|
||||
{
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'ini_get'
|
||||
)->with('disable_functions')->once()->andReturn('foo bar shell_exec baz');
|
||||
|
||||
$provider = new SystemDceSecurityProvider();
|
||||
|
||||
// Test that we catch the exception multiple times, but the ini_get()
|
||||
// function is called only once.
|
||||
$caughtException = 0;
|
||||
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
try {
|
||||
$provider->getGid();
|
||||
} catch (DceSecurityException $e) {
|
||||
$caughtException++;
|
||||
|
||||
$this->assertSame(
|
||||
'Unable to get a group identifier using the system DCE '
|
||||
. 'Security provider; please provide a custom identifier or '
|
||||
. 'use a different provider',
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertSame(5, $caughtException);
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testGetGidForPosixThrowsExceptionIfShellExecReturnsNull(): void
|
||||
{
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'ini_get'
|
||||
)->with('disable_functions')->once()->andReturn('nothing');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'constant'
|
||||
)->with('PHP_OS')->once()->andReturn('Linux');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'shell_exec'
|
||||
)->with('id -g')->once()->andReturnNull();
|
||||
|
||||
$provider = new SystemDceSecurityProvider();
|
||||
|
||||
$this->expectException(DceSecurityException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'Unable to get a group identifier using the system DCE '
|
||||
. 'Security provider; please provide a custom identifier or '
|
||||
. 'use a different provider'
|
||||
);
|
||||
|
||||
$provider->getGid();
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
* @dataProvider providePosixTestValues
|
||||
*/
|
||||
public function testGetGidForPosixSystems(string $os, string $id): void
|
||||
{
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'ini_get'
|
||||
)->with('disable_functions')->once()->andReturn('nothing');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'constant'
|
||||
)->with('PHP_OS')->once()->andReturn($os);
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'shell_exec'
|
||||
)->with('id -g')->once()->andReturn($id);
|
||||
|
||||
$provider = new SystemDceSecurityProvider();
|
||||
|
||||
$gid = $provider->getGid();
|
||||
|
||||
$this->assertInstanceOf(IntegerValue::class, $gid);
|
||||
$this->assertSame($id, $gid->toString());
|
||||
$this->assertSame($gid, $provider->getGid());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
* @dataProvider provideWindowsBadValues
|
||||
*/
|
||||
public function testGetGidForWindowsThrowsExceptionWhenShellExecForNetUserReturnsBadValues($value): void
|
||||
{
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'ini_get'
|
||||
)->with('disable_functions')->once()->andReturn('nothing');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'constant'
|
||||
)->with('PHP_OS')->once()->andReturn('Windows_NT');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'shell_exec'
|
||||
)->with('net user %username% | findstr /b /i "Local Group Memberships"')->once()->andReturn($value);
|
||||
|
||||
$provider = new SystemDceSecurityProvider();
|
||||
|
||||
$this->expectException(DceSecurityException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'Unable to get a group identifier using the system DCE '
|
||||
. 'Security provider; please provide a custom identifier or '
|
||||
. 'use a different provider'
|
||||
);
|
||||
|
||||
$provider->getGid();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
* @dataProvider provideWindowsBadGroupValues
|
||||
*/
|
||||
public function testGetGidForWindowsThrowsExceptionWhenShellExecForWmicGroupGetReturnsBadValues($value): void
|
||||
{
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'ini_get'
|
||||
)->with('disable_functions')->once()->andReturn('nothing');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'constant'
|
||||
)->with('PHP_OS')->once()->andReturn('Windows_NT');
|
||||
|
||||
$shellExec = PHPMockery::mock('Ramsey\Uuid\Provider\Dce', 'shell_exec');
|
||||
|
||||
$shellExec
|
||||
->with('net user %username% | findstr /b /i "Local Group Memberships"')
|
||||
->once()
|
||||
->andReturn('Local Group Memberships *Users');
|
||||
|
||||
$shellExec
|
||||
->with("wmic group get name,sid | findstr /b /i 'Users'")
|
||||
->once()
|
||||
->andReturn($value);
|
||||
|
||||
$provider = new SystemDceSecurityProvider();
|
||||
|
||||
$this->expectException(DceSecurityException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'Unable to get a group identifier using the system DCE '
|
||||
. 'Security provider; please provide a custom identifier or '
|
||||
. 'use a different provider'
|
||||
);
|
||||
|
||||
$provider->getGid();
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
* @dataProvider provideWindowsGoodNetUserAndWmicGroupValues
|
||||
*/
|
||||
public function testGetGidForWindowsSucceeds(
|
||||
string $netUserResponse,
|
||||
string $wmicGroupResponse,
|
||||
string $expectedGroup,
|
||||
string $expectedId
|
||||
): void {
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'ini_get'
|
||||
)->with('disable_functions')->once()->andReturn('nothing');
|
||||
|
||||
PHPMockery::mock(
|
||||
'Ramsey\Uuid\Provider\Dce',
|
||||
'constant'
|
||||
)->with('PHP_OS')->once()->andReturn('Windows_NT');
|
||||
|
||||
$shellExec = PHPMockery::mock('Ramsey\Uuid\Provider\Dce', 'shell_exec');
|
||||
|
||||
$shellExec
|
||||
->with('net user %username% | findstr /b /i "Local Group Memberships"')
|
||||
->once()
|
||||
->andReturn($netUserResponse);
|
||||
|
||||
$shellExec
|
||||
->with("wmic group get name,sid | findstr /b /i '{$expectedGroup}'")
|
||||
->once()
|
||||
->andReturn($wmicGroupResponse);
|
||||
|
||||
$provider = new SystemDceSecurityProvider();
|
||||
|
||||
$gid = $provider->getGid();
|
||||
|
||||
$this->assertInstanceOf(IntegerValue::class, $gid);
|
||||
$this->assertSame($expectedId, $gid->toString());
|
||||
$this->assertSame($gid, $provider->getGid());
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification
|
||||
*/
|
||||
public function provideWindowsGoodNetUserAndWmicGroupValues(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'netUserResponse' => 'Local Group Memberships *Administrators *Users',
|
||||
'wmicGroupResponse' => 'Administrators S-1-5-32-544',
|
||||
'expectedGroup' => 'Administrators',
|
||||
'expectedId' => '544',
|
||||
],
|
||||
[
|
||||
'netUserResponse' => 'Local Group Memberships Users',
|
||||
'wmicGroupResponse' => 'Users S-1-5-32-545',
|
||||
'expectedGroup' => 'Users',
|
||||
'expectedId' => '545',
|
||||
],
|
||||
[
|
||||
'netUserResponse' => 'Local Group Memberships Guests Nobody',
|
||||
'wmicGroupResponse' => 'Guests S-1-5-32-546',
|
||||
'expectedGroup' => 'Guests',
|
||||
'expectedId' => '546',
|
||||
],
|
||||
[
|
||||
'netUserReponse' => 'Local Group Memberships Some Group Another Group',
|
||||
'wmicGroupResponse' => 'Some Group S-1-5-80-19088743-1985229328-4294967295-1324',
|
||||
'expectedGroup' => 'Some Group',
|
||||
'expectedId' => '1324',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification
|
||||
*/
|
||||
public function providePosixTestValues(): array
|
||||
{
|
||||
return [
|
||||
['os' => 'Darwin', 'id' => '1042'],
|
||||
['os' => 'FreeBSD', 'id' => '672'],
|
||||
['os' => 'GNU', 'id' => '1008'],
|
||||
['os' => 'Linux', 'id' => '567'],
|
||||
['os' => 'NetBSD', 'id' => '7234'],
|
||||
['os' => 'OpenBSD', 'id' => '2347'],
|
||||
['os' => 'OS400', 'id' => '1234'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification
|
||||
*/
|
||||
public function provideWindowsBadValues(): array
|
||||
{
|
||||
return [
|
||||
['value' => null],
|
||||
['value' => 'foobar'],
|
||||
['value' => 'foo,bar,baz'],
|
||||
['value' => ''],
|
||||
['value' => '1234'],
|
||||
['value' => 'Local Group Memberships'],
|
||||
['value' => 'Local Group Memberships **** Foo'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification
|
||||
*/
|
||||
public function provideWindowsBadGroupValues(): array
|
||||
{
|
||||
return array_merge(
|
||||
$this->provideWindowsBadValues(),
|
||||
[
|
||||
['value' => 'Users Not a valid SID string'],
|
||||
['value' => 'Users 344aab9758bb0d018b93739e7893fb3a'],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -12,11 +12,11 @@ use Ramsey\Uuid\Math\BrickMathCalculator;
|
||||
use Ramsey\Uuid\Rfc4122\Fields;
|
||||
use Ramsey\Uuid\Rfc4122\UuidBuilder;
|
||||
use Ramsey\Uuid\Rfc4122\UuidV1;
|
||||
use Ramsey\Uuid\Rfc4122\UuidV2;
|
||||
use Ramsey\Uuid\Rfc4122\UuidV3;
|
||||
use Ramsey\Uuid\Rfc4122\UuidV4;
|
||||
use Ramsey\Uuid\Rfc4122\UuidV5;
|
||||
use Ramsey\Uuid\Test\TestCase;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
class UuidBuilderTest extends TestCase
|
||||
{
|
||||
@@ -57,7 +57,7 @@ class UuidBuilderTest extends TestCase
|
||||
],
|
||||
[
|
||||
'uuid' => 'ff6f8cb0-c57d-21e1-9b21-0800200c9a66',
|
||||
'expectedClass' => Uuid::class,
|
||||
'expectedClass' => UuidV2::class,
|
||||
'expectedVersion' => 2,
|
||||
],
|
||||
[
|
||||
|
||||
+25
-13
@@ -4,15 +4,18 @@ declare(strict_types=1);
|
||||
|
||||
namespace Ramsey\Uuid\Test;
|
||||
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Ramsey\Uuid\Builder\UuidBuilderInterface;
|
||||
use Ramsey\Uuid\Codec\CodecInterface;
|
||||
use Ramsey\Uuid\Converter\NumberConverterInterface;
|
||||
use Ramsey\Uuid\FeatureSet;
|
||||
use Ramsey\Uuid\Generator\DceSecurityGeneratorInterface;
|
||||
use Ramsey\Uuid\Generator\RandomGeneratorInterface;
|
||||
use Ramsey\Uuid\Generator\TimeGeneratorInterface;
|
||||
use Ramsey\Uuid\Provider\NodeProviderInterface;
|
||||
use Ramsey\Uuid\UuidFactory;
|
||||
use Ramsey\Uuid\Validator\ValidatorInterface;
|
||||
|
||||
class UuidFactoryTest extends TestCase
|
||||
{
|
||||
@@ -49,37 +52,46 @@ class UuidFactoryTest extends TestCase
|
||||
|
||||
public function testGettersReturnValueFromFeatureSet(): void
|
||||
{
|
||||
$codec = $this->getMockBuilder(CodecInterface::class)->getMock();
|
||||
$nodeProvider = $this->getMockBuilder(NodeProviderInterface::class)->getMock();
|
||||
$randomGenerator = $this->getMockBuilder(RandomGeneratorInterface::class)->getMock();
|
||||
$timeGenerator = $this->getMockBuilder(TimeGeneratorInterface::class)->getMock();
|
||||
$codec = Mockery::mock(CodecInterface::class);
|
||||
$nodeProvider = Mockery::mock(NodeProviderInterface::class);
|
||||
$randomGenerator = Mockery::mock(RandomGeneratorInterface::class);
|
||||
$timeGenerator = Mockery::mock(TimeGeneratorInterface::class);
|
||||
$dceSecurityGenerator = Mockery::mock(DceSecurityGeneratorInterface::class);
|
||||
$numberConverter = Mockery::mock(NumberConverterInterface::class);
|
||||
$builder = Mockery::mock(UuidBuilderInterface::class);
|
||||
$validator = Mockery::mock(ValidatorInterface::class);
|
||||
|
||||
$featureSet = $this->getMockBuilder(FeatureSet::class)->getMock();
|
||||
$featureSet->method('getCodec')->willReturn($codec);
|
||||
$featureSet->method('getNodeProvider')->willReturn($nodeProvider);
|
||||
$featureSet->method('getRandomGenerator')->willReturn($randomGenerator);
|
||||
$featureSet->method('getTimeGenerator')->willReturn($timeGenerator);
|
||||
$featureSet = Mockery::mock(FeatureSet::class, [
|
||||
'getCodec' => $codec,
|
||||
'getNodeProvider' => $nodeProvider,
|
||||
'getRandomGenerator' => $randomGenerator,
|
||||
'getTimeGenerator' => $timeGenerator,
|
||||
'getDceSecurityGenerator' => $dceSecurityGenerator,
|
||||
'getNumberConverter' => $numberConverter,
|
||||
'getBuilder' => $builder,
|
||||
'getValidator' => $validator,
|
||||
]);
|
||||
|
||||
$uuidFactory = new UuidFactory($featureSet);
|
||||
$this->assertEquals(
|
||||
$this->assertSame(
|
||||
$codec,
|
||||
$uuidFactory->getCodec(),
|
||||
'getCodec did not return CodecInterface from FeatureSet'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->assertSame(
|
||||
$nodeProvider,
|
||||
$uuidFactory->getNodeProvider(),
|
||||
'getNodeProvider did not return NodeProviderInterface from FeatureSet'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->assertSame(
|
||||
$randomGenerator,
|
||||
$uuidFactory->getRandomGenerator(),
|
||||
'getRandomGenerator did not return RandomGeneratorInterface from FeatureSet'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->assertSame(
|
||||
$timeGenerator,
|
||||
$uuidFactory->getTimeGenerator(),
|
||||
'getTimeGenerator did not return TimeGeneratorInterface from FeatureSet'
|
||||
|
||||
Reference in New Issue
Block a user