Oauth.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Oauth;
  7. use Magento\Framework\Encryption\Helper\Security;
  8. use Magento\Framework\Phrase;
  9. class Oauth implements OauthInterface
  10. {
  11. /**
  12. * @var \Magento\Framework\Oauth\Helper\Oauth
  13. */
  14. protected $_oauthHelper;
  15. /**
  16. * @var \Zend_Oauth_Http_Utility
  17. */
  18. protected $_httpUtility;
  19. /**
  20. * @var \Magento\Framework\Oauth\NonceGeneratorInterface
  21. */
  22. protected $_nonceGenerator;
  23. /**
  24. * @var \Magento\Framework\Oauth\TokenProviderInterface
  25. */
  26. protected $_tokenProvider;
  27. /**
  28. * @param Helper\Oauth $oauthHelper
  29. * @param NonceGeneratorInterface $nonceGenerator
  30. * @param TokenProviderInterface $tokenProvider
  31. * @param \Zend_Oauth_Http_Utility $httpUtility
  32. */
  33. public function __construct(
  34. Helper\Oauth $oauthHelper,
  35. NonceGeneratorInterface $nonceGenerator,
  36. TokenProviderInterface $tokenProvider,
  37. \Zend_Oauth_Http_Utility $httpUtility = null
  38. ) {
  39. $this->_oauthHelper = $oauthHelper;
  40. $this->_nonceGenerator = $nonceGenerator;
  41. $this->_tokenProvider = $tokenProvider;
  42. // null default to prevent ObjectManagerFactory from injecting, see MAGETWO-30809
  43. $this->_httpUtility = $httpUtility ?: new \Zend_Oauth_Http_Utility();
  44. }
  45. /**
  46. * Retrieve array of supported signature methods.
  47. *
  48. * @return string[] - Supported HMAC-SHA1 and HMAC-SHA256 signature methods.
  49. */
  50. public static function getSupportedSignatureMethods()
  51. {
  52. return [self::SIGNATURE_SHA1, self::SIGNATURE_SHA256];
  53. }
  54. /**
  55. * {@inheritdoc}
  56. */
  57. public function getRequestToken($params, $requestUrl, $httpMethod = 'POST')
  58. {
  59. $this->_validateProtocolParams($params);
  60. $consumer = $this->_tokenProvider->getConsumerByKey($params['oauth_consumer_key']);
  61. $this->_tokenProvider->validateConsumer($consumer);
  62. $this->_validateSignature($params, $consumer->getSecret(), $httpMethod, $requestUrl);
  63. return $this->_tokenProvider->createRequestToken($consumer);
  64. }
  65. /**
  66. * {@inheritdoc}
  67. */
  68. public function getAccessToken($params, $requestUrl, $httpMethod = 'POST')
  69. {
  70. $required = [
  71. 'oauth_consumer_key',
  72. 'oauth_signature',
  73. 'oauth_signature_method',
  74. 'oauth_nonce',
  75. 'oauth_timestamp',
  76. 'oauth_token',
  77. 'oauth_verifier',
  78. ];
  79. $this->_validateProtocolParams($params, $required);
  80. $consumer = $this->_tokenProvider->getConsumerByKey($params['oauth_consumer_key']);
  81. $tokenSecret = $this->_tokenProvider->validateRequestToken(
  82. $params['oauth_token'],
  83. $consumer,
  84. $params['oauth_verifier']
  85. );
  86. $this->_validateSignature($params, $consumer->getSecret(), $httpMethod, $requestUrl, $tokenSecret);
  87. return $this->_tokenProvider->getAccessToken($consumer);
  88. }
  89. /**
  90. * {@inheritdoc}
  91. */
  92. public function validateAccessTokenRequest($params, $requestUrl, $httpMethod = 'POST')
  93. {
  94. $required = [
  95. 'oauth_consumer_key',
  96. 'oauth_signature',
  97. 'oauth_signature_method',
  98. 'oauth_nonce',
  99. 'oauth_timestamp',
  100. 'oauth_token',
  101. ];
  102. $this->_validateProtocolParams($params, $required);
  103. $consumer = $this->_tokenProvider->getConsumerByKey($params['oauth_consumer_key']);
  104. $tokenSecret = $this->_tokenProvider->validateAccessTokenRequest($params['oauth_token'], $consumer);
  105. $this->_validateSignature($params, $consumer->getSecret(), $httpMethod, $requestUrl, $tokenSecret);
  106. return $consumer->getId();
  107. }
  108. /**
  109. * {@inheritdoc}
  110. */
  111. public function validateAccessToken($accessToken)
  112. {
  113. return $this->_tokenProvider->validateAccessToken($accessToken);
  114. }
  115. /**
  116. * {@inheritdoc}
  117. */
  118. public function buildAuthorizationHeader(
  119. $params,
  120. $requestUrl,
  121. $signatureMethod = self::SIGNATURE_SHA1,
  122. $httpMethod = 'POST'
  123. ) {
  124. $required = ["oauth_consumer_key", "oauth_consumer_secret", "oauth_token", "oauth_token_secret"];
  125. $this->_checkRequiredParams($params, $required);
  126. $consumer = $this->_tokenProvider->getConsumerByKey($params['oauth_consumer_key']);
  127. $headerParameters = [
  128. 'oauth_nonce' => $this->_nonceGenerator->generateNonce($consumer),
  129. 'oauth_timestamp' => $this->_nonceGenerator->generateTimestamp(),
  130. 'oauth_version' => '1.0',
  131. ];
  132. $headerParameters = array_merge($headerParameters, $params);
  133. $headerParameters['oauth_signature'] = $this->_httpUtility->sign(
  134. $params,
  135. $signatureMethod,
  136. $headerParameters['oauth_consumer_secret'],
  137. $headerParameters['oauth_token_secret'],
  138. $httpMethod,
  139. $requestUrl
  140. );
  141. $authorizationHeader = $this->_httpUtility->toAuthorizationHeader($headerParameters);
  142. // toAuthorizationHeader adds an optional realm="" which is not required for now.
  143. // http://tools.ietf.org/html/rfc2617#section-1.2
  144. return str_replace('realm="",', '', $authorizationHeader);
  145. }
  146. /**
  147. * Validate signature based on the signature method used.
  148. *
  149. * @param array $params
  150. * @param string $consumerSecret
  151. * @param string $httpMethod
  152. * @param string $requestUrl
  153. * @param string $tokenSecret
  154. * @return void
  155. * @throws Exception|OauthInputException
  156. */
  157. protected function _validateSignature($params, $consumerSecret, $httpMethod, $requestUrl, $tokenSecret = null)
  158. {
  159. if (!in_array($params['oauth_signature_method'], self::getSupportedSignatureMethods())) {
  160. throw new OauthInputException(
  161. new Phrase(
  162. 'Signature method %1 is not supported',
  163. [$params['oauth_signature_method']]
  164. )
  165. );
  166. }
  167. $allowedSignParams = $params;
  168. unset($allowedSignParams['oauth_signature']);
  169. $calculatedSign = $this->_httpUtility->sign(
  170. $allowedSignParams,
  171. $params['oauth_signature_method'],
  172. $consumerSecret,
  173. $tokenSecret,
  174. $httpMethod,
  175. $requestUrl
  176. );
  177. if (!Security::compareStrings($calculatedSign, $params['oauth_signature'])) {
  178. throw new Exception(new Phrase('The signatire is invalid. Verify and try again.'));
  179. }
  180. }
  181. /**
  182. * Validate oauth version.
  183. *
  184. * @param string $version
  185. * @return void
  186. * @throws OauthInputException
  187. */
  188. protected function _validateVersionParam($version)
  189. {
  190. // validate version if specified
  191. if ('1.0' != $version) {
  192. throw new OauthInputException(new Phrase('The "%1" Oauth version isn\'t supported.', [$version]));
  193. }
  194. }
  195. /**
  196. * Validate request and header parameters.
  197. *
  198. * @param array $protocolParams
  199. * @param array $requiredParams
  200. * @return void
  201. * @throws OauthInputException
  202. */
  203. protected function _validateProtocolParams($protocolParams, $requiredParams = [])
  204. {
  205. // validate version if specified.
  206. if (isset($protocolParams['oauth_version'])) {
  207. $this->_validateVersionParam($protocolParams['oauth_version']);
  208. }
  209. // Required parameters validation. Default to minimum required params if not provided.
  210. if (empty($requiredParams)) {
  211. $requiredParams = [
  212. "oauth_consumer_key",
  213. "oauth_signature",
  214. "oauth_signature_method",
  215. "oauth_nonce",
  216. "oauth_timestamp",
  217. ];
  218. }
  219. $this->_checkRequiredParams($protocolParams, $requiredParams);
  220. if (isset(
  221. $protocolParams['oauth_token']
  222. ) && !$this->_tokenProvider->validateOauthToken(
  223. $protocolParams['oauth_token']
  224. )
  225. ) {
  226. throw new OauthInputException(new Phrase('The token length is invalid. Check the length and try again.'));
  227. }
  228. // Validate signature method.
  229. if (!in_array($protocolParams['oauth_signature_method'], self::getSupportedSignatureMethods())) {
  230. throw new OauthInputException(
  231. new Phrase(
  232. 'Signature method %1 is not supported',
  233. [$protocolParams['oauth_signature_method']]
  234. )
  235. );
  236. }
  237. $consumer = $this->_tokenProvider->getConsumerByKey($protocolParams['oauth_consumer_key']);
  238. $this->_nonceGenerator->validateNonce(
  239. $consumer,
  240. $protocolParams['oauth_nonce'],
  241. $protocolParams['oauth_timestamp']
  242. );
  243. }
  244. /**
  245. * Check if mandatory OAuth parameters are present.
  246. *
  247. * @param array $protocolParams
  248. * @param array $requiredParams
  249. * @return void
  250. * @throws OauthInputException
  251. */
  252. protected function _checkRequiredParams($protocolParams, $requiredParams)
  253. {
  254. $exception = new OauthInputException();
  255. foreach ($requiredParams as $param) {
  256. if (!isset($protocolParams[$param])) {
  257. $exception->addError(
  258. new Phrase('"%fieldName" is required. Enter and try again.', ['fieldName' => $param])
  259. );
  260. }
  261. }
  262. if ($exception->wasErrorAdded()) {
  263. throw $exception;
  264. }
  265. }
  266. }