PageRenderTime 43ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/DevApp/library/ServerLibraries/ZendFramework/trunk/tests/Zend/Auth/Adapter/Http/ProxyTest.php

http://firephp.googlecode.com/
PHP | 480 lines | 241 code | 76 blank | 163 comment | 1 complexity | adc0d934c5de90b405f7d22b32b6289e MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, MIT, Apache-2.0
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Auth
  17. * @subpackage UnitTests
  18. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: ProxyTest.php 24593 2012-01-05 20:35:02Z matthew $
  21. */
  22. /**
  23. * @see Zend_Auth_Adapter_Http
  24. */
  25. require_once 'Zend/Auth/Adapter/Http.php';
  26. /**
  27. * @see Zend_Auth_Adapter_Http_Resolver_File
  28. */
  29. require_once 'Zend/Auth/Adapter/Http/Resolver/File.php';
  30. /**
  31. * @see Zend_Controller_Request_Http
  32. */
  33. require_once 'Zend/Controller/Request/Http.php';
  34. /**
  35. * @see Zend_Controller_Response_Http
  36. */
  37. require_once 'Zend/Controller/Response/Http.php';
  38. /**
  39. * @category Zend
  40. * @package Zend_Auth
  41. * @subpackage UnitTests
  42. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  43. * @license http://framework.zend.com/license/new-bsd New BSD License
  44. * @group Zend_Auth
  45. */
  46. class Zend_Auth_Adapter_Http_ProxyTest extends PHPUnit_Framework_TestCase
  47. {
  48. /**
  49. * Path to test files
  50. *
  51. * @var string
  52. */
  53. protected $_filesPath;
  54. /**
  55. * HTTP Basic configuration
  56. *
  57. * @var array
  58. */
  59. protected $_basicConfig;
  60. /**
  61. * HTTP Digest configuration
  62. *
  63. * @var array
  64. */
  65. protected $_digestConfig;
  66. /**
  67. * HTTP Basic Digest configuration
  68. *
  69. * @var array
  70. */
  71. protected $_bothConfig;
  72. /**
  73. * File resolver setup against with HTTP Basic auth file
  74. *
  75. * @var Zend_Auth_Adapter_Http_Resolver_File
  76. */
  77. protected $_basicResolver;
  78. /**
  79. * File resolver setup against with HTTP Digest auth file
  80. *
  81. * @var Zend_Auth_Adapter_Http_Resolver_File
  82. */
  83. protected $_digestResolver;
  84. /**
  85. * Sets up test configuration
  86. *
  87. * @return void
  88. */
  89. public function __construct()
  90. {
  91. $this->_filesPath = dirname(__FILE__) . '/_files';
  92. $this->_basicResolver = new Zend_Auth_Adapter_Http_Resolver_File("{$this->_filesPath}/htbasic.1");
  93. $this->_digestResolver = new Zend_Auth_Adapter_Http_Resolver_File("{$this->_filesPath}/htdigest.3");
  94. $this->_basicConfig = array(
  95. 'accept_schemes' => 'basic',
  96. 'realm' => 'Test Realm',
  97. 'proxy_auth' => true
  98. );
  99. $this->_digestConfig = array(
  100. 'accept_schemes' => 'digest',
  101. 'realm' => 'Test Realm',
  102. 'digest_domains' => '/ http://localhost/',
  103. 'nonce_timeout' => 300,
  104. 'proxy_auth' => true
  105. );
  106. $this->_bothConfig = array(
  107. 'accept_schemes' => 'basic digest',
  108. 'realm' => 'Test Realm',
  109. 'digest_domains' => '/ http://localhost/',
  110. 'nonce_timeout' => 300,
  111. 'proxy_auth' => true
  112. );
  113. }
  114. public function testBasicChallenge()
  115. {
  116. // Trying to authenticate without sending an Proxy-Authorization header
  117. // should result in a 407 reply with a Proxy-Authenticate header, and a
  118. // false result.
  119. // The expected Basic Proxy-Authenticate header value
  120. $basic = 'Basic realm="' . $this->_bothConfig['realm'] . '"';
  121. $data = $this->_doAuth('', 'basic');
  122. $this->_checkUnauthorized($data, $basic);
  123. }
  124. public function testDigestChallenge()
  125. {
  126. // Trying to authenticate without sending an Proxy-Authorization header
  127. // should result in a 407 reply with a Proxy-Authenticate header, and a
  128. // false result.
  129. // The expected Digest Proxy-Authenticate header value
  130. $digest = $this->_digestChallenge();
  131. $data = $this->_doAuth('', 'digest');
  132. $this->_checkUnauthorized($data, $digest);
  133. }
  134. public function testBothChallenges()
  135. {
  136. // Trying to authenticate without sending an Proxy-Authorization header
  137. // should result in a 407 reply with at least one Proxy-Authenticate
  138. // header, and a false result.
  139. $data = $this->_doAuth('', 'both');
  140. extract($data); // $result, $status, $headers
  141. // The expected Proxy-Authenticate header values
  142. $basic = 'Basic realm="' . $this->_bothConfig['realm'] . '"';
  143. $digest = $this->_digestChallenge();
  144. // Make sure the result is false
  145. $this->assertType('Zend_Auth_Result', $result);
  146. $this->assertFalse($result->isValid());
  147. // Verify the status code and the presence of both challenges
  148. $this->assertEquals(407, $status);
  149. $this->assertEquals('Proxy-Authenticate', $headers[0]['name']);
  150. $this->assertEquals('Proxy-Authenticate', $headers[1]['name']);
  151. // Check to see if the expected challenges match the actual
  152. $this->assertEquals($basic, $headers[0]['value']);
  153. $this->assertEquals($digest, $headers[1]['value']);
  154. }
  155. public function testBasicAuthValidCreds()
  156. {
  157. // Attempt Basic Authentication with a valid username and password
  158. $data = $this->_doAuth('Basic ' . base64_encode('Bryce:ThisIsNotMyPassword'), 'basic');
  159. $this->_checkOK($data);
  160. }
  161. public function testBasicAuthBadCreds()
  162. {
  163. // Ensure that credentials containing invalid characters are treated as
  164. // a bad username or password.
  165. // The expected Basic WWW-Authenticate header value
  166. $basic = 'Basic realm="' . $this->_basicConfig['realm'] . '"';
  167. $data = $this->_doAuth('Basic ' . base64_encode("Bad\tChars:In:Creds"), 'basic');
  168. $this->_checkUnauthorized($data, $basic);
  169. }
  170. public function testBasicAuthBadUser()
  171. {
  172. // Attempt Basic Authentication with a bad username and password
  173. // The expected Basic Proxy-Authenticate header value
  174. $basic = 'Basic realm="' . $this->_basicConfig['realm'] . '"';
  175. $data = $this->_doAuth('Basic ' . base64_encode('Nobody:NotValid'), 'basic');
  176. $this->_checkUnauthorized($data, $basic);
  177. }
  178. public function testBasicAuthBadPassword()
  179. {
  180. // Attempt Basic Authentication with a valid username, but invalid
  181. // password
  182. // The expected Basic WWW-Authenticate header value
  183. $basic = 'Basic realm="' . $this->_basicConfig['realm'] . '"';
  184. $data = $this->_doAuth('Basic ' . base64_encode('Bryce:Invalid'), 'basic');
  185. $this->_checkUnauthorized($data, $basic);
  186. }
  187. public function testDigestAuthValidCreds()
  188. {
  189. // Attempt Digest Authentication with a valid username and password
  190. $data = $this->_doAuth($this->_digestReply('Bryce', 'ThisIsNotMyPassword'), 'digest');
  191. $this->_checkOK($data);
  192. }
  193. public function testDigestAuthDefaultAlgo()
  194. {
  195. // If the client omits the aglorithm argument, it should default to MD5,
  196. // and work just as above
  197. $cauth = $this->_digestReply('Bryce', 'ThisIsNotMyPassword');
  198. $cauth = preg_replace('/algorithm="MD5", /', '', $cauth);
  199. $data = $this->_doAuth($cauth, 'digest');
  200. $this->_checkOK($data);
  201. }
  202. public function testDigestAuthQuotedNC()
  203. {
  204. // The nonce count isn't supposed to be quoted, but apparently some
  205. // clients do anyway.
  206. $cauth = $this->_digestReply('Bryce', 'ThisIsNotMyPassword');
  207. $cauth = preg_replace('/nc=00000001/', 'nc="00000001"', $cauth);
  208. $data = $this->_doAuth($cauth, 'digest');
  209. $this->_checkOK($data);
  210. }
  211. public function testDigestAuthBadCreds()
  212. {
  213. // Attempt Digest Authentication with a bad username and password
  214. // The expected Digest Proxy-Authenticate header value
  215. $digest = $this->_digestChallenge();
  216. $data = $this->_doAuth($this->_digestReply('Nobody', 'NotValid'), 'digest');
  217. $this->_checkUnauthorized($data, $digest);
  218. }
  219. public function testDigestTampered()
  220. {
  221. // Create the tampered header value
  222. $tampered = $this->_digestReply('Bryce', 'ThisIsNotMyPassword');
  223. $tampered = preg_replace(
  224. '/ nonce="[a-fA-F0-9]{32}", /',
  225. ' nonce="' . str_repeat('0', 32).'", ',
  226. $tampered
  227. );
  228. // The expected Digest Proxy-Authenticate header value
  229. $digest = $this->_digestChallenge();
  230. $data = $this->_doAuth($tampered, 'digest');
  231. $this->_checkUnauthorized($data, $digest);
  232. }
  233. public function testBadSchemeRequest()
  234. {
  235. // Sending a request for an invalid authentication scheme should result
  236. // in a 400 Bad Request response.
  237. $data = $this->_doAuth('Invalid ' . base64_encode('Nobody:NotValid'), 'basic');
  238. $this->_checkBadRequest($data);
  239. }
  240. public function testBadDigestRequest()
  241. {
  242. // If any of the individual parts of the Digest Proxy-Authorization header
  243. // are bad, it results in a 400 Bad Request. But that's a lot of
  244. // possibilities, so we're just going to pick one for now.
  245. $bad = $this->_digestReply('Bryce', 'ThisIsNotMyPassword');
  246. $bad = preg_replace(
  247. '/realm="([^"]+)"/', // cut out the realm
  248. '', $bad
  249. );
  250. $data = $this->_doAuth($bad, 'digest');
  251. $this->_checkBadRequest($data);
  252. }
  253. /**
  254. * Acts like a client sending the given Authenticate header value.
  255. *
  256. * @param string $clientHeader Authenticate header value
  257. * @param string $scheme Which authentication scheme to use
  258. * @return array Containing the result, the response headers, and the status
  259. */
  260. public function _doAuth($clientHeader, $scheme)
  261. {
  262. // Set up stub request and response objects
  263. $request = $this->getMock('Zend_Controller_Request_Http');
  264. $response = new Zend_Controller_Response_Http;
  265. $response->setHttpResponseCode(200);
  266. $response->headersSentThrowsException = false;
  267. // Set stub method return values
  268. $request->expects($this->any())
  269. ->method('getRequestUri')
  270. ->will($this->returnValue('/'));
  271. $request->expects($this->any())
  272. ->method('getMethod')
  273. ->will($this->returnValue('GET'));
  274. $request->expects($this->any())
  275. ->method('getServer')
  276. ->will($this->returnValue('PHPUnit'));
  277. $request->expects($this->any())
  278. ->method('getHeader')
  279. ->will($this->returnValue($clientHeader));
  280. // Select an Authentication scheme
  281. switch ($scheme) {
  282. case 'basic':
  283. $use = $this->_basicConfig;
  284. break;
  285. case 'digest':
  286. $use = $this->_digestConfig;
  287. break;
  288. case 'both':
  289. default:
  290. $use = $this->_bothConfig;
  291. }
  292. // Create the HTTP Auth adapter
  293. $a = new Zend_Auth_Adapter_Http($use);
  294. $a->setBasicResolver($this->_basicResolver);
  295. $a->setDigestResolver($this->_digestResolver);
  296. // Send the authentication request
  297. $a->setRequest($request);
  298. $a->setResponse($response);
  299. $result = $a->authenticate();
  300. $return = array(
  301. 'result' => $result,
  302. 'status' => $response->getHttpResponseCode(),
  303. 'headers' => $response->getHeaders()
  304. );
  305. return $return;
  306. }
  307. /**
  308. * Constructs a local version of the digest challenge we expect to receive
  309. *
  310. * @return string
  311. */
  312. protected function _digestChallenge()
  313. {
  314. $timeout = ceil(time() / 300) * 300;
  315. $nonce = md5($timeout . ':PHPUnit:Zend_Auth_Adapter_Http');
  316. $opaque = md5('Opaque Data:Zend_Auth_Adapter_Http');
  317. $wwwauth = 'Digest '
  318. . 'realm="' . $this->_digestConfig['realm'] . '", '
  319. . 'domain="' . $this->_digestConfig['digest_domains'] . '", '
  320. . 'nonce="' . $nonce . '", '
  321. . 'opaque="' . $opaque . '", '
  322. . 'algorithm="MD5", '
  323. . 'qop="auth"';
  324. return $wwwauth;
  325. }
  326. /**
  327. * Constructs a client digest Proxy-Authorization header
  328. *
  329. * @param string $user
  330. * @param string $pass
  331. * @return string
  332. */
  333. protected function _digestReply($user, $pass)
  334. {
  335. $nc = '00000001';
  336. $timeout = ceil(time() / 300) * 300;
  337. $nonce = md5($timeout . ':PHPUnit:Zend_Auth_Adapter_Http');
  338. $opaque = md5('Opaque Data:Zend_Auth_Adapter_Http');
  339. $cnonce = md5('cnonce');
  340. $response = md5(md5($user . ':' . $this->_digestConfig['realm'] . ':' . $pass) . ":$nonce:$nc:$cnonce:auth:"
  341. . md5('GET:/'));
  342. $cauth = 'Digest '
  343. . 'username="Bryce", '
  344. . 'realm="' . $this->_digestConfig['realm'] . '", '
  345. . 'nonce="' . $nonce . '", '
  346. . 'uri="/", '
  347. . 'response="' . $response . '", '
  348. . 'algorithm="MD5", '
  349. . 'cnonce="' . $cnonce . '", '
  350. . 'opaque="' . $opaque . '", '
  351. . 'qop="auth", '
  352. . 'nc=' . $nc;
  353. return $cauth;
  354. }
  355. /**
  356. * Checks for an expected 407 Proxy-Unauthorized response
  357. *
  358. * @param array $data Authentication results
  359. * @param string $expected Expected Proxy-Authenticate header value
  360. * @return void
  361. */
  362. protected function _checkUnauthorized($data, $expected)
  363. {
  364. extract($data); // $result, $status, $headers
  365. // Make sure the result is false
  366. $this->assertType('Zend_Auth_Result', $result);
  367. $this->assertFalse($result->isValid());
  368. // Verify the status code and the presence of the challenge
  369. $this->assertEquals(407, $status);
  370. $this->assertEquals('Proxy-Authenticate', $headers[0]['name']);
  371. // Check to see if the expected challenge matches the actual
  372. $this->assertEquals($expected, $headers[0]['value']);
  373. }
  374. /**
  375. * Checks for an expected 200 OK response
  376. *
  377. * @param array $data Authentication results
  378. * @return void
  379. */
  380. protected function _checkOK($data)
  381. {
  382. extract($data); // $result, $status, $headers
  383. // Make sure the result is true
  384. $this->assertType('Zend_Auth_Result', $result);
  385. $this->assertTrue($result->isValid());
  386. // Verify we got a 200 response
  387. $this->assertEquals(200, $status);
  388. }
  389. /**
  390. * Checks for an expected 400 Bad Request response
  391. *
  392. * @param array $data Authentication results
  393. * @return void
  394. */
  395. protected function _checkBadRequest($data)
  396. {
  397. extract($data); // $result, $status, $headers
  398. // Make sure the result is false
  399. $this->assertType('Zend_Auth_Result', $result);
  400. $this->assertFalse($result->isValid());
  401. // Make sure it set the right HTTP code
  402. $this->assertEquals(400, $status);
  403. }
  404. }