PageRenderTime 72ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

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

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