oauth_common.pages.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. <?php
  2. /**
  3. * Combined menu callback for tests of consumers and access tokens
  4. */
  5. function _oauth_common_validate_request_callback($type, $unsigned = NULL) {
  6. try {
  7. module_load_include('inc', 'oauth_common');
  8. list($signed, $consumer, $token) = oauth_common_verify_request();
  9. if ($consumer == NULL) {
  10. throw new OAuthException('Missing consumer token');
  11. }
  12. if (!$signed && $unsigned != 'unsigned') {
  13. throw new OAuthException("The request wasn't signed");
  14. }
  15. if ($token == NULL && $type == 'access token') {
  16. throw new OAuthException('Missing access token');
  17. }
  18. }
  19. catch (OAuthException $e) {
  20. drupal_add_http_header('Status', '401 Unauthorized: ' . $e->getMessage());
  21. drupal_add_http_header('WWW-Authenticate', sprintf('OAuth realm="%s"', url('', array('absolute' => TRUE))));
  22. }
  23. exit;
  24. }
  25. /**
  26. * Menu callback for when something has been authorized - used in both client and provider flow
  27. *
  28. * @param $csid Should contain the id of the consumer when used in the client flow
  29. */
  30. function oauth_common_page_authorized($csid = NULL) {
  31. // If we have an oauth_token we're acting as a consumer and just got authorized
  32. if (!empty($_GET['oauth_token'])) {
  33. //TODO: Add documentation on how to use the callback url with
  34. $consumer = $csid ? DrupalOAuthConsumer::loadById($csid, FALSE) : FALSE;
  35. if ($consumer) {
  36. $request_token = DrupalOAuthToken::loadByKey($_GET['oauth_token'], $consumer, OAUTH_COMMON_TOKEN_TYPE_REQUEST);
  37. }
  38. else {
  39. // Backwards compatibility with 6.x-3.0-beta3
  40. $request_token = DrupalOAuthToken::load($_GET['oauth_token'], FALSE);
  41. $consumer = $request_token ? $request_token->consumer : FALSE;
  42. }
  43. if (!empty($request_token)) {
  44. $client = new DrupalOAuthClient($consumer, $request_token);
  45. $verifier = isset($_GET['oauth_verifier']) ? $_GET['oauth_verifier'] : NULL;
  46. $access_token = $client->getAccessToken(NULL, array('verifier' => $verifier));
  47. if ($access_token) {
  48. // We recieved a new token - save it
  49. if (!$access_token->in_database) {
  50. $access_token->write();
  51. }
  52. $request_token->delete();
  53. module_invoke_all('oauth_common_authorized', $consumer, $access_token, $request_token);
  54. }
  55. }
  56. }
  57. return t('The application has been authorized');
  58. }
  59. /**
  60. * Form for granting access to the consumer
  61. */
  62. function oauth_common_form_authorize() {
  63. module_load_include('inc', 'oauth_common');
  64. $req = DrupalOAuthRequest::from_request();
  65. $context = oauth_common_context_from_request($req);
  66. $auth_ops = $context->authorization_options;
  67. if (!$context) {
  68. drupal_set_message(t("Can't find OAuth context, check the site's settings."), 'error');
  69. return;
  70. }
  71. $token = $req->get_parameter('oauth_token');
  72. $callback = $req->get_parameter('oauth_callback');
  73. $token = DrupalOAuthToken::loadByKey($token, FALSE, OAUTH_COMMON_TOKEN_TYPE_REQUEST);
  74. // Check that we have a valid token
  75. if (!$token) {
  76. drupal_set_message(t('Please include a valid OAuth token in your request.'), 'error');
  77. return;
  78. }
  79. $consumer = $token->consumer;
  80. // Redirect to the right form, or present an error.
  81. global $user;
  82. if ($user->uid) {
  83. // There's some strange bug in the ?destination=... handling
  84. // This is not exactly beautiful, but it gets the work done
  85. // TODO: Find out why!
  86. if (drupal_substr($_SERVER['REQUEST_URI'], 0, 2) == '//') {
  87. header('Location: ' . drupal_substr($_SERVER['REQUEST_URI'], 1), TRUE, 302);
  88. }
  89. if (!(user_access('oauth authorize any consumers') || user_access('oauth authorize consumers in ' . $consumer->context))) {
  90. drupal_set_message(t('You are not authorized to allow external services access to this system.'), 'error');
  91. return drupal_access_denied();
  92. }
  93. if (!empty($auth_ops['automatic_authorization'])
  94. && $auth_ops['automatic_authorization']
  95. && !empty($consumer->callback_url)) {
  96. // Authorize the request token
  97. $token->uid = $user->uid;
  98. $token->authorized = 1;
  99. $token->services = $context->authorization_options['default_authorization_levels'];
  100. $token->write(TRUE);
  101. // Pick the callback url apart and add the token parameter
  102. $callback = parse_url($consumer->callback_url);
  103. $query = array();
  104. parse_str($callback['query'], $query);
  105. $query['oauth_token'] = $token->key;
  106. $callback['query'] = http_build_query($query, 'idx_', '&');
  107. // Return to the consumer site
  108. header('Location: ' . _oauth_common_glue_url($callback), TRUE, 302);
  109. exit;
  110. }
  111. $tvars = array(
  112. '@user' => $user->name,
  113. '@appname' => $consumer->name,
  114. '@sitename' => variable_get('site_name', ''),
  115. );
  116. $title = !empty($context->title) ? $context->title : 'Authorize @appname';
  117. drupal_set_title(t($title, $tvars), PASS_THROUGH);
  118. $form = array();
  119. $form['token'] = array(
  120. '#type' => 'value',
  121. '#value' => $token,
  122. );
  123. $message = !empty($auth_ops['message']) ? $auth_ops['message'] :
  124. 'The application @appname wants to access @sitename on your behalf, check the permissions that you would like the application to have.';
  125. $form['message'] = array(
  126. '#type' => 'item',
  127. '#markup' => t($message, $tvars),
  128. );
  129. $message = !empty($auth_ops['warning']) ? $auth_ops['warning'] :
  130. 'If you don\'t know what @appname is, or don\'t want to give it access to your content, just click here and we\'ll take you away from this page without granting @appname any access to @sitename.';
  131. $form['warning'] = array(
  132. '#type' => 'item',
  133. '#markup' => l(t($message, $tvars), 'oauth/authorization/deny/' . $token->key),
  134. '#attributes' => array(
  135. 'class' => array('abort-authorization'),
  136. ),
  137. );
  138. $disable_selection = !empty($auth_ops['disable_auth_level_selection'])
  139. && !empty($auth_ops['default_authorization_levels'])
  140. && $auth_ops['disable_auth_level_selection'];
  141. if (!$disable_selection) {
  142. $authorization_title = !empty($auth_ops['authorization_title']) ? $auth_ops['authorization_title'] :
  143. 'Permissions';
  144. $form['authorization'] = array(
  145. '#type' => 'fieldset',
  146. '#title' => t($authorization_title, $tvars),
  147. );
  148. $form['authorization']['levels'] = array(
  149. '#tree' => TRUE,
  150. );
  151. foreach ($context->authorization_levels as $name => $level) {
  152. $auth_level_opt = array(
  153. '#type' => 'checkbox',
  154. '#title' => t($level['title'], $tvars),
  155. '#description' => t($level['description'], $tvars),
  156. '#value' => $level['default'],
  157. );
  158. $form['authorization']['levels'][$name] = $auth_level_opt;
  159. }
  160. }
  161. else {
  162. $form['authorization']['levels'] = array(
  163. '#tree' => TRUE,
  164. );
  165. foreach ($auth_ops['default_authorization_levels'] as $level) {
  166. $form['authorization']['levels'][$level] = array(
  167. '#type' => 'value',
  168. '#value' => $level,
  169. );
  170. }
  171. }
  172. $deny_title = !empty($auth_ops['deny_access_title']) ? $auth_ops['deny_access_title'] :
  173. 'Deny access';
  174. $form['deny'] = array(
  175. '#type' => 'item',
  176. '#markup' => l(t($deny_title), 'oauth/authorization/deny/' . $token->key),
  177. '#attributes' => array(
  178. 'class' => array('deny-access'),
  179. ),
  180. );
  181. $grant_title = !empty($auth_ops['grant_access_title']) ? $auth_ops['grant_access_title'] :
  182. 'Grant access';
  183. $form['actions'] = array('#type' => 'actions');
  184. $form['actions']['confirm'] = array(
  185. '#type' => 'submit',
  186. '#value' => t($grant_title),
  187. );
  188. return $form;
  189. }
  190. else {
  191. $query = $_GET;
  192. unset($query['q']); // why are there so few q's?
  193. drupal_goto('user/login', array('query' => array(
  194. 'destination' => url('oauth/authorize', array(
  195. 'query' => $query,
  196. )),
  197. )));
  198. }
  199. }
  200. /**
  201. * Validation of the form for granting access to the consumer
  202. */
  203. function oauth_common_form_authorize_validate($form, &$form_state) {
  204. $values = $form_state['values'];
  205. $got_permission = FALSE;
  206. $consumer = $values['token']->consumer;
  207. $context = oauth_common_context_load($consumer->context);
  208. if (!$context) {
  209. form_set_error('confirm', t("Can't find OAuth context."));
  210. return;
  211. }
  212. if (!$context->authorization_options['disable_auth_level_selection']) {
  213. foreach ($context->authorization_levels as $name => $level) {
  214. if ($values['levels'][$name]) {
  215. $got_permission = TRUE;
  216. break;
  217. }
  218. }
  219. if (!$got_permission) {
  220. form_set_error('confirm', t("You haven't given the application access to anything. Click on 'Deny access' or just close this window if you don't want to authorize it."));
  221. }
  222. }
  223. }
  224. /**
  225. * Form submit handler that grants access to the consumer
  226. */
  227. function oauth_common_form_authorize_submit(&$form, &$form_state) {
  228. global $user;
  229. $values = $form_state['values'];
  230. // Save the list of all services that the user allowed the
  231. // consumer to do
  232. $token = $values['token'];
  233. $token->uid = $user->uid;
  234. $token->authorized = 1;
  235. $consumer = $token->consumer;
  236. $context = oauth_common_context_load($consumer->context);
  237. if (!$context) {
  238. drupal_set_message(t("Can't find OAuth context, check the site's settings."), 'error');
  239. return;
  240. }
  241. // Add services
  242. if (!empty($values['full_access'])) { // TODO: Full access should be a configurable auth level
  243. $token->services = array('*');
  244. }
  245. elseif (!empty($values['levels'])) {
  246. $token->services = array_keys(array_filter($values['levels']));
  247. }
  248. else {
  249. $token->services = array();
  250. }
  251. $token->write(TRUE);
  252. if (!empty($consumer->callback_url) && $consumer->callback_url !== 'oob') {
  253. // Pick the callback url apart and add the token parameter
  254. $callback = parse_url($consumer->callback_url);
  255. $query = array();
  256. if (!empty($callback['query'])) {
  257. parse_str($callback['query'], $query);
  258. }
  259. $query['oauth_token'] = $token->key;
  260. $callback['query'] = http_build_query($query, 'idx_', '&');
  261. // Return to the consumer site
  262. header('Location: ' . _oauth_common_glue_url($callback), TRUE, 302);
  263. exit;
  264. }
  265. else {
  266. drupal_goto('oauth/authorized');
  267. }
  268. }
  269. /**
  270. * Constructs the url to which to return someone who has asked for access to a consumer
  271. */
  272. function _oauth_common_glue_url($parsed) {
  273. $uri = isset($parsed['scheme']) ? $parsed['scheme'] . '://' : '';
  274. $uri .= isset($parsed['user']) ? $parsed['user'] . (isset($parsed['pass']) ? ':' . $parsed['pass'] : '') . '@' : '';
  275. $uri .= isset($parsed['host']) ? $parsed['host'] : '';
  276. $uri .= isset($parsed['port']) ? ':' . $parsed['port'] : '';
  277. if (isset($parsed['path'])) {
  278. $uri .= (substr($parsed['path'], 0, 1) == '/') ?
  279. $parsed['path'] :
  280. ((!empty($uri) ? '/' : '' ) . $parsed['path']);
  281. }
  282. $uri .= isset($parsed['query']) ? '?' . $parsed['query'] : '';
  283. return $uri;
  284. }
  285. /**
  286. * Generate a request token from the request.
  287. */
  288. function oauth_common_callback_request_token() {
  289. try {
  290. $req = DrupalOAuthRequest::from_request();
  291. $context = oauth_common_context_from_request($req);
  292. if (!$context) {
  293. throw new OAuthException('No OAuth context found');
  294. }
  295. $server = new DrupalOAuthServer($context);
  296. print $server->fetch_request_token($req);
  297. }
  298. catch (OAuthException $e) {
  299. drupal_add_http_header('Status', '401 Unauthorized: ' . $e->getMessage());
  300. drupal_add_http_header('WWW-Authenticate', sprintf('OAuth realm="%s"', url('', array('absolute' => TRUE))));
  301. }
  302. }
  303. /**
  304. * Get a access token for the request
  305. */
  306. function oauth_common_callback_access_token() {
  307. try {
  308. $req = DrupalOAuthRequest::from_request();
  309. $context = oauth_common_context_from_request($req);
  310. if (!$context) {
  311. throw new OAuthException('No OAuth context found');
  312. }
  313. $server = new DrupalOAuthServer($context);
  314. $access_token = $server->fetch_access_token($req);
  315. // Set the expiry time based on context settings or get parameter
  316. $expires = !empty($context->authorization_options['access_token_lifetime']) ? REQUEST_TIME + $context->authorization_options['access_token_lifetime'] : 0;
  317. if ($_GET['expires'] && intval($_GET['expires'])) {
  318. $hint = intval($_GET['expires']);
  319. // Only accept more restrictive expiry times
  320. if ($expires == 0 || $hint < $expires) {
  321. $expires = $hint;
  322. }
  323. }
  324. // Store the expiry time if the access token should expire
  325. if ($expires) {
  326. $access_token->expires = $expires;
  327. $access_token->write(TRUE);
  328. }
  329. print $access_token;
  330. }
  331. catch (OAuthException $e) {
  332. drupal_add_http_header('Status', '401 Unauthorized: ' . $e->getMessage());
  333. drupal_add_http_header('WWW-Authenticate', sprintf('OAuth realm="%s"', url('', array('absolute' => TRUE))));
  334. }
  335. }