diff --git a/api/panel/index.php b/api/panel/index.php index cba32848..98caa912 100644 --- a/api/panel/index.php +++ b/api/panel/index.php @@ -5,7 +5,7 @@ * you can call this HTTP(s) API as follow: * from the base url https://panel.example.fr/api/ * 1. /api/post use GETted data (?token=xx&object=xx&action=yy&option1=value1&option2=value2 - * 2. /api/post use POSTED data using the same keys + * 2. /api/post use POSTED json data using the same keys * 3. use a sub-url (rest-style) of the form /api/rest/object/action?token=xx&option1=value1&option2=value2 * 4. the same (REST) but options and value are POSTED * @@ -17,19 +17,12 @@ * Authentication is done by asking for /api/auth/?option1=value1&option2=value2 * or POSTED data * a token is returned for this session - * + * Use /api/auth to know which method you can use and what parameter they expect + * @todo add HTML pages that will self-document this API */ - - // bootstrap AlternC require_once("bootstrap.php"); -// Which api method is used ? -define("API_CALL_GET", 1 ); -define("API_CALL_POST", 2 ); -define("API_CALL_POST_REST", 3 ); -define("API_CALL_GET_REST", 4 ); - /** * Attempts to load a class in multiple path, the PSR-0 or old style way * @@ -38,171 +31,186 @@ define("API_CALL_GET_REST", 4 ); * @param string $class_name * @return boolean */ - -function __autoload($class_name) -{ +function __autoload($class_name) { // Contains (Namespace) => directory - static $srcPathList = array(); - static $init=null; + static $srcPathList = array(); + static $init = null; // Attempts to set include path and directories once - if( is_null( $init )){ + if (is_null($init)) { // Sets init flag - $init = true; - + $init = true; + // Sets a contextual directory - $srcPathList["standard"] = "/usr/share/php"; + $srcPathList["standard"] = "/usr/share/php"; // Updates include_path according to this list - $includePathList = explode(PATH_SEPARATOR, get_include_path()); + $includePathList = explode(PATH_SEPARATOR, get_include_path()); - foreach($srcPathList as $path){ - if ( !in_array($path, $includePathList)){ - $includePathList[] = $path; + foreach ($srcPathList as $path) { + if (!in_array($path, $includePathList)) { + $includePathList[] = $path; } } // Reverses the path for search efficiency - $finalIncludePathList = array_reverse($includePathList); - + $finalIncludePathList = array_reverse($includePathList); + // Sets the updated include_path set_include_path(implode(PATH_SEPARATOR, $finalIncludePathList)); - } - + // Accepts old Foo_Bar namespacing - if(preg_match("/_/", $class_name)){ - $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name) . '.php'; - - // Accepts 5.3 Foo\Bar PSR-0 namespacing - } else if(preg_match("/\\/", $class_name)){ - $file_name = str_replace('\\', DIRECTORY_SEPARATOR, ltrim($class_name,'\\')) . '.php'; - - // Accepts non namespaced classes + if (preg_match("/_/", $class_name)) { + $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name) . '.php'; + + // Accepts 5.3 Foo\Bar PSR-0 namespacing + } else if (preg_match("/\\/", $class_name)) { + $file_name = str_replace('\\', DIRECTORY_SEPARATOR, ltrim($class_name, '\\')) . '.php'; + + // Accepts non namespaced classes } else { - $file_name = $class_name . '.php'; + $file_name = $class_name . '.php'; } // Attempts to find file in namespace - foreach($srcPathList as $namespace => $path ){ - $file_path = $path.DIRECTORY_SEPARATOR.$file_name; - if(is_file($file_path) && is_readable($file_path)){ + foreach ($srcPathList as $namespace => $path) { + $file_path = $path . DIRECTORY_SEPARATOR . $file_name; + if (is_file($file_path) && is_readable($file_path)) { require $file_path; return true; } } - + // Failed to find file return false; } +function apicall($data, $token) { + global $dbh; + $options["databaseAdapter"] = $dbh; + $options["loginAdapterList"] = array("sharedsecret", "login"); + // TODO (no loggerAdapter PSR3-Interface-compliant class as of now) + try { + $data["token_hash"] = $token; + $service = new Alternc_Api_Service($options); -function apicall($data,$token,$mode) { - global $dbh; - $options["databaseAdapter"]=$dbh; - $options["loginAdapterList"]=array("sharedsecret","login"); - // TODO (no loggerAdapter PSR3-Interface-compliant class as of now) - try { - $data["token_hash"]=$token; - $service=new Alternc_Api_Service($options); + $response = $service->call( + new Alternc_Api_Request($data) + ); - $response = $service->call( - new Alternc_Api_Request($data) - ); - - header("Content-Type: application/json"); - echo $response->toJson(); - exit(); - - } catch (Exception $e) { - // something went wrong, we spit out the exception as an Api_Response - // TODO : Don't do that on production! spit out a generic "fatal error" code and LOG the exception ! - header("Content-Type: application/json"); - $response=new Alternc_Api_Response(array("code" => $e->getCode(), "message" => $e->getMessage() )); - echo $response->toJson(); - exit(); - } + header("Content-Type: application/json"); + echo $response->toJson(); + exit(); + } catch (Exception $e) { + // something went wrong, we spit out the exception as an Api_Response + // TODO : Don't do that on production! spit out a generic "fatal error" code and LOG the exception ! + header("Content-Type: application/json"); + $response = new Alternc_Api_Response(array("code" => $e->getCode(), "message" => $e->getMessage())); + echo $response->toJson(); + exit(); + } } -function apiauth($data,$mode) { - global $dbh; - $options["databaseAdapter"]=$dbh; - // TODO (no loggerAdapter PSR3-Interface-compliant class as of now) - try { - - $service=new Alternc_Api_Service($options); - - $response = $service->auth($data); - - header("Content-Type: application/json"); - echo $response->toJson(); - exit(); - - } catch (Exception $e) { - // something went wrong, we spit out the exception as an Api_Response - // TODO : Don't do that on production! spit out a generic "fatal error" code and LOG the exception ! - header("Content-Type: application/json"); - $response=new Alternc_Api_Response(array("code" => $e->code, "message" => $e->message)); - echo $response->toJson(); - exit(); - } +function apiauth($data) { + global $dbh; + $options["databaseAdapter"] = $dbh; + // TODO (no loggerAdapter PSR3-Interface-compliant class as of now) + try { + $service = new Alternc_Api_Service($options); + $response = $service->auth($data); + header("Content-Type: application/json"); + echo $response->toJson(); + exit(); + } catch (Exception $e) { + // something went wrong, we spit out the exception as an Api_Response + // TODO : Don't do that on production! spit out a generic "fatal error" code and LOG the exception ! + header("Content-Type: application/json"); + $response = new Alternc_Api_Response(array("code" => $e->code, "message" => $e->message)); + echo $response->toJson(); + exit(); + } } - +/** + * Main code: either we are authenticating + * or calling one of the APIs + * or asking for some documentation + */ // Authentication -if (preg_match("#^/api/auth/([^/\?]*)[/\?]?#",$_SERVER["REQUEST_URI"],$mat)) { - if ($_SERVER["REQUEST_METHOD"]=="POST") { - $data=array("options" => $_POST, - "method" => $mat[1]); - apiauth($data,API_CALL_GET); - exit(); - } else { - $data=array("options" => $_GET, - "method" => $mat[1]); - apiauth($data,API_CALL_POST); - exit(); - } +if (preg_match("#^/api/auth/([^/\?]*)[/\?]?#", $_SERVER["REQUEST_URI"], $mat)) { + if ($_SERVER["REQUEST_METHOD"] == "POST") { + $data = array("options" => $_POST, + "method" => $mat[1]); + apiauth($data); + exit(); + } else { + $data = array("options" => $_GET, + "method" => $mat[1]); + apiauth($data); + exit(); + } } // We support 4 api calls methods: -if ($_SERVER["REQUEST_URI"]=="/api/post") { - // simple ?q or POST of json data - if ($_SERVER["REQUEST_METHOD"]=="POST") { - $data=array("options" => $_POST, - "object" => $_POST["object"], - "action" => $_POST["action"], - ); - $token=$_POST["token"]; - apicall($data,$token,API_CALL_POST); - exit(); - } else { - $data=array("options" => $_GET, - "object" => $_GET["object"], - "action" => $_GET["action"], - ); - $token=$_GET["token"]; - apicall($data,$token,API_CALL_GET); - exit(); - } +if ($_SERVER["REQUEST_URI"] == "/api/post") { + // simple ?q or POST of json data + if ($_SERVER["REQUEST_METHOD"] == "POST") { + $data = array("options" => $_POST, + "object" => $_POST["object"], + "action" => $_POST["action"], + ); + $token = $_POST["token"]; + apicall($data, $token); + exit(); + } else { + $data = array("options" => $_GET, + "object" => $_GET["object"], + "action" => $_GET["action"], + ); + $token = $_GET["token"]; + apicall($data, $token); + exit(); + } } -if (preg_match("#^/api/rest/([^/]*)/([^/\?]*)[/\?]?#",$_SERVER["REQUEST_URI"],$mat)) { - if ($_SERVER["REQUEST_METHOD"]=="POST") { - $data=array("options" => $_POST, - "object" => $mat[1], - "action" => $mat[2] - ); - $token=$_POST["token"]; - apicall($data,$token,API_CALL_POST_REST); - exit(); - } else { - $data=array("options" => $_GET, - "object" => $mat[1], - "action" => $mat[2] - ); - $token=$_GET["token"]; - apicall($data,$token,API_CALL_GET_REST); - exit(); - } +if (preg_match("#^/api/rest/([^/]*)/([^/\?]*)[/\?]?#", $_SERVER["REQUEST_URI"], $mat)) { + if ($_SERVER["REQUEST_METHOD"] == "POST") { + $data = array("options" => $_POST, + "object" => $mat[1], + "action" => $mat[2] + ); + $token = $_POST["token"]; + apicall($data, $token); + exit(); + } else { + $data = array("options" => $_GET, + "object" => $mat[1], + "action" => $mat[2] + ); + $token = $_GET["token"]; + apicall($data, $token); + exit(); + } } -echo "I did nothing. Did you call the api properly?"; \ No newline at end of file +function doc($data) { + global $dbh; + $options["databaseAdapter"] = $dbh; + try { + $service = new Alternc_Api_Service($options); + + $response = $service->documentation($data); + return $response; + } catch (Exception $e) { + // something went wrong, we spit out the exception as an Api_Response + // TODO : Don't do that on production! spit out a generic "fatal error" code and LOG the exception ! + header("Content-Type: application/json"); + $response = new Alternc_Api_Response(array("code" => $e->getCode(), "message" => $e->getMessage())); + echo $response->toJson(); + exit(); + } +} + +doc("auth/login"); + +echo "I did nothing. Did you call the api properly?"; diff --git a/lib/Alternc/Api/Auth/Interface.php b/lib/Alternc/Api/Auth/Interface.php index b7042bc9..a716ca27 100644 --- a/lib/Alternc/Api/Auth/Interface.php +++ b/lib/Alternc/Api/Auth/Interface.php @@ -19,8 +19,8 @@ interface Alternc_Api_Auth_Interface { function auth($options); /** - * instructions on how to use this Auth class + * Api Documentation * @return array("fields" => array("fields to send, required or not"), "description" => "description of this auth") */ - function instructions(); + function documentation(); } diff --git a/lib/Alternc/Api/Auth/Login.php b/lib/Alternc/Api/Auth/Login.php index 8b96b17f..ef15f31d 100644 --- a/lib/Alternc/Api/Auth/Login.php +++ b/lib/Alternc/Api/Auth/Login.php @@ -1,7 +1,8 @@ array("fields to send, required or not"), "description" => "description of this auth") */ - function instructions() { + function documentation() { return array("fields" => array("login" => "AlternC user account", "password" => "AlternC's user password stored in membres table."), "description" => "Authenticate against an AlternC user and password, the same as for the control panel" ); diff --git a/lib/Alternc/Api/Auth/Sharedsecret.php b/lib/Alternc/Api/Auth/Sharedsecret.php index 363a40da..fc331207 100644 --- a/lib/Alternc/Api/Auth/Sharedsecret.php +++ b/lib/Alternc/Api/Auth/Sharedsecret.php @@ -28,8 +28,6 @@ class Alternc_Api_Auth_Sharedsecret implements Alternc_Api_Auth_Interface { $this->db = $service->getDb(); } -// __construct - /** * Authenticate a user * @@ -67,10 +65,10 @@ class Alternc_Api_Auth_Sharedsecret implements Alternc_Api_Auth_Interface { } /** - * instructions on how to use this Auth class + * Api Documentation * @return array("fields" => array("fields to send, required or not"), "description" => "description of this auth") */ - function instructions() { + function documentation() { return array("fields" => array("login" => "AlternC user account", "secret" => "API Key, Shared secrets, valid for this account, stored in sharedsecret table."), "description" => "Authenticate against an Api Key, also called SharedSecret. distinct from the account's password, can be plenty and revoked independently" ); diff --git a/lib/Alternc/Api/Object/Mysql.php b/lib/Alternc/Api/Object/Mysql.php index bdacb47d..6852c877 100644 --- a/lib/Alternc/Api/Object/Mysql.php +++ b/lib/Alternc/Api/Object/Mysql.php @@ -9,17 +9,8 @@ class Alternc_Api_Object_Mysql extends Alternc_Api_Legacyobject { function __construct($service) { global $mysql; - if (!($service instanceof Alternc_Api_Service)) { - throw new \Exception("Bad argument: service is not an Alternc_Api_Service", self::ERR_INVALID_ARGUMENT); - } - // We store the global $cuid to AlternC legacy classes - $this->cuid = $cuid = $service->token->uid; - $this->isAdmin = $service->token->isAdmin; - // We use the global $admin from AlternC legacy classes - $this->admin = $admin; + parent::__construct($service); $this->mysql = $mysql; - // Set the legacy rights: - $this->admin->enabled = $this->isAdmin; } /** API Method from legacy class method admin->add_mem() @@ -216,4 +207,4 @@ class Alternc_Api_Object_Mysql extends Alternc_Api_Legacyobject { } -// class Alternc_Api_Object_Account \ No newline at end of file +// class Alternc_Api_Object_Mysql \ No newline at end of file diff --git a/lib/Alternc/Api/Service.php b/lib/Alternc/Api/Service.php index 52fa3b44..19128878 100644 --- a/lib/Alternc/Api/Service.php +++ b/lib/Alternc/Api/Service.php @@ -5,6 +5,8 @@ /** * Service API used by server to export API methods + * this class can be used to implement an API service / endpoint + * a REST and POST api is provided as an example */ class Alternc_Api_Service { @@ -64,8 +66,6 @@ class Alternc_Api_Service { } } -// __construct - /** * Authenticate into an AlternC server * @param $auth hash with @@ -144,7 +144,7 @@ class Alternc_Api_Service { $action = $request->action; - if (strpos($action,"-")!==false) { + if (strpos($action, "-") !== false) { // replace - by an uppercase letter: $action = lcfirst(str_replace(" ", "", implode("", array_map("ucfirst", explode("-", $action))))); } @@ -156,6 +156,33 @@ class Alternc_Api_Service { return $object->$action($request->options); } + /** + * Return documentation of the API, either general (no parameters) + * or for a specific action or auth class + * @param string $element the name of the object for which documentation is requested + * @return array a documentation hash (key/value) + */ + function doc($element) { + if (substr($element, 0, 5) == "auth/") { + $adapterName = "Alternc_Api_Auth_" . ucfirst(strtolower(substr($element, 5))); + if (!class_exists($adapterName)) + return false; + $authAdapter = new $adapterName($this); + return $authAdapter->documentation(); + } else { + list($class, $action) = explode("/", $element); + $className = "Alternc_Api_Object_" . ucfirst(strtolower($class)); + if (!class_exists($className)) + return false; + $object = new $className($this); + if (!$action) { + return $authAdapter->documentation(); + } else { + return $authAdapter->documentation($action); + } + } + } + /** * Getter for the databaseAdapter * (used by authAdapter) @@ -167,4 +194,3 @@ class Alternc_Api_Service { } // class Alternc_Api_Service -