You are here

oauth_common.module in OAuth 1.0 7.4

Same filename and directory in other branches
  1. 6.3 oauth_common.module
  2. 7.3 oauth_common.module

File

oauth_common.module
View source
<?php

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

//TODO: Don't act as a provider by default.

//TODO: Check for other functions with breaking changes

//TODO: Move admin pages to more regular places

//TODO: Add watchdog messages about deprecated methods?

//TODO: Move provider ui related pages to provider ui

/**
 * Implements hook_init().
 *
 * Adds the OAuth library. This is needed until Libraries API does it.
 * @see http://drupal.org/node/1092270
 */
function oauth_common_init() {
  if (!file_exists(libraries_get_path('oauth') . '/OAuth.php')) {
    module_disable(array(
      'oauth_common',
    ));
  }
  else {
    require_once libraries_get_path('oauth') . '/OAuth.php';
  }
}

/**
 * Implements hook_permission().
 */
function oauth_common_permission() {
  $permissions = array(
    'oauth authorize any consumers' => array(
      'title' => t('Authorize any OAuth consumers'),
      'restrict access' => TRUE,
    ),
    'oauth register any consumers' => array(
      'title' => t('Register any OAuth consumers'),
      'restrict access' => TRUE,
    ),
    'administer oauth' => array(
      'title' => t('Administer OAuth'),
      'restrict access' => TRUE,
    ),
    'administer consumers' => array(
      'title' => t('Administer OAuth consumers'),
      'restrict access' => TRUE,
    ),
  );

  // Add seperate permissions for creating and
  // authorizing consumers in each context.
  foreach (oauth_common_context_list() as $name => $title) {
    $permissions[sprintf('oauth register consumers in %s', $name)] = array(
      'title' => t('Register OAuth consumers in %context', array(
        '%context' => $title,
      )),
    );
    $permissions[sprintf('oauth authorize consumers in %s', $name)] = array(
      'title' => t('Authorize OAuth consumers in %context', array(
        '%context' => $title,
      )),
    );
  }
  return $permissions;
}

/**
 * Implements hook_menu().
 */
function oauth_common_menu() {
  $menu = array();
  $admin_base = array(
    'access arguments' => array(
      'administer oauth',
    ),
    'file' => 'oauth_common.admin.inc',
  );
  $menu['admin/config/services/oauth'] = array(
    'title' => 'OAuth',
    'description' => 'Settings for OAuth',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      '_oauth_common_admin',
    ),
    'type' => MENU_NORMAL_ITEM,
  ) + $admin_base;
  $menu['admin/config/services/oauth/settings'] = array(
    'title' => 'Settings',
    'description' => 'Settings for OAuth',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      '_oauth_common_admin',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => 0,
  ) + $admin_base;

  // OAuth doesn't need different endpoints for the different context as all
  // actions are done with a specific consumer, which in itself is associated
  // with a context.
  $provider_base = array(
    'access callback' => 'oauth_commmon_is_provider',
    'file' => 'oauth_common.pages.inc',
    'type' => MENU_CALLBACK,
  );

  // The endpoint that consumers use to get a request token.
  $menu['oauth/request_token'] = array(
    'page callback' => 'oauth_common_callback_request_token',
  ) + $provider_base;

  // The page a user gets sent to to authorize a request token.
  $menu['oauth/authorize'] = array(
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'oauth_common_form_authorize',
    ),
  ) + $provider_base;

  // The endpoint that consumers use to get a access token.
  $menu['oauth/access_token'] = array(
    'page callback' => 'oauth_common_callback_access_token',
  ) + $provider_base;

  // This page is used both in consumer and provider mode. For consumers it is
  // the callback url and triggers hook_oauth_common_authorized(). For
  // providers it is the page where users end up if no callback url exists.
  $menu['oauth/authorized'] = array(
    'title' => 'Authorization finished',
    'page callback' => 'oauth_common_page_authorized',
    'access arguments' => array(
      'access content',
    ),
    'file' => 'oauth_common.pages.inc',
    'type' => MENU_CALLBACK,
  );

  // TODO: Different structures makes sense depending on whether oauth_common is
  // acting as a provider or as a consumer.
  $menu['oauth/test/valid-consumer'] = array(
    'file' => 'oauth_common.pages.inc',
    'page callback' => '_oauth_common_validate_request_callback',
    'page arguments' => array(
      'consumer',
    ),
    'access callback' => 'oauth_commmon_is_provider',
    'type' => MENU_CALLBACK,
  );
  $menu['oauth/test/valid-access-token'] = array(
    'file' => 'oauth_common.pages.inc',
    'page callback' => '_oauth_common_validate_request_callback',
    'page arguments' => array(
      'access token',
    ),
    'access callback' => 'oauth_commmon_is_provider',
    'type' => MENU_CALLBACK,
  );
  return $menu;
}

/**
 * Menu system wildcard loader for provider consumers.
 *
 * @param string $key
 */
function oauth_common_consumer_load($csid) {
  $consumer = DrupalOAuthConsumer::loadById($csid, TRUE);
  if (!$consumer) {
    $consumer = FALSE;
  }
  return $consumer;
}

/**
 * Menu system wildcard loader for provider tokens.
 *
 * @param string $key
 */
function oauth_common_provider_token_load($tid) {
  $token = DrupalOAuthToken::loadByID($tid);
  if (!$token) {
    $token = FALSE;
  }
  return $token;
}

/**
 * Implements hook_cron().
 */
function oauth_common_cron() {
  $token_condition = db_and()
    ->condition('expires', 0, '<>')
    ->condition('expires', REQUEST_TIME, '<=');
  db_delete('oauth_common_provider_token')
    ->condition('tid', db_select('oauth_common_token', 't')
    ->condition($token_condition)
    ->fields('t', array(
    'tid',
  )), 'IN')
    ->execute();
  db_delete('oauth_common_token')
    ->condition($token_condition)
    ->execute();

  // Should 300 be overriden in DrupalOAuthServer and made configurable?
  db_delete('oauth_common_nonce')
    ->condition('timestamp', REQUEST_TIME - 300, '<')
    ->execute();
}

/**
 * Implements hook_oauth_default_contexts().
 */
function oauth_common_default_oauth_common_context() {
  $contexts = array();
  $context = new stdClass();
  $context->disabled = FALSE;

  /* Edit this to true to make a default context disabled initially */
  $context->name = 'default';
  $context->title = 'Default context';
  $context->authorization_options = array();
  $context->authorization_levels = array(
    '*' => array(
      'title' => 'Full access',
      'description' => 'This will give @appname the same permissions that you normally have and will allow it to access the full range of services that @sitename provides.',
    ),
    'read' => array(
      'title' => 'Read access',
      'description' => 'This will allow @appname to fetch content that you have access to on @sitename.',
    ),
    'update' => array(
      'title' => 'Update access',
      'description' => 'This will allow @appname to update content that you have permissions to edit.',
    ),
    'create' => array(
      'title' => 'Create access',
      'description' => 'This will allow @appname to create new content on @sitename.',
    ),
    'delete' => array(
      'title' => 'Delete access',
      'description' => 'This will allow @appname to delete content from @sitename.',
    ),
  );
  $contexts[$context->name] = $context;
  return $contexts;
}

/**
 * Implements hook_user_delete().
 */
function oauth_common_user_delete($account) {

  // Delete all tokens and consumers related to a user
  module_load_include('inc', 'oauth_common');
  $consumer_condition = db_select('oauth_common_provider_consumer', 'c')
    ->condition('uid', $account->uid)
    ->fields('c', array(
    'csid',
  ));
  $token_condition = db_or()
    ->condition('uid', $account->uid)
    ->condition('csid', $consumer_condition, 'IN');
  db_delete('oauth_common_provider_token')
    ->condition('tid', db_select('oauth_common_token', 't')
    ->condition($token_condition)
    ->fields('t', array(
    'tid',
  )), 'IN')
    ->execute();
  db_delete('oauth_common_token')
    ->condition($token_condition)
    ->execute();
  db_delete('oauth_common_consumer')
    ->condition('csid', $consumer_condition, 'IN')
    ->execute();
  db_delete('oauth_common_provider_consumer')
    ->condition('uid', $account->uid)
    ->execute();
}

/**
 * Implements hook_xrds().
 */
function services_oauth_xrds() {
  $xrds = array();
  $xrds['oauth'] = array(
    'services' => array(
      array(
        'data' => array(
          'Type' => array(
            'http://oauth.net/discovery/1.0',
          ),
          'URI' => array(
            '#main',
          ),
        ),
      ),
      array(
        'data' => array(
          'Type' => array(
            'http://oauth.net/core/1.0/endpoint/request',
            'http://oauth.net/core/1.0/parameters/auth-header',
            'http://oauth.net/core/1.0/parameters/uri-query',
            'http://oauth.net/core/1.0/signature/HMAC-SHA1',
          ),
          'URI' => array(
            url('oauth/request_token', array(
              'absolute' => TRUE,
            )),
          ),
        ),
      ),
      array(
        'data' => array(
          'Type' => array(
            'http://oauth.net/core/1.0/endpoint/authorize',
            'http://oauth.net/core/1.0/parameters/uri-query',
          ),
          'URI' => array(
            url('oauth/authorize', array(
              'absolute' => TRUE,
            )),
          ),
        ),
      ),
      array(
        'data' => array(
          'Type' => array(
            'http://oauth.net/core/1.0/endpoint/access',
            'http://oauth.net/core/1.0/parameters/auth-header',
            'http://oauth.net/core/1.0/parameters/uri-query',
            'http://oauth.net/core/1.0/signature/HMAC-SHA1',
          ),
          'URI' => array(
            url('oauth/access_token', array(
              'absolute' => TRUE,
            )),
          ),
        ),
      ),
    ),
  );
  return $xrds;
}

/**
 * Access callback function used by several menu items.
 *
 * @param stdClass $user
 *  A user object.
 * @param string $permission
 *  The permission that is needed in addition to edit access on the $user.
 */
function _oauth_common_user_access($user, $permission = NULL) {
  return user_edit_access($user) && (empty($permission) || user_access($permission));
}

/**
 * Checks if the user has permission to edit the consumer. Edit access is
 * granted if the user has the 'administer consumers' permission or may
 * edit the account connected to the consumer.
 *
 * @param DrupalOAuthConsumer $consumer
 * @return bool
 */
function oauth_common_can_edit_consumer($consumer) {
  $may_edit = user_access('administer consumers');

  // If the user doesn't have consumer admin privileges, check for account
  // edit access.
  if (!$may_edit && $consumer->uid) {
    $user = user_load($consumer->uid);
    $may_edit = user_edit_access($user);
  }
  return $may_edit;
}

/**
 * Deterines if a user has the necessary permissions to create consumers.
 *
 * @param object $account
 *  The user account to check permissions for. Defaults to the currently
 *  logged in user.
 * @return bool
 */
function oauth_common_can_create_consumers($account = NULL) {
  global $user;
  if (!$account) {
    $account = $user;
  }
  $can_register_consumers = user_access('oauth register any consumers', $account);
  if (!$can_register_consumers) {
    foreach (oauth_common_context_list() as $context => $title) {
      $can_register_consumers = $can_register_consumers || user_access(sprintf('oauth register consumers in %s', $context), $account);
    }
  }
  return $can_register_consumers;
}

/**
 * This function is used as a access callback
 * when the authentication of the request shouldn't be
 * done by the menu system.
 *
 * @return bool
 *  Always returns TRUE
 */
function _oauth_common_always_true() {
  return TRUE;
}

/**
 * Access callback that checks if a user may create authorizations in the
 * consumers context.
 *
 * @param DrupalOAuthConsumer $consumer
 * @return bool
 */
function oauth_common_can_authorize_consumer($consumer) {
  return user_access(sprintf('oauth authorize consumers in %s', $consumer->context));
}

/**
 * Check if oauth_common is acting as a provider.
 */
function oauth_commmon_is_provider() {
  return variable_get('oauth_common_enable_provider', TRUE);
}

/**
 * Gets a request token from a oauth provider and returns the authorization
 * url. The request token is saved in the database.
 *
 * @param OAuthToken $consumer_token
 *  The consumer token to use
 * @param string $request_endpoint
 *  Optional. Pass a custom endpoint if needed. Defaults to '/oauth/request_token'.
 * @param string $authorize_endpoint
 *  Optional. Pass a custom endpoint if needed. Defaults to '/oauth/authorize'.
 * @return string
 *  The url that the client should be redirected to to authorize
 *  the request token.
 */
function oauth_common_get_request_token($consumer_token, $request_endpoint = '/oauth/request_token', $authorize_endpoint = '/oauth/authorize') {
  $client = new DrupalOAuthClient($consumer_token);
  $request_token = $client
    ->getRequestToken($request_endpoint);
  $request_token
    ->write();
  return $client
    ->getAuthorizationUrl($authorize_endpoint);
}

/**
 * Gets the tokens for a user.
 *
 * @param string $uid
 * @param string $type
 * @return array
 */
function oauth_common_get_user_provider_tokens($uid) {
  $q = db_select('oauth_common_token', 't')
    ->condition('t.uid', $uid, '=')
    ->condition('t.type', OAUTH_COMMON_TOKEN_TYPE_ACCESS, '=');
  $q
    ->join('oauth_common_provider_token', 'pt', 'pt.tid = t.tid');
  $res = $q
    ->fields('t')
    ->fields('pt', array(
    'created',
    'changed',
    'services',
    'authorized',
  ))
    ->execute();
  $tokens = array();
  while ($token = DrupalOAuthToken::fromResult($res)) {
    $tokens[] = $token;
  }
  return $tokens;
}

/**
 * Create a new context with defaults appropriately set from schema.
 *
 * @return stdClass
 *  An context initialized with the default values.
 */
function oauth_common_context_new() {
  if (!module_exists('ctools')) {
    return FALSE;
  }
  ctools_include('export');
  return ctools_export_new_object('oauth_common_context');
}

/**
 * Load a single context.
 *
 * @param string $name
 *  The name of the context.
 * @return stdClass
 *  The context configuration.
 */
function oauth_common_context_load($name) {
  if (!module_exists('ctools')) {
    return FALSE;
  }
  ctools_include('export');
  $result = ctools_export_load_object('oauth_common_context', 'names', array(
    $name,
  ));
  if (isset($result[$name])) {
    return $result[$name];
  }
  else {
    return FALSE;
  }
}

/**
 * Loads the context for a request.
 *
 * @param OAuthRequest $request
 * @return object
 *  The context configuration.
 */
function oauth_common_context_from_request($request) {
  $context = NULL;
  $consumer_key = $request
    ->get_parameter('oauth_consumer_key');
  $token_key = $request
    ->get_parameter('oauth_token');
  if (empty($consumer_key) && !empty($token_key)) {
    $token = DrupalOAuthToken::loadByKey($token_key, FALSE, OAUTH_COMMON_TOKEN_TYPE_REQUEST);
    if ($token) {
      $consumer = $token->consumer;
    }
  }
  if (!empty($consumer_key)) {
    $consumer = DrupalOAuthConsumer::loadProviderByKey($consumer_key);
  }
  if (!empty($consumer)) {
    $context = oauth_common_context_load($consumer->context);
  }
  return $context;
}

/**
 * Load all contexts.
 *
 * @return array
 *  Array of context objects keyed by context names.
 */
function oauth_common_context_load_all() {
  if (!module_exists('ctools')) {
    return FALSE;
  }
  ctools_include('export');
  return ctools_export_load_object('oauth_common_context');
}

/**
 * Saves an context in the database.
 *
 * @return void
 */
function oauth_common_context_save($context) {
  $update = isset($context->cid) ? array(
    'cid',
  ) : array();
  drupal_write_record('oauth_common_context', $context, $update);
}

/**
 * Remove an context.
 *
 * @return void
 */
function oauth_common_context_delete($context) {
  db_delete('oauth_common_context')
    ->condition('name', $context->name)
    ->condition('cid', $context->cid)
    ->execute();
}

/**
 * Export an context.
 *
 * @return string
 */
function oauth_common_context_export($context, $indent = '') {
  if (!module_exists('ctools')) {
    return FALSE;
  }
  ctools_include('export');
  $output = ctools_export_object('oauth_common_context', $context, $indent);
  return $output;
}

/**
 * Lists all available contexts.
 *
 * @return array
 */
function oauth_common_context_list() {
  $return = array();
  $contexts = oauth_common_context_load_all();
  if ($contexts) {
    foreach ($contexts as $context) {
      $return[$context->name] = $context->title;
    }
  }
  return $return;
}

/**
 * Finds the current version of the OAuth module, used in eg. user agents
 *
 * @return string
 */
function _oauth_common_version() {
  static $version;
  if (!isset($version)) {
    $info = db_query("SELECT info FROM {system} WHERE name = 'oauth_common'")
      ->fetchField();
    $info = $info ? unserialize($info) : FALSE;
    if (!$info || empty($info['version'])) {
      $version = OAUTH_COMMON_CODE_BRANCH;
    }
    else {
      $version = $info['version'];
    }
  }
  return $version;
}

Functions

Namesort descending Description
oauth_commmon_is_provider Check if oauth_common is acting as a provider.
oauth_common_can_authorize_consumer Access callback that checks if a user may create authorizations in the consumers context.
oauth_common_can_create_consumers Deterines if a user has the necessary permissions to create consumers.
oauth_common_can_edit_consumer Checks if the user has permission to edit the consumer. Edit access is granted if the user has the 'administer consumers' permission or may edit the account connected to the consumer.
oauth_common_consumer_load Menu system wildcard loader for provider consumers.
oauth_common_context_delete Remove an context.
oauth_common_context_export Export an context.
oauth_common_context_from_request Loads the context for a request.
oauth_common_context_list Lists all available contexts.
oauth_common_context_load Load a single context.
oauth_common_context_load_all Load all contexts.
oauth_common_context_new Create a new context with defaults appropriately set from schema.
oauth_common_context_save Saves an context in the database.
oauth_common_cron Implements hook_cron().
oauth_common_default_oauth_common_context Implements hook_oauth_default_contexts().
oauth_common_get_request_token Gets a request token from a oauth provider and returns the authorization url. The request token is saved in the database.
oauth_common_get_user_provider_tokens Gets the tokens for a user.
oauth_common_init Implements hook_init().
oauth_common_menu Implements hook_menu().
oauth_common_permission Implements hook_permission().
oauth_common_provider_token_load Menu system wildcard loader for provider tokens.
oauth_common_user_delete Implements hook_user_delete().
services_oauth_xrds Implements hook_xrds().
_oauth_common_always_true This function is used as a access callback when the authentication of the request shouldn't be done by the menu system.
_oauth_common_user_access Access callback function used by several menu items.
_oauth_common_version Finds the current version of the OAuth module, used in eg. user agents

Constants