start api implementation

Signed-off-by: Michael Kaufmann (d00p) <d00p@froxlor.org>
This commit is contained in:
Michael Kaufmann (d00p)
2018-02-15 07:47:35 +01:00
parent 5c30961d3c
commit dd371c72a2
9 changed files with 339 additions and 13 deletions

View File

@@ -0,0 +1,129 @@
<?php
abstract class ApiCommand
{
private $is_admin = false;
private $user_data = null;
private $logger = null;
private $cmd_params = null;
public function __construct($header = null, $params = null, $userinfo = null)
{
$this->cmd_params = $params;
if (! empty($header)) {
$this->readUserData($header);
} elseif (! empty($userinfo)) {
$this->user_data = $userinfo;
$this->is_admin = ($userinfo['adminsession'] == 1 && $userinfo['adminid'] > 0) ? true : false;
} else {
throw new Exception("Invalid user data", 500);
}
$this->logger = FroxlorLogger::getInstanceOf($this->user_data);
}
public static function getLocal($userinfo = null, $params = null)
{
return new static(null, $params, $userinfo);
}
/**
* admin flag
*
* @return boolean
*/
protected function isAdmin()
{
return $this->is_admin;
}
/**
* return field from user-table
*
* @param string $detail
*
* @return string
*/
protected function getUserDetail($detail = null)
{
return (isset($this->user_data[$detail]) ? $this->user_data[$detail] : null);
}
/**
* receive field from parameter-list
*
* @param string $param
*
* @throws Exception
* @return mixed
*/
protected function getParam($param = null)
{
if (isset($this->cmd_params[$param])) {
return $this->cmd_params[$param];
}
return null;
}
/**
* return logger instance
*
* @return FroxlorLogger
*/
protected function logger()
{
return $this->logger;
}
protected function response($status, $status_message, $data = null)
{
header("HTTP/1.1 " . $status);
$response['status'] = $status;
$response['status_message'] = $status_message;
$response['data'] = $data;
$json_response = json_encode($response, JSON_PRETTY_PRINT);
return $json_response;
}
public abstract function list();
public abstract function get();
public abstract function add();
public abstract function update();
public abstract function delete();
private function readUserData($header = null)
{
$sel_stmt = Database::prepare("SELECT * FROM `api_keys` WHERE `apikey` = :ak AND `secret` = :as");
$result = Database::pexecute_first($sel_stmt, array(
'ak' => $header['apikey'],
'as' => $header['secret']
), true, true);
if ($result) {
// admin or customer?
if ($result['customerid'] == 0) {
$this->is_admin = true;
$table = 'panel_admins';
$key = "adminid";
} else {
$this->is_admin = false;
$table = 'panel_customers';
$key = "customerid";
}
$sel_stmt = Database::prepare("SELECT * FROM `" . $table . "` WHERE `" . $key . "` = :id");
$this->user_data = Database::pexecute_first($sel_stmt, array(
'id' => ($this->is_admin ? $result['adminid'] : $result['customerid'])
), true, true);
return true;
}
throw new Exception("Invalid API credentials", 400);
}
}

View File

@@ -0,0 +1,6 @@
<?php
if (! defined('FROXLOR_INSTALL_DIR')) {
define('FROXLOR_INSTALL_DIR', dirname(dirname(dirname(__DIR__))));
require_once FROXLOR_INSTALL_DIR . '/lib/tables.inc.php';
require_once FROXLOR_INSTALL_DIR . '/lib/functions.php';
}

View File

@@ -0,0 +1,100 @@
<?php
class FroxlorRPC
{
/**
* validate a given request
*
* @param array $request
*
* @throws Exception
* @return array
*/
public static function validateRequest($request)
{
// check header
if (! isset($request['header']) || empty($request['header'])) {
throw new Exception("Invalid request header", 400);
}
// check authorization
if (! isset($request['header']['apikey']) || empty($request['header']['apikey']) || ! isset($request['header']['secret']) || empty($request['header']['secret'])) {
throw new Exception("No authorization credentials given", 400);
}
self::validateAuth($request['header']['apikey'], $request['header']['secret']);
// check command
return self::validateBody($request);
}
/**
* validates the given api credentials
*
* @param string $key
* @param string $secret
*
* @throws Exception
* @return boolean
*/
private static function validateAuth($key, $secret)
{
$sel_stmt = Database::prepare("SELECT * FROM `api_keys` WHERE `apikey` = :ak AND `secret` = :as");
$result = Database::pexecute_first($sel_stmt, array(
'ak' => $key,
'as' => $secret
), true, true);
if ($result) {
if ($result['apikey'] == $key && $result['secret'] == $secret && ($result['valid_until'] == -1 || $result['valid_until'] >= time())) {
if (!empty($result['allowed_from'])) {
$ip_list = explode(",", $result['allowed_from']);
$access_ip = $_SERVER['REMOTE_ADDR'];
// @fixme finish me
}
return true;
}
}
throw new Exception("Invalid authorization credentials", 400);
}
/**
* validates the given command
*
* @param array $body
*
* @throws Exception
* @return boolean
*/
private static function validateBody($request)
{
// check body
if (! isset($request['body']) || empty($request['body'])) {
throw new Exception("Invalid request body", 400);
}
// check command exists
if (! isset($request['body']['command']) || empty($request['body']['command'])) {
throw new Exception("No command given", 400);
}
$command = explode(".", $request['body']['command']);
if (count($command) != 2) {
throw new Exception("Invalid command", 400);
}
// simply check for file-existance, as we do not want to use our autoloader because this way
// it will recognize non-api classes+methods as valid commands
$apiclass = FROXLOR_INSTALL_DIR . '/lib/classes/api/commands/class.' . $command[0] . '.php';
if (! file_exists($apiclass) || ! @method_exists($command[0], $command[1])) {
// there will be an exception from the autoloader for class_exists hence the try-catch-block
throw new Exception("Unknown command", 400);
}
return array(
'command' => array(
'class' => $command[0],
'method' => $command[1]
),
'params' => isset($request['body']['params']) ? $request['body']['params'] : null
);
}
}