Browse Source

Fix DigestAuthenticate with simulated HTTP methods.

Digest authentication headers depend on the original HTTP method. When
we use simulated request methods, we break digest authentication as the
hashes never match. This change adds `ORIGINAL_REQUEST_METHOD` to
request->env(), which allows us to access the original HTTP method.
I went down this route as I wanted to avoid more global dependencies,
and I thought having a way to access the original HTTP method could be
generally useful.

Refs #4889
Mark Story 11 years ago
parent
commit
8b9dfc43e8

+ 1 - 1
src/Auth/DigestAuthenticate.php

@@ -112,7 +112,7 @@ class DigestAuthenticate extends BasicAuthenticate {
 		$password = $user[$field];
 		unset($user[$field]);
 
-		$hash = $this->generateResponseHash($digest, $password, $request->env('REQUEST_METHOD'));
+		$hash = $this->generateResponseHash($digest, $password, $request->env('ORIGINAL_REQUEST_METHOD'));
 		if ($digest['response'] === $hash) {
 			return $user;
 		}

+ 5 - 2
src/Network/Request.php

@@ -253,14 +253,16 @@ class Request implements \ArrayAccess {
 
 /**
  * Sets the REQUEST_METHOD environment variable based on the simulated _method
- * HTTP override value.
+ * HTTP override value. The 'ORIGINAL_REQUEST_METHOD' is also preserved, if you
+ * want the read the non-simulated HTTP method the client used.
  *
  * @param array $data Array of post data.
  * @return array
  */
 	protected function _processPost($data) {
+		$method = $this->env('REQUEST_METHOD');
 		if (
-			in_array($this->env('REQUEST_METHOD'), array('PUT', 'DELETE', 'PATCH')) &&
+			in_array($method, array('PUT', 'DELETE', 'PATCH')) &&
 			strpos($this->env('CONTENT_TYPE'), 'application/x-www-form-urlencoded') === 0
 		) {
 			$data = $this->input();
@@ -269,6 +271,7 @@ class Request implements \ArrayAccess {
 		if ($this->env('HTTP_X_HTTP_METHOD_OVERRIDE')) {
 			$data['_method'] = $this->env('HTTP_X_HTTP_METHOD_OVERRIDE');
 		}
+		$this->_environment['ORIGINAL_REQUEST_METHOD'] = $method;
 		if (isset($data['_method'])) {
 			$this->_environment['REQUEST_METHOD'] = $data['_method'];
 			unset($data['_method']);

+ 36 - 0
tests/TestCase/Auth/DigestAuthenticateTest.php

@@ -176,6 +176,42 @@ DIGEST;
 	}
 
 /**
+ * test authenticate success
+ *
+ * @return void
+ */
+	public function testAuthenticateSuccessSimulatedRequestMethod() {
+		$request = new Request([
+			'url' => 'posts/index',
+			'post' => ['_method' => 'PUT'],
+			'environment' => ['REQUEST_METHOD' => 'GET']
+		]);
+		$request->addParams(array('pass' => array()));
+
+		$digest = <<<DIGEST
+Digest username="mariano",
+realm="localhost",
+nonce="123",
+uri="/dir/index.html",
+qop=auth,
+nc=1,
+cnonce="123",
+response="06b257a54befa2ddfb9bfa134224aa29",
+opaque="123abc"
+DIGEST;
+		$request->env('PHP_AUTH_DIGEST', $digest);
+
+		$result = $this->auth->authenticate($request, $this->response);
+		$expected = array(
+			'id' => 1,
+			'username' => 'mariano',
+			'created' => new Time('2007-03-17 01:16:23'),
+			'updated' => new Time('2007-03-17 01:18:31')
+		);
+		$this->assertEquals($expected, $result);
+	}
+
+/**
  * test scope failure.
  *
  * @expectedException \Cake\Network\Exception\UnauthorizedException

+ 7 - 0
tests/TestCase/Network/RequestTest.php

@@ -425,6 +425,13 @@ class RequestTest extends TestCase {
 
 		$request = new Request(['environment' => ['HTTP_X_HTTP_METHOD_OVERRIDE' => 'PUT']]);
 		$this->assertEquals('PUT', $request->env('REQUEST_METHOD'));
+
+		$request = new Request([
+			'environment' => ['REQUEST_METHOD' => 'POST'],
+			'post' => ['_method' => 'PUT']
+		]);
+		$this->assertEquals('PUT', $request->env('REQUEST_METHOD'));
+		$this->assertEquals('POST', $request->env('ORIGINAL_REQUEST_METHOD'));
 	}
 
 /**