Browse Source

Adding some handy methods to BaseAuthenticate to be used for migrating
passwords

Jose Lorenzo Rodriguez 11 years ago
parent
commit
2ef3ff0e3f
2 changed files with 84 additions and 2 deletions
  1. 42 1
      src/Auth/BaseAuthenticate.php
  2. 42 1
      tests/TestCase/Auth/FormAuthenticateTest.php

+ 42 - 1
src/Auth/BaseAuthenticate.php

@@ -73,6 +73,17 @@ abstract class BaseAuthenticate {
 	protected $_passwordHasher;
 
 /**
+ * Contains a key indicated whether or not the user authenticated by this class
+ * requires their password to be rehashed with another algorithm.
+ *
+ * @var array
+ */
+	protected $passwordInfo = [
+		'password' => null,
+		'needsRehash' => false
+	];
+
+/**
  * Constructor
  *
  * @param ComponentRegistry $registry The Component registry used on this request.
@@ -123,9 +134,14 @@ abstract class BaseAuthenticate {
 		}
 
 		if ($password !== null) {
-			if (!$this->passwordHasher()->check($password, $result[$fields['password']])) {
+			$hasher = $this->passwordHasher();
+			$hashedPassword = $result[$fields['password']];
+			if (!$hasher->check($password, $hashedPassword)) {
 				return false;
 			}
+
+			$this->_passwordInfo['password'] = $password;
+			$this->_passwordInfo['needsRehash'] = $hasher->needsRehash($hashedPassword);
 			unset($result[$fields['password']]);
 		}
 
@@ -149,6 +165,31 @@ abstract class BaseAuthenticate {
 	}
 
 /**
+ * Returns whether or not the password stored in the repository for the logged in user
+ * requires to be rehashed with another algorithm
+ *
+ * @return bool
+ */
+	public function needsPasswordRehash() {
+		return $this->_passwordInfo['needsRehash'];
+	}
+
+/**
+ * Returns a freshly hashed password using the hashing alogrithm from the configured
+ * password hasher. This method uses the plain password provided when successfully
+ * authenticating the user.
+ *
+ * @return string
+ * @throws \RuntimeException if no user has been authenticated using this object
+ */
+	public function rehashPassword() {
+		if ($this->_passwordInfo['password'] === null) {
+			throw new \RuntimeException('No user has been authenticated using this provider');
+		}
+		return $this->passwordHasher()->hash($this->_passwordInfo['password']);
+	}
+
+/**
  * Authenticate a user based on the request information.
  *
  * @param \Cake\Network\Request $request Request to get authentication information from.

+ 42 - 1
tests/TestCase/Auth/FormAuthenticateTest.php

@@ -52,7 +52,7 @@ class FormAuthenticateTest extends TestCase {
 		$this->auth = new FormAuthenticate($this->Collection, [
 			'userModel' => 'Users'
 		]);
-		$password = Security::hash('password', 'blowfish', false);
+		$password = password_hash('password', PASSWORD_DEFAULT);
 		$Users = TableRegistry::get('Users');
 		$Users->updateAll(['password' => $password], []);
 		$this->response = $this->getMock('Cake\Network\Response');
@@ -309,4 +309,45 @@ class FormAuthenticateTest extends TestCase {
 		$this->assertFalse($this->auth->authenticate($request, $this->response));
 	}
 
+/**
+ * Tests that using default means password don't need to be rehashed
+ *
+ * @return void
+ */
+	public function testAuthenticateNoRehash() {
+		$request = new Request('posts/index');
+		$request->data = [
+			'username' => 'mariano',
+			'password' => 'password'
+		];
+		$result = $this->auth->authenticate($request, $this->response);
+		$this->assertNotEmpty($result);
+		$this->assertFalse($this->auth->needsPasswordRehash());
+	}
+
+/**
+ * Tests that not using the Simple password hasher means that the password
+ * needs to be rehashed
+ *
+ * @return void
+ */
+	public function testAuthenticateRehash() {
+		$this->auth = new FormAuthenticate($this->Collection, [
+			'userModel' => 'Users',
+			'passwordHasher' => 'Weak'
+		]);
+		$password = $this->auth->passwordHasher()->hash('password');
+		TableRegistry::get('Users')->updateAll(['password' => $password], []);
+
+		$request = new Request('posts/index');
+		$request->data = [
+			'username' => 'mariano',
+			'password' => 'password'
+		];
+		$result = $this->auth->authenticate($request, $this->response);
+		$this->assertNotEmpty($result);
+		$this->assertTrue($this->auth->needsPasswordRehash());
+		$this->assertSame($password, $this->auth->rehashPassword());
+	}
+
 }