Файл: monst/Xsolla/Guzzle/Plugin/Oauth/OauthPlugin.php
Строк: 414
<?php
namespace GuzzlePluginOauth;
use GuzzleCommonEvent;
use GuzzleCommonCollection;
use GuzzleHttpMessageRequestInterface;
use GuzzleHttpMessageEntityEnclosingRequestInterface;
use GuzzleHttpQueryString;
use GuzzleHttpUrl;
use SymfonyComponentEventDispatcherEventSubscriberInterface;
/**
* OAuth signing plugin
* @link http://oauth.net/core/1.0/#rfc.section.9.1.1
*/
class OauthPlugin implements EventSubscriberInterface
{
/**
* Consumer request method constants. See http://oauth.net/core/1.0/#consumer_req_param
*/
const REQUEST_METHOD_HEADER = 'header';
const REQUEST_METHOD_QUERY = 'query';
/** @var Collection Configuration settings */
protected $config;
/**
* Create a new OAuth 1.0 plugin
*
* @param array $config Configuration array containing these parameters:
* - string 'request_method' Consumer request method. Use the class constants.
* - string 'callback' OAuth callback
* - string 'consumer_key' Consumer key
* - string 'consumer_secret' Consumer secret
* - string 'token' Token
* - string 'token_secret' Token secret
* - string 'verifier' OAuth verifier.
* - string 'version' OAuth version. Defaults to 1.0
* - string 'signature_method' Custom signature method
* - bool 'disable_post_params' Set to true to prevent POST parameters from being signed
* - array|Closure 'signature_callback' Custom signature callback that accepts a string to sign and a signing key
*/
public function __construct($config)
{
$this->config = Collection::fromConfig($config, array(
'version' => '1.0',
'request_method' => self::REQUEST_METHOD_HEADER,
'consumer_key' => 'anonymous',
'consumer_secret' => 'anonymous',
'signature_method' => 'HMAC-SHA1',
'signature_callback' => function($stringToSign, $key) {
return hash_hmac('sha1', $stringToSign, $key, true);
}
), array(
'signature_method', 'signature_callback', 'version',
'consumer_key', 'consumer_secret'
));
}
public static function getSubscribedEvents()
{
return array(
'request.before_send' => array('onRequestBeforeSend', -1000)
);
}
/**
* Request before-send event handler
*
* @param Event $event Event received
* @return array
* @throws InvalidArgumentException
*/
public function onRequestBeforeSend(Event $event)
{
$timestamp = $this->getTimestamp($event);
$request = $event['request'];
$nonce = $this->generateNonce($request);
$authorizationParams = $this->getOauthParams($timestamp, $nonce);
$authorizationParams['oauth_signature'] = $this->getSignature($request, $timestamp, $nonce);
switch ($this->config['request_method']) {
case self::REQUEST_METHOD_HEADER:
$request->setHeader(
'Authorization',
$this->buildAuthorizationHeader($authorizationParams)
);
break;
case self::REQUEST_METHOD_QUERY:
foreach ($authorizationParams as $key => $value) {
$request->getQuery()->set($key, $value);
}
break;
default:
throw new InvalidArgumentException(sprintf(
'Invalid consumer method "%s"',
$this->config['request_method']
));
}
return $authorizationParams;
}
/**
* Builds the Authorization header for a request
*
* @param array $authorizationParams Associative array of authorization parameters
*
* @return string
*/
private function buildAuthorizationHeader($authorizationParams)
{
$authorizationString = 'OAuth ';
foreach ($authorizationParams as $key => $val) {
if ($val) {
$authorizationString .= $key . '="' . urlencode($val) . '", ';
}
}
return substr($authorizationString, 0, -2);
}
/**
* Calculate signature for request
*
* @param RequestInterface $request Request to generate a signature for
* @param integer $timestamp Timestamp to use for nonce
* @param string $nonce
*
* @return string
*/
public function getSignature(RequestInterface $request, $timestamp, $nonce)
{
$string = $this->getStringToSign($request, $timestamp, $nonce);
$key = urlencode($this->config['consumer_secret']) . '&' . urlencode($this->config['token_secret']);
return base64_encode(call_user_func($this->config['signature_callback'], $string, $key));
}
/**
* Calculate string to sign
*
* @param RequestInterface $request Request to generate a signature for
* @param int $timestamp Timestamp to use for nonce
* @param string $nonce
*
* @return string
*/
public function getStringToSign(RequestInterface $request, $timestamp, $nonce)
{
$params = $this->getParamsToSign($request, $timestamp, $nonce);
// Convert booleans to strings.
$params = $this->prepareParameters($params);
// Build signing string from combined params
$parameterString = clone $request->getQuery();
$parameterString->replace($params);
$url = Url::factory($request->getUrl())->setQuery('')->setFragment(null);
return strtoupper($request->getMethod()) . '&'
. rawurlencode($url) . '&'
. rawurlencode((string) $parameterString);
}
/**
* Get the oauth parameters as named by the oauth spec
*
* @param $timestamp
* @param $nonce
* @return Collection
*/
protected function getOauthParams($timestamp, $nonce)
{
$params = new Collection(array(
'oauth_consumer_key' => $this->config['consumer_key'],
'oauth_nonce' => $nonce,
'oauth_signature_method' => $this->config['signature_method'],
'oauth_timestamp' => $timestamp,
));
// Optional parameters should not be set if they have not been set in the config as
// the parameter may be considered invalid by the Oauth service.
$optionalParams = array(
'callback' => 'oauth_callback',
'token' => 'oauth_token',
'verifier' => 'oauth_verifier',
'version' => 'oauth_version'
);
foreach ($optionalParams as $optionName => $oauthName) {
if (isset($this->config[$optionName]) == true) {
$params[$oauthName] = $this->config[$optionName];
}
}
return $params;
}
/**
* Get all of the parameters required to sign a request including:
* * The oauth params
* * The request GET params
* * The params passed in the POST body (with a content-type of application/x-www-form-urlencoded)
*
* @param RequestInterface $request Request to generate a signature for
* @param integer $timestamp Timestamp to use for nonce
* @param string $nonce
*
* @return array
*/
public function getParamsToSign(RequestInterface $request, $timestamp, $nonce)
{
$params = $this->getOauthParams($timestamp, $nonce);
// Add query string parameters
$params->merge($request->getQuery());
// Add POST fields to signing string if required
if ($this->shouldPostFieldsBeSigned($request))
{
$params->merge($request->getPostFields());
}
// Sort params
$params = $params->toArray();
uksort($params, 'strcmp');
return $params;
}
/**
* Decide whether the post fields should be added to the base string that Oauth signs.
* This implementation is correct. Non-conformant APIs may require that this method be
* overwritten e.g. the Flickr API incorrectly adds the post fields when the Content-Type
* is 'application/x-www-form-urlencoded'
*
* @param $request
* @return bool Whether the post fields should be signed or not
*/
public function shouldPostFieldsBeSigned($request)
{
if (!$this->config->get('disable_post_params') &&
$request instanceof EntityEnclosingRequestInterface &&
false !== strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded'))
{
return true;
}
return false;
}
/**
* Returns a Nonce Based on the unique id and URL. This will allow for multiple requests in parallel with the same
* exact timestamp to use separate nonce's.
*
* @param RequestInterface $request Request to generate a nonce for
*
* @return string
*/
public function generateNonce(RequestInterface $request)
{
return sha1(uniqid('', true) . $request->getUrl());
}
/**
* Gets timestamp from event or create new timestamp
*
* @param Event $event Event containing contextual information
*
* @return int
*/
public function getTimestamp(Event $event)
{
return $event['timestamp'] ?: time();
}
/**
* Convert booleans to strings, removed unset parameters, and sorts the array
*
* @param array $data Data array
*
* @return array
*/
protected function prepareParameters($data)
{
ksort($data);
foreach ($data as $key => &$value) {
switch (gettype($value)) {
case 'NULL':
unset($data[$key]);
break;
case 'array':
$data[$key] = self::prepareParameters($value);
break;
case 'boolean':
$data[$key] = $value ? 'true' : 'false';
break;
}
}
return $data;
}
}