diff options
author | Arnold Daniels <arnold@jasny.net> | 2016-12-23 02:42:13 +0100 |
---|---|---|
committer | Arnold Daniels <arnold@jasny.net> | 2016-12-23 02:42:13 +0100 |
commit | 5e8dd90f786a6fa7e7da93fa5c31400e15c7dfb6 (patch) | |
tree | 628a7f938f4d7d0d46ee2adaf68329bf44020d4d | |
parent | 1b92c1150a65306d045fbf03c40b436d368950f8 (diff) | |
download | auth-5e8dd90f786a6fa7e7da93fa5c31400e15c7dfb6.zip auth-5e8dd90f786a6fa7e7da93fa5c31400e15c7dfb6.tar.gz auth-5e8dd90f786a6fa7e7da93fa5c31400e15c7dfb6.tar.bz2 |
Test Auth\Confirmation
Made a few fixes
Support (long) hexidecimal ids
-rw-r--r-- | composer.json | 6 | ||||
-rw-r--r-- | src/Auth/Confirmation.php | 56 | ||||
-rw-r--r-- | tests/Auth/ConfirmationTest.php | 135 |
3 files changed, 176 insertions, 21 deletions
diff --git a/composer.json b/composer.json index d571c81..832bf96 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,10 @@ } }, "require-dev": { - "jasny/php-code-quality": "^2.0" + "jasny/php-code-quality": "^2.0", + "hashids/hashids": "~2.0.0" + }, + "suggest": { + "hashids/hashids": "For generating confirmation tokens" } } diff --git a/src/Auth/Confirmation.php b/src/Auth/Confirmation.php index 3dfb878..5db5088 100644 --- a/src/Auth/Confirmation.php +++ b/src/Auth/Confirmation.php @@ -5,9 +5,9 @@ namespace Jasny\Auth; use Hashids\Hashids; /** - * Generate and verify a hash to confirm a new user. + * Generate and verify confirmation tokens. * - * Uses the hashids library + * Uses the hashids library. * @link http://hashids.org/php/ * * <code> @@ -43,16 +43,35 @@ trait Confirmation /** + * Create a heashids interface + * + * @param string $secret + * @return Hashids + */ + protected function createHashids($subject) + { + if (!class_exists(Hashids::class)) { + // @codeCoverageIgnoreStart + throw new \Exception("Unable to generate a confirmation hash: Hashids library is not installed"); + // @codeCoverageIgnoreEnd + } + + $salt = hash('sha256', $this->getConfirmationSecret() . $subject); + + return new Hashids($salt); + } + + /** * Generate a confirm hash based on a user id * * @param string $id - * @return string + * @return int */ protected function generateConfirmHash($id) { - $confirmHash = hash('sha256', $id . $this->getConfirmationSecret()); + $confirmHash = md5($id . $this->getConfirmationSecret()); - return sprintf('%012s', substr(base_convert($confirmHash, 16, 36), -12)); + return hexdec(substr($confirmHash, 0, 8)); } /** @@ -64,19 +83,15 @@ trait Confirmation */ public function getConfirmationToken(User $user, $subject) { - if (!class_exists(Hashids::class)) { - throw new \Exception("Unable to generate a confirmation hash: Hashids library is not installed"); - } + $hashids = $this->createHashids($subject); $id = $user->getId(); $confirm = $this->generateConfirmHash($id); - $salt = hash('sha256', $this->getConfirmationSecret()); - $hashids = new Hashids($salt); - - $decId = hexdec($id); // Will work if id is hexidecimal or decimal + $parts = array_map('hexdec', str_split($id, 8)); // Will work if id is hexidecimal or decimal + $parts[] = $confirm; - return $hashids->encode($subject, $decId, $confirm); + return $hashids->encode($parts); } /** @@ -88,17 +103,18 @@ trait Confirmation */ public function fetchUserForConfirmation($token, $subject) { - if (!class_exists(Hashids::class)) { - throw new \Exception("Unable to generate a confirmation hash: Hashids library is not installed"); - } + $hashids = $this->createHashids($subject); - $hashids = new Hashids($this->getConfirmationSalt()); + $parts = $hashids->decode($token); - list($decId, $tokenSubject, $confirm) = (array)$hashids->decode($token) + [null, null, null]; + if (empty($parts)) { + return null; + } - $id = isset($decId) ? hexdec($decId) : null; // Inverse action of getConfirmationToken + $confirm = array_pop($parts); + $id = join(array_map('dechex', $parts)); - if (!isset($id) || $tokenSubject !== $subject || $confirm !== $this->generateConfirmHash($id)) { + if ($confirm !== $this->generateConfirmHash($id)) { return null; } diff --git a/tests/Auth/ConfirmationTest.php b/tests/Auth/ConfirmationTest.php new file mode 100644 index 0000000..08e440f --- /dev/null +++ b/tests/Auth/ConfirmationTest.php @@ -0,0 +1,135 @@ +<?php + +namespace Jasny\Auth; + +use Jasny\Auth; +use PHPUnit_Framework_TestCase as TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * @covers Jasny\Auth\Confirmation + */ +class ConfirmationTest extends TestCase +{ + /** + * @var Confirmation|MockObject + */ + public $auth; + + public function setUp() + { + $this->auth = $this->getMockForTrait(Auth\Confirmation::class); + } + + /** + * @return string + */ + public function testGetConfirmationToken() + { + $this->auth->method('getConfirmationSecret')->willReturn('very secret'); + + $user = $this->createMock(Auth\User::class); + $user->method('getId')->willReturn(123); + + $token = $this->auth->getConfirmationToken($user, 'foo bar'); + + $this->assertInternalType('string', $token); + $this->assertNotEmpty($token); + + return $token; + } + + /** + * @depends testGetConfirmationToken + * + * @param string $token + */ + public function testFetchUserForConfirmationWithValidToken($token) + { + $user = $this->createMock(Auth\User::class); + + $this->auth->method('getConfirmationSecret')->willReturn('very secret'); + $this->auth->expects($this->once())->method('fetchUserById')->with(123)->willReturn($user); + + $result = $this->auth->fetchUserForConfirmation($token, 'foo bar'); + + $this->assertSame($user, $result); + } + + public function testFetchUserForConfirmationWithInvalidToken() + { + $this->auth->method('getConfirmationSecret')->willReturn('very secret'); + $this->auth->expects($this->never())->method('fetchUserById'); + + $result = $this->auth->fetchUserForConfirmation('faKeToken', 'foo bar'); + + $this->assertNull($result); + } + + /** + * @depends testGetConfirmationToken + * + * @param string $token + */ + public function testFetchUserForConfirmationWithOtherSecret($token) + { + $this->auth->method('getConfirmationSecret')->willReturn('other secret'); + $this->auth->expects($this->never())->method('fetchUserById'); + + $result = $this->auth->fetchUserForConfirmation($token, 'foo bar'); + + $this->assertNull($result); + } + + /** + * @depends testGetConfirmationToken + * + * @param string $token + */ + public function testFetchUserForConfirmationWithOtherSubject($token) + { + $this->auth->method('getConfirmationSecret')->willReturn('very secret'); + $this->auth->expects($this->never())->method('fetchUserById'); + + $result = $this->auth->fetchUserForConfirmation($token, 'other subject'); + + $this->assertNull($result); + } + + + /** + * @return string + */ + public function testGetConfirmationTokenWithHexId() + { + $this->auth->method('getConfirmationSecret')->willReturn('very secret'); + + $user = $this->createMock(Auth\User::class); + $user->method('getId')->willReturn('585c7f9a22a9037a1c8b4567'); + + $token = $this->auth->getConfirmationToken($user, 'foo bar'); + + $this->assertInternalType('string', $token); + $this->assertNotEmpty($token); + + return $token; + } + + /** + * @depends testGetConfirmationTokenWithHexId + * + * @param string $token + */ + public function testFetchUserForConfirmationWithHexId($token) + { + $user = $this->createMock(Auth\User::class); + + $this->auth->method('getConfirmationSecret')->willReturn('very secret'); + $this->auth->expects($this->once())->method('fetchUserById')->with('585c7f9a22a9037a1c8b4567') + ->willReturn($user); + + $result = $this->auth->fetchUserForConfirmation($token, 'foo bar'); + + $this->assertSame($user, $result); + } +} |