<?php // This files deals with keys used to call the API (over HTTPS) require_once($INC . '/sql.inc.php'); require_once($INC . '/state.inc.php'); require_once($INC . '/prof.inc.php'); require_once($INC . '/events.inc.php'); require_once($INC . '/cache.inc.php'); $rg_ak_error = ''; function rg_ak_set_error($str) { global $rg_ak_error; $rg_ak_error = $str; rg_log($str); } function rg_ak_error() { global $rg_ak_error; return $rg_ak_error; } /* * Events functions */ $rg_ak_functions = array( 'ak_event_new' => 'rg_ak_event_new', 'ak_event_del' => 'rg_ak_event_del', 'ak_event_notify_user' => 'rg_ak_event_notify_user' ); rg_event_register_functions($rg_ak_functions); /* * Event for adding a new api key */ function rg_ak_event_new($db, $event) { $ret = array(); $event['op'] = 'new'; // notify user $a = array('category' => 'ak_event_notify_user', 'prio' => 100); $ret[] = array_merge($event, $a); return $ret; } /* * Event for deleting a key */ function rg_ak_event_del($db, $event) { $ret = array(); $event['op'] = 'del'; // notify user $a = array('category' => 'ak_event_notify_user', 'prio' => 100); $ret[] = array_merge($event, $a); return $ret; } /* * Notify user that a new key was added to the keyring */ function rg_ak_event_notify_user($db, $event) { rg_prof_start('ak_event_notify_user'); rg_log('ak_event_notify_user: event=' . rg_array2string($event)); $ret = FALSE; while (1) { $r = rg_mail_template('mail/user/ak/' . $event['op'], $event); if ($r === FALSE) break; $ret = array(); break; } rg_prof_end('ak_event_notify_user'); return $ret; } /* * Remove api keys from database for user 'ui' */ function rg_ak_remove($db, $ui, $list) { rg_prof_start('ak_remove'); rg_log_enter('ak_remove: list=' . rg_array2string($list)); $ret = FALSE; while (1) { $my_list = array(); foreach ($list as $key_id => $junk) $my_list[] = sprintf('%u', $key_id); $params = array('uid' => $ui['uid']); $sql_list = implode(', ', $my_list); $sql = 'DELETE FROM apikeys' . ' WHERE uid = @@uid@@' . ' AND key_id IN (' . $sql_list . ')'; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_ak_set_error('cannot delete api keys'); break; } rg_sql_free_result($res); $event = array( 'category' => 'ak_event_del', 'prio' => 50, 'ui' => $ui, 'keys' => implode(',', $my_list)); $r = rg_event_add($db, $event); if ($r !== TRUE) { rg_ak_set_error('cannot add event' . ' (' . rg_event_error() . ')'); break; } $key = 'user' . '::' . $ui['uid'] . '::' . 'apikeys' . '::' . 'list'; foreach ($my_list as $_key_id) rg_cache_unset($key . '::' . $_key_id, RG_SOCKET_NO_WAIT); rg_event_signal_daemon('', 0); $ret = TRUE; break; } rg_log_exit(); rg_prof_end('ak_remove'); return $ret; } /* * Adds an apikey * Returns the key_id of the key. */ function rg_ak_add($db, $ui, $key, $name) { rg_prof_start('ak_add'); rg_log_enter('ak_add: key=' . $key . ' name=' . $name); $ret = FALSE; $do_rollback = 0; while (1) { $itime = time(); $r = rg_sql_begin($db); if ($r !== TRUE) { rg_ak_set_error('cannot start transaction'); break; } $do_rollback = 1; $params = array( 'itime' => $itime, 'uid' => $ui['uid'], 'key' => $key, 'name' => $name, 'count' => 0, 'first_use' => 0, 'last_use' => 0, 'last_ip' => '', 'last_cmd' => ''); $sql = 'INSERT INTO apikeys (itime, uid, key, name)' . ' VALUES (@@itime@@, @@uid@@, @@key@@, @@name@@)' . ' RETURNING key_id'; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_ak_set_error('cannot insert key'); break; } $row = rg_sql_fetch_array($res); $key_id = $row['key_id']; rg_sql_free_result($res); $event = array( 'category' => 'ak_event_new', 'prio' => 50, 'ui' => $ui, 'key' => $key, 'name' => $name, 'key_id' => $key_id); $r = rg_event_add($db, $event); if ($r !== TRUE) { rg_ak_set_error('cannot add event' . ' (' . rg_event_error() . ')'); break; } $r = rg_sql_commit($db); if ($r !== TRUE) { rg_ak_set_error('cannot commit transaction'); break; } $do_rollback = 0; $_key = 'user' . '::' . $ui['uid'] . '::' . 'apikeys' . '::' . 'list' . '::' . $key_id; rg_cache_merge($_key, $params, RG_SOCKET_NO_WAIT); rg_event_signal_daemon('', 0); $ret = $key_id; break; } if ($do_rollback == 1) rg_sql_rollback($db); rg_log_exit(); rg_prof_end('ak_add'); return $ret; } /* * Update first_use, last_use, last_ip and count */ function rg_ak_update_use($db, $uid, $key_id, $ip, $cmd) { rg_prof_start('ak_update_use'); rg_log_enter('ak_update_use: uid=' . $uid . ' key_id=' . $key_id . ', ip=' . $ip . ', cmd=' . $cmd); $ret = FALSE; while (1) { $now = time(); $update_first_use = TRUE; $update_last_use = TRUE; $key = 'user' . '::' . $uid . '::' . 'apikeys' . ' ::' . 'list' . '::' . $key_id; $c = rg_cache_get($key); if ($c !== FALSE) { if (isset($c['first_use']) && ($c['first_use'] > 0)) $update_first_use = FALSE; // We will not update the field if is too soon if (isset($c['last_use']) && (strcmp($ip, $c['last_ip']) == 0) && (strcmp($cmd, $c['last_cmd']) == 0) && ($now - $c['last_use'] < 60)) $update_last_use = FALSE; } $params = array( 'now' => $now, 'key_id' => $key_id, 'last_ip' => $ip, 'last_cmd' => $cmd); if ($update_first_use) { $sql = 'UPDATE apikeys SET first_use = @@now@@' . ' WHERE first_use = 0' . ' AND key_id = @@key_id@@'; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_ak_set_error('cannot update apikey\'s first use'); break; } rg_sql_free_result($res); rg_cache_set($key . '::' . 'first_use', $now, RG_SOCKET_NO_WAIT); } if ($update_last_use) { $sql = 'UPDATE apikeys SET last_use = @@now@@' . ', last_ip = @@last_ip@@' . ', last_cmd = @@last_cmd@@' . ', count = count + 1' . ' WHERE key_id = @@key_id@@'; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_ak_set_error('cannot update key'); break; } rg_sql_free_result($res); $a = array( 'last_use' => $now, 'last_ip' => $ip, 'last_cmd' => $cmd); rg_cache_merge($key, $a, RG_SOCKET_NO_WAIT); } $ret = TRUE; break; } rg_log_exit(); rg_prof_end('ak_update_use'); return $ret; } /* * Cosmetic changes for a key */ function rg_ak_cosmetic(&$row) { if ($row['itime'] == 0) $row['itime_nice'] = 'N/A'; else $row['itime_nice'] = gmdate('Y-m-d H:i', $row['itime']); if ($row['first_use'] == 0) $row['first_use_nice'] = 'N/A'; else $row['first_use_nice'] = gmdate('Y-m-d H:i', $row['first_use']); if (empty($row['last_ip'])) $row['last_ip'] = 'N/A'; if ($row['last_use'] == 0) $row['last_use_nice'] = 'N/A'; else $row['last_use_nice'] = gmdate('Y-m-d H:i', $row['last_use']); if (empty($row['last_cmd'])) $row['last_cmd'] = 'N/A'; } /* * List keys */ function rg_ak_list($db, $uid) { rg_prof_start('ak_list'); rg_log_enter('ak_list: uid=' . $uid); $ret = FALSE; while (1) { $key = 'user' . '::' . $uid . '::' . 'apikeys'; $c = rg_cache_get($key); //rg_log_ml('DEBUG: cache: ' . print_r($c, TRUE)); if (($c !== FALSE) && isset($c['LOADED'])) { $ret = $c['list']; foreach ($ret as $key_id => &$i) rg_ak_cosmetic($i); break; } $params = array('uid' => $uid); $sql = 'SELECT * FROM apikeys WHERE uid = @@uid@@' . ' ORDER BY itime DESC'; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_ak_set_error('cannot query'); break; } $ret = array(); while (($row = rg_sql_fetch_array($res))) { $key_id = $row['key_id']; $ret[$key_id] = $row; } rg_sql_free_result($res); $a = array('LOADED' => 1, 'list' => $ret); rg_cache_set($key, $a, RG_SOCKET_NO_WAIT); foreach ($ret as $key_id => &$i) rg_ak_cosmetic($i); break; } rg_log_exit(); rg_prof_end('ak_list'); return $ret; } /* * Validates a api keys * Returns the key_id of the matching key or FALSE */ function rg_ak_valid($db, $uid, $apikey) { rg_prof_start('ak_valid'); rg_log_enter('ak_valid'); $ret = FALSE; while (1) { $key = 'user' . '::' . $uid . '::' . 'apikeys'; $c = rg_cache_get($key); if (($c === FALSE) || !isset($c['LOADED'])) { $params = array('uid' => $uid); $sql = 'SELECT * FROM apikeys WHERE uid = @@uid@@'; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { rg_ak_set_error('canno load list'); break; } $list = array(); while (($row = rg_sql_fetch_array($res))) { $key_id = intval($row['key_id']); unset($row['key_id']); $list[$key_id] = $row; } rg_sql_free_result($res); $a = array('LOADED' => 1, 'list' => $list); rg_cache_set($key, $a, RG_SOCKET_NO_WAIT); } else { $list = $c['list']; } foreach ($list as $key_id => $info) { if (strcmp($info['key'], $apikey) == 0) { $ret = $key_id; break; } } break; } rg_log_exit(); rg_prof_end('ak_valid'); return $ret; } /* * High-level function for listing api keys */ function rg_ak_list_high_level($db, $rg, $paras) { rg_prof_start('ak_list_high_level'); rg_log_enter('ak_list_high_level'); $ret = ''; $errmsg = array(); $rg['HTML:status'] = ''; $doit = rg_var_uint('doit'); while ($doit == 1) { if (!rg_valid_referer()) { $errmsg[] = 'invalid referer; try again'; break; } if (!rg_token_valid($db, $rg, 'ak_list', FALSE)) { $errmsg[] = 'invalid token; try again.'; break; } $list = rg_var_str('key_delete_ids'); $r = rg_ak_remove($db, $rg['login_ui'], $list); if ($r !== TRUE) { $errmsg[] = 'cannot delete: ' . rg_ak_error(); break; } $rg['HTML:status'] = rg_template( 'user/settings/apikeys/delete_ok.html', $rg, TRUE /*xss*/); break; } $r = rg_ak_list($db, $rg['login_ui']['uid']); if ($r === FALSE) { $rg['errmsg'] = rg_ak_error(); $ret .= rg_template('user/settings/apikeys/list_err.html', $rg, TRUE /*xss*/); } else { $rg['rg_form_token'] = rg_token_get($db, $rg, 'ak_list'); $rg['HTML:errmsg'] = rg_template_errmsg($errmsg); $ret .= rg_template_table('user/settings/apikeys/list', $r, $rg); } rg_log_exit(); rg_prof_end('ak_list_high_level'); return $ret; } /* * Enroll function for adding a api key */ function rg_ak_add_high_level($db, $rg, $paras) { rg_prof_start('ak_add_high_level'); rg_log_enter('ak_add_high_level'); $ret = ''; $errmsg = array(); $rg['ak'] = array(); $rg['ak']['name'] = ''; $rg['ak']['key'] = rg_id(32);; $doit = rg_var_uint('doit'); rg_log('DEBUG: doit=' . $doit); while ($doit == 1) { $rg['ak']['name'] = rg_var_str('ak::name'); $rg['ak']['key'] = rg_var_str('ak::key'); if (strlen($rg['ak']['name']) == 0) { $errmsg[] = 'invalid name'; break; } if (!rg_valid_referer()) { $errmsg[] = 'invalid referer; try again'; break; } if (!rg_token_valid($db, $rg, 'ak_add', FALSE)) { $errmsg[] = 'invalid token; try again'; break; } $r = rg_ak_add($db, $rg['login_ui'], $rg['ak']['key'], $rg['ak']['name']); if ($r === FALSE) { $errmsg[] = rg_ak_error(); break; } $ret .= rg_template('user/settings/apikeys/add_ok.html', $rg, TRUE /*xss*/); $rg['ak']['name'] = ''; $rg['ak']['key'] = rg_id(32); break; } $rg['HTML:errmsg'] = rg_template_errmsg($errmsg); $rg['rg_form_token'] = rg_token_get($db, $rg, 'ak_add'); $ret .= rg_template('user/settings/apikeys/add.html', $rg, TRUE /*xss*/); rg_log_exit(); rg_prof_end('ak_add_high_level'); return $ret; } /* * Main HL function for api keys */ function rg_ak_high_level($db, &$rg, $paras) { rg_prof_start('ak_high_level'); rg_log_enter('ak_high_level paras: ' . rg_array2string($paras)); $ret = ''; $op = empty($paras) ? 'list' : array_shift($paras); $rg['menu']['ak'][$op] = 1; $rg['HTML:menu_level2'] = rg_template('user/settings/apikeys/menu.html', $rg, TRUE /*xss*/); switch ($op) { case 'add': $ret .= rg_ak_add_high_level($db, $rg, $paras); break; default: $ret .= rg_ak_list_high_level($db, $rg, $paras); break; } $hints = array(); $hints[]['HTML:hint'] = rg_template('user/settings/apikeys/hints.html', $rg, TRUE /*xss*/); $ret .= rg_template_table('hints/list', $hints, $rg); rg_log_exit(); rg_prof_end('ak_high_level'); return $ret; } ?>