/** * Execute the method and return the result. * * OKAPI methods return OkapiHttpResponses, but some MAY also return * PHP objects (see OkapiRequest::construct_inside_request for details). * * If $request must be consistent with given method's options (must * include Consumer and Token, if they are required). */ public static function call($service_name, OkapiRequest $request) { Okapi::init_internals(); if (!self::exists($service_name)) { throw new Exception("Method does not exist: '{$service_name}'"); } $options = self::options($service_name); if ($options['min_auth_level'] >= 2 && $request->consumer == null) { throw new Exception("Method '{$service_name}' called with mismatched OkapiRequest: " . "\$request->consumer MAY NOT be empty for Level 2 and Level 3 methods. Provide " . "a dummy Consumer if you have to."); } if ($options['min_auth_level'] >= 3 && $request->token == null) { throw new Exception("Method '{$service_name}' called with mismatched OkapiRequest: " . "\$request->token MAY NOT be empty for Level 3 methods."); } $time_started = microtime(true); Okapi::gettext_domain_init(); try { require_once $GLOBALS['rootpath'] . "okapi/{$service_name}.php"; $response = call_user_func(array('\\okapi\\' . str_replace('/', '\\', $service_name) . '\\WebService', 'call'), $request); Okapi::gettext_domain_restore(); } catch (Exception $e) { Okapi::gettext_domain_restore(); throw $e; } $runtime = microtime(true) - $time_started; # Log the request to the stats table. Only valid requests (these which didn't end up # with an exception) are logged. self::save_stats($service_name, $request, $runtime); return $response; }
public static function dispatch_request($uri) { # Chop off the ?args=... part. if (strpos($uri, '?') !== false) { $uri = substr($uri, 0, strpos($uri, '?')); } # Chop off everything before "/okapi/". This should work for okay for most "weird" # server configurations. It will also address a more subtle issue described here: # http://stackoverflow.com/questions/8040461/request-uri-unexpectedly-contains-fqdn if (strpos($uri, "/okapi/") !== false) { $uri = substr($uri, strpos($uri, "/okapi/")); } # Make sure we're in the right directory (.htaccess should make sure of that). if (strpos($uri, "/okapi/") !== 0) { throw new Exception("'{$uri}' is outside of the /okapi/ path."); } $uri = substr($uri, 7); # Initializing internals and running pre-request cronjobs (we don't want # cronjobs to be run before "okapi/update", for example before database # was installed). $allow_cronjobs = $uri != "update"; Okapi::init_internals($allow_cronjobs); # Checking for allowed patterns... try { foreach (OkapiUrls::$mapping as $pattern => $namespace) { $matches = null; if (preg_match("#{$pattern}#", $uri, $matches)) { # Pattern matched! Moving on to the proper View... array_shift($matches); require_once $GLOBALS['rootpath'] . "okapi/views/{$namespace}.php"; $response = call_user_func_array(array('\\okapi\\views\\' . str_replace('/', '\\', $namespace) . '\\View', 'call'), $matches); if ($response) { $response->display(); } return; } } } catch (Http404 $e) { /* pass */ } # None of the patterns matched OR method threw the Http404 exception. require_once $GLOBALS['rootpath'] . "okapi/views/http404.php"; $response = \okapi\views\http404\View::call(); $response->display(); }
public function __construct($options) { Okapi::init_internals(); $this->init_request(); # # Parsing options. # $DEBUG_AS_USERNAME = null; foreach ($options as $key => $value) { switch ($key) { case 'min_auth_level': if (!in_array($value, array(0, 1, 2, 3))) { throw new Exception("'min_auth_level' option has invalid value: {$value}"); } $this->opt_min_auth_level = $value; break; case 'token_type': if (!in_array($value, array("request", "access"))) { throw new Exception("'token_type' option has invalid value: {$value}"); } $this->opt_token_type = $value; break; case 'DEBUG_AS_USERNAME': $DEBUG_AS_USERNAME = $value; break; default: throw new Exception("Unknown option: {$key}"); break; } } if ($this->opt_min_auth_level === null) { throw new Exception("Required 'min_auth_level' option is missing."); } if ($DEBUG_AS_USERNAME != null) { # Enables debugging Level 2 and Level 3 methods. Should not be committed # at any time! If run on production server, make it an error. if (!Settings::get('DEBUG')) { throw new Exception("Attempted to use DEBUG_AS_USERNAME in " . "non-debug environment. Accidental commit?"); } # Lower required authentication to Level 0, to pass the checks. $this->opt_min_auth_level = 0; } # # Let's see if the request is signed. If it is, verify the signature. # It it's not, check if it isn't against the rules defined in the $options. # if ($this->get_parameter('oauth_signature')) { # User is using OAuth. # Check for duplicate keys in the parameters. (Datastore doesn't # do that on its own, it caused vague server errors - issue #307.) $this->get_parameter('oauth_consumer'); $this->get_parameter('oauth_version'); $this->get_parameter('oauth_token'); $this->get_parameter('oauth_nonce'); # Verify OAuth request. list($this->consumer, $this->token) = Okapi::$server->verify_request2($this->request, $this->opt_token_type, $this->opt_min_auth_level == 3); if ($this->get_parameter('consumer_key') && $this->get_parameter('consumer_key') != $this->get_parameter('oauth_consumer_key')) { throw new BadRequest("Inproper mixing of authentication types. You used both 'consumer_key' " . "and 'oauth_consumer_key' parameters (Level 1 and Level 2), but they do not match with " . "each other. Were you trying to hack me? ;)"); } if ($this->opt_min_auth_level == 3 && !$this->token) { throw new BadRequest("This method requires a valid Token to be included (Level 3 " . "Authentication). You didn't provide one."); } } else { if ($this->opt_min_auth_level >= 2) { throw new BadRequest("This method requires OAuth signature (Level " . $this->opt_min_auth_level . " Authentication). You didn't sign your request."); } else { $consumer_key = $this->get_parameter('consumer_key'); if ($consumer_key) { $this->consumer = Okapi::$data_store->lookup_consumer($consumer_key); if (!$this->consumer) { throw new InvalidParam('consumer_key', "Consumer does not exist."); } } if ($this->opt_min_auth_level == 1 && !$this->consumer) { throw new BadRequest("This method requires the 'consumer_key' argument (Level 1 " . "Authentication). You didn't provide one."); } } } if (is_object($this->consumer) && $this->consumer->hasFlag(OkapiConsumer::FLAG_SKIP_LIMITS)) { $this->skip_limits = true; } # # Prevent developers from accessing request parameters with PHP globals. # Remember, that OKAPI requests can be nested within other OKAPI requests! # Search the code for "new OkapiInternalRequest" to see examples. # $_GET = $_POST = $_REQUEST = null; # When debugging, simulate as if been run using a proper Level 3 Authentication. if ($DEBUG_AS_USERNAME != null) { # Note, that this will override any other valid authentication the # developer might have issued. $debug_user_id = Db::select_value("select user_id from user where username='******'DEBUG_AS_USERNAME']) . "'"); if ($debug_user_id == null) { throw new Exception("Invalid user name in DEBUG_AS_USERNAME: '******'DEBUG_AS_USERNAME'] . "'"); } $this->consumer = new OkapiDebugConsumer(); $this->token = new OkapiDebugAccessToken($debug_user_id); } # Read the ETag. if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) { $this->etag = $_SERVER['HTTP_IF_NONE_MATCH']; } }
# order to get it to work with your code. Just call this after you # include the Facade file: Facade::disable_error_handling(). # EXAMPLE OF USAGE: # require_once($rootpath.'okapi/facade.php'); # \okapi\Facade::schedule_user_entries_check(...); # \okapi\Facade::disable_error_handling(); use Exception; use okapi\OkapiServiceRunner; use okapi\OkapiInternalRequest; use okapi\OkapiFacadeConsumer; use okapi\OkapiFacadeAccessToken; use okapi\Cache; require_once $GLOBALS['rootpath'] . "okapi/core.php"; OkapiErrorHandler::$treat_notices_as_errors = true; require_once $GLOBALS['rootpath'] . "okapi/service_runner.php"; Okapi::init_internals(); /** * Use this class to access OKAPI's services from external code (i.e. OC code). */ class Facade { /** * Perform OKAPI service call, signed by internal 'facade' consumer key, and return the result * (this will be PHP object or OkapiHttpResponse, depending on the method). Use this method * whenever you need to access OKAPI services from within OC code. If you want to simulate * Level 3 Authentication, you should supply user's internal ID (the second parameter). */ public static function service_call($service_name, $user_id_or_null, $parameters) { $request = new OkapiInternalRequest(new OkapiFacadeConsumer(), $user_id_or_null !== null ? new OkapiFacadeAccessToken($user_id_or_null) : null, $parameters); $request->perceive_as_http_request = true;