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 <front>
-  */
+ * 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 <front>
+ */
 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('<front>');
   }
-  if (empty($variables->secure)) {
-    $variables->secure = 0;
-  }
   if (empty($variables->session)) {
     $variables->session = 1;
   }
-  if (empty($variables->redirect)) {
-    $variables->redirect = '<front>';
-  }
-
   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:<br/><ul>
+    <li>An error occured during the Authentication process with Google.</li>
+    <li>The page was reached either manually or from a web site other than Google </li>
+     </ul><br/><p>If this error continues to occur please contact your site 
+     administrator</p>');
+    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<br />
+                        You must set your Google target path to <b>http://'
+                        .$_SERVER['HTTP_HOST'].'/gauth</b> or
+                        <b> 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');
+}
