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 08:30:41 -0000
@@ -9,7 +9,18 @@
  * 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.
  */
 
 /**
@@ -22,9 +33,6 @@
   *       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>
@@ -34,38 +42,57 @@
     $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
-  *
+  * 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 the current url
+  *    A string representing an urlsecure
   */
-function _google_auth_next() {
-  $uri = request_uri();
+function _google_auth_get_url($registered, $next) {
+  if ($registered == 1 && $next == 1){
+    $uri = '/gauth';
+  }
+  else {
+    $uri = request_uri();
+  }
   if (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') {
     $p = 'https://';
   }
@@ -96,29 +123,185 @@
   $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"] .'"';
-    $session_request = str_replace( "AuthSubRequest", "AuthSubSessionToken", $url);
-    $session_exchange = drupal_http_request($session_request, $headers);
-    if ($session_exchange->code == 200) {
-      $token = str_replace('Token=', '', $session_exchange->data);
-      $_SESSION['google_auth_token'] = trim($token);
-      $msg = t("You have authenticated with Google.");
-      drupal_set_message($msg);
-    }
-    else {
-      $msg = t('Authentication with Google has failed.') .'  '. $session_exchange->error;
-      watchdog('google_auth', 'google authentication error - '. print_r($session_exchange, TRUE), array(), WATCHDOG_ERROR);
-      drupal_set_message($msg, 'error');
-      drupal_goto($data['redirect']);
+    _google_auth_exchange($data);
+  }
+}
+
+/**
+ * Exchanges a single use token for a session token from Google
+ * 
+ * @param $data array
+ *  an array containing url and session information
+ */
+function _google_auth_exchange($data){
+  $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) {
+    $token = str_replace('Token=', '', $session_exchange->data);
+    $_SESSION['google_auth_token'] = trim($token);
+    $msg = t("You have authenticated with Google.");
+    drupal_set_message($msg);
+  }
+  else {
+    $msg = t('Authentication with Google has failed.') .'  '. $session_exchange->error;
+    watchdog('google_auth', 'google authentication error - '. print_r($session_exchange, TRUE), array(), WATCHDOG_ERROR);
+    drupal_set_message($msg, 'error');
+    drupal_goto($data['redirect']);
+  }
+}
+
+/**
+  * 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'];
+    if ($data['session'] == 1){
+      google_auth_exchange($data);
     }
+    //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');
+}
