diff options
Diffstat (limited to 'vendor/spomky-labs/otphp/src/Factory.php')
-rw-r--r-- | vendor/spomky-labs/otphp/src/Factory.php | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/vendor/spomky-labs/otphp/src/Factory.php b/vendor/spomky-labs/otphp/src/Factory.php new file mode 100644 index 000000000..70df63945 --- /dev/null +++ b/vendor/spomky-labs/otphp/src/Factory.php @@ -0,0 +1,115 @@ +<?php + +declare(strict_types=1); + +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2019 Spomky-Labs + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +namespace OTPHP; + +use Assert\Assertion; +use InvalidArgumentException; +use function Safe\parse_url; +use function Safe\sprintf; +use Throwable; + +/** + * This class is used to load OTP object from a provisioning Uri. + */ +final class Factory implements FactoryInterface +{ + public static function loadFromProvisioningUri(string $uri): OTPInterface + { + try { + $parsed_url = parse_url($uri); + } catch (Throwable $throwable) { + throw new InvalidArgumentException('Not a valid OTP provisioning URI', $throwable->getCode(), $throwable); + } + Assertion::isArray($parsed_url, 'Not a valid OTP provisioning URI'); + self::checkData($parsed_url); + + $otp = self::createOTP($parsed_url); + + self::populateOTP($otp, $parsed_url); + + return $otp; + } + + /** + * @param array<string, mixed> $data + */ + private static function populateParameters(OTPInterface &$otp, array $data): void + { + foreach ($data['query'] as $key => $value) { + $otp->setParameter($key, $value); + } + } + + /** + * @param array<string, mixed> $data + */ + private static function populateOTP(OTPInterface &$otp, array $data): void + { + self::populateParameters($otp, $data); + $result = explode(':', rawurldecode(mb_substr($data['path'], 1))); + + if (2 > \count($result)) { + $otp->setIssuerIncludedAsParameter(false); + + return; + } + + if (null !== $otp->getIssuer()) { + Assertion::eq($result[0], $otp->getIssuer(), 'Invalid OTP: invalid issuer in parameter'); + $otp->setIssuerIncludedAsParameter(true); + } + $otp->setIssuer($result[0]); + } + + /** + * @param array<string, mixed> $data + */ + private static function checkData(array &$data): void + { + foreach (['scheme', 'host', 'path', 'query'] as $key) { + Assertion::keyExists($data, $key, 'Not a valid OTP provisioning URI'); + } + Assertion::eq('otpauth', $data['scheme'], 'Not a valid OTP provisioning URI'); + parse_str($data['query'], $data['query']); + Assertion::keyExists($data['query'], 'secret', 'Not a valid OTP provisioning URI'); + } + + /** + * @param array<string, mixed> $parsed_url + */ + private static function createOTP(array $parsed_url): OTPInterface + { + switch ($parsed_url['host']) { + case 'totp': + $totp = TOTP::create($parsed_url['query']['secret']); + $totp->setLabel(self::getLabel($parsed_url['path'])); + + return $totp; + case 'hotp': + $hotp = HOTP::create($parsed_url['query']['secret']); + $hotp->setLabel(self::getLabel($parsed_url['path'])); + + return $hotp; + default: + throw new InvalidArgumentException(sprintf('Unsupported "%s" OTP type', $parsed_url['host'])); + } + } + + private static function getLabel(string $data): string + { + $result = explode(':', rawurldecode(mb_substr($data, 1))); + + return 2 === \count($result) ? $result[1] : $result[0]; + } +} |