Digest.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * Redistributions of files must retain the above copyright notice.
  8. *
  9. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  10. * @link http://cakephp.org CakePHP(tm) Project
  11. * @since 3.0.0
  12. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  13. */
  14. namespace Cake\Http\Client\Auth;
  15. use Cake\Http\Client;
  16. use Cake\Http\Client\Request;
  17. /**
  18. * Digest authentication adapter for Cake\Http\Client
  19. *
  20. * Generally not directly constructed, but instead used by Cake\Http\Client
  21. * when $options['auth']['type'] is 'digest'
  22. */
  23. class Digest
  24. {
  25. /**
  26. * Instance of Cake\Http\Client
  27. *
  28. * @var \Cake\Http\Client
  29. */
  30. protected $_client;
  31. /**
  32. * Constructor
  33. *
  34. * @param \Cake\Http\Client $client Http client object.
  35. * @param array|null $options Options list.
  36. */
  37. public function __construct(Client $client, $options = null)
  38. {
  39. $this->_client = $client;
  40. }
  41. /**
  42. * Add Authorization header to the request.
  43. *
  44. * @param \Cake\Http\Client\Request $request The request object.
  45. * @param array $credentials Authentication credentials.
  46. * @return \Cake\Http\Client\Request The updated request.
  47. * @see http://www.ietf.org/rfc/rfc2617.txt
  48. */
  49. public function authentication(Request $request, array $credentials)
  50. {
  51. if (!isset($credentials['username'], $credentials['password'])) {
  52. return $request;
  53. }
  54. if (!isset($credentials['realm'])) {
  55. $credentials = $this->_getServerInfo($request, $credentials);
  56. }
  57. if (!isset($credentials['realm'])) {
  58. return $request;
  59. }
  60. $value = $this->_generateHeader($request, $credentials);
  61. return $request->withHeader('Authorization', $value);
  62. }
  63. /**
  64. * Retrieve information about the authentication
  65. *
  66. * Will get the realm and other tokens by performing
  67. * another request without authentication to get authentication
  68. * challenge.
  69. *
  70. * @param \Cake\Http\Client\Request $request The request object.
  71. * @param array $credentials Authentication credentials.
  72. * @return array modified credentials.
  73. */
  74. protected function _getServerInfo(Request $request, $credentials)
  75. {
  76. $response = $this->_client->get(
  77. $request->url(),
  78. [],
  79. ['auth' => []]
  80. );
  81. if (!$response->getHeader('WWW-Authenticate')) {
  82. return [];
  83. }
  84. preg_match_all(
  85. '@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@',
  86. $response->getHeaderLine('WWW-Authenticate'),
  87. $matches,
  88. PREG_SET_ORDER
  89. );
  90. foreach ($matches as $match) {
  91. $credentials[$match[1]] = $match[2];
  92. }
  93. if (!empty($credentials['qop']) && empty($credentials['nc'])) {
  94. $credentials['nc'] = 1;
  95. }
  96. return $credentials;
  97. }
  98. /**
  99. * Generate the header Authorization
  100. *
  101. * @param \Cake\Http\Client\Request $request The request object.
  102. * @param array $credentials Authentication credentials.
  103. * @return string
  104. */
  105. protected function _generateHeader(Request $request, $credentials)
  106. {
  107. $path = $request->getUri()->getPath();
  108. $a1 = md5($credentials['username'] . ':' . $credentials['realm'] . ':' . $credentials['password']);
  109. $a2 = md5($request->method() . ':' . $path);
  110. if (empty($credentials['qop'])) {
  111. $response = md5($a1 . ':' . $credentials['nonce'] . ':' . $a2);
  112. } else {
  113. $credentials['cnonce'] = uniqid();
  114. $nc = sprintf('%08x', $credentials['nc']++);
  115. $response = md5($a1 . ':' . $credentials['nonce'] . ':' . $nc . ':' . $credentials['cnonce'] . ':auth:' . $a2);
  116. }
  117. $authHeader = 'Digest ';
  118. $authHeader .= 'username="' . str_replace(['\\', '"'], ['\\\\', '\\"'], $credentials['username']) . '", ';
  119. $authHeader .= 'realm="' . $credentials['realm'] . '", ';
  120. $authHeader .= 'nonce="' . $credentials['nonce'] . '", ';
  121. $authHeader .= 'uri="' . $path . '", ';
  122. $authHeader .= 'response="' . $response . '"';
  123. if (!empty($credentials['opaque'])) {
  124. $authHeader .= ', opaque="' . $credentials['opaque'] . '"';
  125. }
  126. if (!empty($credentials['qop'])) {
  127. $authHeader .= ', qop="auth", nc=' . $nc . ', cnonce="' . $credentials['cnonce'] . '"';
  128. }
  129. return $authHeader;
  130. }
  131. }
  132. // @deprecated Add backwards compat alias.
  133. class_alias('Cake\Http\Client\Auth\Digest', 'Cake\Network\Http\Auth\Digest');