API: Remove api response structure from OC_OCS_Result, handle multiple registered methods for api calls

This commit is contained in:
Tom Needham 2013-01-25 12:48:59 +00:00
parent 1dac2ba496
commit 934735043b
2 changed files with 158 additions and 45 deletions

View File

@ -33,6 +33,13 @@ class OC_API {
const USER_AUTH = 1; const USER_AUTH = 1;
const SUBADMIN_AUTH = 2; const SUBADMIN_AUTH = 2;
const ADMIN_AUTH = 3; const ADMIN_AUTH = 3;
/**
* API Response Codes
*/
const RESPOND_UNAUTHORISED = 997;
const RESPOND_SERVER_ERROR = 996;
const RESPOND_NOT_FOUND = 998;
const RESPOND_UNKNOWN_ERROR = 999;
private static $server; private static $server;
@ -42,12 +49,12 @@ class OC_API {
private static function init() { private static function init() {
self::$server = new OC_OAuth_Server(new OC_OAuth_Store()); self::$server = new OC_OAuth_Server(new OC_OAuth_Store());
} }
/** /**
* api actions * api actions
*/ */
protected static $actions = array(); protected static $actions = array();
/** /**
* registers an api call * registers an api call
* @param string $method the http method * @param string $method the http method
@ -58,7 +65,7 @@ class OC_API {
* @param array $defaults * @param array $defaults
* @param array $requirements * @param array $requirements
*/ */
public static function register($method, $url, $action, $app, public static function register($method, $url, $action, $app,
$authLevel = OC_API::USER_AUTH, $authLevel = OC_API::USER_AUTH,
$defaults = array(), $defaults = array(),
$requirements = array()) { $requirements = array()) {
@ -71,9 +78,9 @@ class OC_API {
->action('OC_API', 'call'); ->action('OC_API', 'call');
self::$actions[$name] = array(); self::$actions[$name] = array();
} }
self::$actions[$name] = array('app' => $app, 'action' => $action, 'authlevel' => $authLevel); self::$actions[$name][] = array('app' => $app, 'action' => $action, 'authlevel' => $authLevel);
} }
/** /**
* handles an api call * handles an api call
* @param array $parameters * @param array $parameters
@ -86,29 +93,99 @@ class OC_API {
parse_str(file_get_contents("php://input"), $parameters['_delete']); parse_str(file_get_contents("php://input"), $parameters['_delete']);
} }
$name = $parameters['_route']; $name = $parameters['_route'];
// Check authentication and availability // Foreach registered action
if(self::isAuthorised(self::$actions[$name])) { $responses = array();
if(is_callable(self::$actions[$name]['action'])) { foreach(self::$actions[$name] as $action) {
$response = call_user_func(self::$actions[$name]['action'], $parameters); // Check authentication and availability
if(!($response instanceof OC_OCS_Result)) { if(!self::isAuthorised(self::$actions[$name])) {
$response = new OC_OCS_Result(null, 996, 'Internal Server Error'); $responses[] = array(
} 'app' => $action['app'],
} else { 'response' => new OC_OCS_Result(null, OC_API::RESPOND_UNAUTHORISED, 'Unauthorised'),
$response = new OC_OCS_Result(null, 998, 'Api method not found'); );
continue;
} }
} else { if(!is_callable($action['action'])) {
header('WWW-Authenticate: Basic realm="Authorization Required"'); $responses[] = array(
header('HTTP/1.0 401 Unauthorized'); 'app' => $action['app'],
$response = new OC_OCS_Result(null, 997, 'Unauthorised'); 'response' => new OC_OCS_Result(null, OC_API::RESPOND_NOT_FOUND, 'Api method not found'),
);
continue;
}
// Run the action
$responses[] = array(
'app' => $action['app'],
'response' => call_user_func($action['action'], $parameters),
);
} }
// Send the response
$response = self::mergeResponses($responses);
$formats = array('json', 'xml'); $formats = array('json', 'xml');
$format = !empty($_GET['format']) && in_array($_GET['format'], $formats) ? $_GET['format'] : 'xml'; $format = !empty($_GET['format']) && in_array($_GET['format'], $formats) ? $_GET['format'] : 'xml';
self::respond($response, $format); self::respond($response);
// logout the user to be stateless
OC_User::logout(); OC_User::logout();
} }
/**
* merge the returned result objects into one response
* @param array $responses
*/
private static function mergeResponses($responses) {
$response = array();
// Sort into shipped and thirdparty
$shipped = array(
'succeded' => array(),
'failed' => array(),
);
$thirdparty = array(
'succeeded' => array(),
'failed' => array(),
);
foreach($responses as $response) {
if(OC_App::isShipped($response['app'])) {
if($response['response']->succeeded()) {
$shipped['succeeded'][$response['app']] = $response['response'];
} else {
$shipped['failed'][$response['app']] = $response['response'];
}
} else {
if($response['response']->succeeded()) {
$thirdparty['succeeded'][$response['app']] = $response['response'];
} else {
$thirdparty['failed'][$response['app']] = $response['response'];
}
}
}
// Remove any error responses if there is one shipped response that succeeded
if(!empty($shipped['succeeded'])){
$responses = array_merge($shipped['succeeded'], $thirdparty['succeeded']);
} else if(!empty($shipped['failed'])){
// Which shipped response do we use if they all failed?
// They may have failed for different reasons (different status codes)
// Which reponse code should we return?
// Maybe any that are not OC_API::RESPOND_SERVER_ERROR
$response = $shipped['failed'][0];
return $response;
} else {
// Return the third party failure result
$response = $thirdparty['failed'][0];
return $response;
}
// Merge the successful responses
$meta = array();
$data = array();
foreach($responses as $app => $response) {
if(OC_App::isShipped($app)) {
$data = array_merge_recursive($response->getData(), $data);
} else {
$data = array_merge_recursive($data, $response->getData());
}
}
$result = new OC_OCS_Result($data, 100);
return $result;
}
/** /**
* authenticate the api call * authenticate the api call
* @param array $action the action details as supplied to OC_API::register() * @param array $action the action details as supplied to OC_API::register()
@ -132,7 +209,8 @@ class OC_API {
return false; return false;
} else { } else {
$subAdmin = OC_SubAdmin::isSubAdmin($user); $subAdmin = OC_SubAdmin::isSubAdmin($user);
if($subAdmin) { $admin = OC_Group::inGroup($user, 'admin');
if($subAdmin || $admin) {
return true; return true;
} else { } else {
return false; return false;
@ -145,7 +223,7 @@ class OC_API {
if(!$user) { if(!$user) {
return false; return false;
} else { } else {
return OC_User::isAdminUser($user); return OC_Group::inGroup($user, 'admin');
} }
break; break;
default: default:
@ -153,25 +231,35 @@ class OC_API {
return false; return false;
break; break;
} }
} }
/** /**
* http basic auth * http basic auth
* @return string|false (username, or false on failure) * @return string|false (username, or false on failure)
*/ */
private static function loginUser(){ private static function loginUser(){
$authUser = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : ''; $authUser = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '';
$authPw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : ''; $authPw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
return OC_User::login($authUser, $authPw) ? $authUser : false; return OC_User::login($authUser, $authPw) ? $authUser : false;
} }
/** /**
* respond to a call * respond to a call
* @param int|array $result the result from the api method * @param OC_OCS_Result $result
* @param string $format the format xml|json * @param string $format the format xml|json
*/ */
private static function respond($result, $format='xml') { private static function respond($result, $format='xml') {
$response = array('ocs' => $result->getResult()); // Send 401 headers if unauthorised
if($result->getStatusCode() === self::RESPOND_UNAUTHORISED) {
header('WWW-Authenticate: Basic realm="Authorisation Required"');
header('HTTP/1.0 401 Unauthorized');
}
$response = array(
'ocs' => array(
'meta' => $result->getMeta(),
'data' => $result->getData(),
),
);
if ($format == 'json') { if ($format == 'json') {
OC_JSON::encodedPrint($response); OC_JSON::encodedPrint($response);
} else if ($format == 'xml') { } else if ($format == 'xml') {
@ -188,10 +276,13 @@ class OC_API {
private static function toXML($array, $writer) { private static function toXML($array, $writer) {
foreach($array as $k => $v) { foreach($array as $k => $v) {
if (is_numeric($k)) { if ($k[0] === '@') {
$writer->writeAttribute(substr($k, 1), $v);
continue;
} else if (is_numeric($k)) {
$k = 'element'; $k = 'element';
} }
if (is_array($v)) { if(is_array($v)) {
$writer->startElement($k); $writer->startElement($k);
self::toXML($v, $writer); self::toXML($v, $writer);
$writer->endElement(); $writer->endElement();
@ -200,5 +291,5 @@ class OC_API {
} }
} }
} }
} }

View File

@ -49,26 +49,48 @@ class OC_OCS_Result{
public function setItemsPerPage(int $items) { public function setItemsPerPage(int $items) {
$this->perPage = $items; $this->perPage = $items;
} }
/** /**
* returns the data associated with the api result * get the status code
* @return int
*/
public function getStatusCode() {
return $this->statusCode;
}
/**
* get the meta data for the result
* @return array * @return array
*/ */
public function getResult() { public function getMeta() {
$return = array(); $meta = array();
$return['meta'] = array(); $meta['status'] = ($this->statusCode === 100) ? 'ok' : 'failure';
$return['meta']['status'] = ($this->statusCode === 100) ? 'ok' : 'failure'; $meta['statuscode'] = $this->statusCode;
$return['meta']['statuscode'] = $this->statusCode; $meta['message'] = $this->message;
$return['meta']['message'] = $this->message;
if(isset($this->items)) { if(isset($this->items)) {
$return['meta']['totalitems'] = $this->items; $meta['totalitems'] = $this->items;
} }
if(isset($this->perPage)) { if(isset($this->perPage)) {
$return['meta']['itemsperpage'] = $this->perPage; $meta['itemsperpage'] = $this->perPage;
} }
$return['data'] = $this->data; return $meta;
// Return the result data.
return $return; }
/**
* get the result data
* @return array|string|int
*/
public function getData() {
return $this->data;
}
/**
* return bool if the method succedded
* @return bool
*/
public function succeeded() {
return (substr($this->statusCode, 0, 1) === '1');
} }