diff --git CHANGELOG CHANGELOG index 152251f..f87eb57 100644 --- CHANGELOG +++ CHANGELOG @@ -3,6 +3,8 @@ OAuth 6.x-3.0-beta4, 2011-XX-XX ------------------------------ by voxpelli: Better coding style +#1002482 by voxpelli: Large database refactoring focused on basing relations on internal id numbers +by voxpelli: Removed consumer UI as it became complicated with new DB and there's no apparent need for it OAuth 6.x-3.0-beta3, 2010-11-05 ------------------------------ diff --git includes/DrupalOAuthClient.inc includes/DrupalOAuthClient.inc index b8b64a4..6d9f3fd 100644 --- includes/DrupalOAuthClient.inc +++ includes/DrupalOAuthClient.inc @@ -134,10 +134,8 @@ class DrupalOAuthClient { $this->version = OAUTH_1; } - $this->requestToken = new DrupalOAuthToken($params['oauth_token'], $params['oauth_token_secret'], array( - 'type' => 'request', - 'consumer_key' => $this->consumer->key, - 'provider_token' => FALSE, + $this->requestToken = new DrupalOAuthToken($params['oauth_token'], $params['oauth_token_secret'], $this->consumer, array( + 'type' => OAUTH_COMMON_TOKEN_TYPE_REQUEST, 'version' => $this->version, )); @@ -255,13 +253,11 @@ class DrupalOAuthClient { } // Check if we've has recieved this token previously and if so use the old one - $this->accessToken = DrupalOAuthToken::load($params['oauth_token'], FALSE); + $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'], array( - 'type' => 'access', - 'consumer_key' => $this->consumer->key, - 'provider_token' => FALSE, + $this->accessToken = new DrupalOAuthToken($params['oauth_token'], $params['oauth_token_secret'], $this->consumer, array( + 'type' => OAUTH_COMMON_TOKEN_TYPE_ACCESS, )); } diff --git includes/DrupalOAuthConsumer.inc includes/DrupalOAuthConsumer.inc index 6954a65..6c3b56c 100644 --- includes/DrupalOAuthConsumer.inc +++ includes/DrupalOAuthConsumer.inc @@ -2,56 +2,105 @@ // $Id$ class DrupalOAuthConsumer extends OAuthConsumer { + public $csid = 0; + public $uid = 0; - public $provider_consumer = TRUE; 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, $callback_url='oob', $provider_consumer=TRUE, $params=array()) { - parent::__construct($key, $secret, $callback_url); - $this->provider_consumer = $provider_consumer; - foreach($params as $key => $value) { - if (isset($this->$key)) { - $this->$key = $value; + 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 * - * @param bool $update - * Optional. Set to TRUE if the consumer exists and should be updated. Defaults to FALSE. * @return void */ - public function write($update=FALSE) { - $primary = $update ? array('consumer_key', 'provider_consumer') : array(); + public function write() { + $update = !empty($this->csid); + $primary = $update ? array('csid') : array(); - if (!$update) { - $this->created = time(); - } - $this->changed = time(); + if ($this->provider_consumer) { + $this->changed = time(); - $values = get_object_vars($this); - $values['consumer_key'] = $this->key; + $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 (empty($values['configuration'])) { - $values['configuration'] = array(); + if ($update) { + $values['csid'] = $this->csid; + } + else { + $this->created = 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['configuration'] = serialize($values['configuration']); - // Stop the created time from being modified + $values = array( + 'key_hash' => sha1($this->key), + 'consumer_key' => $this->key, + 'secret' => $this->secret, + 'configuration' => serialize(empty($this->configuration) ? array() : $this->configuration), + ); + if ($update) { - unset($values['created']); + $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')); + } } /** @@ -60,30 +109,28 @@ class DrupalOAuthConsumer extends OAuthConsumer { * @return void */ public function delete() { - self::deleteConsumer($this->key, $this->provider_consumer); + self::deleteConsumer($this->csid); } /** - * Deletes the consumer with the key from the database. + * Deletes the consumer with the id from the database. * - * @param string $key - * The consumer key. - * @param bool $provider_consumer - * Optional. Whether the consumer we're about to delete is a provider or - * consumer consumer. Defaults to TRUE. + * @param string $csid + * The consumer id. * @return void */ - public static function deleteConsumer($key, $provider_consumer=TRUE) { - db_query("DELETE FROM {oauth_common_consumer} - WHERE consumer_key='%s' - AND provider_consumer=%d", array( - ':consumer_key' => $key, - ':provider_consumer' => $provider_consumer, + public static function deleteConsumer($csid) { + db_query("DELETE c, pc, t, pt FROM {oauth_common_consumer} c + LEFT JOIN {oauth_common_provider_consumer} pc ON pc.csid = c.csid + LEFT JOIN {oauth_common_token} t ON t.csid = c.csid + LEFT JOIN {oauth_common_provider_token} pt ON pt.tid = t.tid + WHERE c.csid = %d", array( + ':csid' => $csid, )); } /** - * Gets the consumer with the specified key + * Deprecated - Gets the consumer with the specified key * * @param string $key * The key of the consumer to get @@ -93,11 +140,55 @@ class DrupalOAuthConsumer extends OAuthConsumer { * @return DrupalOAuthConsumer * The loaded consumer object or FALSE if load failed */ - public static function load($key, $provider_consumer=TRUE) { - return self::fromResult(db_query("SELECT * FROM {oauth_common_consumer} - WHERE consumer_key='%s' AND provider_consumer='%d'", array( - ':consumer_key' => $key, - ':provider_consumer' => $provider_consumer, + 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) { + $fields = 'c.csid, c.consumer_key, c.secret, c.configuration'; + $join = ''; + + if ($load_provider_data) { + $fields .= ', pc.created, pc.changed, pc.uid, pc.name, pc.context, pc.callback_url'; + $join = 'LEFT JOIN {oauth_common_provider_consumer} pc ON pc.csid = c.csid'; + } + + return self::fromResult(db_query("SELECT " . $fields . " FROM {oauth_common_consumer} c " . $join . " WHERE c.csid = %d", array( + ':csid' => $csid, + ))); + } + + /** + * 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) { + // Only INNER supported - LEFT is only for backwards compatability with deprecated DrupalOAuthConsumer::load() from 6.x-3.0-beta3 + $join = $provider ? 'INNER' : 'LEFT'; + // For backwards compatibility with deprecated DrupalOAuthConsumer::load() from 6.x-3.0-beta3 + $where = $provider ? '' : ' AND pc.csid IS NULL'; + // For backwards compatibility with deprecated DrupalOAuthConsumer::load() from 6.x-3.0-beta3 + $fields = $provider ? 'pc.*, c.secret, c.configuration' : 'c.csid, c.consumer_key, c.secret, c.configuration, pc.created, pc.changed, pc.uid, pc.name, pc.context, pc.callback_url'; + + $query = "SELECT " . $fields . " FROM {oauth_common_consumer} c " . $join . " JOIN {oauth_common_provider_consumer} pc ON pc.csid = c.csid WHERE c.key_hash = '%s'" . $where; + return self::fromResult(db_query($query, array( + ':key_hash' => sha1($key), ))); } @@ -115,7 +206,7 @@ class DrupalOAuthConsumer extends OAuthConsumer { $data['configuration'] = unserialize($data['configuration']); } $data['in_database'] = TRUE; - return new DrupalOAuthConsumer($data['consumer_key'], $data['secret'], $data['callback_url'], $data['provider_consumer'], $data); + return new DrupalOAuthConsumer($data['consumer_key'], $data['secret'], $data); } return NULL; } diff --git includes/DrupalOAuthDataStore.inc includes/DrupalOAuthDataStore.inc index 0bcb352..fb7bffb 100644 --- includes/DrupalOAuthDataStore.inc +++ includes/DrupalOAuthDataStore.inc @@ -24,7 +24,7 @@ class DrupalOAuthDataStore extends OAuthDataStore { * An exception is thrown when the consumer cannot be found */ public function lookup_consumer($consumer_key, $provider_consumer = TRUE) { - $consumer = DrupalOAuthConsumer::load($consumer_key, $provider_consumer); + $consumer = DrupalOAuthConsumer::loadProviderByKey($consumer_key); if (!$consumer) { throw new OAuthException('Consumer not found'); } @@ -40,24 +40,18 @@ class DrupalOAuthDataStore extends OAuthDataStore { * The type of the token: 'request' or 'access'. * @param string $token * The token key. - * @param bool $provider_token - * Whether the token should be a provider token * @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, $provider_token = TRUE) { - $token = DrupalOAuthToken::load($token, $provider_token); - if ($token) { - if ($token->type == $token_type - && $token->consumer_key == $consumer->key - && $token->provider_token == $provider_token) { - return $token; - } - throw new OAuthException('No matching token was found, token type or consumer key mismatch'); + 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'); } - throw new OAuthException('Token not found'); + return $token; } /** @@ -79,17 +73,17 @@ class DrupalOAuthDataStore extends OAuthDataStore { public function lookup_nonce($consumer, $token, $nonce, $timestamp) { $stored_nonce = db_result(db_query( "SELECT nonce FROM {oauth_common_nonce} - WHERE nonce='%s' AND timestamp <= %d and token = '%s'", array( + WHERE nonce = '%s' AND timestamp <= %d and token_key = '%s'", array( ':nonce' => $nonce, ':timestamp' => $timestamp, - ':token' => $token->key, + ':token_key' => $token ? $token->key : '', ))); if (!$stored_nonce) { $values = array( 'nonce' => $nonce, 'timestamp' => $timestamp, - 'token' => $token->key, + 'token_key' => $token ? $token->key : '', ); drupal_write_record('oauth_common_nonce', $values); return NULL; @@ -107,12 +101,10 @@ class DrupalOAuthDataStore extends OAuthDataStore { * The request token */ function new_request_token($consumer, $callback = NULL) { - $token = new DrupalOAuthToken(user_password(32), user_password(32), array( - 'consumer_key' => $consumer->key, - 'type' => 'request', - 'uid' => 0, - 'provider_token' => TRUE, - 'expires' => time() + variable_get('oauth_common_request_token_lifetime', 7200), + $token = new DrupalOAuthToken(user_password(32), user_password(32), $consumer, array( + 'type' => OAUTH_COMMON_TOKEN_TYPE_REQUEST, + 'uid' => 0, + 'expires' => time() + variable_get('oauth_common_request_token_lifetime', 7200), )); $token->write(); return $token; @@ -130,13 +122,11 @@ class DrupalOAuthDataStore extends OAuthDataStore { module_load_include('inc', 'oauth_common'); if ($token_old && $token_old->authorized) { - $token_new = new DrupalOAuthToken(user_password(32), user_password(32), array( - 'type' => 'access', - 'uid' => $token_old->uid, - 'consumer_key' => $consumer->key, - 'provider_token' => TRUE, - 'services' => $token_old->services, - 'authorized' => 1, + $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(); diff --git includes/DrupalOAuthToken.inc includes/DrupalOAuthToken.inc index 96b9faf..8369cc0 100644 --- includes/DrupalOAuthToken.inc +++ includes/DrupalOAuthToken.inc @@ -2,52 +2,107 @@ // $Id$ class DrupalOAuthToken extends OAuthToken { - public $type = ''; - public $provider_token = TRUE; + public $tid = 0; + public $csid = 0; + public $expires = 0; + public $type = OAUTH_COMMON_TOKEN_TYPE_REQUEST; public $uid = 0; + public $created = 0; public $changed = 0; - public $consumer_key = ''; public $services = array(); public $authorized = 0; - public $expires = 0; + public $in_database = FALSE; - public function __construct($key, $secret, $params = array()) { - parent::__construct($key, $secret); - foreach($params as $key => $value) { - if (isset($this->$key)) { - $this->$key = $value; + 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 * - * @param bool $update - * Optional. Set to TRUE if the token exists and should be updated. Defaults to FALSE. * @return void */ - $primary = $update ? array('token_key', 'provider_token') : array(); - public function write($update = FALSE) { + public function write() { + $update = !empty($this->tid); - if (!$update) { - $this->created = time(); + $primary = $update ? array('tid') : array(); + + if ($this->consumer->provider_consumer) { + $this->changed = 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 = time(); + $values['created'] = $this->created; + } + + $ready = drupal_write_record('oauth_common_provider_token', $values, $primary); + + if (!$ready) { + throw new OAuthException("Couldn't save token"); + } } - $this->changed = time(); - $values = get_object_vars($this); - $values['token_key'] = $this->key; - $values['services'] = json_encode($values['services']); + $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, + ); - // Stop the created time from being modified if ($update) { - unset($values['created']); + $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')); + } } /** @@ -56,7 +111,7 @@ class DrupalOAuthToken extends OAuthToken { * @return void */ public function delete() { - self::deleteToken($this->key, $this->provider_token); + self::deleteToken($this->key, $this->consumer); } /** @@ -64,31 +119,97 @@ class DrupalOAuthToken extends OAuthToken { * * @param string $key * The key of the token to delete. - * @param bool $provider_token - * Whether the token to delete is a provider token. + * @param object $consumer + * The consumer for which to fetch a token * @return void */ - public static function deleteToken($key, $provider_token=TRUE) { - db_query("DELETE FROM {oauth_common_token} WHERE token_key='%s' AND provider_token=%d", array( - ':key' => $key, - ':provider_token' => $provider_token, + public static function deleteToken($key, $consumer) { + db_query("DELETE t, pt FROM {oauth_common_token} t LEFT JOIN {oauth_common_provider_token} pt ON t.tid = pt.tid + WHERE t.key_hash = '%s' AND t.csid = %d", array( + ':key_hash' => sha1($key), + ':consumer' => $consumer->csid, )); } /** + * 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 bool $provider_token - * Whether the token to load is a provider token. + * @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) { + $fields = 't.*'; + $join = ''; + $where = "t.key_hash = '%s'"; + $values = array( + ':key_hash' => sha1($key), + ); + + // Only add if defined - needed for backwards compatibility with deprecated DrupalOAuthToken::load() from 6.x-3.0-beta3 + if ($type !== FALSE) { + $where .= ' AND t.type = %d'; + $values[':type'] = $type; + } + + if (!$consumer || is_object($consumer) && $consumer->provider_consumer) { + $fields .= ', pt.created, pt.changed, pt.services, pt.authorized'; + $join = 'INNER JOIN {oauth_common_provider_token} pt ON pt.tid = t.tid'; + } + + // Only fetch non-provider tokens - needed for backwards compatibility with deprecated DrupalOAuthToken::load() from 6.x-3.0-beta3 + if ($consumer === TRUE) { + $join = 'LEFT JOIN {oauth_common_provider_token} pt ON pt.tid = t.tid'; + $where .= ' AND pt.tid IS NULL'; + } + else if ($consumer) { + $where .= ' AND t.csid = %d'; + $values[':consumer'] = $consumer->csid; + } + + return self::fromResult(db_query("SELECT " . $fields . " FROM {oauth_common_token} t " . $join . " WHERE " . $where, $values), $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 load($key, $provider_token=TRUE) { - return self::fromResult(db_query("SELECT * FROM {oauth_common_token} WHERE token_key='%s' AND provider_token=%d", array( - ':key' => $key, - ':provider_token' => $provider_token, + public static function loadById($tid, $load_provider_data = TRUE) { + $fields = 't.*'; + $join = ''; + + if ($load_provider_data) { + $fields .= ', pt.created, pt.changed, pt.services, pt.authorized'; + $join = 'INNER JOIN {oauth_common_provider_token} pt ON pt.tid = t.tid'; + } + + return self::fromResult(db_query("SELECT " . $fields . " FROM {oauth_common_token} t " . $join . " WHERE t.tid = %d", array( + ':tid' => $tid, ))); } @@ -100,11 +221,21 @@ class DrupalOAuthToken extends OAuthToken { * @return DrupalOAuthToken * The constructed token object or NULL if no rows could be read or construction failed */ - public static function fromResult($res) { + public static function fromResult($res, $consumer = FALSE) { if ($data = db_fetch_array($res)) { - $data['services'] = json_decode($data['services']); + if (isset($data['services'])) { + $data['services'] = json_decode($data['services']); + } $data['in_database'] = TRUE; - return new DrupalOAuthToken($data['token_key'], $data['secret'], $data); + + if ($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; } diff --git oauth_common.admin.inc oauth_common.admin.inc index 032a2f8..a1a5df4 100644 --- oauth_common.admin.inc +++ oauth_common.admin.inc @@ -167,7 +167,7 @@ function oauth_common_edit_form_context(&$form_state, $context) { $form['name'] = array( '#type' => 'textfield', '#size' => 24, - '#maxlength' => 255, + '#maxlength' => 32, '#default_value' => $context->name, '#title' => t('Context name'), '#description' => t('A unique name used to identify this preset internally. It must be only be alpha characters and underscores. No spaces, numbers or uppercase characters.'), @@ -177,7 +177,7 @@ function oauth_common_edit_form_context(&$form_state, $context) { $form['title'] = array( '#type' => 'textfield', '#size' => 24, - '#maxlength' => 255, + '#maxlength' => 100, '#default_value' => $context->title, '#title' => t('Context title'), '#required' => TRUE, @@ -344,7 +344,7 @@ function oauth_common_edit_form_auth_level($context, $idx, $title, $name = '', $ '#title' => t('Name'), '#description' => t('The name of the authorization level.'), '#size' => 40, - '#maxlength' => 255, + '#maxlength' => 32, '#default_value' => $name, '#oauth_common_panel' => 'left', ), @@ -353,7 +353,7 @@ function oauth_common_edit_form_auth_level($context, $idx, $title, $name = '', $ '#title' => t('Title'), '#description' => t('The title of the authorization level.'), '#size' => 40, - '#maxlength' => 255, + '#maxlength' => 100, '#default_value' => $level['title'], '#oauth_common_panel' => 'left', ), @@ -450,7 +450,6 @@ function oauth_common_edit_form_context_validate(&$form, &$form_state) { else if (preg_match("/[^A-Za-z0-9_\*]/", $level['name'])) { form_error($form["l_{$idx}_name"], t('Authorization level name must be alphanumeric or underscores only.')); } - if (empty($level['title'])) { form_error($form["l_{$idx}_title"], t('Authorization levels must have a title.')); } diff --git oauth_common.authorizations.inc oauth_common.authorizations.inc index 830abb7..71f889f 100644 --- oauth_common.authorizations.inc +++ oauth_common.authorizations.inc @@ -6,82 +6,74 @@ * Functions related to a user's authorization section */ -function oauth_common_page_user_authorizations($account, $provider = TRUE) { - $url = $provider ? - 'user/%d/oauth/authorizations/%s': - 'user/%d/oauth/access/%s'; - $class = $provider ? - 'authorization': - 'access-token'; - +function oauth_common_page_user_authorizations($account) { $header = array( - array('data' => t('Application'), 'class' => "oauth-common-{$class}-application"), - array('data' => t('Key'), 'class' => "oauth-common-{$class}-key"), - array('data' => t('Created'), 'class' => "oauth-common-{$class}-created"), - array('data' => t('Expires'), 'class' => "oauth-common-{$class}-expires"), - array('data' => t('Operations'), 'class' => "oauth-common-{$class}-operations"), + array('data' => t('Application'), 'class' => "oauth-common-authorization-application"), + array('data' => t('Key'), 'class' => "oauth-common-authorization-key"), + array('data' => t('Created'), 'class' => "oauth-common-authorization-created"), + array('data' => t('Expires'), 'class' => "oauth-common-authorization-expires"), + array('data' => t('Operations'), 'class' => "oauth-common-authorization-operations"), ); - $access_tokens = oauth_common_get_user_tokens($account->uid, 'access', $provider); + $access_tokens = oauth_common_get_user_provider_tokens($account->uid); $rows = array(); foreach ($access_tokens as $token) { - if (!isset($consumers[$token->consumer_key])) { - $consumers[$token->consumer_key] = DrupalOAuthConsumer::load($token->consumer_key, $provider); + if (!isset($consumers[$token->csid])) { + $consumers[$token->csid] = DrupalOAuthConsumer::loadById($token->csid); } - $consumer = $consumers[$token->consumer_key]; + $consumer = $consumers[$token->csid]; $data = array( 'application' => array( - 'data' => $consumer->name, - 'class' => "oauth-common-{$class}-application", + 'data' => check_plain($consumer->name), + 'class' => "oauth-common-authorization-application", ), 'key' => array( 'data' => substr($token->key, 0, 6) . '...', - 'class' => "oauth-common-{$class}-key", + 'class' => "oauth-common-authorization-key", ), 'created' => array( 'data' => format_date($token->created), - 'class' => "oauth-common-{$class}-created", + 'class' => "oauth-common-authorization-created", ), ); $operations = array(); $operations[] = array( - 'title' => $provider ? t('Edit') : t('Show'), - 'href' => sprintf($url, $account->uid, $token->key), + 'title' => t('Edit'), + 'href' => sprintf('user/%d/oauth/authorizations/%s', $account->uid, $token->key), 'query' => array('destination' => $_GET['q']), ); $operations[] = array( 'title' => t('Delete'), - 'href' => sprintf($url, $account->uid, $token->key) . '/delete', + 'href' => sprintf('user/%d/oauth/authorizations/%s', $account->uid, $token->key) . '/delete', 'query' => array('destination' => $_GET['q']), ); $data['expires'] = array( 'data' => $token->expires ? format_date($token->expires) : t('Never'), - 'class' => "oauth-common-{$class}-expires", + 'class' => "oauth-common-authorization-expires", ); $rows[] = array( 'data' => $data + array( 'operations' => array( 'data' => theme('links', $operations), - 'class' => "oauth-common-{$class}-operations", + 'class' => "oauth-common-authorization-operations", ), ), ); } - $table = theme('table', $header, $rows, array('id' => "oauth-common-list-{$class}")); + $table = theme('table', $header, $rows, array('id' => "oauth-common-list-authorization")); return $table; } -function oauth_common_authorization_add($consumer, $provider) { - $token = new DrupalOAuthToken(user_password(32), user_password(32), array( - 'provider_token' => $provider, +function oauth_common_authorization_add($consumer) { + $token = new DrupalOAuthToken(user_password(32), user_password(32), $consumer, array( 'uid' => $account->uid, )); return drupal_get_form('oauth_common_form_authorization', $token); @@ -90,7 +82,7 @@ function oauth_common_authorization_add($consumer, $provider) { function oauth_common_form_authorization($form_state, $token) { $form = array(); - $consumer = DrupalOAuthConsumer::load($token->consumer_key, $token->provider_token); + $consumer = $token->consumer; $context = oauth_common_context_load($consumer->context); drupal_set_title(t('Authorization for @app', array('@app' => $consumer->name))); @@ -100,15 +92,11 @@ function oauth_common_form_authorization($form_state, $token) { '#value' => $token, ); - // Consumers doesn't care about the authorized-flag. Either you have a - // access token or you don't. - if ($token->provider_token) { - $form['authorized'] = array( - '#type' => 'checkbox', - '#title' => t('Authorized'), - '#default_value' => $token->authorized, - ); - } + $form['authorized'] = array( + '#type' => 'checkbox', + '#title' => t('Authorized'), + '#default_value' => $token->authorized, + ); $form['created'] = array( '#type' => 'item', @@ -136,9 +124,7 @@ function oauth_common_form_authorization($form_state, $token) { '#value' => substr($token->secret, 0, 6) . '...', ); - $ahah_path = $token->provider_token ? - "user/{$token->uid}/oauth/authorizations/{$token->key}/ahah/secret": - "user/{$token->uid}/oauth/access/{$token->key}/ahah/secret"; + $ahah_path = "user/{$token->uid}/oauth/authorizations/{$token->key}/ahah/secret"; $form['show_secret'] = array( '#type' => 'button', '#value' => t('Show secret'), @@ -158,18 +144,16 @@ function oauth_common_form_authorization($form_state, $token) { ); } - if ($token->provider_token) { - $form['allowed'] = array( - '#type' => 'fieldset', - '#title' => t('Permissions'), - ); - oauth_common_permissions_form($user, $form['allowed'], $consumer, $context, $token->services); + $form['allowed'] = array( + '#type' => 'fieldset', + '#title' => t('Permissions'), + ); + oauth_common_permissions_form($user, $form['allowed'], $consumer, $context, $token->services); - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Save'), - ); - } + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Save'), + ); return $form; } @@ -197,7 +181,7 @@ function oauth_common_permissions_form($account, &$form, $consumer, $context, $d function oauth_common_form_authorization_submit($form, $form_state) { $values = $form_state['values']; $token = $values['token_object']; - $consumer = DrupalOAuthConsumer::load($token->consumer_key, $token->provider_token); + $consumer = $token->consumer; // Collect the authorization levels if (isset($values['levels'])) { @@ -214,20 +198,15 @@ function oauth_common_form_authorization_submit($form, $form_state) { } function oauth_common_form_authorization_delete($form_state, $user, $token) { - $consumer = DrupalOAuthConsumer::load($token->consumer_key, $token->provider_token); - - $name = $token->provider_token ? - t('authorization'): - t('access token'); - $cancel_url = $token->provider_token ? - 'user/%d/oauth/authorizations': - 'user/%d/oauth/access'; + $consumer = $token->consumer; + + $cancel_url = 'user/%d/oauth/authorizations'; + if (!empty($_GET['destination'])) { $cancel_url = $_GET['destination']; } - drupal_set_title(t('Deleting @name for "@consumer"', array( - '@name' => $name, + drupal_set_title(t('Deleting authorization for "@consumer"', array( '@consumer' => $consumer->name, ))); @@ -250,8 +229,7 @@ function oauth_common_form_authorization_delete($form_state, $user, $token) { $form['description'] = array( '#type' => 'item', - '#value' => t('Are you sure that you want to delete the @name for "@consumer"?', array( - '@name' => $name, + '#value' => t('Are you sure that you want to delete the authorization for "@consumer"?', array( '@consumer' => $consumer->name, )), ); @@ -272,7 +250,7 @@ function oauth_common_form_authorization_delete($form_state, $user, $token) { function oauth_common_form_authorization_delete_submit($form, $form_state) { $values = $form_state['values']; $token = $values['token_object']; - $consumer = DrupalOAuthConsumer::load($token->consumer_key, $token->provider_token); + $consumer = $token->consumer; $token->delete(); drupal_set_message(t('The @consumer token @token was deleted.', array( '@consumer' => $consumer->name, diff --git oauth_common.consumers.inc oauth_common.consumers.inc index 5022edc..9132830 100644 --- oauth_common.consumers.inc +++ oauth_common.consumers.inc @@ -6,14 +6,10 @@ * * @param object $account */ -function oauth_common_page_user_consumers($account, $provider = TRUE) { +function oauth_common_page_user_consumers($account) { module_load_include('inc', 'oauth_common'); - $url = $provider ? - 'user/%d/oauth/consumer/%s' : - 'admin/settings/oauth/consumers/%s'; - - $ci = oauth_common_user_consumers($account->uid, $provider); + $ci = oauth_common_user_consumers($account->uid); $header = array( array('data' => t('Name'), 'class' => 'oauth-common-consumer-name'), array('data' => t('Key'), 'class' => 'oauth-common-consumer-key'), @@ -42,11 +38,11 @@ function oauth_common_page_user_consumers($account, $provider = TRUE) { if (oauth_common_can_edit_consumer($consumer)) { $operations[] = array( 'title' => t('Edit'), - 'href' => sprintf($url, $account->uid, $consumer->key), + 'href' => sprintf('user/%d/oauth/consumer/%s', $account->uid, $consumer->csid), ); $operations[] = array( 'title' => t('Delete'), - 'href' => sprintf($url, $account->uid, $consumer->key) . '/delete', + 'href' => sprintf('user/%d/oauth/consumer/%s', $account->uid, $consumer->csid) . '/delete', ); } @@ -70,7 +66,7 @@ function oauth_common_page_user_consumers($account, $provider = TRUE) { * Menu system callback for the add consumer page. */ function oauth_common_add_consumer($account) { - $consumer = new DrupalOAuthConsumer(user_password(32), user_password(32), '', TRUE); + $consumer = new DrupalOAuthConsumer(user_password(32), user_password(32), array('callback_url' => '')); $consumer->uid = $account->uid; return drupal_get_form('oauth_common_form_consumer', $consumer); } @@ -149,7 +145,7 @@ function oauth_common_form_consumer(&$form_state, $consumer) { '#type' => 'button', '#value' => t('Show secret'), '#ahah' => array( - 'path' => "user/{$consumer->uid}/oauth/consumer/{$consumer->key}/ahah/secret", + 'path' => "user/{$consumer->uid}/oauth/consumer/{$consumer->csid}/ahah/secret", 'wrapper' => 'consumer-secret-wrapper', 'method' => 'replace', ), @@ -201,7 +197,7 @@ function oauth_common_form_consumer_submit($form, &$form_state) { // Update or create the consumer. $update = $consumer->in_database; - $consumer->write($update); + $consumer->write(); if ($update) { drupal_set_message(t('Updated the consumer @name', array('@name' => $v['name']))); diff --git oauth_common.inc oauth_common.inc index 579cb12..36865b7 100644 --- oauth_common.inc +++ oauth_common.inc @@ -6,24 +6,14 @@ * * @param int $uid * User ID to retrieve consumer info for. - * @param mixed $provider_consumer - * Optional. Pass TRUE or FALSE to load either provider or consumer consumers. - * Omit or pass NULL to get both. * @return array * An array of consumer info */ -function oauth_common_user_consumers($uid, $provider_consumer = NULL) { - $query = 'SELECT * FROM {oauth_common_consumer} - WHERE uid = %d'; - $args = array( - ':uid' => $uid, - ); - if ($provider_consumer !== NULL) { - $query .= ' - AND provider_consumer = %d'; - $args[':provider_consumer'] = $provider_consumer; - } - $result = db_query($query, $args); +function oauth_common_user_consumers($uid) { + $result = db_query($query = 'SELECT c.secret, c.configuration, pc.* + FROM {oauth_common_consumer} c + INNER JOIN {oauth_common_provider_consumer} pc ON pc.csid = c.csid + WHERE pc.uid = %d' . $where, array(':uid' => $uid)); $consumers = array(); while ($consumer = DrupalOAuthConsumer::fromResult($result)) { @@ -42,8 +32,9 @@ function oauth_common_user_consumers($uid, $provider_consumer = NULL) { * An array of consumer info */ function oauth_common_user_access_tokens($uid) { - $result = db_query("SELECT * FROM {oauth_common_token} WHERE uid = %d AND type = 'access'", array( + $result = db_query("SELECT * FROM {oauth_common_token} WHERE uid = %d AND type = '%d'", array( ':uid' => $uid, + ':type' => OAUTH_COMMON_TOKEN_TYPE_ACCESS, )); $tokens = array(); while ($token = DrupalOAuthToken::fromResult($result)) { @@ -64,7 +55,7 @@ function oauth_common_verify_request() { // Verify $consumer_key = $req->get_parameter('oauth_consumer_key'); if (!empty($consumer_key)) { - $consumer = DrupalOAuthConsumer::load($consumer_key, TRUE); + $consumer = DrupalOAuthConsumer::loadProviderByKey($consumer_key); if ($consumer) { $context = oauth_common_context_load($consumer->context); @@ -82,7 +73,7 @@ function oauth_common_verify_request() { } else { $token_key = $req->get_parameter('oauth_token'); - if (empty($token_key) || !($token=DrupalOAuthToken::load($token_key, TRUE))) { + if (empty($token_key) || !($token = DrupalOAuthToken::loadbyKey($token_key, $consumer))) { $token = NULL; } return array(FALSE, $consumer, $token); diff --git oauth_common.install oauth_common.install index e809307..b2251af 100644 --- oauth_common.install +++ oauth_common.install @@ -72,27 +72,99 @@ function oauth_common_schema() { ), ); - $schema['oauth_common_consumer'] = array( - 'description' => t('Information that allows external applications to access services.'), + $schema['oauth_common_consumer'] = _oauth_common_consumer_schema_1(); + $schema['oauth_common_provider_consumer'] = _oauth_common_provider_consumer_schema_1(); + $schema['oauth_common_token'] = _oauth_common_token_schema_1(); + $schema['oauth_common_provider_token'] = _oauth_common_provider_token_schema_1(); + + $schema['oauth_common_nonce'] = array( + 'description' => t('Stores timestamp against nonce for repeat attacks.'), 'fields' => array( - 'consumer_key' => array( - 'description' => t('Consumer key.'), + 'nonce' => array( + 'description' => t('The random string used on each request.'), 'type' => 'varchar', 'length' => 255, + 'not null' => TRUE + ), + 'timestamp' => array( + 'description' => t('The timestamp of the request.'), + 'type' => 'int', + 'not null' => TRUE + ), + 'token_key' => array( + 'description' => t('Token key.'), + // This is our own internal key - it's 0 or 32 characters long + 'type' => 'varchar', + 'length' => 32, 'not null' => TRUE, ), - 'provider_consumer' => array( - 'description' => t('Whether this is a provider consumer or a consumer consumer'), - 'type' => 'int', - 'size' => 'tiny', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 1, + ), + 'primary key' => array('nonce'), + 'indexes' => array( + 'timekey' => array('timestamp', 'token_key'), + ), + ); + + return $schema; +} + +function _oauth_common_consumer_schema() { + return array( + 'description' => 'Keys and secrets for OAuth consumers, both those provided by this site and other sites.', + 'fields' => array( + 'csid' => array( + 'type' => 'serial', + 'description' => 'Primary ID field for the table. Not used for anything except internal lookups.', + 'not null' => TRUE, + ), + 'key_hash' => array( + 'description' => t('SHA1-hash of consumer_key.'), + 'type' => 'char', + 'length' => 40, + 'not null' => TRUE, + ), + // Key is a reserved word in MySQL so lets avoid that + 'consumer_key' => array( + 'description' => t('Consumer key.'), + 'type' => 'text', + 'not null' => TRUE, ), 'secret' => array( 'description' => t('Consumer secret.'), - 'type' => 'varchar', - 'length' => 255, + 'type' => 'text', + 'not null' => TRUE, + ), + 'configuration' => array( + 'description' => t('Consumer configuration'), + 'type' => 'text', + 'serialized' => TRUE, + 'size' => 'big', + 'not null' => TRUE, + 'object default' => array(), + ), + ), + 'primary key' => array('csid'), + 'indexes' => array( + 'key_hash' => array('key_hash'), + ), + ); +} + +function _oauth_common_provider_consumer_schema() { + return array( + 'description' => 'Additional data for OAuth consumers provided by this site.', + 'fields' => array( + 'csid' => array( + 'description' => 'The {oauth_common_consumer}.csid this data is related to.', + 'type' => 'int', + 'unsigned' => TRUE, + 'default' => 0 + ), + 'consumer_key' => array( + 'description' => t('Consumer key.'), + // This is our own internal key - it's always 32 characters long + 'type' => 'char', + 'length' => 32, 'not null' => TRUE, ), 'created' => array( @@ -132,54 +204,49 @@ function oauth_common_schema() { 'length' => 255, 'not null' => TRUE, ), - 'configuration' => array( - 'description' => t('Consumer configuration'), - 'type' => 'text', - 'serialized' => TRUE, - 'size' => 'big', - 'not null' => TRUE, - 'object default' => array(), - ), ), - 'primary key' => array('consumer_key', 'provider_consumer'), - 'index' => array( - 'user' => array('uid'), + 'primary key' => array('consumer_key'), + 'unique keys' => array( + 'csid' => array('csid'), + ), + 'indexes' => array( + 'uid' => array('uid'), ), ); - $schema['oauth_common_token'] = array( +} + +function _oauth_common_token_schema() { + return array( 'description' => t('Tokens stored on behalf of providers or consumers for request and services accesses.'), 'fields' => array( - 'token_key' => array( - 'description' => t('Token key.'), - 'type' => 'varchar', - 'length' => 255, + 'tid' => array( + 'type' => 'serial', + 'description' => 'Primary ID field for the table. Not used for anything except internal lookups.', 'not null' => TRUE, ), - 'provider_token' => array( - 'description' => t('Whether this is a consumer or a provider token'), + 'csid' => array( + 'description' => 'The {oauth_common_consumer}.csid this token is related to.', 'type' => 'int', - 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, - 'default' => 1, + 'default' => 0 ), - 'secret' => array( - 'description' => t('Token secret.'), - 'type' => 'varchar', - 'length' => 255, + 'key_hash' => array( + 'description' => t('SHA1-hash of token_key.'), + 'type' => 'char', + 'length' => 40, 'not null' => TRUE, ), - 'created' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The time that the token was created, as a Unix timestamp.', + // Key is a reserved word in MySQL so lets avoid that + 'token_key' => array( + 'description' => t('Token key.'), + 'type' => 'text', + 'not null' => TRUE, ), - 'changed' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The last time the token was edited, as a Unix timestamp.', + 'secret' => array( + 'description' => t('Token secret.'), + 'type' => 'text', + 'not null' => TRUE, ), 'expires' => array( 'type' => 'int', @@ -189,9 +256,10 @@ function oauth_common_schema() { ), 'type' => array( 'description' => t('Token type: request or access.'), - 'type' => 'varchar', - 'length' => 7, + 'type' => 'int', + 'size' => 'tiny', 'not null' => TRUE, + 'default' => 1, //OAUTH_COMMON_TOKEN_TYPE_ACCESS ), 'uid' => array( 'description' => t('User ID from {user}.uid.'), @@ -200,12 +268,43 @@ function oauth_common_schema() { 'not null' => TRUE, 'default' => 0, ), - 'consumer_key' => array( - 'description' => t('Consumer key from {oauth_common_consumer}.consumer_key.'), - 'type' => 'varchar', - 'length' => 255, + ), + 'primary key' => array('tid'), + 'indexes' => array( + 'key_hash' => array('key_hash'), + ), + ); +} + +function _oauth_common_provider_token_schema() { + return array( + 'description' => 'Additional data for OAuth tokens provided by this site.', + 'fields' => array( + 'tid' => array( + 'description' => 'The {oauth_common_token}.tid this data is related to.', + 'type' => 'int', + 'unsigned' => TRUE, + 'default' => 0 + ), + 'token_key' => array( + 'description' => t('Token key.'), + // This is our own internal key - it's always 32 characters long + 'type' => 'char', + 'length' => 32, 'not null' => TRUE, ), + 'created' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The time that the token was created, as a Unix timestamp.', + ), + 'changed' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The last time the token was edited, as a Unix timestamp.', + ), 'services' => array( 'description' => t('An array of services that the user allowed the consumer to access.'), 'type' => 'text', @@ -218,41 +317,11 @@ function oauth_common_schema() { 'default' => 0, ), ), - //TODO: The OAuth standard doesn't say that a token has to be unique - especially not across - // different providers and consumers - 'primary key' => array('token_key', 'provider_token'), - 'indexes' => array( - 'token_key_type' => array('token_key', 'provider_token', 'type'), - 'consumer_key' => array('consumer_key', 'provider_token'), - ), - ); - - $schema['oauth_common_nonce'] = array( - 'description' => t('Stores timestamp against nonce for repeat attacks.'), - 'fields' => array( - 'nonce' => array( - 'description' => t('The random 32 characters long string used on each request.'), - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE - ), - 'timestamp' => array( - 'description' => t('The timestamp of the request.'), - 'type' => 'int', - 'not null' => TRUE - ), - 'token' => array( - 'description' => t('Tokens for request and services accesses.'), - 'type' => 'varchar', - 'length' => 32 - ), - ), - 'primary key' => array('nonce'), - 'indexes' => array( - 'timestamp' => array('timestamp'), + 'primary key' => array('token_key'), + 'unique keys' => array( + 'tid' => array('tid'), ), ); - return $schema; } /** @@ -320,3 +389,11 @@ function oauth_common_update_6202() { module_load_include('6202.inc', 'oauth_common', 'updates/update'); return _oauth_common_update_6202(); } + +/** + * This update massively refactors the database. + */ +function oauth_common_update_6300() { + module_load_include('6300.inc', 'oauth_common', 'updates/update'); + return _oauth_common_update_6300(); +} diff --git oauth_common.module oauth_common.module index a7f7c17..c6800a2 100644 --- oauth_common.module +++ oauth_common.module @@ -3,6 +3,9 @@ define('OAUTH_COMMON_CODE_BRANCH', '6.x-3.x'); +define('OAUTH_COMMON_TOKEN_TYPE_REQUEST', 0); +define('OAUTH_COMMON_TOKEN_TYPE_ACCESS', 1); + define('OAUTH_COMMON_VERSION_1', 1); // The original 1.0 spec define('OAUTH_COMMON_VERSION_1_RFC', 2); // The RFC 5849 1.0 spec @@ -167,8 +170,8 @@ function oauth_common_menu() { * * @param string $key */ -function oauth_common_consumer_load($key) { - $consumer = DrupalOAuthConsumer::load($key, TRUE); +function oauth_common_consumer_load($csid) { + $consumer = DrupalOAuthConsumer::loadById($csid, TRUE); if (!$consumer) { $consumer = FALSE; } @@ -180,47 +183,20 @@ function oauth_common_consumer_load($key) { * * @param string $key */ -function oauth_common_provider_token_load($key) { - $consumer = DrupalOAuthToken::load($key, TRUE); - if (!$consumer) { - $consumer = FALSE; - } - return $consumer; -} - -/** - * Menu system wildcard loader for consumer consumers. - * - * @param string $key - */ -function oauth_common_consumer_consumer_load($key) { - $consumer = DrupalOAuthConsumer::load($key, FALSE); - if (!$consumer) { - $consumer = FALSE; - } - return $consumer; -} - -/** - * Menu system wildcard loader for consumer tokens. - * - * @param string $key - */ -function oauth_common_consumer_token_load($key) { - $consumer = DrupalOAuthToken::load($key, FALSE); - if (!$consumer) { - $consumer = FALSE; +function oauth_common_provider_token_load($tid) { + $token = DrupalOAuthToken::loadByID($tid); + if (!$token) { + $token = FALSE; } - return $consumer; + return $token; } /** * Implementation of hook_cron(). */ function oauth_common_cron() { - db_query("DELETE FROM {oauth_common_token} - WHERE expires!=0 - AND expires <= %d", array( + db_query("DELETE t, pt FROM {oauth_common_token} t LEFT JOIN {oauth_common_provider_token} pt ON pt.tid = t.tid + WHERE t.expires != 0 AND t.expires <= %d", array( ':now' => time(), )); @@ -276,18 +252,18 @@ function oauth_common_user($op, &$edit, &$account, $category = NULL) { if ($op == 'delete') { // Delete all tokens and consumers related to a user module_load_include('inc', 'oauth_common'); - db_query('DELETE FROM {oauth_common_consumer} - WHERE uid = %d', array( + db_query("DELETE c, pc, t, pt FROM {oauth_common_consumer} c + INNER JOIN {oauth_common_provider_consumer} pc ON pc.csid = c.csid + LEFT JOIN {oauth_common_token} t ON t.csid = c.csid + LEFT JOIN {oauth_common_provider_token} pt ON pt.tid = t.tid + WHERE pc.uid = %d", array( + ':uid' => $account->uid, + )); + db_query("DELETE t, pt FROM {oauth_common_token} t + LEFT JOIN {oauth_common_provider_token} pt ON pt.tid = t.tid + WHERE uid = %d", array( ':uid' => $account->uid, )); - $consumers = oauth_common_user_consumers($account->uid); - foreach ($consumers as $consumer) { - db_query("DELETE FROM {oauth_common_token} - WHERE uid = %d OR consumer_key = '%s'", array( - ':uid' => $account->uid, - ':consumer_key' => $consumer->key, - )); - } } } @@ -448,51 +424,17 @@ function oauth_common_get_request_token($consumer_token, $request_endpoint = '/o } /** - * Gets stored tokens for a consumer. - * - * @param string $consumer_key - * The key of the consumer to get tokens for. - * @param string $type - * The token type: 'request' or 'access'. - * @param string $provider_tokens - * Optional. Pass TRUE if provider tokens should be return, FALSE if consumer - * tokens should be returned. Defaults to TRUE. - * @return array - * An array of DrupalOAuthToken objects. - */ -function oauth_common_get_tokens($consumer_key, $type = 'access', $provider_tokens = TRUE) { - $res = db_query("SELECT * - FROM {oauth_common_token} - WHERE consumer_key = '%s' - AND type = '%s', - AND provider_token = %d", array( - ':consumer_key' => $consumer_key, - ':type' => $type, - ':provider_token' => $provider_tokens, - )); - $tokens = array(); - while ($token = DrupalOAuthToken::fromResult($res)) { - $tokens[] = $token; - } - return $tokens; -} - -/** * Gets the tokens for a user. * * @param string $uid * @param string $type * @return array */ -function oauth_common_get_user_tokens($uid, $type = 'access', $provider_tokens = TRUE) { - $res = db_query("SELECT * - FROM {oauth_common_token} - WHERE uid = %d - AND type = '%s' - AND provider_token = %d", array( - ':uid' => $uid, - ':type' => $type, - ':provider_token' => $provider_tokens, +function oauth_common_get_user_provider_tokens($uid) { + $res = db_query("SELECT t.*, pt.created, pt.changed, pt.services, pt.authorized FROM {oauth_common_token} t + INNER JOIN {oauth_common_provider_token} pt WHERE t.uid = %d AND t.type = %d", array( + ':uid' => $uid, + ':type' => OAUTH_COMMON_TOKEN_TYPE_ACCESS, )); $tokens = array(); while ($token = DrupalOAuthToken::fromResult($res)) { @@ -550,18 +492,20 @@ function oauth_common_context_from_request($request) { $token_key = $request->get_parameter('oauth_token'); if (empty($consumer_key) && !empty($token_key)) { - $token = DrupalOAuthToken::load($token_key, TRUE); + $token = DrupalOAuthToken::loadByKey($token_key, FALSE, OAUTH_COMMON_TOKEN_TYPE_REQUEST); if ($token) { - $consumer_key = $token->consumer_key; + $consumer = $token->consumer; } } + if (!empty($consumer_key)) { - $consumer = DrupalOAuthConsumer::load($consumer_key, TRUE); + $consumer = DrupalOAuthConsumer::loadProviderByKey($consumer_key); } if ($consumer) { $context = oauth_common_context_load($consumer->context); } + return $context; } diff --git oauth_common.pages.inc oauth_common.pages.inc index fec5913..81feb90 100644 --- oauth_common.pages.inc +++ oauth_common.pages.inc @@ -26,13 +26,20 @@ function _oauth_common_validate_request_callback($type, $unsigned=NULL) { exit; } -function oauth_common_page_authorized() { - // If we have a outh_token we're acting as a consumer and just got authorized +function oauth_common_page_authorized($csid = NULL) { + // If we have an oauth_token we're acting as a consumer and just got authorized if (!empty($_GET['oauth_token'])) { - $request_token = DrupalOAuthToken::load($_GET['oauth_token'], FALSE); - if ($request_token) { - $consumer_token = DrupalOAuthConsumer::load($request_token->consumer_key, FALSE); - $client = new DrupalOAuthClient($consumer_token, $request_token); + $consumer = DrupalOAuthConsumer::loadById($csid, FALSE); + if ($consumer) { + $request_token = DrupalOAuthToken::loadByKey($_GET['oauth_token'], $consumer); + } + else { + // Backwards compatibility with 6.x-3.0-beta3 + $request_token = DrupalOAuthToken::load($_GET['oauth_token'], FALSE); + $consumer = $request_token->consumer; + } + if (!empty($request_token)) { + $client = new DrupalOAuthClient($consumer, $request_token); $verifier = isset($_GET['oauth_verifier']) ? $_GET['oauth_verifier'] : NULL; @@ -43,7 +50,7 @@ function oauth_common_page_authorized() { $access_token->write(); } $request_token->delete(); - module_invoke_all('oauth_common_authorized', $consumer_token, $access_token, $request_token); + module_invoke_all('oauth_common_authorized', $consumer, $access_token, $request_token); } } } @@ -65,7 +72,7 @@ function oauth_common_form_authorize() { $token = $req->get_parameter('oauth_token'); $callback = $req->get_parameter('oauth_callback'); - $token = DrupalOAuthToken::load($token, TRUE); + $token = DrupalOAuthToken::loadByKey($token, FALSE, OAUTH_COMMON_TOKEN_TYPE_REQUEST); // Check that we have a valid token if (!$token) { @@ -73,7 +80,7 @@ function oauth_common_form_authorize() { return; } - $consumer = DrupalOAuthConsumer::load($token->consumer_key, TRUE); + $consumer = $token->consumer; // Redirect to the right form, or present an error. global $user; @@ -119,14 +126,10 @@ function oauth_common_form_authorize() { drupal_set_title(t($title, $tvars)); $form = array(); - $form['oauth_parameters'] = array( - '#type' => 'value', - '#value' => serialize($req->get_parameters()), - ); - $form['oauth_consumer'] = array( - '#type' => 'value', - '#value' => $consumer->key, + $form['token'] = array( + '#type' => 'value', + '#value' => $token, ); $message = !empty($context->authorization_options['message']) ? $context->authorization_options['message'] : @@ -153,7 +156,6 @@ function oauth_common_form_authorize() { $form['authorization'] = array( '#type' => 'fieldset', '#title' => t($authorization_title, $tvars), - '#options' => $auth_levels, ); $form['authorization']['levels'] = array( @@ -214,7 +216,7 @@ function oauth_common_form_authorize_validate($form, &$form_state) { $values = $form_state['values']; $got_permission = FALSE; - $consumer = DrupalOAuthConsumer::load($values['oauth_consumer'], TRUE); + $consumer = $values['token']->consumer; $context = oauth_common_context_load($consumer->context); if (!$context) { @@ -239,16 +241,13 @@ function oauth_common_form_authorize_validate($form, &$form_state) { function oauth_common_form_authorize_submit(&$form, &$form_state) { global $user; $values = $form_state['values']; - // Unserialize the stored oauth parameters - $parameters = unserialize($values['oauth_parameters']); // Save the list of all services that the user allowed the // consumer to do - $token = DrupalOAuthToken::load($parameters['oauth_token'], TRUE); + $token = $values['token']; $token->uid = $user->uid; $token->authorized = 1; - $token->services = array(); - $consumer = DrupalOAuthConsumer::load($token->consumer_key, TRUE); + $consumer = $token->consumer; $context = oauth_common_context_load($consumer->context); if (!$context) { @@ -269,8 +268,6 @@ function oauth_common_form_authorize_submit(&$form, &$form_state) { $token->write(TRUE); - $got_permission = $got_permission || $values['full_access']; // TODO: Full access should be a configurable auth level - if (!empty($consumer->callback_url) && $consumer->callback_url !== 'oob') { // Pick the callback url apart and add the token parameter $callback = parse_url($consumer->callback_url); diff --git oauth_common_consumerui.inc oauth_common_consumerui.inc deleted file mode 100644 index ea903e7..0000000 --- oauth_common_consumerui.inc +++ /dev/null @@ -1,6 +0,0 @@ - 0), FALSE); -} diff --git oauth_common_consumerui.info oauth_common_consumerui.info deleted file mode 100644 index cde3fbc..0000000 --- oauth_common_consumerui.info +++ /dev/null @@ -1,8 +0,0 @@ -name = OAuth Consumer UI -description = Provides a UI for when OAuth is acting as a consumer. -package = "OAuth" - -dependencies[] = oauth_common - -core = 6.x -php = 5.2 diff --git oauth_common_consumerui.module oauth_common_consumerui.module deleted file mode 100644 index 366b58f..0000000 --- oauth_common_consumerui.module +++ /dev/null @@ -1,77 +0,0 @@ - array('administer oauth'), - 'file' => 'oauth_common_consumerui.inc', - ); - - $menu['admin/settings/oauth/consumers'] = array( - 'title' => 'Consumers', - 'description' => 'Consumers that are used to access external services', - 'page callback' => 'oauth_common_consumerui_list_consumers', - 'type' => MENU_LOCAL_TASK, - 'weight' => 10, - ) + $admin_base; - - if ($only_consumer) { - $menu['user/%user/oauth'] = array( - 'title' => 'Access', - 'page callback' => 'oauth_common_page_user_authorizations', - 'page arguments' => array(1, FALSE), - 'access callback' => '_oauth_common_user_access', - 'access arguments' => array(1), - 'file' => 'oauth_common.authorizations.inc', - 'type' => MENU_LOCAL_TASK, - ); - } - - $menu['user/%user/oauth/access'] = array( - 'title' => 'Access tokens', - 'page callback' => 'oauth_common_page_user_authorizations', - 'page arguments' => array(1, FALSE), - 'access callback' => '_oauth_common_user_access', - 'access arguments' => array(1), - 'file' => 'oauth_common.authorizations.inc', - 'type' => $only_consumer ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, - ); - - $menu['user/%user/oauth/access/%oauth_common_consumer_token'] = array( - 'title' => 'Edit access token', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('oauth_common_form_authorization', 4), - 'access callback' => '_oauth_common_user_access', - 'access arguments' => array(1, 'oauth authorize consumers'), - 'file' => 'oauth_common.authorizations.inc', - 'type' => MENU_LOCAL_TASK, - ); - - $menu['user/%user/oauth/access/%oauth_common_consumer_token/ahah/secret'] = array( - 'page callback' => 'oauth_common_ahah_secret', - 'page arguments' => array(4), - 'access callback' => '_oauth_common_user_access', - 'access arguments' => array(1, 'oauth authorize consumers'), - 'file' => 'oauth_common.inc', - 'type' => MENU_CALLBACK, - ); - - $menu['user/%user/oauth/access/%oauth_common_consumer_token/delete'] = array( - 'title' => 'Delete access token', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('oauth_common_form_authorization_delete', 1, 4), - // We always want to allow the user to delete a authorization, that - // shouldn't be a permission that can be rescinded. - 'access callback' => 'user_edit_access', - 'access arguments' => array(1), - 'file' => 'oauth_common.authorizations.inc', - 'type' => MENU_LOCAL_TASK, - ); - - return $menu; -} diff --git updates/update.6300.inc updates/update.6300.inc new file mode 100644 index 0000000..9378166 --- /dev/null +++ updates/update.6300.inc @@ -0,0 +1,79 @@ + array('csid'))); + db_add_field($ret, 'oauth_common_consumer', 'key_hash', $oauth_common_consumer['fields']['key_hash'], array('indexes' => array('key_hash' => array('key_hash')))); + + db_add_field($ret, 'oauth_common_token', 'tid', $oauth_common_token['fields']['tid'], array('primary key' => array('tid'))); + db_add_field($ret, 'oauth_common_token', 'csid', $oauth_common_token['fields']['csid']); + db_add_field($ret, 'oauth_common_token', 'key_hash', $oauth_common_token['fields']['key_hash'], array('indexes' => array('key_hash' => array('key_hash')))); + db_add_field($ret, 'oauth_common_token', 'type_new', $oauth_common_token['fields']['type']); + + db_change_field($ret, 'oauth_common_consumer', 'consumer_key', 'consumer_key', $oauth_common_consumer['fields']['consumer_key']); + db_change_field($ret, 'oauth_common_consumer', 'secret', 'secret', $oauth_common_consumer['fields']['secret']); + + db_change_field($ret, 'oauth_common_token', 'token_key', 'token_key', $oauth_common_token['fields']['token_key']); + db_change_field($ret, 'oauth_common_token', 'secret', 'secret', $oauth_common_token['fields']['secret']); + + db_change_field($ret, 'oauth_common_nonce', 'nonce', 'nonce', array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE + )); + db_change_field($ret, 'oauth_common_nonce', 'token', 'token_key', array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + ), array('indexes' => array( + 'timekey' => array('timestamp', 'token_key'), + ))); + + db_create_table($ret, 'oauth_common_provider_consumer', $oauth_common_provider_consumer); + db_create_table($ret, 'oauth_common_provider_token', $oauth_common_provider_token); + + $ret[] = update_sql('INSERT INTO {oauth_common_provider_consumer} + (csid, consumer_key, created, changed, uid, name, context, callback_url) + SELECT c.csid, c.consumer_key, c.created, c.changed, c.uid, c.name, c.context, c.callback_url + FROM {oauth_common_consumer} c WHERE c.provider_consumer = 1'); + $ret[] = update_sql('INSERT INTO {oauth_common_provider_token} + (tid, token_key, created, changed, services, authorized) + SELECT t.tid, t.token_key, t.created, t.changed, t.services, t.authorized + FROM {oauth_common_token} t WHERE t.provider_token = 1'); + + $ret[] = update_sql("UPDATE {oauth_common_consumer} SET key_hash = SHA1(consumer_key)"); + $ret[] = update_sql("UPDATE {oauth_common_token} SET type_new = (type != 'access'), key_hash = SHA1(token_key)"); + $ret[] = update_sql("UPDATE {oauth_common_token} t, {oauth_common_consumer} c SET t.csid = c.csid WHERE t.consumer_key = c.consumer_key"); + + $to_drop = array('provider_consumer', 'created', 'changed', 'uid', 'name', 'context', 'callback_url'); + foreach ($to_drop as $field) { + db_drop_field($ret, 'oauth_common_consumer', $field); + } + + $to_drop = array('provider_token', 'type', 'consumer_key', 'created', 'changed', 'services', 'authorized'); + foreach ($to_drop as $field) { + db_drop_field($ret, 'oauth_common_token', $field); + } + + db_change_field($ret, 'oauth_common_token', 'type_new', 'type', $oauth_common_token['fields']['type']); + + return $ret; +}