function features_ensure_enabled($flags) { if (!features_is_enabled($flags)) { error_disabled(); } # Happy sauce! }
function _cache_prepare_key($key, $more = array()) { if (features_is_enabled("cache_prefixes")) { if ($prefix = $GLOBALS['cfg']['cache_prefix']) { $key = "{$prefix}_{$key}"; } } return $key; }
function api_auth_oauth2_has_auth(&$method, $key_row = null) { $access_token = api_auth_oauth2_get_access_token($method); if (!$access_token) { return array('ok' => 0, 'error' => 'Required access token missing', 'error_code' => 400); } $token_row = api_oauth2_access_tokens_get_by_token($access_token); if (!$token_row) { return array('ok' => 0, 'error' => 'Invalid access token', 'error_code' => 400); } if ($token_row['disabled']) { return array('ok' => 0, 'error' => 'Access token is disabled', 'error_code' => 502); } if ($token_row['expires'] && $token_row['expires'] < time()) { return array('ok' => 0, 'error' => 'Access token has expired', 'error_code' => 400); } # I find it singularly annoying that we have to do this here # but OAuth gets what [redacted] wants. See also: notes in # lib_api.php around ln 65 (20121026/straup) $key_row = api_keys_get_by_id($token_row['api_key_id']); $rsp = api_keys_utils_is_valid_key($key_row); if (!$rsp['ok']) { return $rsp; } if (isset($method['requires_perms'])) { if ($token_row['perms'] < $method['requires_perms']) { $perms_map = api_oauth2_access_tokens_permissions_map(); $required = $perms_map[$method['requires_perms']]; return array('ok' => 0, 'error' => "Insufficient permissions, method requires a token with '{$required}' permissions", 'error_code' => 403); } } # Ensure user-iness - this may seem like a no-brainer until you think # about how the site itself uses the API in the absence of a logged-in # user (20130508/straup) $ensure_user = 1; $user = null; if (!$token_row['user_id'] && $key_row && features_is_enabled("api_oauth2_tokens_null_users")) { $key_role_id = $key_row['role_id']; $roles_map = api_keys_roles_map('string keys'); $valid_roles = $GLOBALS['cfg']['api_oauth2_tokens_null_users_allowed_roles']; $valid_roles_ids = array(); foreach ($valid_roles as $role) { $valid_roles_ids[] = $roles_map[$role]; } $ensure_user = $key_role_id && in_array($key_role_id, $valid_roles_ids) ? 0 : 1; } if ($ensure_user) { $user = users_get_by_id($token_row['user_id']); if (!$user || $user['deleted']) { return array('ok' => 0, 'error' => 'Not a valid user', 'error_code' => 400); } } # return array('ok' => 1, 'access_token' => $token_row, 'api_key' => $key_row, 'user' => $user); }
function api_oauth2_access_tokens_for_key(&$key, $more = array()) { $enc_key = AddSlashes($key['id']); $sql = "SELECT * FROM OAuth2AccessTokens WHERE api_key_id='{$enc_key}' AND (expires=0 OR expires > UNIX_TIMESTAMP(NOW()))"; if (features_is_enabled(array("api_site_keys", "api_site_tokens"))) { # pretty sure we don't want to filter on this # but just in case... (20130711/straup) # $sql .= " AND api_key_role_id=0"; } $sql .= " ORDER BY created DESC"; $rsp = db_fetch_paginated($sql, $more); return $rsp; }
function api_keys_utils_is_valid_key($key_row) { if (!$key_row) { return array('ok' => 0, 'error' => 'Unknown API key'); } if ($key_row['deleted']) { return array('ok' => 0, 'error' => 'Invalid API key'); } if ($key_row['disabled']) { return array('ok' => 0, 'error' => 'API key is disabled'); } if (features_is_enabled("api_throttling") && api_throttle_is_key_throttled($key_row)) { return array('ok' => 0, 'error' => 'API key is throttled'); } return array('ok' => 1); }
function api_log($data, $dispatch = 0) { if (!features_is_enabled("api_logging")) { return; } # We could also use apache_note to store data (serialized to # JSON) as we go but since that's really just ... a global # variable it seems kind of pointless not to just do this # (20121026/straup) $GLOBALS['cfg']['api_log'] = array_merge($GLOBALS['cfg']['api_log'], $data); if ($dispatch) { $pid = getmypid(); $note = json_encode($GLOBALS['cfg']['api_log']); error_log("[API][{$pid}] {$note}"); } }
function api_output_send($rsp, $more = array()) { $rsp['stat'] = isset($more['is_error']) ? 'error' : 'ok'; api_log(array('stat' => $rsp['stat']), 'write'); api_output_utils_start_headers($rsp, $more); if (features_is_enabled("api_cors")) { if ($origin = $GLOBALS['cfg']['api_cors_allow_origin']) { header("Access-Control-Allow-Origin: " . htmlspecialchars($origin)); } } if (!request_isset("inline")) { header("Content-Type: text/json"); } $json = json_encode($rsp); header("Content-Length: " . strlen($json)); echo $json; exit; }
function api_auth_oauth2_has_auth(&$method, $key_row = null) { $access_token = api_auth_oauth2_get_access_token($method); if (!$access_token) { return array('ok' => 0, 'error' => 'Required access token missing', 'error_code' => 400); } $token_row = api_oauth2_access_tokens_get_by_token($access_token); if (!$token_row) { return array('ok' => 0, 'error' => 'Invalid access token', 'error_code' => 400); } if ($token_row['expires'] && $token_row['expires'] < time()) { return array('ok' => 0, 'error' => 'Access token has expired', 'error_code' => 400); } # I find it singularly annoying that we have to do this here # but OAuth gets what [redacted] wants. See also: notes in # lib_api.php around ln 65 (20121026/straup) $key_row = api_keys_get_by_id($token_row['api_key_id']); $rsp = api_keys_utils_is_valid_key($key_row); if (!$rsp['ok']) { return $rsp; } if (isset($method['requires_perms'])) { if ($token_row['perms'] < $method['requires_perms']) { return array('ok' => 0, 'error' => 'Insufficient permissions', 'error_code' => 403); } } # Ensure user-iness - this may seem like a no-brainer until you think # about how the site itself uses the API in the absence of a logged-in # user (20130508/straup) $ensure_user = 1; $user = null; if (features_is_enabled("api_site_keys", "api_site_tokens")) { # check that API key is a site key $ensure_user = $token_row['user_id'] ? 1 : 0; } if ($ensure_user) { $user = users_get_by_id($token_row['user_id']); if (!$user || $user['deleted']) { return array('ok' => 0, 'error' => 'Not a valid user', 'error_code' => 400); } } # return array('ok' => 1, 'access_token' => $token_row, 'api_key' => $key_row, 'user' => $user); }
function api_output_send($rsp, $callback, $more = array()) { $rsp['stat'] = isset($more['is_error']) ? 'error' : 'ok'; api_log(array('stat' => $rsp['stat']), 'write'); api_output_utils_start_headers($rsp, $more); if (features_is_enabled("api_cors")) { if ($origin = $GLOBALS['cfg']['api_cors_allow_origin']) { header("Access-Control-Allow-Origin: " . htmlspecialchars($origin)); } } $json = json_encode($rsp); # http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ $jsonp = "/**/" . $callback . "(" . $json . ")"; header("Content-Disposition: attachment; filename=f.txt,"); header("X-Content-Type-Options: nosniff"); header("Content-Length: " . strlen($jsonp)); if (!request_isset("inline")) { header("Content-Type: application/javascript"); } echo $jsonp; exit; }
function auth_has_role($role, $who = 0) { $who = $who ? $who : $GLOBALS['cfg']['user']['id']; # This still needs better documentation but is here to account # for glue-code that Cal added for dev environments (20150613/straup) if (!$who && $role == "staff" && features_is_enabled("auth_roles_autopromote_staff")) { if ($GLOBALS['cfg']['environment'] == 'dev' && features_is_enabled("auth_roles_autopromote_staff_dev")) { return 1; } if ($GLOBALS['this_is_shell'] && features_is_enabled("auth_roles_autopromote_staff_shell")) { return 1; } } if (!$who) { return 0; } if (!isset($GLOBALS['cfg']['auth_users'][$who])) { return 0; } $details = $GLOBALS['cfg']['auth_users'][$who]; $roles = $details['roles']; return in_array($role, $roles) ? 1 : 0; }
<?php $root = dirname(dirname(__FILE__)); ini_set("include_path", "{$root}/www:{$root}/www/include"); set_time_limit(0); # include "include/init.php"; loadlib("cli"); loadlib("flickr_backups"); loadlib("flickr_push"); loadlib("flickr_push_subscriptions"); $features = array("backups", "flickr_push", "flickr_push_backups"); if (!features_is_enabled($features)) { echo "backups are currently disabled\n"; exit; } $spec = array("url" => array("flag" => "u", "required" => 1, "help" => "the *root* URL of your copy of parallel-ogram (the need to specify this here is not a feature...)")); $opts = cli_getopts($spec); $topic = $opts['topic']; # This sucks to have to do but I am uncertain what the # better alternative is right now... (20120601/straup) $root = rtrim($opts['url'], '/') . "/"; $GLOBALS['cfg']['abs_root_url'] = $root; log_info("set 'abs_root_url' to '{$GLOBALS['cfg']['abs_root_url']}'"); $topic_map = flickr_push_topic_map("string keys"); $topics = array("my_photos", "my_faves"); foreach (flickr_backups_users() as $user) { foreach ($topics as $topic) { $sub = array('user_id' => $user['id'], 'topic_id' => $topic_map[$topic]); $rsp = flickr_push_subscriptions_register_subscription($sub); log_info("[{$user['username']}] {$topic}: {$rsp['ok']}");
<?php # Note the order here – it's important # (20121024/straup) $GLOBALS['this_is_api'] = 1; include "include/init.php"; loadlib("api"); if (features_is_enabled('ensure_post_data')) { api_utils_ensure_post_data(); } $method = request_str("method"); api_dispatch($method); exit;
<?php $root = dirname(dirname(__FILE__)); ini_set("include_path", "{$root}/www:{$root}/www/include"); set_time_limit(0); # include "include/init.php"; loadlib("flickr_backups"); if (!features_is_enabled("backups")) { echo "backups are currently disabled\n"; exit; } foreach (flickr_backups_users() as $user) { log_info("backup photos for {$user['username']}"); $rsp = flickr_backups_get_photos($user); dumper($rsp); }
function api_dispatch($method) { if (!$GLOBALS['cfg']['enable_feature_api']) { api_output_error(999, 'API disabled'); } $method = filter_strict($method); $api_key = request_str("api_key"); $access_token = request_str("access_token"); # Log the basics api_log(array('api_key' => $api_key, 'method' => $method, 'access_token' => $access_token, 'remote_addr' => $_SERVER['REMOTE_ADDR'])); $methods = $GLOBALS['cfg']['api']['methods']; if (!$method || !isset($methods[$method])) { $enc_method = htmlspecialchars($method); api_output_error(404, "Method '{$enc_method}' not found"); } apache_setenv("API_METHOD", $method); $method_row = $methods[$method]; $key_row = null; $token_row = null; if (!$method_row['enabled']) { $enc_method = htmlspecialchars($method); api_output_error(404, "Method '{$enc_method}' not found"); } $method_row['name'] = $method; if ($GLOBALS['cfg']['api_auth_type'] == 'oauth2') { if ($_SERVER['REQUEST_METHOD'] != 'POST' && !$GLOBALS['cfg']['api_oauth2_allow_get_parameters']) { api_output_error(405, 'Method not allowed'); } } if (isset($method_row['request_method'])) { if ($_SERVER['REQUEST_METHOD'] != $method_row['request_method']) { api_output_error(405, 'Method not allowed'); } } # Okay – now we get in to validation and authorization. Which means a # whole world of pedantic stupid if we're using Oauth2. Note that you # could use OAuth2 and require API keys be passed explictly but since # that's not part of the spec if you enable the two features simultaneously # don't be surprised when hilarity ensues. Good times. (20121026/straup) # First API keys if (features_is_enabled("api_require_keys")) { if (!$api_key) { api_output_error(999, "Required API key is missing"); } $key_row = api_keys_get_by_key($api_key); api_keys_utils_ensure_valid_key($key_row); } # Second auth-y bits $auth_rsp = api_auth_ensure_auth($method_row, $key_row); if (isset($auth_rsp['api_key'])) { $key_row = $auth_rsp['api_key']; } if (isset($auth_rsp['access_token'])) { $token_row = $auth_rsp['access_token']; } if ($auth_rsp['user']) { $GLOBALS['cfg']['user'] = $auth_rsp['user']; } apache_setenv("API_KEY", $key_row['api_key']); # Check for require-iness of users here ? # Roles - for API keys (things like only the site keys) api_config_ensure_role($method_row, $key_row, $token_row); # Blessings and other method specific access controls api_config_ensure_blessing($method_row, $key_row, $token_row); # Finally, crumbs - because they are tastey if ($method_row['requires_crumb']) { api_auth_ensure_crumb($method_row); } # GO! loadlib($method_row['library']); $parts = explode(".", $method); $method = array_pop($parts); $func = "{$method_row['library']}_{$method}"; if (!function_exists($func)) { api_output_error(404, "Method not found"); } call_user_func($func); exit; }
error_404(); } } else { error_404(); } } echo get_str("challenge"); exit; } # TO DO: check $subscription['topic_id'] here against # the 'flickr_push_enable_*' flags (20111203/straup) $xml = file_get_contents('php://input'); $atom = syndication_atom_parse_str($xml); $user = users_get_by_id($subscription['user_id']); $flickr_user = flickr_users_get_by_user_id($user['id']); $do_push_backups = features_is_enabled("flickr_push_backups"); $is_push_backup = flickr_push_subscriptions_is_push_backup($subscription); $is_backup_user = flickr_backups_is_registered_user($user, "ensure enabled"); $to_backup = array(); $new = 0; foreach ($atom->items as $e) { # for debugging... # $fh = fopen("/tmp/wtf.json", "w"); # fwrite($fh, json_encode($e)); # fclose($fh); # TO DO: check $subscription['topic_id'] here because # at some point if we start to use the push stuff to # track things we're backing we'll need to store the # data in another table (20111203/straup) if (!preg_match("!.*/(\\d+)\$!", $e['id'], $m)) { continue;
function _flickr_photos_import_store($path, &$bits) { $fh = fopen($path, "w"); if (!$fh) { log_info("failed to create filehandle for '{$path}'"); return 0; } fwrite($fh, $bits); fclose($fh); # The perms dance (ensuring that all files are group writable # is necessary if we're doing push-based backups since when a # push update comes through the web server needs to be able to # write (or update) the file. But we also need to be able to # write (or update) files using the backup scripts in the bin # directory. Good times. (20120607/straup) $do_perms_dance = features_is_enabled(array('flickr_push', 'flickr_push_backups')); if ($do_perms_dance) { $stat = stat($path); $owner = $stat['uid']; $whoami = getmyuid(); if ($whoami == $owner) { chmod($path, 0664); } } return 1; }
function api_config_init_blessings() { # $GLOBALS['timing_keys']["api_blessings"] = "API blessings"; # $GLOBALS['timings']['api_blessings_count'] = 0; # $GLOBALS['timings']['api_blessings_time'] = 0; foreach ($GLOBALS['cfg']['api']['blessings'] as $api_key => $key_details) { # $GLOBALS['timings']['api_blessings_count'] += 1; $start = microtime_ms(); $whoami = $api_key; if ($api_key == 'site_key') { loadlib("api_keys"); $blessed_site_keys = features_is_enabled(array("api_site_keys", "api_site_keys_blessed")) ? 1 : 0; if ($blessed_site_keys && ($site_key = api_keys_fetch_site_key())) { $api_key = $site_key['api_key']; } } $blessing_defaults = array(); foreach (array('hosts', 'tokens', 'environments') as $prop) { if (isset($key_details[$prop])) { $blessing_defaults[$prop] = $key_details[$prop]; } } if (is_array($key_details['method_classes'])) { foreach ($key_details['method_classes'] as $class_spec => $blessing_details) { foreach ($GLOBALS['cfg']['api']['methods'] as $method_name => $method_details) { if (!$method_details['requires_blessing']) { continue; } if (!preg_match("/^{$class_spec}/", $method_name)) { continue; } $blessing = array_merge($blessing_defaults, $blessing_details); _api_config_apply_blessing($method_name, $api_key, $blessing); } } } if (is_array($key_details['methods'])) { foreach ($key_details['methods'] as $method_name => $blessing_details) { $blessing = array_merge($blessing_defaults, $blessing_details); _api_config_apply_blessing($method_name, $api_key, $blessing); } } # _api_config_apply_blessing('api.test.isBlessed', $api_key, $blessing_defaults); $end = microtime_ms(); $time = $end - $start; # $GLOBALS['timings']['api_blessings_time'] += $time; } }
function flickr_backups_is_push_backup(&$backup, $check_subscription = 0) { $push_features = array("flickr_push", "flickr_push_backups"); if (!features_is_enabled($push_features)) { return 0; } $type_id = $backup['type_id']; $map = flickr_backups_push_topics_map(); if (!isset($map[$type_id])) { return 0; } # Stub subscription data $user = users_get_by_id($backup['user_id']); $topic_id = $map[$type_id]; $sub = array('user_id' => $user['id'], 'topic_id' => $topic_id); if (!flickr_backups_is_registered_push_subscription($sub)) { return 0; } if ($check_subscription) { if (!flickr_push_subscriptions_get_by_user_and_topic($user, $topic_id)) { return 0; } } return 1; }
function api_utils_features_ensure_enabled($f) { if (!features_is_enabled($f)) { api_output_error(502, "This feature is disabled"); } }
loadlib('http'); loadlib('paginate'); $end = microtime_ms(); $time = $end - $start; $GLOBALS['timings']['loadlib_default_count'] = 17; $GLOBALS['timings']['loadlib_default_time'] = $time; $start = microtime_ms(); if (isset($GLOBALS['cfg']['autoload_libs']) && is_array($GLOBALS['cfg']['autoload_libs'])) { foreach ($GLOBALS['cfg']['autoload_libs'] as $lib) { $GLOBALS['timings']['loadlib_auto_count'] += 1; loadlib($lib); } } if (isset($GLOBALS['cfg']['autoload_libs_if_enabled']) && is_array($GLOBALS['cfg']['autoload_libs_if_enabled'])) { foreach ($GLOBALS['cfg']['autoload_libs_if_enabled'] as $feature => $libs) { if (features_is_enabled($feature)) { if (!is_array($libs)) { $libs = array($libs); } foreach ($libs as $lib) { $GLOBALS['timings']['loadlib_auto_count'] += 1; loadlib($lib); } } } } $end = microtime_ms(); $time = $end - $start; $GLOBALS['timings']['loadlib_auto_time'] = $time; if ($GLOBALS['cfg']['site_disabled'] && !$this_is_shell) { loadlib("http_codes");