Added phpunit, URI tests revealed errors

master
Brady McDonough 2 years ago
parent faabdb9aee
commit 35221b979e

@ -5,6 +5,14 @@
"psr/clock": "1.0.0",
"chillerlan/php-qrcode": "5.0.1",
"paragonie/constant_time_encoding": "2.6.3"
}
},
"require-dev": {
"phpunit/phpunit": "^11"
},
"autoload": {
"psr-4": {
"BradyMcD\\TAATP\\": "src/"
}
}
}

@ -7,12 +7,12 @@ use InvalidArgumentException;
class Otpauth
{
public function __construct(
public readonly string $issuer,
public readonly string $userid,
public readonly string $issuer,
public readonly string $secret,
public readonly string $algo,
public readonly int $period,
public readonly string $algorithm,
public readonly int $digits,
public readonly int $period,
)
{}
@ -23,7 +23,7 @@ class Otpauth
{
foreach($checkArray as $c)
{
!($c[0]($target))? :throw new InvalidArgumentException($c[1]);
($c[0]($target))? :throw new InvalidArgumentException($c[1]);
}
};
$uriChecks = [
@ -31,8 +31,11 @@ class Otpauth
{ return $arr !== false; },
$uri . " is not a valid URI."],
[ function($arr)
{ return \array_key_exists('scheme', $arr) && $arr['scheme'] === 'otpauth';},
$uri . " has no scheme or the wrong scheme for an otpauth uri."],
{ return \array_key_exists('scheme', $arr);},
$uri . " has no scheme."],
[ function($arr)
{ return $arr['scheme'] === 'otpauth';},
$uri . " is not an otpauth:// uri."],
[ function($arr)
{ return \array_key_exists('path', $arr);},
$uri . " isn't tagged for a user or issuer."],
@ -50,7 +53,7 @@ class Otpauth
$parsedUri['query'] . " has no issuer information."],
[ function($que)
{ return \array_key_exists('secret', $que);},
$parsedUri['query'] . "has no secret key."]
$parsedUri['query'] . "has no secret key."],
];
$parsedQuery = [];
@ -66,45 +69,59 @@ class Otpauth
$label = \explode(":", $parsedUri['path']);
$runCheck($labelChecks, $label);
$applyDefaults = function(array &$arr, array $defaults) {
foreach($defaults as $k => $v)
{
if(\array_key_exists($k, $arr))
$queryDefaults = [
"algorithm" => "SHA1",
"period" => "30",
"digits" => "6",
"issuer" => \rawurldecode(\ltrim($label[0], "/")),
];
$parsedQuery = \array_merge($queryDefaults, $parsedQuery);
$convertFields = function(array $conversion, array &$target) {
foreach ($conversion as $k => $v)
{
$arr[$k] = $v;
}
$target[$k] = $v($target[$k]);
}
};
$queryDefaults = [
"algorithm" => "SHA1",
"period" => 30,
"digits" => 6,
$queryConversions = [
"period" => '\intval',
"digits" => '\intval',
"secret" => '\rawurldecode'
];
$labelConversions = [
0 => '\rawurldecode',
];
$applyDefaults($queryDefaults, $parsedQuery);
$convertFields($queryConversions, $parsedQuery);
$convertFields($labelConversions, $label);
// END SCHEMEING
\ltrim($label[0], "/") !== $parsedQuery['issuer']
|| throw new InvalidArgumentException($uri . " has mismatching issuer information.");
\ltrim($label[0], "/") === $parsedQuery['issuer']
|| throw new InvalidArgumentException($uri . " has mismatching issuer information.\n" .
\ltrim($label[0], "/") . "\n" .
$parsedQuery['issuer']);
return self(
$parsedQuery['issuer'],
return new self(
$label[1],
$parsedQuery['issuer'],
$parsedQuery['secret'],
$parsedQuery['algorithm'],
$parsedQuery['period'],
$parsedQuery['digits']
$parsedQuery['digits'],
$parsedQuery['period']
);
}
public function emitStr(): string
{
$label = $this->provider . ":" . $this->userid;
$provider = "provider=" . $this->provider;
$algo = "algorithm=" . $this->algo;
$encodeIssuer = \rawurlencode($this->issuer);
$label = $encodeIssuer . ":" . $this->userid;
$issuer = "issuer=" . $encodeIssuer;
$algo = "algorithm=" . $this->algorithm;
$digits = "digits=" . $this->digits;
$period = "period=" . $this->period;
$secret = "secret=" . $this->secret;
$query = \implode("&", [$secret, $provider, $period, $digits, $algo]);
$secret = "secret=" . \rawurlencode($this->secret);
$query = \implode("&", [$secret, $issuer, $algo, $period, $digits]);
return "otpauth://totp/" . $label . "?" . $query;
}

@ -0,0 +1,71 @@
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
use BradyMcD\TAATP\URI\Otpauth;
/** @SuppressWarnings(PHPMD.StaticAccess)*/
final class URITest extends TestCase
{
public function testExampleURI(): void
{
// Sourced from Google's otpauth URI specification
// https://github.com/google/google-authenticator/wiki/Key-Uri-Format
$string = 'otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=MD5&digits=8&period=60';
$urlComponents = [ "scheme" => "otpauth",
"host" => "totp",
"path" => "ACME%20Co:john.doe@email.com/",
"query" => "secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30",];
$queryComponents = [ "secret" => "HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ",
"issuer" => "ACME Co",
"algorithm" => "MD5",
"digits" => 8,
"period" => 60,];
$provisioningUri = Otpauth::fromString($string);
$this->assertSame($provisioningUri->secret, $queryComponents['secret']);
$this->assertSame($provisioningUri->issuer, $queryComponents['issuer']);
$this->assertSame($provisioningUri->digits, $queryComponents['digits']);
$this->assertSame($provisioningUri->period, $queryComponents['period']);
$this->assertSame($provisioningUri->algorithm, $queryComponents['algorithm']);
$calculatedUrl = $provisioningUri->emitStr();
$parsedOtp = \parse_url($calculatedUrl);
$parsedTest = \parse_url($string);
$parsedOtpQuery = [];
$parsedTestQuery = [];
\parse_str($parsedOtp['query'], $parsedOtpQuery);
unset($parsedOtp['query']);
\parse_str($parsedTest['query'], $parsedTestQuery);
unset($parsedTest['query']);
$this->assertEqualsCanonicalizing($parsedOtp, $parsedTest);
$this->assertEqualsCanonicalizing($parsedOtpQuery, $parsedTestQuery);
}
public function testIncompleteURI(): void
{
$string = 'otpauth://totp/Example:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=Example';
$queryComponents = [ "secret" => "HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ",
"issuer" => "Example",
"algorithm" => "SHA1",
"digits" => 6,
"period" => 30,];
$provisioningUri = Otpauth::fromString($string);
$this->assertSame($provisioningUri->secret, $queryComponents['secret']);
$this->assertSame($provisioningUri->algorithm, $queryComponents['algorithm']);
$this->assertSame($provisioningUri->digits, $queryComponents['digits']);
$this->assertSame($provisioningUri->period, $queryComponents['period']);
}
}
?>
Loading…
Cancel
Save