Index: google_auth.module =================================================================== RCS file: /cvs/drupal/contributions/modules/google_auth/google_auth.module,v retrieving revision 1.1.2.1 diff -u -r1.1.2.1 google_auth.module --- google_auth.module 12 Apr 2009 12:38:57 -0000 1.1.2.1 +++ google_auth.module 17 Apr 2009 11:13:26 -0000 @@ -3,69 +3,99 @@ /** * @file - * This module was originally written to work for the Picasa module but has been - * modified to serve as a generic implementation for use by other modules. - * Google authentication for web applications is outlined at + * This module was originally written to work for the Picasa module but has been + * modified to serve as a generic implementation for use by other modules. + * Google authentication for web applications is outlined at * http://code.google.com/apis/accounts/docs/AuthForWebApps.html - * + * * This implementation of Google authentication uses AuthStub which is designed - * for non-registered web applications. http://code.google.com/apis/accounts/docs/AuthSub.html + * for both registered and non-registered web applications. + * http://code.google.com/apis/accounts/docs/AuthSub.html + * + * Registered web applications have to provide google with a valid return Target Path. + * This path is used by Google to return authentication tokens back to the + * registered domain. + * see http://code.google.com/apis/accounts/docs/RegistrationForWebAppsAuto.html + * + * In this implementation the relative path /gauthsub is used for registered web + * applications. Site administrators of registered domains should go to + * https://www.google.com/accounts/ManageDomains and set the Target Path of their domain. + * For example: http://www.example.com/gauth. */ /** - * This method gets called when you need to associate a google account with a - * Drupal action. - * - * @param $variables an object containing a combination of the following: - * - string $variables->scope (required) URL identifying the service(s) to be accessed; - * see documentation for the service for the correct value(s). The resulting - * token enables access to the specified service(s) only. To specify more - * than one scope, list each one separated with a space (encodes as "%20"). - * example: http://picasaweb.google.com/data/ - * - boolean $variables->secure (optional) Boolean flag indicating whether the authentication - * transaction should issue a secure token (1) or a non-secure token (0). Secure - * tokens are available to registered applications only. - * - boolean (optional) Boolean flag indicating whether the one-time-use token - * may be exchanged for a session token (1) or not (0). - * - string $variables->redirect (optional) An optional page to redirect to defaulting to - */ + * This method gets called when you need to associate a google account with a + * Drupal action. + * + * @param $variables an object containing a combination of the following: + * - string $variables->scope (required) URL identifying the service(s) to be accessed; + * see documentation for the service for the correct value(s). The resulting + * token enables access to the specified service(s) only. To specify more + * than one scope, list each one separated with a space (encodes as "%20"). + * example: http://picasaweb.google.com/data/ + * - boolean (optional) Boolean flag indicating whether the one-time-use token + * may be exchanged for a session token (1) or not (0). + * - string $variables->redirect (optional) An optional page to redirect to defaulting to + */ function google_auth_required($variables) { if (empty($variables->scope)) { $backtrace = debug_backtrace(); $error = $backtrace[0]['file']; $error .= ' '. t('line') .' '. $backtrace[0]['line']; - drupal_set_message(t('You must provide an object containing a scope when calling google_auth_required: %error', array('%error' => $error)), 'error'); + drupal_set_message(t('You must provide an object containing a scope when + calling google_auth_required: %error', + array('%error' => $error)), 'error'); drupal_goto(''); } - if (empty($variables->secure)) { - $variables->secure = 0; - } if (empty($variables->session)) { $variables->session = 1; } - if (empty($variables->redirect)) { - $variables->redirect = ''; - } - if (!isset($_SESSION['google_auth_token'])) { + $data['registered'] = variable_get('google_auth_registered', 0); $data['scope'] = $variables->scope; $data['url'] = 'https://www.google.com/accounts/AuthSubRequest'; - $data["secure"] = $variables->secure; - $data["session"] = $variables->session; - $data["next"] = _google_auth_next(); - $data["redirect"] = $variables->redirect; + $data['session'] = $variables->session; + $data['next'] = _google_auth_get_url($data['registered'], 1); + $data['redirect'] = _google_auth_get_url($data['registered'], 0); + + //Only handle security if domain is registered + if ($data['registered'] == 1){ + $data['secure'] = variable_get('google_auth_secure', 0); + $data['sig'] = variable_get('google_auth_sign', ''); + $data['sigalg'] = variable_get('google_auth_sigalg', 'rsa_sha1'); + + //Create a session variable to allow google_auth_handoff() to work correctly. + if (!isset($_SESSION['google_auth_data'])){ + $_SESSION['google_auth_data'] = $data; + } + } _google_auth_sub_request($data); } } - /** - * Get the next url to pass to Google - * - * @return $url - * A string representing the current url - */ -function _google_auth_next() { - $uri = request_uri(); +/** + * Get either the next url to pass to Google or the current url to + * pass to google_auth_handoff(). + * + * @param boolean $registered + * Boolean flag indicating whether the domain is registered with Google + * or not-registered. + * @param boolean $next (optional) + * Boolean flag indicating whether the url should be the next url + * to pass to Google or the url to pass to google_auth_handoff(). + * @return $url + * A string representing an urlsecure + */ +function _google_auth_get_url($registered, $next=1) { + + + if ($registered == 1 && $next == 1){ + $uri = '/gauth'; + } + else { + $uri = request_uri(); + } + if (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') { $p = 'https://'; } @@ -75,36 +105,48 @@ $h = $_SERVER['HTTP_HOST']; $port = ''; if ($_SERVER['SERVER_PORT'] != '' && - (($p == 'http://' && $_SERVER['SERVER_PORT'] != '80') || - ($p == 'https://' && $_SERVER['SERVER_PORT'] != '443'))) { - $port .= ':'. $_SERVER['SERVER_PORT']; + (($p == 'http://' && $_SERVER['SERVER_PORT'] != '80') || + ($p == 'https://' && $_SERVER['SERVER_PORT'] != '443'))) { + $port .= ':'. $_SERVER['SERVER_PORT']; } $url = $p . $h . $port . $uri; return $url; } + /** - * Call AuthSubRequest to acquire single-use authentication. AuthSubRequest is - * called as a URL; make a request to: - * https://www.google.com/accounts/AuthSubRequest with the following query - * parameters: - * - * @param array $data (required) Contains the url and session information. - */ + * Call AuthSubRequest to acquire single-use authentication. AuthSubRequest is + * called as a URL; make a request to: + * https://www.google.com/accounts/AuthSubRequest with the following query + * parameters: + * + * @param array $data (required) Contains the url and session information. + */ function _google_auth_sub_request($data) { $url = $data['url']; $url .= '?next='. drupal_urlencode($data['next']); $url .= '&scope='. drupal_urlencode($data['scope']); $url .= "&session=". $data['session']; + if($data['registered']== 1 && $data['secure']==1){ + $url .= "&secure=". $data['secure']; + } if (!isset($_GET["token"])) { $url = str_replace( "http%3A/%252F", "http%3A//", $url); drupal_goto($url); } else { - // exchange the single use token for a session token from Google $headers['Content-Type'] = 'application/x-www-form-urlencoded'; $headers['Authorization'] = 'AuthSub token="'. $_GET["token"] .'"'; + /*if ($data['registered' == 1 && $data['secure'] == 1]){ + $http_request_url = 'GET https://www.google.com/accounts/AuthSubSessionToken'; + $timestamp = time(); + $nonce = 0; //need to figure out how to generate a 64 bit random number? + $sig = $data['sig']; + $sigalg = $data['sigalg']; + $headers['Authorization'] .= ' data="'.$http_request_url.' '.$timestamp.' '.$nonce.'" '; + $headers['Authorization'] .= 'sig="'.$sig.'" sigalg="'.$sigalg.'"'; + }*/ $session_request = str_replace( "AuthSubRequest", "AuthSubSessionToken", $url); $session_exchange = drupal_http_request($session_request, $headers); if ($session_exchange->code == 200) { @@ -122,3 +164,137 @@ } } +/** + * Requests a session token from Google if required and handover to the + * originating web application. + * + * This function should only be called by drupals menu system in response to + * Google returning an authentication token. Unexpected entry to the relative path + * /gauth is handled by displaying a Google Authentication Error page. + * + */ +function _google_auth_handoff(){ + foreach($_SESSION['google_auth_data'] as $key=>$value){ + $data[$key] = $value; + } + if (isset($data['redirect']) && isset($data['scope'])){ + $redirect_url = $data['redirect']; + $variables->scope = $data['scope']; + google_auth_required($variables); + //Token received so no need to keep session variables for redirect + unset($_SESSION['google_auth_data']); + drupal_goto($redirect_url); + } + else { + $msg = t("Authentication with Google has failed. Please try again."); + watchdog('google_auth', 'google authentication error - '. print_r($session_exchange, TRUE)); + drupal_set_message($msg); + $page_content = t('You are reading this page because:
    +
  • An error occured during the Authentication process with Google.
  • +
  • The page was reached either manually or from a web site other than Google
  • +

If this error continues to occur please contact your site + administrator

'); + return $page_content; + } +} + + +// probably need to implement hook_help() at some future point in time + +/** + * Implementation of hook_admin(). + */ +function google_auth_admin(){ + $form['google_auth_registered'] = array( + '#title' => t('Site Registered with Google'), + '#type' => 'checkbox', + '#description' => t('Tick this box if you have registered + your site as a web app with Google
+ You must set your Google target path to http://' + .$_SERVER['HTTP_HOST'].'/gauth or + https://'.$_SERVER['HTTP_HOST'].'/gauth'), + '#default_value' => variable_get('google_auth_registered', 0) + ); + $form['google_auth_secure'] = array( + '#title' => t('Site uses secure request transaction'), + '#type' => 'checkbox', + '#description' => t('Tick this box of you have uploaded a RSA SHA1 + security certificate to Google'), + '#default_value' => variable_get('google_auth_secure', 0), + ); + $form['google_auth_sign'] = array( + '#title' => t('Secure Signature'), + '#type' => 'textfield', + '#description' => t('Enter your domain signature made by the private key + corresponding to the certificate you provided during + Google registration. It must be encoded in BASE64, + and must use the algorithm specified below.'), + '#default_value' => variable_get('google_auth_sign', ''), + ); + $form['google_auth_sigalg'] = array( + '#title' => t('Signature Algorith'), + '#type' => 'select', + '#default_value' => variable_get('google_auth_sigalg', 'rsa_sha1'), + '#options' => array( + 'rsa_sha1' => t('RSA-SHA1'), + ), + '#description' => t('Select the security signature algorithm used to + create the above signature and the certificate you + provided to Google'), + ); + return system_settings_form($form); +} +/** + * Implementation of hook_admin_validate() + */ +function google_auth_admin_validate($form, &$form_state){ + $admin_registered = $form_state['values']['google_auth_registered']; + $admin_secure = $form_state['values']['google_auth_secure']; + $admin_sig = $form_state['values']['google_auth_sign']; + if ($admin_registered == 0 && $admin_secure == 1){ + form_set_error('google_auth_registered', t('Your site must be registered with + Google to use secure request + transactions.')); + } + if ($admin_secure == 1 && $admin_sig == ''){ + form_set_error('google_auth_sign', t('You must provide a valid signature + to use secure request transactions.')); + } + // Possibly need more error checking re: signature?. +} +/** + * Implementation of hook_menu() + * + * This implementation uses the drupal menu system to provides a valid relative path + * at /gauth for registered web applications. When Google returns to this URL + * drupal invokes a menu_callback to _google_auth_handover(). + */ +function google_auth_menu(){ + $items = array(); + $items['admin/settings/google_auth'] = array( + 'title' => 'Google Authentication', + 'description' => 'Sets your site configuration to use Google + Authentication API', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('google_auth_admin'), + 'access arguments' => array('access administration pages'), + 'type' => MENU_NORMAL_ITEM, + ); + + // callback link to _google_auth_handoff() + $items['gauth'] = array( + 'title' => 'Google Authentication Error', + 'page callback' => '_google_auth_handoff', + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK + ); + return $items; +} + +function google_auth_uninstall(){ + + variable_del('google_auth_registered'); + variable_del('google_auth_secure'); + variable_del('google_auth_sign'); + variable_del('google_auth_sigalg'); +}