? 642584.patch
Index: deploy.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/deploy/deploy.install,v
retrieving revision 1.1.4.2
diff -u -p -r1.1.4.2 deploy.install
--- deploy.install	19 Mar 2009 04:19:21 -0000	1.1.4.2
+++ deploy.install	7 Dec 2009 14:28:36 -0000
@@ -252,3 +252,22 @@ function deploy_update_6002() {
 
   return $update;
 }
+
+/**
+ * Implementation of hook_update().
+ *
+ * Add new auth_type field for pluggable authentication 
+ * and drop unused api_key field.
+ */
+function deploy_update_6003() {
+  $update = array();
+  
+  db_add_field($update, 'deploy_servers', 'auth_type', array('type' => 'varchar', 'length' => 100, 'not null' > TRUE));
+  db_drop_field($update, 'deploy_servers', 'api_key');
+
+  // Also update all existing servers to use session ids (the only choice
+  // possible previously).  
+  db_query("update {deploy_servers} set auth_type = 'deploy_sessid'");
+
+  return $update;
+}
Index: deploy.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/deploy/deploy.module,v
retrieving revision 1.2.2.5
diff -u -p -r1.2.2.5 deploy.module
--- deploy.module	5 Nov 2009 15:56:48 -0000	1.2.2.5
+++ deploy.module	7 Dec 2009 14:28:36 -0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: deploy.module,v 1.2.2.5 2009/11/05 15:56:48 heyrocker Exp $
+// $Id: deploy.module,v 1.2.2.4 2009/05/05 14:00:33 heyrocker Exp $
 /**
  * @file
  * Deployment API which enables modules to deploy items between servers.
@@ -164,6 +164,13 @@ function deploy_menu() {
     'weight' => 1,
   );
 
+  // Server form AHAH callback.
+  $items['admin/build/deploy/ahah/server-options'] = array(
+    'page callback' => '_deploy_ahah_server_options',
+    'access arguments' => array('administer deployment'),
+    'type' => MENU_CALLBACK,
+  );
+
   // Deployment logs
   $items['admin/build/deploy/logs'] = array(
     'title' => 'Deployment Log',
@@ -359,14 +366,10 @@ function deploy_pushform_submit($form, &
   global $user;
   $pid = $form_state['values']['pid'];
   $sid = $form_state['values']['sid'];
-  $username = $form_state['values']['username'];
-  $password = $form_state['values']['password'];
-
-  deploy_init_deployment($pid, $sid);
 
   // If a session is successfully created, then go on to the deploy_check
   // batch process. Otherwise quit out and show the error log.
-  if (deploy_set_session($username, $password)) {
+  if (deploy_init_deployment($pid, $sid, $form_state)) {
     $form_state['redirect'] = "admin/build/deploy/deploy_check_batch";
   }
   else {
@@ -608,17 +611,11 @@ function deploy_get_min_weight($pid) {
  *   Results of the remote call.
  */
 function deploy_send($protocol_args, $function_args) {
-  // In the end $protocol_args needs to be URL of XMLRPC endpoint, function name, session ID.
-  // The one exception to this is system.connect, which never requires a session id (it is the
-  // equivalent of your first page hit to a drupal site, establishing an anonymous session.)
+  $auth_type = variable_get('deploy_server_auth_type', '');
   $url = variable_get('deploy_server_url', '');
-  if ($protocol_args[0] == 'system.connect') {
-    $protocol_args = array($url, $protocol_args[0]);
-  }
-  else {
-    $sessid = variable_get('deploy_sessid', '');
-    $protocol_args = array($url, $protocol_args[0], $sessid);
-  }
+  array_unshift($protocol_args, $url);
+  deploy_auth_invoke($auth_type, 'arguments callback', $protocol_args, $function_args);
+
   return call_user_func_array('xmlrpc', array_merge($protocol_args, $function_args));
 }
 
@@ -674,7 +671,17 @@ function deploy_delete_plan($pid) {
   db_query("DELETE FROM {deploy_plan} WHERE pid = %d", $pid);
 }
 
-function deploy_init_deployment($pid, $sid) {
+/**
+ * Initiate depolyment.
+ *
+ * @param $pid
+ *   ID of the plan to deploy.
+ * @param $sid
+ *   ID of the server to deploy to.
+ * @param $form_state
+ *   The state of the submitted authentication form.
+ */
+function deploy_init_deployment($pid, $sid, $form_state) {
   include_once('./includes/xmlrpc.inc');
 
   global $user;
@@ -685,6 +692,7 @@ function deploy_init_deployment($pid, $s
 
   // Save this data out so the other modules can get it. Not sure of a better
   // way to handle this.
+  variable_set('deploy_server_auth_type', $server['auth_type']);
   variable_set('deploy_server_url', $url);
   variable_set('deploy_pid', $pid);
 
@@ -697,36 +705,11 @@ function deploy_init_deployment($pid, $s
   // associated data gets deleted.
   db_query("INSERT INTO {deploy_log} (plan, server, username, ts) VALUES ('%s', '%s', '%s', %d)", $plan['name'], $server['description'], $user->name, time());
   variable_set('deploy_log_id', db_last_insert_id('deploy_log', 'dlid'));
+  return deploy_auth_invoke($server['auth_type'], 'init callback', $form_state);
 }
 
 
 /**
- * Setup a user session for deployment.
- */
-function deploy_set_session($username, $password) {
-  // In order to prevent bots from cluttering up the sessions table, you must
-  // have an active anonymous session before logging in to Drupal. So that is
-  // the first thing we do with system.connect. This session ID is saved
-  // to the 'deploy_sessid' variable, which all other xmlrpc calls to the
-  // remote server passes.
-  $result = deploy_send(array('system.connect'), array());
-  variable_set('deploy_sessid', $result['sessid']);
-
-  // Then we can log in.
-  $result = deploy_send(array('user.login'), array($username, $password));
-
-  // If it fails, then add a message to the log. Otherwise set the session ID into
-  // a drupal variable for the other functions to grab.
-  if ($result === FALSE) {
-    db_query("INSERT INTO {deploy_log_details} (dlid, module, description, result, message) VALUES (%d, '%s', '%s', '%s', '%s')", variable_get('deploy_log_id', ''), 'login', 'Remote user login', 'Error', xmlrpc_error_msg());
-  }
-  else {
-    variable_set('deploy_sessid', $result['sessid']);
-    return TRUE;
-  }
-}
-
-/**
  * Run the dependency checking hooks for the specified deployment plan.
  *
  * @param $pid
@@ -824,9 +807,12 @@ function deploy_plan_cleanup() {
   // clear remote cache
   deploy_send(array('system.cacheClearAll'), array());
   
+  $auth_type = variable_get('deploy_server_auth_type', '');
+  deploy_auth_invoke($auth_type, 'cleanup callback');
+
   variable_del('deploy_server_url');
   variable_del('deploy_pid');
-  variable_del('deploy_sessid');
+  variable_del('deploy_server_auth_type');
   variable_del('deploy_log_id');
   variable_del('deploy_fatal');
 }
@@ -853,7 +839,7 @@ function deploy_get_remote_key($uuid, $m
 }
 
 /**
- * Standard form with server list, username, and password. Used in many places.
+ * Standard form with server list. Used in many places.
  */
 function deploy_get_server_form() {
   $servers = deploy_get_servers();
@@ -861,38 +847,81 @@ function deploy_get_server_form() {
     drupal_set_message(t("There are no servers defined. Please define a server using the Servers tab before pushing your deployment plan."));
     drupal_goto("admin/build/deploy");
   }
-  
+  // Rebuild the server list so we also have an empty option.
+  $options = array('' => t('-- Select a server'));
+  foreach ($servers as $sid => $server) {
+    $options[$sid] = $server;
+  }
+
   $form['sid'] = array(
     '#title' => t('Server'),
     '#type' => 'select',
-    '#options' => $servers,
+    '#options' => $options,
     '#description' => t('The server you want to deploy to'),
+    '#required' => TRUE,
+    '#ahah' => array(
+      'path' => 'admin/build/deploy/ahah/server-options',
+      'wrapper' => 'deploy-server-options',
+      'method' => 'replace',
+    ),
   );
-  $form['username'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Username'),
-    '#size' => 30,
-    '#maxlength' => 60,
-    '#default_value' => variable_get('deploy_remote_username', ''),
-    '#description' => t('Username for the remote user Deploy should log in as.'),
-  );
-  $form['password'] = array(
-    '#type' => 'password',
-    '#title' => t('Password'),
-    '#size' => 20,
-    '#maxlength' => 32,
-    '#default_value' => variable_get('deploy_remote_password', ''),
-    '#description' => t('Password of the remote user Deploy should log in as.'),
+  $form['options'] = array(
+    '#prefix' => '<div id="deploy-server-options">',
+    '#suffix' => '</div>',
+  );
+  // This form element will be replaced with the response from an AHAH request
+  // when a server is selected from the list.
+  $form['options']['settings'] = array(
+    '#prefix' => '<div class="description">',
+    '#suffix' => '</div>',
+    '#value' => t('Select a server to configure.'),
   );
   $form['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Push Deployment Plan'),
   );
-  
+
   return $form;
 }
 
 /**
+ * AHAH callback for the security configuration form.
+ */
+function _deploy_ahah_server_options() {
+  $cached_form_state = array();
+  $cached_form = form_get_cache($_POST['form_build_id'], $cached_form_state);
+
+  if (!empty($_POST['sid'])) {
+    $server = deploy_get_server($_POST['sid']);
+    $auth_type = deploy_get_auth_type($server['auth_type']);
+    $settings = call_user_func($auth_type['form callback']);
+  }
+
+  if (is_array($settings)) {
+    $cached_form['options']['settings'] = $settings;
+  }
+  else {
+    unset($cached_form['options']['settings']);
+  }
+
+  form_set_cache($_POST['form_build_id'], $cached_form, $cached_form_state);
+
+  $form_state = array('submitted' => FALSE);
+  $options = $cached_form['options'];
+  unset($options['#prefix'], $options['#suffix']);
+  $options = form_builder('_deploy_ahah_server_options', $options, $form_state);
+  $output = drupal_render($options);
+
+  print drupal_to_js(
+    array(
+      'status' => TRUE,
+      'data' => $output,
+    )
+  );
+  exit;
+}
+
+/**
  * Implementation of hook_views_api().
  */
 function deploy_views_api() {
@@ -900,4 +929,228 @@ function deploy_views_api() {
     'api' => 2,
     'path' => drupal_get_path('module', 'deploy') . '/includes',
   );
-}
\ No newline at end of file
+}
+
+/**
+* Invokes an authentication callback.
+*/
+function deploy_auth_invoke($type_name, $callback_type, &$a1 = NULL, &$a2 = NULL, &$a3 = NULL) {
+ $type = deploy_get_auth_type($type_name);
+ $function = $type[$callback_type];
+
+ if ($function === TRUE) {
+   return TRUE;
+ }
+ elseif (function_exists($function)) {
+   return $function($a1, $a2, $a3);
+ }
+}
+
+/**
+* Get all the available authentication types.
+*
+* @return
+*   An associative array of the authentication type definitions.
+*/
+function deploy_get_auth_types() {
+ $types = array();
+ $auth_modules = module_implements('deploy_auth_info');
+
+ foreach ($auth_modules as $module) {
+   $items = module_invoke($module, 'deploy_auth_info');
+   $types = array_merge($types, $items);
+ }
+
+ return $types;
+}
+
+/**
+* Get a specific authentication type.
+*
+* @param $type
+*   The name of the type to get.
+* @return
+*   The authentication type definition.
+*/
+function deploy_get_auth_type($type) {
+ $types = deploy_get_auth_types();
+ return $types[$type];
+}
+
+/**
+* Returns a hash to be used with the key authentication.
+*
+* @param $key
+*   The API key to be used in the hash.
+* @param $timestamp
+*   The timestamp to be used in the hash.
+* @param $domain
+*   The domain to be used in the hash.
+* @param $nonce
+*   The nonce to be used in the hash.
+* @param $method
+*   The method to be used in the hash.
+* @todo
+*   Add signed arguments.
+*/
+function deploy_get_auth_key_hash($key, $timestamp, $domain, $nonce, $method) {
+ $hash_parameters = array($timestamp, $domain, $nonce, $method);
+ return hash_hmac('sha256', implode(';', $hash_parameters), $key);
+}
+
+/**
+* Implementation of hook_deploy_auth_info().
+*
+* This hook implements support for the two basic authentication types
+* that comes with the Services module by default. Those are authentication
+* with just a valid session id, or authentication with just an API key.
+*
+* Session authentication (with username and password) does only make
+* sense when deploying against another Drupal sites (which isn't always the
+* case).
+*
+* @return
+*   An array of this modules' authentication types.
+* @todo
+*   Make it so callbacks can be stored in a separate file.
+*/
+function deploy_deploy_auth_info() {
+ $items['deploy_key'] = array(
+   'title' => t('Key authentication'),
+   'description' => t('All method calls must include a valid token to authenticate themselves with the server.'),
+   'form callback' => 'deploy_auth_key_form',
+   'init callback' => 'deploy_auth_key_init',
+   'arguments callback' => 'deploy_auth_key_arguments',
+   'cleanup callback' => 'deploy_auth_key_cleanup',
+ );
+ $items['deploy_sessid'] = array(
+   'title' => t('Session id'),
+   'description' => t('All method calls must include a valid session id.'),
+   'form callback' => 'deploy_auth_sessid_form',
+   'init callback' => 'deploy_auth_sessid_init',
+   'arguments callback' => 'deploy_auth_sessid_arguments',
+   'cleanup callback' => 'deploy_auth_sessid_cleanup',
+ );
+
+ return $items;
+}
+
+/**
+* Implementation of the init callback.
+*/
+function deploy_auth_key_init($form_state) {
+ variable_set('deploy_auth_key', $form_state['values']['key']);
+ variable_set('deploy_auth_domain', $form_state['values']['domain']);
+ return TRUE;
+}
+
+/**
+* Implementation of the init callback.
+*/
+function deploy_auth_sessid_init($form_state) {
+ // In order to prevent bots from cluttering up the sessions table, you must
+ // have an active anonymous session before logging in to Drupal. So that is
+ // the first thing we do with system.connect. This session ID is saved
+ // to the 'deploy_sessid' variable, which all other xmlrpc calls to the
+ // remote server passes.
+ $result = deploy_send(array('system.connect'), array());
+ variable_set('deploy_auth_sessid', $result['sessid']);
+
+ // Then we can log in.
+ $result = deploy_send(array('user.login'), array($form_state['values']['username'], $form_state['values']['password']));
+
+ // If it fails, then add a message to the log. Otherwise set the session ID into
+ // a drupal variable for the other functions to grab.
+ if ($result === FALSE) {
+   db_query("INSERT INTO {deploy_log_details} (dlid, module, description, result, message) VALUES (%d, '%s', '%s', '%s', '%s')", variable_get('deploy_log_id', ''), 'login', 'Remote user login', 'Error', xmlrpc_error_msg());
+ }
+ else {
+   variable_set('deploy_auth_sessid', $result['sessid']);
+   return TRUE;
+ }
+}
+
+/**
+* Implementation of the arguments callback.
+*/
+function deploy_auth_key_arguments(&$protocol_args, &$function_args) {
+ $key = variable_get('deploy_auth_key', '');
+ $domain = variable_get('deploy_auth_domain', '');
+ $timestamp = time();
+ $method = $protocol_args[1];
+ // Use Drupal's built in password generator to generate a random string.
+ $nonce = user_password();
+
+ $hash = deploy_get_auth_key_hash($key, $timestamp, $domain, $nonce, $method);
+ array_push($protocol_args, $hash, $domain, $timestamp, $nonce);
+}
+
+/**
+* Implementation of the arguments callback.
+*/
+function deploy_auth_sessid_arguments(&$protocol_args, &$function_args) {
+ // Check the method name. The server URL is the first argument.
+ if ($protocol_args[1] != 'system.connect') {
+   $sessid = variable_get('deploy_auth_sessid', '');
+   array_push($protocol_args, $sessid);
+ }
+}
+
+/**
+* Implementation of the cleanup callback.
+*/
+function deploy_auth_key_cleanup() {
+ variable_del('deploy_auth_key');
+ variable_del('deploy_auth_domain');
+}
+
+/**
+* Implementation of the cleanup callback.
+*/
+function deploy_auth_sessid_cleanup() {
+ variable_del('deploy_auth_sessid');
+}
+
+/**
+* Implementation of the form callback for the authentication.
+*/
+function deploy_auth_key_form() {
+ $form['key'] = array(
+   '#type' => 'textfield',
+   '#title' => t('API key'),
+   '#required' => TRUE,
+   '#description' => t('The API key to use when authenticating with the remote server.'),
+ );
+ $form['domain'] = array(
+   '#type' => 'textfield',
+   '#title' => t('Domain'),
+   '#required' => TRUE,
+   '#description' => t('Domain using this key (note this is not necessarily the same as your domain name).'),
+ );
+
+ return $form;
+}
+
+/**
+* Implementation of the form callback for the authentication.
+*/
+function deploy_auth_sessid_form() {
+ $form['username'] = array(
+   '#type' => 'textfield',
+   '#title' => t('Username'),
+   '#size' => 30,
+   '#maxlength' => 60,
+   '#required' => TRUE,
+   '#description' => t('Username for the remote user Deploy should log in as.'),
+ );
+ $form['password'] = array(
+   '#type' => 'password',
+   '#title' => t('Password'),
+   '#size' => 20,
+   '#maxlength' => 32,
+   '#required' => TRUE,
+   '#description' => t('Password of the remote user Deploy should log in as.'),
+ );
+
+ return $form;
+}
Index: deploy.servers.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/deploy/Attic/deploy.servers.admin.inc,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 deploy.servers.admin.inc
--- deploy.servers.admin.inc	20 Mar 2009 14:49:40 -0000	1.1.2.2
+++ deploy.servers.admin.inc	7 Dec 2009 14:28:37 -0000
@@ -12,11 +12,12 @@
  *   Themed table output.
  */
 function deploy_server_overview() {
-  $header = array(t('Name'), t('URL'), array('data' => t('Operations'), 'colspan' => 2));
+  $header = array(t('Name'), t('URL'), t('Authentication type'), array('data' => t('Operations'), 'colspan' => 2));
 
   $result = db_query("SELECT * FROM {deploy_servers}");
   while ($row = db_fetch_array($result)) {
-    $row = array(check_plain($row['description']), check_plain($row['url']), l(t('edit'), 'admin/build/deploy/server/'. $row['sid']), l(t('delete'), 'admin/build/deploy/delete/server/'. $row['sid']));
+    $auth_type = deploy_get_auth_type($row['auth_type']);
+    $row = array(check_plain($row['description']), check_plain($row['url']), $auth_type['title'], l(t('edit'), 'admin/build/deploy/server/'. $row['sid']), l(t('delete'), 'admin/build/deploy/delete/server/'. $row['sid']));
     $rows[] = $row;
   }
 
@@ -74,6 +75,22 @@ function deploy_server_form($form_state,
     '#default_value' => $server['url'],
     '#description' => t('Domain name and path to xmlrpc service.'),
   );
+
+  $auth_options = array();
+  $auth_types = deploy_get_auth_types();
+  foreach ($auth_types as $key => $type) {
+    $auth_options[$key] = $type['title'];
+  }
+
+  $form['auth_type'] = array(
+    '#title' => t('Authentication type'),
+    '#description' => t('Select which authentication type to be used when deploying to this specific server.'),
+    '#type' => 'radios',
+    '#required' => TRUE,
+    '#options' => $auth_options,
+    '#default_value' => $server['auth_type'],
+  );
+
   $form['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Save Deployment Server'),
@@ -88,16 +105,17 @@ function deploy_server_form($form_state,
 function deploy_server_form_submit($form, &$form_state) {
   $url = $form_state['values']['url'];
   $description = $form_state['values']['description'];
+  $auth_type = $form_state['values']['auth_type'];
 
   // If 'sid' exists in the submitted form, then this is an edit. Otherwise it is
   // a new server.
   if (array_key_exists('sid', $form_state['values'])) {
     $sid = $form_state['values']['sid'];
-    db_query("UPDATE {deploy_servers} SET description = '%s', url = '%s' WHERE sid = %d", $description, $url, $sid);
+    db_query("UPDATE {deploy_servers} SET description = '%s', url = '%s', auth_type = '%s' WHERE sid = %d", $description, $url, $auth_type, $sid);
     drupal_set_message(t('Deployment server updated'));
   }
   else {
-    db_query("INSERT INTO {deploy_servers} (description, url) VALUES ('%s', '%s')", $description, $url);
+    db_query("INSERT INTO {deploy_servers} (description, url, auth_type) VALUES ('%s', '%s', '%s')", $description, $url, $auth_type);
     drupal_set_message(t('Deployment server added'));
   }
 
Index: modules/node_deploy/node_deploy.pages.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/deploy/modules/node_deploy/Attic/node_deploy.pages.inc,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 node_deploy.pages.inc
--- modules/node_deploy/node_deploy.pages.inc	3 Aug 2009 03:55:22 -0000	1.1.2.2
+++ modules/node_deploy/node_deploy.pages.inc	7 Dec 2009 14:28:37 -0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: node_deploy.pages.inc,v 1.1.2.2 2009/08/03 03:55:22 heyrocker Exp $
+// $Id: node_deploy.pages.inc,v 1.1.2.1 2009/03/19 04:29:50 heyrocker Exp $
 /**
  * @file
  * Page handlers for node deployment pages.
@@ -153,8 +153,6 @@ function node_deploy_operations_add_now_
  */
 function node_deploy_operations_add_now_form_submit($form, &$form_state) {
   $sid = $form_state['values']['sid'];
-  $username = $form_state['values']['username'];
-  $password = $form_state['values']['password'];
 
   // If there is already aplan for this vocabulary, then empty it and use it.
   // Otherwise create one.
@@ -174,8 +172,7 @@ function node_deploy_operations_add_now_
   }
 
   // Now do the deployment
-  deploy_init_deployment($pid, $sid);
-  if (deploy_set_session($username, $password)) {
+  if (deploy_init_deployment($pid, $sid, $form_state)) {
     // Redirect to the log overview page for this push.
     $form_state['redirect'] = "admin/build/deploy/deploy_check_batch";
   }
@@ -184,6 +181,6 @@ function node_deploy_operations_add_now_
     deploy_plan_cleanup();
 
     // Redirect to the log overview page for this push.
-    $form_state['redirect'] = "admin/build/deploy/logs/details/$dlid";    
+    $form_state['redirect'] = "/admin/build/deploy/logs/details/$dlid";    
   }
 }
