Index: services.install =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/services/Attic/services.install,v retrieving revision 1.3.2.17 diff -u -r1.3.2.17 services.install --- services.install 6 Sep 2008 23:17:30 -0000 1.3.2.17 +++ services.install 11 Jan 2009 12:08:03 -0000 @@ -34,6 +34,20 @@ 'not null' => TRUE, 'default' => '' ), + 'secret' => array( + 'description' => 'The service key secret.', + 'type' => 'char', + 'length' => 32, + 'not null' => TRUE, + 'default' => '' + ), + 'uid' => array( + 'description' => 'Who can view and regenerate the secret?', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), ), 'primary key' => array('kid') ); @@ -55,18 +69,15 @@ 'not null' => TRUE, 'default' => '' ), - 'domain' => array( - 'description' => 'The domain that submitted the request.', - 'type' => 'varchar', - 'length' => 255, + 'kid' => array( + 'description' => 'The service key ID.', + 'type' => 'char', + 'length' => 32, 'not null' => TRUE, 'default' => '' ), ), - 'indexes' => array( - 'timestamp' => array('timestamp'), - ), - 'primary key' => array('nonce'), + 'unique key' => array('timestamp', 'nonce', 'kid'), ); return $schema; } @@ -124,3 +135,37 @@ db_create_table($update, 'services_timestamp_nonce', $schema['services_timestamp_nonce']); return $update; } + +function services_update_6002() { + menu_rebuild(); + + $secret = array( + 'description' => 'The service key secret.', + 'type' => 'char', + 'length' => 32, + 'not null' => TRUE, + 'default' => '' + ); + $uid = array( + 'description' => 'Who can view and regenerate the secret?', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ); + $kid = array( + 'description' => 'The service key ID.', + 'type' => 'char', + 'length' => 32, + 'not null' => TRUE, + 'default' => '' + ); + $new_keys = array('unique key' => array('timestamp', 'nonce', 'kid')); + + $update = array(); + db_add_field($update, 'services_keys', 'secret', $secret); + db_add_field($update, 'services_keys', 'uid', $uid); + db_drop_unique_key($update, 'services_timestamp_nonce'); + db_change_field($update, 'services_timestamp_nonce', 'domain', 'kid', $kid, $new_keys); + return $update; +} Index: services.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/services/Attic/services.module,v retrieving revision 1.8.2.73 diff -u -r1.8.2.73 services.module --- services.module 6 Sep 2008 23:17:30 -0000 1.8.2.73 +++ services.module 11 Jan 2009 13:05:56 -0000 @@ -140,10 +140,30 @@ 'page arguments' => array(1), 'type' => MENU_CALLBACK, ); + $items['user/%user/services_key'] = array( + 'title' => 'Services API key', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('services_admin_keys_user_regenerate_secret'), + 'access callback' => 'services_user_key_access', + 'type' => MENU_LOCAL_TASK, + 'file' => 'services_admin_keys.inc', + ); return $items; } +function services_user_key_access() { + global $user; + if ($user->uid) { + $has_key = db_result(db_query("SELECT COUNT(*) FROM {services_keys} WHERE uid = %d", $user->uid)); + if ($has_key) { + return TRUE; + } + } + + return FALSE; +} + /** * Implementation of hook_theme(). */ @@ -279,7 +299,7 @@ if ($method['#key'] and variable_get('services_use_key', TRUE)) { $hash = array_shift($args); - $domain = array_shift($args); + $api_key = array_shift($args); $timestamp = array_shift($args); $nonce = array_shift($args); @@ -291,19 +311,17 @@ // Still in time but has it been used before if (db_result(db_query("SELECT count(*) FROM {services_timestamp_nonce} - WHERE domain = '%s' AND timestamp = %d AND nonce = '%s'", - $domain, $timestamp, $nonce))) { + WHERE kid = '%s' AND timestamp = %d AND nonce = '%s'", + $api_key, $timestamp, $nonce))) { return services_error(t('Token has been used previously for a request.')); } else{ - db_query("INSERT INTO {services_timestamp_nonce} (domain, timestamp, nonce) - VALUES ('%s', %d, '%s')", $domain, $timestamp, $nonce); + db_query("INSERT INTO {services_timestamp_nonce} (kid, timestamp, nonce) + VALUES ('%s', %d, '%s')", $api_key, $timestamp, $nonce); } - $api_key = db_result(db_query("SELECT kid FROM {services_keys} WHERE domain = '%s'", $domain)); - - if (!services_validate_key($api_key, $timestamp, $domain, $nonce, $method_name, $hash_parameters, $hash)) { - return services_error(t('Invalid API key.')); + if ($hash != services_get_hash($timestamp, $api_key, $nonce, $method_name, $hash_parameters)) { + return services_error(t('Invalid API key hash.')); } } @@ -357,37 +375,34 @@ if (!isset($methods_cache)) { $methods = module_invoke_all('service'); - // api_key arg - $arg_api_key = array( + $arg_hash = array( '#name' => 'hash', '#type' => 'string', - '#description' => t('A valid API key.'), + '#description' => t('A valid hash.'), ); - // sessid arg $arg_sessid = array( '#name' => 'sessid', '#type' => 'string', '#description' => t('A valid sessid.'), ); - // domain arg - $arg_domain_name = array( - '#name' => 'domain_name', + $arg_api_key = array( + '#name' => 'api_key', '#type' => 'string', - '#description' => t('A valid domain for the API key.'), + '#description' => t('A valid API key.'), ); - $arg_domain_time_stamp = array( - '#name' => 'domain_time_stamp', + $arg_time_stamp = array( + '#name' => 'time_stamp', '#type' => 'string', - '#description' => t('Time stamp used to hash key.'), + '#description' => t('Time stamp.'), ); $arg_nonce = array( '#name' => 'nonce', '#type' => 'string', - '#description' => t('One time use nonce also used hash key.'), + '#description' => t('One time use nonce.'), ); foreach ($methods as $key => $method) { @@ -418,9 +433,9 @@ if ($methods[$key]['#key'] and variable_get('services_use_key', TRUE)) { $methods[$key]['#args'] = array_merge(array($arg_nonce), $methods[$key]['#args']); - $methods[$key]['#args'] = array_merge(array($arg_domain_time_stamp), $methods[$key]['#args']); - $methods[$key]['#args'] = array_merge(array($arg_domain_name), $methods[$key]['#args']); + $methods[$key]['#args'] = array_merge(array($arg_time_stamp), $methods[$key]['#args']); $methods[$key]['#args'] = array_merge(array($arg_api_key), $methods[$key]['#args']); + $methods[$key]['#args'] = array_merge(array($arg_hash), $methods[$key]['#args']); } // set defaults for args @@ -467,10 +482,10 @@ return $method_cache[$method_name]; } -function services_validate_key($kid, $timestamp, $domain, $nonce, $method_name, $hash_parameters, $hash) { - $hash_parameters = array_merge(array($timestamp, $domain, $nonce, $method_name), $hash_parameters); - $rehash = hash_hmac("sha256", implode(';', $hash_parameters), $kid); - return ($rehash == $hash) ? TRUE : FALSE; +function services_get_hash($timestamp, $kid, $nonce, $method_name, $hash_parameters) { + $hash_parameters = array_merge(array($timestamp, $kid, $nonce, $method_name), $hash_parameters); + $secret = db_result(db_query("SELECT secret FROM {services_keys} WHERE kid = '%s'", $kid)); + return hash_hmac("sha256", implode(';', $hash_parameters), $secret); } function services_get_key($kid) { Index: services_admin_browse.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/services/Attic/services_admin_browse.inc,v retrieving revision 1.5.2.34 diff -u -r1.5.2.34 services_admin_browse.inc --- services_admin_browse.inc 6 Sep 2008 04:13:07 -0000 1.5.2.34 +++ services_admin_browse.inc 11 Jan 2009 13:03:49 -0000 @@ -95,11 +95,13 @@ function services_admin_browse_test() { $form = array(); - $method = services_method_get(arg(4)); + $method_name = arg(4); + $method = services_method_get($method_name); $form['arg'] = array('#tree' => TRUE); $timestamp = time(); $nonce = user_password(); + $api_key = services_admin_browse_get_first_key(); foreach ($method['#args'] as $key => $arg) { $form['name'][$key] = array( @@ -114,7 +116,7 @@ $form['arg'][$key] = array( '#title' => 'Hash', '#type' => 'textfield', - '#default_value' => hash_hmac('sha256', $timestamp .';'. $_SERVER['HTTP_HOST'] .';'. $nonce .';'. arg(4), services_admin_browse_get_first_key()) + '#default_value' => services_get_hash($timestamp, $api_key, $nonce, $method_name, array()), ); break; @@ -126,15 +128,15 @@ ); break; - case 'domain_name': + case 'api_key': $form['arg'][$key] = array( - '#title' => 'Domain name', + '#title' => 'API key', '#type' => 'textfield', - '#default_value' => $_SERVER['HTTP_HOST'] + '#default_value' => $api_key ); break; - case 'domain_time_stamp': + case 'time_stamp': $form['arg'][$key] = array( '#title' => 'Timestamp', '#type' => 'textfield', Index: services_admin_keys.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/services/Attic/services_admin_keys.inc,v retrieving revision 1.3.2.13 diff -u -r1.3.2.13 services_admin_keys.inc --- services_admin_keys.inc 6 Sep 2008 04:13:07 -0000 1.3.2.13 +++ services_admin_keys.inc 11 Jan 2009 12:14:02 -0000 @@ -45,17 +45,26 @@ $kid = arg(4); $key = db_fetch_object(db_query("SELECT * FROM {services_keys} WHERE kid = '%s'", $kid)); + $user_name = ($key->uid) ? db_result(db_query("SELECT name FROM {users} WHERE uid = %d", $key->uid)) : ''; $form['kid'] = array( '#type' => 'hidden', '#default_value' => $key->kid, ); + $form['secret'] = array( + '#type' => 'hidden', + '#default_value' => $key->secret, + ); if ($key->kid) { - $form['key'] = array( - '#type' => 'markup', - '#title' => t('Key'), - '#value' => ''. t('API Key') .': '. $key->kid, + $form['key_secret'] = array( + '#value' => '
'. t('API Key') .': '. $key->kid .'
'. t('API Key\'s secret') .': '. $key->secret .'
'. t('API Key') .': '. $key->kid .'
'. t('API Key\'s secret') .': '. $key->secret .'
Regeneration of the secret can not be undone and has an impact on all webservice requests you make.
', + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Regenerate secret'), + ); + + return $form; +} + +function services_admin_keys_user_regenerate_secret_submit($form, &$form_state) { + $key = $form_state['values']['kid']; + $secret = md5(uniqid(mt_rand(), TRUE)); + db_query("UPDATE {services_keys} SET secret = '%s' WHERE kid = '%s'", $secret, $key); + + drupal_set_message('Key regenerated'); +} + function services_admin_keys_delete($kid) { db_query("DELETE FROM {services_keys} WHERE kid = '%s'", $kid); }