first import
This commit is contained in:
397
sites/all/modules/oauth/includes/DrupalOAuthClient.inc
Normal file
397
sites/all/modules/oauth/includes/DrupalOAuthClient.inc
Normal file
@@ -0,0 +1,397 @@
|
||||
<?php
|
||||
|
||||
class DrupalOAuthClient {
|
||||
public $version = OAUTH_COMMON_VERSION_1_RFC;
|
||||
|
||||
protected $consumer;
|
||||
protected $requestToken;
|
||||
protected $accessToken;
|
||||
protected $signatureMethod;
|
||||
|
||||
/**
|
||||
* Creates an instance of the DrupalOAuthClient.
|
||||
*
|
||||
* @param DrupalOAuthConsumer $consumer
|
||||
* The consumer.
|
||||
* @param OAuthToken $request_token
|
||||
* Optional. A request token to use.
|
||||
* @param OAuthSignatureMethod $signature_method
|
||||
* Optional. The signature method to use.
|
||||
* @param integer $version
|
||||
* Optional. The version to use - either OAUTH_COMMON_VERSION_1_RFC or OAUTH_COMMON_VERSION_1.
|
||||
*/
|
||||
public function __construct($consumer, $request_token = NULL, $signature_method = NULL, $version = NULL) {
|
||||
$this->consumer = $consumer;
|
||||
$this->requestToken = $request_token;
|
||||
$this->signatureMethod = $signature_method;
|
||||
|
||||
if ($version) {
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
// Set to the default signature method if no method was specified
|
||||
if (!$this->signatureMethod) {
|
||||
if (!empty($this->consumer->configuration['signature_method'])) {
|
||||
$signature_method = substr(strtolower($this->consumer->configuration['signature_method']), 5);
|
||||
}
|
||||
else {
|
||||
$signature_method = 'SHA1';
|
||||
}
|
||||
$this->signatureMethod = self::signatureMethod($signature_method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to get signing method implementations.
|
||||
*
|
||||
* @param string $method
|
||||
* Optional. The hmac hashing algorithm to use. Defaults to 'sha512' which
|
||||
* has superseded sha1 as the recommended alternative.
|
||||
* @param bool $fallback_to_sha1
|
||||
* Optional. Whether sha1 should be used as a fallback if the selected
|
||||
* hashing algorithm is unavailable.
|
||||
* @return OAuthSignatureMethod
|
||||
* The signature method object.
|
||||
*/
|
||||
public static function signatureMethod($method = 'SHA1', $fallback_to_sha1 = TRUE) {
|
||||
$sign = NULL;
|
||||
|
||||
if (in_array(drupal_strtolower($method), hash_algos())) {
|
||||
$sign = new OAuthSignatureMethod_HMAC($method);
|
||||
}
|
||||
else if ($fallback_to_sha1) {
|
||||
$sign = new OAuthSignatureMethod_HMAC('SHA1');
|
||||
}
|
||||
|
||||
return $sign;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a request token from the provider.
|
||||
*
|
||||
* @param string $endpoint
|
||||
* Optional. The endpoint path for the provider.
|
||||
* - If you provide the full URL (e.g. "http://example.com/oauth/request_token"),
|
||||
* then it will be used.
|
||||
* - If you provide only the path (e.g. "oauth/request_token"), it will
|
||||
* be converted into a full URL by prepending the provider_url.
|
||||
* - If you provide nothing it will default to '/oauth/request_token'.
|
||||
* @param array $options
|
||||
* An associative array of additional optional options, with the following keys:
|
||||
* - 'params'
|
||||
* An associative array of parameters that should be included in the
|
||||
* request.
|
||||
* - 'realm'
|
||||
* A string to be used as the http authentication realm in the request.
|
||||
* - 'get' (default FALSE)
|
||||
* Whether to use GET as the HTTP-method instead of POST.
|
||||
* - 'callback'
|
||||
* A full URL of where the user should be sent after the request token
|
||||
* has been authorized.
|
||||
* Only used by versions higher than OAUTH_COMMON_VERSION_1.
|
||||
* @return DrupalOAuthToken
|
||||
* The returned request token.
|
||||
*/
|
||||
public function getRequestToken($endpoint = NULL, $options = array()) {
|
||||
if ($this->requestToken) {
|
||||
return clone $this->requestToken;
|
||||
}
|
||||
|
||||
$options += array(
|
||||
'params' => array(),
|
||||
'realm' => NULL,
|
||||
'get' => FALSE,
|
||||
'callback' => NULL,
|
||||
);
|
||||
|
||||
if (empty($endpoint)) {
|
||||
if (!empty($this->consumer->configuration['request_endpoint'])) {
|
||||
$endpoint = $this->consumer->configuration['request_endpoint'];
|
||||
}
|
||||
else {
|
||||
$endpoint = '/oauth/request_token';
|
||||
}
|
||||
}
|
||||
if ($this->version > OAUTH_COMMON_VERSION_1) {
|
||||
$options['params']['oauth_callback'] = $options['callback'] ? $options['callback'] : 'oob';
|
||||
}
|
||||
|
||||
$response = $this->get($endpoint, array(
|
||||
'params' => $options['params'],
|
||||
'realm' => $options['realm'],
|
||||
'get' => $options['get'],
|
||||
));
|
||||
|
||||
$params = array();
|
||||
parse_str($response, $params);
|
||||
|
||||
if (empty($params['oauth_token']) || empty($params['oauth_token_secret'])) {
|
||||
throw new Exception('No valid request token was returned');
|
||||
}
|
||||
|
||||
if ($this->version > OAUTH_COMMON_VERSION_1 && empty($params['oauth_callback_confirmed'])) {
|
||||
$this->version = OAUTH_COMMON_VERSION_1;
|
||||
}
|
||||
|
||||
$this->requestToken = new DrupalOAuthToken($params['oauth_token'], $params['oauth_token_secret'], $this->consumer, array(
|
||||
'type' => OAUTH_COMMON_TOKEN_TYPE_REQUEST,
|
||||
'version' => $this->version,
|
||||
));
|
||||
|
||||
return clone $this->requestToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the url that the user should be sent to to authorize the
|
||||
* request token.
|
||||
*
|
||||
* @param string $endpoint
|
||||
* Optional. The endpoint path for the provider.
|
||||
* - If you provide the full URL (e.g. "http://example.com/oauth/authorize"),
|
||||
* then it will be used.
|
||||
* - If you provide only the path (e.g. "oauth/authorize"), it will
|
||||
* be converted into a full URL by prepending the provider_url.
|
||||
* - If you provide nothing it will default to '/oauth/authorize'.
|
||||
* @param array $options
|
||||
* An associative array of additional optional options, with the following keys:
|
||||
* - 'params'
|
||||
* An associative array of parameters that should be included in the
|
||||
* request.
|
||||
* - 'callback'
|
||||
* A full URL of where the user should be sent after the request token
|
||||
* has been authorized.
|
||||
* Only used by version OAUTH_COMMON_VERSION_1.
|
||||
* @return string
|
||||
* The url.
|
||||
*/
|
||||
public function getAuthorizationUrl($endpoint = NULL, $options = array()) {
|
||||
$options += array(
|
||||
'params' => array(),
|
||||
'callback' => NULL,
|
||||
);
|
||||
|
||||
if (empty($endpoint)) {
|
||||
if (!empty($this->consumer->configuration['authorization_endpoint'])) {
|
||||
$endpoint = $this->consumer->configuration['authorization_endpoint'];
|
||||
}
|
||||
else {
|
||||
$endpoint = '/oauth/authorize';
|
||||
}
|
||||
}
|
||||
if ($this->version == OAUTH_COMMON_VERSION_1 && $options['callback']) {
|
||||
$options['params']['oauth_callback'] = $options['callback'];
|
||||
}
|
||||
$options['params']['oauth_token'] = $this->requestToken->key;
|
||||
|
||||
$endpoint = $this->getAbsolutePath($endpoint);
|
||||
$append_query = strpos($endpoint, '?') === FALSE ? '?' : '&';
|
||||
return $endpoint . $append_query . http_build_query($options['params'], NULL, '&');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the access token using the request token.
|
||||
*
|
||||
* @param string $endpoint
|
||||
* Optional. The endpoint path for the provider.
|
||||
* - If you provide the full URL (e.g. "http://example.com/oauth/access_token"),
|
||||
* then it will be used.
|
||||
* - If you provide only the path (e.g. "oauth/access_token"), it will
|
||||
* be converted into a full URL by prepending the provider_url.
|
||||
* - If you provide nothing it will default to '/oauth/access_token'.
|
||||
* @param array $options
|
||||
* An associative array of additional optional options, with the following keys:
|
||||
* - 'params'
|
||||
* An associative array of parameters that should be included in the
|
||||
* request.
|
||||
* - 'realm'
|
||||
* A string to be used as the http authentication realm in the request.
|
||||
* - 'get' (default FALSE)
|
||||
* Whether to use GET as the HTTP-method instead of POST.
|
||||
* - 'verifier'
|
||||
* A string containing a verifier for he user from the provider.
|
||||
* Only used by versions higher than OAUTH_COMMON_VERSION_1.
|
||||
* @return DrupalOAuthToken
|
||||
* The access token.
|
||||
*/
|
||||
public function getAccessToken($endpoint = NULL, $options = array()) {
|
||||
if ($this->accessToken) {
|
||||
return clone $this->accessToken;
|
||||
}
|
||||
|
||||
$options += array(
|
||||
'params' => array(),
|
||||
'realm' => NULL,
|
||||
'get' => FALSE,
|
||||
'verifier' => NULL,
|
||||
);
|
||||
|
||||
if (empty($endpoint)) {
|
||||
if (!empty($this->consumer->configuration['access_endpoint'])) {
|
||||
$endpoint = $this->consumer->configuration['access_endpoint'];
|
||||
}
|
||||
else {
|
||||
$endpoint = '/oauth/access_token';
|
||||
}
|
||||
}
|
||||
if ($this->version > OAUTH_COMMON_VERSION_1 && $options['verifier'] !== NULL) {
|
||||
$options['params']['oauth_verifier'] = $options['verifier'];
|
||||
}
|
||||
|
||||
$response = $this->get($endpoint, array(
|
||||
'token' => TRUE,
|
||||
'params' => $options['params'],
|
||||
'realm' => $options['realm'],
|
||||
'get' => $options['get'],
|
||||
));
|
||||
|
||||
$params = array();
|
||||
parse_str($response, $params);
|
||||
|
||||
if (empty($params['oauth_token']) || empty($params['oauth_token_secret'])) {
|
||||
throw new Exception('No valid access token was returned');
|
||||
}
|
||||
|
||||
// Check if we've has recieved this token previously and if so use the old one
|
||||
//TODO: Is this safe!? What if eg. multiple users are getting the same access token from the provider?
|
||||
$this->accessToken = DrupalOAuthToken::loadByKey($params['oauth_token'], $this->consumer);
|
||||
//TODO: Can a secret change even though the token doesn't? If so it needs to be changed.
|
||||
if (!$this->accessToken) {
|
||||
$this->accessToken = new DrupalOAuthToken($params['oauth_token'], $params['oauth_token_secret'], $this->consumer, array(
|
||||
'type' => OAUTH_COMMON_TOKEN_TYPE_ACCESS,
|
||||
));
|
||||
}
|
||||
|
||||
return clone $this->accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an OAuth request.
|
||||
*
|
||||
* @param string $path
|
||||
* The path being requested.
|
||||
* - If you provide the full URL (e.g. "http://example.com/oauth/request_token"),
|
||||
* then it will be used.
|
||||
* - If you provide only the path (e.g. "oauth/request_token"), it will
|
||||
* be converted into a full URL by prepending the provider_url.
|
||||
* @param array $options
|
||||
* An associative array of additional options, with the following keys:
|
||||
* - 'token' (default FALSE)
|
||||
* Whether a token should be used or not.
|
||||
* - 'params'
|
||||
* An associative array of parameters that should be included in the
|
||||
* request.
|
||||
* - 'realm'
|
||||
* A string to be used as the http authentication realm in the request.
|
||||
* - 'get' (default FALSE)
|
||||
* Whether to use GET as the HTTP-method instead of POST.
|
||||
* @return string
|
||||
* a string containing the response body.
|
||||
*/
|
||||
protected function get($path, $options = array()) {
|
||||
$options += array(
|
||||
'token' => FALSE,
|
||||
'params' => array(),
|
||||
'realm' => NULL,
|
||||
'get' => FALSE,
|
||||
);
|
||||
|
||||
if (empty($options['realm']) && !empty($this->consumer->configuration['authentication_realm'])) {
|
||||
$options['realm'] = $this->consumer->configuration['authentication_realm'];
|
||||
}
|
||||
|
||||
$token = $options['token'] ? $this->requestToken : NULL;
|
||||
$path = $this->getAbsolutePath($path);
|
||||
|
||||
$req = OAuthRequest::from_consumer_and_token($this->consumer, $token,
|
||||
$options['get'] ? 'GET' : 'POST', $path, $options['params']);
|
||||
$req->sign_request($this->signatureMethod, $this->consumer, $token);
|
||||
|
||||
$url = $req->get_normalized_http_url();
|
||||
$params = array();
|
||||
foreach ($req->get_parameters() as $param_key => $param_value) {
|
||||
if (substr($param_key, 0, 5) != 'oauth') {
|
||||
$params[$param_key] = $param_value;
|
||||
}
|
||||
}
|
||||
if (!empty($params)) {
|
||||
$url .= '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
$headers = array(
|
||||
'Accept: application/x-www-form-urlencoded',
|
||||
$req->to_header($options['realm']),
|
||||
);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
if (!$options['get']) {
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, '');
|
||||
}
|
||||
$oauth_version = _oauth_common_version();
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Drupal/' . VERSION . ' OAuth/' . $oauth_version);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($ch, CURLOPT_HEADER, 1);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($error) {
|
||||
throw new Exception($error);
|
||||
}
|
||||
|
||||
$result = $this->interpretResponse($response);
|
||||
if ($result->responseCode != 200) {
|
||||
throw new Exception('Failed to fetch data from url "' . $path . '" (HTTP response code ' . $result->responseCode . ' ' . $result->responseMessage . '): ' . $result->body, $result->responseCode);
|
||||
}
|
||||
|
||||
return $result->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure a path is an absolute path
|
||||
*
|
||||
* Prepends provider url if the path isn't absolute.
|
||||
*
|
||||
* @param string $path
|
||||
* The path to make absolute.
|
||||
* @return string
|
||||
* The absolute path.
|
||||
*/
|
||||
protected function getAbsolutePath($path) {
|
||||
$protocols = array(
|
||||
'http',
|
||||
'https'
|
||||
);
|
||||
$protocol = strpos($path, '://');
|
||||
$protocol = $protocol ? substr($path, 0, $protocol) : '';
|
||||
if (!in_array($protocol, $protocols)) {
|
||||
$path = $this->consumer->configuration['provider_url'] . $path;
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
protected function interpretResponse($res) {
|
||||
list($headers, $body) = preg_split('/\r\n\r\n/', $res, 2);
|
||||
|
||||
$obj = (object) array(
|
||||
'headers' => $headers,
|
||||
'body' => $body,
|
||||
);
|
||||
|
||||
$matches = array();
|
||||
if (preg_match('/HTTP\/1.\d (\d{3}) (.*)/', $headers, $matches)) {
|
||||
$obj->responseCode = trim($matches[1]);
|
||||
$obj->responseMessage = trim($matches[2]);
|
||||
|
||||
// Handle HTTP/1.1 100 Continue
|
||||
if ($obj->responseCode == 100) {
|
||||
return $this->interpretResponse($body);
|
||||
}
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
}
|
228
sites/all/modules/oauth/includes/DrupalOAuthConsumer.inc
Normal file
228
sites/all/modules/oauth/includes/DrupalOAuthConsumer.inc
Normal file
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
class DrupalOAuthConsumer extends OAuthConsumer {
|
||||
public $csid = 0;
|
||||
|
||||
public $uid = 0;
|
||||
public $name = '';
|
||||
public $context = '';
|
||||
public $created = 0;
|
||||
public $changed = 0;
|
||||
public $callback_url = 'oob';
|
||||
public $configuration = array();
|
||||
|
||||
public $provider_consumer = FALSE;
|
||||
public $in_database = FALSE;
|
||||
|
||||
function __construct($key, $secret, $params = array()) {
|
||||
// Backwards compatibility with 6.x-3.0-beta3
|
||||
if (is_string($params)) {
|
||||
$callback_url = $params;
|
||||
if (func_num_args() > 4) {
|
||||
$params = func_get_arg(4);
|
||||
}
|
||||
else {
|
||||
$params = array();
|
||||
}
|
||||
$params['callback_url'] = $callback_url;
|
||||
}
|
||||
|
||||
foreach ($params as $param_key => $value) {
|
||||
if (isset($this->$param_key)) {
|
||||
$this->$param_key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($this->created)) {
|
||||
$this->provider_consumer = TRUE;
|
||||
}
|
||||
|
||||
parent::__construct($key, $secret, $this->callback_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the consumer to the database
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function write() {
|
||||
$update = !empty($this->csid);
|
||||
$primary = $update ? array('csid') : array();
|
||||
|
||||
if ($this->provider_consumer) {
|
||||
$this->changed = REQUEST_TIME;
|
||||
|
||||
$values = array(
|
||||
'consumer_key' => $this->key,
|
||||
'created' => $this->created,
|
||||
'changed' => $this->changed,
|
||||
'uid' => $this->uid,
|
||||
'name' => $this->name,
|
||||
'context' => $this->context,
|
||||
'callback_url' => $this->callback_url,
|
||||
);
|
||||
|
||||
if ($update) {
|
||||
$values['csid'] = $this->csid;
|
||||
}
|
||||
else {
|
||||
$this->created = REQUEST_TIME;
|
||||
$values['created'] = $this->created;
|
||||
}
|
||||
|
||||
$ready = drupal_write_record('oauth_common_provider_consumer', $values, $primary);
|
||||
|
||||
if (!$ready) {
|
||||
throw new OAuthException("Couldn't save consumer");
|
||||
}
|
||||
}
|
||||
|
||||
$values = array(
|
||||
'key_hash' => sha1($this->key),
|
||||
'consumer_key' => $this->key,
|
||||
'secret' => $this->secret,
|
||||
'configuration' => serialize(empty($this->configuration) ? array() : $this->configuration),
|
||||
);
|
||||
|
||||
if ($update) {
|
||||
$values['csid'] = $this->csid;
|
||||
}
|
||||
|
||||
drupal_write_record('oauth_common_consumer', $values, $primary);
|
||||
|
||||
$this->csid = $values['csid'];
|
||||
$this->in_database = TRUE;
|
||||
|
||||
if (!$update) {
|
||||
$values = array(
|
||||
'csid' => $this->csid,
|
||||
'consumer_key' => $this->key,
|
||||
);
|
||||
drupal_write_record('oauth_common_provider_consumer', $values, array('consumer_key'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the consumer from the database
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
self::deleteConsumer($this->csid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the consumer with the id from the database.
|
||||
*
|
||||
* @param string $csid
|
||||
* The consumer id.
|
||||
* @return void
|
||||
*/
|
||||
public static function deleteConsumer($csid) {
|
||||
//TODO: Add compatibility layer?
|
||||
$condition = db_and()->condition('csid', $csid);
|
||||
|
||||
db_delete('oauth_common_provider_token')
|
||||
->condition('tid', db_select('oauth_common_token', 't')->condition($condition)->fields('t', array('tid')), 'IN')
|
||||
->execute();
|
||||
|
||||
foreach (array('oauth_common_token', 'oauth_common_provider_consumer', 'oauth_common_consumer') as $table) {
|
||||
db_delete($table)
|
||||
->condition($condition)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated - Gets the consumer with the specified key
|
||||
*
|
||||
* @param string $key
|
||||
* The key of the consumer to get
|
||||
* @param bool $provider_consumer
|
||||
* Optional. Whether the consumer we're about to load is a provider or
|
||||
* consumer consumer. Defaults to TRUE.
|
||||
* @return DrupalOAuthConsumer
|
||||
* The loaded consumer object or FALSE if load failed
|
||||
*/
|
||||
public static function load($key, $provider_consumer = TRUE) {
|
||||
return DrupalOAuthConsumer::loadProviderByKey($key, $provider_consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a provider consumer with the specified id
|
||||
*
|
||||
* @param int $id
|
||||
* The id of the consumer to get
|
||||
* @param boolean $load_provider_data
|
||||
* Whether to load provider related data or not
|
||||
* @return DrupalOAuthConsumer
|
||||
* The loaded consumer object or FALSE if load failed
|
||||
*/
|
||||
public static function loadById($csid, $load_provider_data = TRUE) {
|
||||
$query = db_select('oauth_common_consumer', 'c');
|
||||
|
||||
$query
|
||||
->condition('c.csid', $csid)
|
||||
->fields('c', array('csid', 'consumer_key', 'secret', 'configuration'));
|
||||
|
||||
if ($load_provider_data) {
|
||||
$query->leftJoin('oauth_common_provider_consumer', 'pc', 'pc.csid = c.csid');
|
||||
$query->fields('pc', array('created', 'changed', 'uid', 'name', 'context', 'callback_url'));
|
||||
}
|
||||
|
||||
return self::fromResult($query->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a provider consumer with the specified key
|
||||
*
|
||||
* @param string $key
|
||||
* The key of the consumer to get
|
||||
* @param boolean $provider
|
||||
* Used internally for backwards compatibility with ::load()
|
||||
* @return DrupalOAuthConsumer
|
||||
* The loaded consumer object or FALSE if load failed
|
||||
*/
|
||||
public static function loadProviderByKey($key, $provider = TRUE) {
|
||||
$query = db_select('oauth_common_consumer', 'c');
|
||||
|
||||
$query
|
||||
->condition('c.key_hash', sha1($key))
|
||||
->fields('c', array('secret', 'configuration'));
|
||||
|
||||
if ($provider) {
|
||||
$query->join('oauth_common_provider_consumer', 'pc', 'pc.csid = c.csid');
|
||||
$query->fields('pc');
|
||||
}
|
||||
else {
|
||||
// For backwards compatibility with deprecated DrupalOAuthConsumer::load() from 6.x-3.0-beta3
|
||||
$query->leftJoin('oauth_common_provider_consumer', 'pc', 'pc.csid = c.csid');
|
||||
$query
|
||||
->fields('c', array('csid', 'consumer_key'))
|
||||
->fields('pc', array('created', 'changed', 'uid', 'name', 'context', 'callback_url'))
|
||||
->isNull('pc.csid');
|
||||
}
|
||||
|
||||
return self::fromResult($query->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a consumer from a db-result resource
|
||||
*
|
||||
* @param resource $res
|
||||
* A database result resource
|
||||
* @return DrupalOAuthConsumer
|
||||
* The constructed consumer object or NULL if no rows could be read or construction failed
|
||||
*/
|
||||
public static function fromResult($res) {
|
||||
//TODO: Ensure this works with old inputs?
|
||||
if ($data = $res->fetchAssoc()) {
|
||||
if (!empty($data['configuration'])) {
|
||||
$data['configuration'] = unserialize($data['configuration']);
|
||||
}
|
||||
$data['in_database'] = TRUE;
|
||||
return new DrupalOAuthConsumer($data['consumer_key'], $data['secret'], $data);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
141
sites/all/modules/oauth/includes/DrupalOAuthDataStore.inc
Normal file
141
sites/all/modules/oauth/includes/DrupalOAuthDataStore.inc
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Database abstraction class
|
||||
*/
|
||||
class DrupalOAuthDataStore extends OAuthDataStore {
|
||||
private $context;
|
||||
|
||||
public function __construct($context) {
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if consumer exists from a given consumer key.
|
||||
*
|
||||
* @param string $consumer_key
|
||||
* The consumer key.
|
||||
* @param bool $provider_consumer
|
||||
* Whether the consumer should be a provider consumer
|
||||
* @return OAuthConsumer
|
||||
* The consumer
|
||||
* @throws OAuthException
|
||||
* An exception is thrown when the consumer cannot be found
|
||||
*/
|
||||
public function lookup_consumer($consumer_key, $provider_consumer = TRUE) {
|
||||
$consumer = DrupalOAuthConsumer::loadProviderByKey($consumer_key);
|
||||
if (!$consumer) {
|
||||
throw new OAuthException('Consumer not found');
|
||||
}
|
||||
return $consumer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the token exists.
|
||||
*
|
||||
* @param OAuthConsumer $consumer
|
||||
* The consumer.
|
||||
* @param string $token_type
|
||||
* The type of the token: 'request' or 'access'.
|
||||
* @param string $token
|
||||
* The token key.
|
||||
* @return DrupalOauthToken
|
||||
* The matching token
|
||||
* @throws OAuthException
|
||||
* An exception is thrown when the token cannot be found or doesn't match
|
||||
*/
|
||||
public function lookup_token($consumer, $token_type, $token) {
|
||||
$type = ($token_type == 'request' ? OAUTH_COMMON_TOKEN_TYPE_REQUEST : OAUTH_COMMON_TOKEN_TYPE_ACCESS);
|
||||
$token = DrupalOAuthToken::loadByKey($token, $consumer, $type);
|
||||
if (!$token) {
|
||||
throw new OAuthException('Token not found');
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the nonce value exists. If not, generate one.
|
||||
*
|
||||
* @param OAuthConsumer $consumer
|
||||
* The service consumer information with both key
|
||||
* and secret values.
|
||||
* @param OAuthToken $token
|
||||
* The current token.
|
||||
* @param string $nonce
|
||||
* A new nonce value, in case a one doesn't current exit.
|
||||
* @param int $timestamp
|
||||
* The current time.
|
||||
* @return string
|
||||
* The existing nonce value or NULL in
|
||||
* case it doesn't exist.
|
||||
*/
|
||||
public function lookup_nonce($consumer, $token, $nonce, $timestamp) {
|
||||
if (strlen($nonce) > 255) {
|
||||
throw new OAuthException('Nonces may not be longer than 255 characters');
|
||||
}
|
||||
|
||||
$stored_nonce = db_query(
|
||||
"SELECT nonce FROM {oauth_common_nonce}
|
||||
WHERE nonce = :nonce AND timestamp <= :timestamp and token_key = :token_key", array(
|
||||
':nonce' => $nonce,
|
||||
':timestamp' => $timestamp,
|
||||
':token_key' => $token ? $token->key : '',
|
||||
));
|
||||
|
||||
if (!$stored_nonce->rowCount()) {
|
||||
$values = array(
|
||||
'nonce' => $nonce,
|
||||
'timestamp' => $timestamp,
|
||||
'token_key' => $token ? $token->key : '',
|
||||
);
|
||||
drupal_write_record('oauth_common_nonce', $values);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return $stored_nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new request token.
|
||||
*
|
||||
* @param OAuthConsumer $consumer
|
||||
* The consumer to generate a token for.
|
||||
* @return DrupalOAuthToken
|
||||
* The request token
|
||||
*/
|
||||
function new_request_token($consumer, $callback = NULL) {
|
||||
$token = new DrupalOAuthToken(user_password(32), user_password(32), $consumer, array(
|
||||
'type' => OAUTH_COMMON_TOKEN_TYPE_REQUEST,
|
||||
'uid' => 0,
|
||||
'expires' => REQUEST_TIME + variable_get('oauth_common_request_token_lifetime', 7200),
|
||||
));
|
||||
$token->write();
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new access token and delete the old request token.
|
||||
*
|
||||
* @param DrupalOAuthToken $token_old
|
||||
* The old request token.
|
||||
* @param OAuthConsumer $consumer
|
||||
* The service consumer information.
|
||||
*/
|
||||
function new_access_token($token_old, $consumer, $verifier = NULL) {
|
||||
module_load_include('inc', 'oauth_common');
|
||||
|
||||
if ($token_old && $token_old->authorized) {
|
||||
$token_new = new DrupalOAuthToken(user_password(32), user_password(32), $consumer, array(
|
||||
'type' => OAUTH_COMMON_TOKEN_TYPE_ACCESS,
|
||||
'uid' => $token_old->uid,
|
||||
'services' => isset($token_old->services) ? $token_old->services : NULL,
|
||||
'authorized' => 1,
|
||||
));
|
||||
$token_old->delete();
|
||||
$token_new->write();
|
||||
return $token_new;
|
||||
}
|
||||
|
||||
throw new OAuthException('Invalid request token');
|
||||
}
|
||||
}
|
56
sites/all/modules/oauth/includes/DrupalOAuthRequest.inc
Normal file
56
sites/all/modules/oauth/includes/DrupalOAuthRequest.inc
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
class DrupalOAuthRequest extends OAuthRequest {
|
||||
/**
|
||||
* Creates a OAuthRequest object from the current request
|
||||
*
|
||||
* @param string $http_method
|
||||
* @param string $http_url
|
||||
* @param array $parameters
|
||||
* @return OAuthRequest
|
||||
* A OAuthRequest generated from the request
|
||||
*/
|
||||
public static function from_request($http_method = NULL, $http_url = NULL, $parameters = NULL) {
|
||||
// Preparations that has to be made if we're going to detect parameters
|
||||
if ($parameters == NULL) {
|
||||
$qs = $_SERVER['QUERY_STRING'];
|
||||
$q = $_GET['q'];
|
||||
|
||||
// Unset $_GET['q'] if it was created by a redirect
|
||||
if (isset($_SERVER['REDIRECT_URL'])) {
|
||||
$q = FALSE;
|
||||
}
|
||||
// Check that the q parameter hasn't been created or altered by drupal
|
||||
elseif (isset($_GET['q'])) {
|
||||
$get = array();
|
||||
parse_str($_SERVER['QUERY_STRING'], $get);
|
||||
// The q parameter was in the original request, make sure it hasn't been altered
|
||||
if (isset($get['q'])) {
|
||||
$q = $get['q'];
|
||||
}
|
||||
// The q parameter was set by drupal, unset it
|
||||
else {
|
||||
$q = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
$parsed = array();
|
||||
parse_str($_SERVER['QUERY_STRING'], $parsed);
|
||||
if ($q === FALSE) {
|
||||
unset($parsed['q']);
|
||||
}
|
||||
else {
|
||||
$parsed['q'] = $q;
|
||||
}
|
||||
$_SERVER['QUERY_STRING'] = http_build_query($parsed, '', '&');
|
||||
}
|
||||
$req = parent::from_request($http_method, $http_url, $parameters);
|
||||
|
||||
// Restore $_SERVER['QUERY_STRING'] if it was touched
|
||||
if (isset($qs)) {
|
||||
$_SERVER['QUERY_STRING'] = $qs;
|
||||
}
|
||||
|
||||
return $req;
|
||||
}
|
||||
}
|
27
sites/all/modules/oauth/includes/DrupalOAuthServer.inc
Normal file
27
sites/all/modules/oauth/includes/DrupalOAuthServer.inc
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
class DrupalOAuthServer extends OAuthServer {
|
||||
public function __construct($context) {
|
||||
parent::__construct(new DrupalOAuthDataStore($context));
|
||||
|
||||
if (isset($this->context->authorization_options['signature_methods'])) {
|
||||
$sig_methods = $this->context->authorization_options['signature_methods'];
|
||||
}
|
||||
else {
|
||||
$sig_methods = array('HMAC-SHA1', 'HMAC-SHA256', 'HMAC-SHA384', 'HMAC-SHA512');
|
||||
}
|
||||
|
||||
foreach ($sig_methods as $signature_method) {
|
||||
if ($signature_method == 'PLAINTEXT') {
|
||||
$this->add_signature_method(new OAuthSignatureMethod_PLAINTEXT());
|
||||
}
|
||||
else {
|
||||
// Check if the system supports the hashing algorithm
|
||||
$algo = explode('-', $signature_method, 2);
|
||||
if ($algo[0] == 'HMAC' && in_array(strtolower($algo[1]), hash_algos())) {
|
||||
$this->add_signature_method(new OAuthSignatureMethod_HMAC($algo[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
244
sites/all/modules/oauth/includes/DrupalOAuthToken.inc
Normal file
244
sites/all/modules/oauth/includes/DrupalOAuthToken.inc
Normal file
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
class DrupalOAuthToken extends OAuthToken {
|
||||
public $tid = 0;
|
||||
public $expires = 0;
|
||||
public $type = OAUTH_COMMON_TOKEN_TYPE_REQUEST;
|
||||
public $uid = 0;
|
||||
|
||||
public $created = 0;
|
||||
public $changed = 0;
|
||||
public $services = array();
|
||||
public $authorized = 0;
|
||||
|
||||
public $in_database = FALSE;
|
||||
|
||||
public function __construct($key, $secret, $consumer, $params = array()) {
|
||||
foreach ($params as $param_key => $value) {
|
||||
if (isset($this->$param_key)) {
|
||||
$this->$param_key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Backwards compatibility with 6.x-3.0-beta3
|
||||
if (empty($consumer) || is_array($consumer)) {
|
||||
if (is_array($consumer)) {
|
||||
$params = $consumer;
|
||||
}
|
||||
if (!empty($params['csid'])) {
|
||||
$consumer = DrupalOAuthConsumer::loadById($params['csid'], isset($params['services']));
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_object($consumer)) {
|
||||
throw new OAuthException("Needs an associated consumer");
|
||||
}
|
||||
else {
|
||||
$this->consumer = $consumer;
|
||||
}
|
||||
|
||||
parent::__construct($key, $secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the token to the database
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function write() {
|
||||
$update = !empty($this->tid);
|
||||
|
||||
$primary = $update ? array('tid') : array();
|
||||
|
||||
if ($this->consumer->provider_consumer) {
|
||||
$this->changed = REQUEST_TIME;
|
||||
|
||||
$values = array(
|
||||
'token_key' => $this->key,
|
||||
'changed' => $this->changed,
|
||||
'services' => json_encode($this->services),
|
||||
'authorized' => $this->authorized,
|
||||
);
|
||||
|
||||
if ($update) {
|
||||
$values['tid'] = $this->tid;
|
||||
}
|
||||
else {
|
||||
$this->created = REQUEST_TIME;
|
||||
$values['created'] = $this->created;
|
||||
}
|
||||
|
||||
$ready = drupal_write_record('oauth_common_provider_token', $values, $primary);
|
||||
|
||||
if (!$ready) {
|
||||
throw new OAuthException("Couldn't save token");
|
||||
}
|
||||
}
|
||||
|
||||
$values = array(
|
||||
'csid' => $this->consumer->csid,
|
||||
'key_hash' => sha1($this->key),
|
||||
'token_key' => $this->key,
|
||||
'secret' => $this->secret,
|
||||
'expires' => $this->expires,
|
||||
'type' => $this->type,
|
||||
'uid' => $this->uid,
|
||||
);
|
||||
|
||||
if ($update) {
|
||||
$values['tid'] = $this->tid;
|
||||
}
|
||||
|
||||
drupal_write_record('oauth_common_token', $values, $primary);
|
||||
|
||||
$this->tid = $values['tid'];
|
||||
$this->in_database = TRUE;
|
||||
|
||||
if (!$update) {
|
||||
$values = array(
|
||||
'tid' => $this->tid,
|
||||
'token_key' => $this->key,
|
||||
);
|
||||
drupal_write_record('oauth_common_provider_token', $values, array('token_key'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the token from the database
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
self::deleteToken($this->key, $this->consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the token with the key from the database
|
||||
*
|
||||
* @param string $key
|
||||
* The key of the token to delete.
|
||||
* @param object $consumer
|
||||
* The consumer for which to fetch a token
|
||||
* @return void
|
||||
*/
|
||||
public static function deleteToken($key, $consumer) {
|
||||
//TODO: Ensure backwards compatibility
|
||||
$condition = db_and()->condition('key_hash', sha1($key))->condition('csid', $consumer->csid);
|
||||
|
||||
db_delete('oauth_common_provider_token')
|
||||
->condition('tid', db_select('oauth_common_token', 't')->condition($condition)->fields('t', array('tid')), 'IN')
|
||||
->execute();
|
||||
|
||||
db_delete('oauth_common_token')
|
||||
->condition($condition)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated - Gets the token with the specified key
|
||||
*
|
||||
* @param string $key
|
||||
* The key of the token to get
|
||||
* @param bool $provider_token
|
||||
* Whether the token to load is a provider token.
|
||||
* @return DrupalOAuthToken
|
||||
* The loaded token object or FALSE if load failed
|
||||
*/
|
||||
public static function load($key, $provider_token = TRUE) {
|
||||
return DrupalOAuthToken::loadByKey($key, !$provider_token, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token with the specified key
|
||||
*
|
||||
* @param string $key
|
||||
* The key of the token to get
|
||||
* @param boolean|object $consumer
|
||||
* The consumer for which to fetch a token or FALSE to fetch a provider token
|
||||
* @param int $type
|
||||
* Used internally for backwards compatibility with ::load()
|
||||
* @return DrupalOAuthToken
|
||||
* The loaded token object or FALSE if load failed
|
||||
*/
|
||||
public static function loadByKey($key, $consumer = FALSE, $type = OAUTH_COMMON_TOKEN_TYPE_ACCESS) {
|
||||
$query = db_select('oauth_common_token', 't');
|
||||
|
||||
$query
|
||||
->condition('t.key_hash', sha1($key))
|
||||
->fields('t');
|
||||
|
||||
// Only add if defined - needed for backwards compatibility with deprecated DrupalOAuthToken::load() from 6.x-3.0-beta3
|
||||
if ($type !== FALSE) {
|
||||
$query->condition('t.type', $type);
|
||||
}
|
||||
|
||||
if (!$consumer || is_object($consumer) && $consumer->provider_consumer) {
|
||||
$query->join('oauth_common_provider_token', 'pt', 'pt.tid = t.tid');
|
||||
$query->fields('pt', array('created', 'changed', 'services', 'authorized'));
|
||||
}
|
||||
|
||||
// Only fetch non-provider tokens - needed for backwards compatibility with deprecated DrupalOAuthToken::load() from 6.x-3.0-beta3
|
||||
if ($consumer === TRUE) {
|
||||
$query->leftJoin('oauth_common_provider_token', 'pt', 'pt.tid = t.tid');
|
||||
$query->isNull('pt.tid');
|
||||
}
|
||||
else if ($consumer) {
|
||||
$query->condition('t.csid', $consumer->csid);
|
||||
}
|
||||
|
||||
return self::fromResult($query->execute(), $consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token with the specified id
|
||||
*
|
||||
* @param int $id
|
||||
* The id of the token to get
|
||||
* @param boolean $load_provider_data
|
||||
* Whether to load provider related data or not
|
||||
* @return DrupalOAuthToken
|
||||
* The loaded token object or FALSE if load failed
|
||||
*/
|
||||
public static function loadById($tid, $load_provider_data = TRUE) {
|
||||
$query = db_select('oauth_common_token', 't');
|
||||
|
||||
$query
|
||||
->condition('t.tid', $tid)
|
||||
->fields('t');
|
||||
|
||||
if ($load_provider_data) {
|
||||
$query->join('oauth_common_provider_token', 'pt', 'pt.tid = t.tid');
|
||||
$query->fields('pt', array('created', 'changed', 'services', 'authorized'));
|
||||
}
|
||||
|
||||
return self::fromResult($query->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a token from a db-result resource
|
||||
*
|
||||
* @param resource $res
|
||||
* A database result resource
|
||||
* @return DrupalOAuthToken
|
||||
* The constructed token object or NULL if no rows could be read or construction failed
|
||||
*/
|
||||
public static function fromResult($res, $consumer = FALSE) {
|
||||
//TODO: Ensure this works with old inputs?
|
||||
if ($data = $res->fetchAssoc()) {
|
||||
if (isset($data['services'])) {
|
||||
$data['services'] = json_decode($data['services']);
|
||||
}
|
||||
$data['in_database'] = TRUE;
|
||||
|
||||
if (is_object($consumer) && $consumer->csid == $data['csid']) {
|
||||
$token_consumer = $consumer;
|
||||
}
|
||||
else {
|
||||
$token_consumer = DrupalOAuthConsumer::loadById($data['csid'], isset($data['services']));
|
||||
}
|
||||
|
||||
return new DrupalOAuthToken($data['token_key'], $data['secret'], $token_consumer, $data);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A generic signature method implementation that leverages hash_hmac() for
|
||||
* increased flexibility.
|
||||
*/
|
||||
class OAuthSignatureMethod_HMAC extends OAuthSignatureMethod {
|
||||
private $algo = NULL;
|
||||
|
||||
/**
|
||||
* Create a HMAC oauth signature method object using the (or one of the)
|
||||
* specified algorithm implementations.
|
||||
*
|
||||
* @param mixed $algo
|
||||
* Pass the name of a algorithm supported by hash_hmac() or an array of
|
||||
* names when you have several candidate algorithms that may be used. The
|
||||
* first algorithm int the array that is supported on the system will be used.
|
||||
* @throws Exception
|
||||
* A exception is thrown when none of the provided algorithms are supported
|
||||
* by the system.
|
||||
*/
|
||||
public function __construct($algo) {
|
||||
$algos = hash_algos();
|
||||
// Create a single-element array from strings to simplify the logic of
|
||||
// support checking and failure handling.
|
||||
if (is_string($algo)) {
|
||||
$algo = array($algo);
|
||||
}
|
||||
|
||||
// Find a supported algorithm among the candidates
|
||||
foreach ($algo as $a) {
|
||||
if (in_array(strtolower($a), $algos)) {
|
||||
$this->algo = strtolower($a);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Throw a exception if no matching algorithm can be found
|
||||
if (empty($this->algo)) {
|
||||
throw new OAuthException(t('None of the suggested hash algorithms (@cand) were '
|
||||
. 'supported by the server. Try one of the following: !algos.', array(
|
||||
'@cand' => join($algo, ', '),
|
||||
'!algos' => join($algos, ', '),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
public function get_name() {
|
||||
return "HMAC-" . strtoupper($this->algo);
|
||||
}
|
||||
|
||||
public function build_signature($request, $consumer, $token) {
|
||||
$base_string = $request->get_signature_base_string();
|
||||
$request->base_string = $base_string;
|
||||
|
||||
$key_parts = array(
|
||||
$consumer->secret,
|
||||
($token) ? $token->secret : ""
|
||||
);
|
||||
|
||||
$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
|
||||
$key = implode('&', $key_parts);
|
||||
|
||||
return base64_encode(hash_hmac($this->algo, $base_string, $key, TRUE));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user