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) { $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 = :uid AND t.type = :type", array( ':uid' => $uid, ':type' => OAUTH_COMMON_TOKEN_TYPE_ACCESS, )); $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; }