summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnold Daniels <arnold@jasny.net>2016-12-23 02:42:13 +0100
committerArnold Daniels <arnold@jasny.net>2016-12-23 02:42:13 +0100
commit5e8dd90f786a6fa7e7da93fa5c31400e15c7dfb6 (patch)
tree628a7f938f4d7d0d46ee2adaf68329bf44020d4d
parent1b92c1150a65306d045fbf03c40b436d368950f8 (diff)
downloadauth-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.json6
-rw-r--r--src/Auth/Confirmation.php56
-rw-r--r--tests/Auth/ConfirmationTest.php135
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);
+ }
+}