public static function delete($app_id, $service_slug, $id) { WpakWebServiceContext::$crud_action = 'delete'; $service_answer = array(); $service_answer = apply_filters('wpak_delete_' . $service_slug, $service_answer, $id, $app_id); $service_answer = apply_filters('wpak_delete', $service_answer, $service_slug, $id, $app_id); return $service_answer; }
public static function read($service_answer, $query_vars, $app_id) { $service_answer = array(); $auth_params = WpakWebServiceContext::getClientAppParams(); if (!empty($auth_params['auth_action'])) { $app_id = WpakApps::get_app_id($app_id); $auth_engine = AuthenticationSettings::get_auth_engine_instance(); $service_answer = $auth_engine->get_webservice_answer($app_id); } else { $service_answer = array('error' => 'no-auth-action'); //This will set webservice answer status to 0. } return (object) $service_answer; }
/** * Retrieve "Live query" web service answer. * * Any custom input params passed through the web service GET data can be * retrieved via WpakWebServiceContext::getClientAppParam( 'my_param' ); * * The following input params are automatically recognized and interpreted : * - 'wpak_component_slug' : if present, the WS automatically retrieve data * about the given component * - 'wpak_query_action' : optionnaly use along with 'wpak_component_slug'. Can be : * -- 'get-component' : default value : retrieves default component data * -- 'get-items' : retrieves only the 'wpak_items_ids' items * - 'wpak_items_ids' : array of items ids to retrieve (when wpak_query_action = get-items) * * @return array $service_answer Web service answer : Advised answer structure : * By default, app core automatically knows what to do with an answer containing * the following keys : * - 'globals' * - 'component' or 'components' * But the answer structure can be totally overriden, provided it is understood on * app side using the dedicated hooks. */ public static function read($service_answer, $query_vars, $app_id) { $app_id = WpakApps::get_app_id($app_id); $component_slug = WpakWebServiceContext::getClientAppParam('wpak_component_slug'); $action = WpakWebServiceContext::getClientAppParam('wpak_query_action'); $action = empty($action) || !in_array($action, array('get-component', 'get-items')) ? 'get-component' : $action; if (!empty($component_slug)) { $service_answer = array('globals' => array(), 'component' => array()); if (is_array($component_slug)) { //The only valid action is 'get-component' if $component_slug is an array : if ($action == 'get-component') { //Retrieve data for all given components and merge globals : unset($service_answer['component']); $service_answer['components'] = array(); foreach ($component_slug as $slug) { $component_data = WpakComponents::get_component_data($app_id, $slug); if (!empty($component_data)) { foreach ($component_data['globals'] as $global => $items) { foreach ($items as $k => $item) { $service_answer['globals'][$global][$k] = $item; } } $service_answer['components'][$slug] = $component_data['component']; } } } } else { //Only one component given : simply retrieve its data : switch ($action) { case 'get-component': $service_answer = WpakComponents::get_component_data($app_id, $component_slug); break; case 'get-items': $items_ids = WpakWebServiceContext::getClientAppParam('wpak_items_ids'); if (!empty($items_ids)) { $items_ids = !is_array($items_ids) && is_numeric($items_ids) ? array(intval($items_ids)) : array_map('intval', $items_ids); $service_answer = WpakComponents::get_component_items($app_id, $component_slug, $items_ids); } break; } } } $query_params = WpakWebServiceContext::getClientAppParams(); $service_answer = apply_filters('wpak_live_query', $service_answer, $query_params, $app_id, $query_vars); return (object) $service_answer; }
private static function exit_handle_request($app_id_or_slug, $service_slug, $action, $id = 0) { global $wp_query; self::log($_SERVER['REQUEST_METHOD'] . ' : ' . $action . ' : ' . print_r($_REQUEST, true)); //Set AJAX WP context : define('DOING_AJAX', true); if (self::cache_on()) { //TODO_WPAK /* $cached_webservice = WpakCache::get_cached_web_service( self::get_web_service_cache_id($service), isset($_GET['force_reload']) && is_numeric($_GET['force_reload']) && $_GET['force_reload'] == 1, isset($_GET['last_update']) && is_numeric($_GET['last_update']) ? $_GET['last_update'] : 0 ); if( !empty($cached_webservice) ){ self::exit_sending_web_service_content($cached_webservice); } */ } $app = WpakApps::get_app($app_id_or_slug); //Check that the asked app exists : if (empty($app)) { header("HTTP/1.0 404 Not Found"); _e('App not found', WpAppKit::i18n_domain) . ' : [' . $app_id_or_slug . ']'; exit; } $app_id = $app->ID; $app_slug = $app->post_name; WpakWebServiceContext::$current_app_id = $app_id; WpakWebServiceContext::$current_app_slug = $app_slug; //Some browsers or viewports on mobile devices cache HTTP resquests, we don't want this! header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Some time in the past if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') { header('Allow: GET, PUT, DELETE, POST'); header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: GET, PUT, DELETE, POST'); header('Access-Control-Allow-Headers: origin, content-type, accept, x-http-method-override'); header('Access-Control-Allow-Credentials: true'); exit; } //If the app current theme has some PHP (hooks!) to be executed before the web //service process, include it here : WpakThemes::include_app_theme_php($app_id); //Include PHP files required by addons activated for this app : WpakAddons::require_app_addons_php_files($app_id); $service_answer = null; switch ($action) { case 'list': if ($_SERVER['REQUEST_METHOD'] == 'POST') { $headers = function_exists('apache_request_headers') ? apache_request_headers() : array(); $is_url_encoded = !empty($headers['Content-Type']) && strpos($headers['Content-Type'], 'application/x-www-form-urlencoded') !== false || !empty($_SERVER['CONTENT_TYPE']) && strpos($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded') !== false; if ($is_url_encoded) { if (isset($_POST['model'])) { //Specific to backbone's "emulateJSON" $json = stripslashes($_POST['model']); $sent = json_decode($json); } else { $sent = $_POST; } } else { $json = file_get_contents("php://input"); $sent = json_decode($json); } $service_answer = WpakWebServiceCrud::create($app_id, $service_slug, $sent); } elseif ($_SERVER['REQUEST_METHOD'] == 'GET') { $service_answer = WpakWebServiceCrud::read($app_id, $service_slug, $wp_query->query_vars); } break; case 'one': if ($_SERVER['REQUEST_METHOD'] == 'GET') { $service_answer = WpakWebServiceCrud::read_one($app_id, $service_slug, $id); } elseif ($_SERVER['REQUEST_METHOD'] == 'PUT') { $json = file_get_contents("php://input"); $new = json_decode($json); $service_answer = WpakWebServiceCrud::update($app_id, $service_slug, $new); } elseif ($_SERVER['REQUEST_METHOD'] == 'DELETE') { $service_answer = WpakWebServiceCrud::delete($app_id, $service_slug, $id); } elseif ($_SERVER['REQUEST_METHOD'] == 'POST') { $http_method_override_method = ''; $headers = function_exists('apache_request_headers') ? apache_request_headers() : array(); if (!empty($headers['X-HTTP-Method-Override'])) { $http_method_override_method = $headers['X-HTTP-Method-Override']; } elseif (!empty($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { $http_method_override_method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; } $is_url_encoded = !empty($headers['Content-Type']) && strpos($headers['Content-Type'], 'application/x-www-form-urlencoded') !== false || !empty($_SERVER['CONTENT_TYPE']) && strpos($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded') !== false; //self::log('$_SERVER : '. print_r($_SERVER,true)); self::log('X-HTTP-Method-Override : ' . $http_method_override_method); if (!empty($http_method_override_method)) { if ($http_method_override_method == 'PUT') { if ($is_url_encoded) { if (isset($_POST['model'])) { //Specific to backbone's "emulateJSON" $json = stripslashes($_POST['model']); $sent = json_decode($json); } else { $sent = $_POST; } self::log('PUT one (X-HTTP-Method-Override + emulateJSON) : ' . $id . ' - json :' . $json . ' - _POST : ' . print_r($_POST, true)); } else { $data = file_get_contents("php://input"); $new = json_decode($data); self::log('PUT one (X-HTTP-Method-Override) : ' . $id . ' : ' . $data); } if ($new !== null) { $service_answer = WpakWebServiceCrud::update($app_id, $service_slug, $new); } } elseif ($http_method_override_method == 'DELETE') { self::log('DELETE one (X-HTTP-Method-Override) : ' . $id); $service_answer = WpakWebServiceCrud::delete($app_id, $service_slug, $id); } } } break; } //Simulate delay : TODO : make this configurable in WP BO : //time_nanosleep(rand(0,1), (floatval(rand(20,100))/100) * 1000000000); //sleep(2); if ($service_answer !== null) { self::exit_sending_answer($service_answer, $app_id, $service_slug); } exit(__('Error : Web service not recognised', WpAppKit::i18n_domain)); }
public function get_webservice_answer($app_id) { $service_answer = array(); $auth_params = WpakWebServiceContext::getClientAppParams(); $debug_mode = WpakBuild::get_app_debug_mode($app_id) === 'on'; switch ($auth_params['auth_action']) { case "get_public_key": if (!empty($auth_params['user'])) { $user = $auth_params['user']; $user_wp = get_user_by('login', $user); if ($user_wp) { //Check the user is not banned : if ($this->check_user_is_allowed_to_authenticate($user_wp->ID, $app_id)) { if (!empty($auth_params['control']) && !empty($auth_params['control_key']) && !empty($auth_params['timestamp'])) { $user = $auth_params['user']; $control = $auth_params['control']; $control_key = $auth_params['control_key']; $timestamp = $auth_params['timestamp']; //First, check that sent data has not been modified : if ($this->check_hmac($auth_params['auth_action'] . $user . $timestamp, $control_key, $control)) { if ($this->check_query_time($timestamp)) { if (function_exists('openssl_pkey_get_private')) { $public_key = $this->get_app_public_key($app_id); if (!empty($public_key)) { //Return public key : $service_answer['public_key'] = $public_key; //Add control key : $service_answer['control'] = $this->generate_hmac($public_key . $user, $control_key); } else { //If not in debug mode, don't give error details for security concern : $service_answer['auth_error'] = $debug_mode ? 'empty-public-key' : 'auth-error'; //Don't give more details for security concern } } else { $service_answer['auth_error'] = $debug_mode ? 'php-openssl-not-found' : 'auth-error'; //Don't give more details for security concern } } else { //If not in debug mode, don't give error details for security concern : $service_answer['auth_error'] = $debug_mode ? 'wrong-query-time' : 'auth-error'; //Don't give more details for security concern } } else { //If not in debug mode, don't give error details for security concern : $service_answer['auth_error'] = $debug_mode ? 'wrong-hmac' : 'auth-error'; //Don't give more details for security concern } } else { //If not in debug mode, don't give error details for security concern : $service_answer['auth_error'] = $debug_mode ? 'wrong-data' : 'auth-error'; //Don't give more details for security concern } } else { $service_answer['auth_error'] = 'user-banned'; } } else { $service_answer['auth_error'] = 'wrong-user'; } } else { $service_answer['auth_error'] = 'empty-user'; } break; case "connect_user": if (!empty($auth_params['user']) && !empty($auth_params['control']) && !empty($auth_params['timestamp']) && !empty($auth_params['encrypted'])) { $service_answer = array('authenticated' => 0); $user = $auth_params['user']; $control = $auth_params['control']; $timestamp = $auth_params['timestamp']; $encrypted = $auth_params['encrypted']; //Decrypt data to retrieve user HMAC secret key : $decrypted = $this->decrypt($app_id, $encrypted); if (is_array($decrypted) && !empty($decrypted['secret'])) { $user_secret_key = $control_key = $decrypted['secret']; if ($this->check_secret_format($user_secret_key)) { if ($this->check_hmac($auth_params['auth_action'] . $user . $timestamp . $encrypted, $control_key, $control) && $user == $decrypted['user']) { if ($this->check_query_time($timestamp)) { //Check user data : $user = $decrypted['user']; $user_wp = get_user_by('login', $user); if ($user_wp) { //Check the user is not banned : if ($this->check_user_is_allowed_to_authenticate($user_wp->ID, $app_id)) { //Check password : $pass = $decrypted['pass']; if (wp_check_password($pass, $user_wp->data->user_pass, $user_wp->ID)) { //Memorize user as registered and store its secret control key $this->authenticate_user($user_wp->ID, $user_secret_key, $app_id); //Return authentication result to client : $service_answer['authenticated'] = 1; //Get user permissions : $service_answer['permissions'] = $this->get_user_permissions($user_wp->ID, $app_id); //Add control key : $service_answer['control'] = $this->generate_hmac('authenticated' . $user, $user_secret_key); } else { $service_answer['auth_error'] = 'wrong-pass'; } } else { $service_answer['auth_error'] = 'user-banned'; } } else { $service_answer['auth_error'] = 'wrong-user'; } } else { //If not in debug mode, don't give error details for security concern : $service_answer['auth_error'] = $debug_mode ? 'wrong-query-time' : 'auth-error'; } } else { //If not in debug mode, don't give error details for security concern : $service_answer['auth_error'] = $debug_mode ? 'wrong-hmac' : 'auth-error'; //Don't give more details for security concern } } else { //If not in debug mode, don't give error details for security concern : $service_answer['auth_error'] = $debug_mode ? 'wrong-secret' : 'auth-error'; //Don't give more details for security concern } } else { //If not in debug mode, don't give error details for security concern : $service_answer['auth_error'] = $debug_mode ? 'wrong-decryption' : 'auth-error'; //Don't give more details for security concern } } else { $service_answer['auth_error'] = 'wrong-auth-data'; } break; case 'check_user_auth': $service_answer['user_auth_ok'] = 0; //Check authentication if (!empty($auth_params['user']) && !empty($auth_params['control']) && !empty($auth_params['timestamp'])) { if (!empty($auth_params['hash']) && !empty($auth_params['hasher'])) { $result = $this->check_authenticated_action($app_id, 'check_user_auth', $auth_params, array($auth_params['hash'], $auth_params['hasher'])); if ($result['ok']) { //Means that the user is authenticated ok on server side, with secret ok. //Now, check that the public key has not changed : $app_public_key = $this->get_app_public_key($app_id); $hash = $this->generate_hmac($app_public_key, $auth_params['hasher']); if ($auth_params['hash'] === $hash) { $service_answer['user_auth_ok'] = 1; } else { $service_answer['auth_error'] = 'wrong-public-key'; } } else { //Depending on $result['auth_error'], can mean that the user //is not authenticated or that his secret has changed (if hmac check failed). $service_answer['auth_error'] = $result['auth_error']; } } else { $service_answer['auth_error'] = 'no-hash'; } } else { $service_answer['auth_error'] = 'wrong-auth-data'; } break; default: $service_answer = array('error' => 'wrong-action'); //This will set webservice answer status to 0. break; } //Note : deliberately don't add hook here to filter $service_answer //so that malicious code can not modify authentication process. return $service_answer; }