From 422950d3863952d3e1837a9512a170f20ad5fbf4 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Sat, 30 Apr 2022 10:18:09 +0200 Subject: [PATCH] add cli command to run API commands Signed-off-by: Michael Kaufmann --- bin/froxlor-cli | 2 + lib/Froxlor/Cli/RunApiCommand.php | 142 ++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 lib/Froxlor/Cli/RunApiCommand.php diff --git a/bin/froxlor-cli b/bin/froxlor-cli index 29753950..664f6be8 100755 --- a/bin/froxlor-cli +++ b/bin/froxlor-cli @@ -27,6 +27,7 @@ declare(strict_types=1); use Symfony\Component\Console\Application; +use Froxlor\Cli\RunApiCommand; use Froxlor\Cli\ConfigServices; use Froxlor\Cli\PhpSessionclean; use Froxlor\Cli\SwitchServerIp; @@ -46,6 +47,7 @@ require dirname(__DIR__) . '/vendor/autoload.php'; require dirname(__DIR__) . '/lib/tables.inc.php'; $application = new Application('froxlor-cli', Froxlor::getFullVersion()); +$application->add(new RunApiCommand()); $application->add(new ConfigServices()); $application->add(new PhpSessionclean()); $application->add(new SwitchServerIp()); diff --git a/lib/Froxlor/Cli/RunApiCommand.php b/lib/Froxlor/Cli/RunApiCommand.php new file mode 100644 index 00000000..5dd8b95e --- /dev/null +++ b/lib/Froxlor/Cli/RunApiCommand.php @@ -0,0 +1,142 @@ + + * @license https://files.froxlor.org/misc/COPYING.txt GPLv2 + */ + +namespace Froxlor\Cli; + +use Exception; +use PDO; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Output\OutputInterface; +use Froxlor\Database\Database; + +final class RunApiCommand extends CliCommand +{ + + protected function configure() + { + $this->setName('froxlor:api-call'); + $this->setDescription('Run an API command as given user'); + $this->addArgument('user', InputArgument::REQUIRED, 'Loginname of the user you want to run the command as') + ->addArgument('api-command', InputArgument::REQUIRED, 'The command to execute in the form "Module.function"') + ->addArgument('parameters', InputArgument::OPTIONAL, 'Paramaters to pass to the command as JSON array'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $result = self::SUCCESS; + + $result = $this->validateRequirements($input, $output); + + try { + $loginname = $input->getArgument('user'); + $userinfo = $this->getUserByName($loginname); + $command = $input->getArgument('api-command'); + $apicmd = $this->validateCommand($command); + $module = "\\Froxlor\\Api\\Commands\\" . $apicmd['class']; + $function = $apicmd['function']; + $params_json = $input->getArgument('parameters'); + $params = json_decode($params_json ?? '', true); + $json_result = $module::getLocal($userinfo, $params)->{$function}(); + $output->write($json_result); + $result = self::SUCCESS; + } catch (Exception $e) { + $output->writeln('' . $e->getMessage() . ''); + $result = self::FAILURE; + } + + return $result; + } + + private function validateCommand(string $command): array + { + $command = explode(".", $command); + + if (count($command) != 2) { + throw new Exception("The given command is invalid."); + } + // 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\\Api\\Commands\\' . $command[0]; + if (!class_exists($apiclass) || !@method_exists($apiclass, $command[1])) { + throw new Exception("Unknown command"); + } + return ['class' => $command[0], 'function' => $command[1]]; + } + + private function getUserByName(?string $loginname): array + { + if (empty($loginname)) { + throw new Exception("Empty username"); + } + + $stmt = Database::prepare(" + SELECT `loginname` AS `customer` + FROM `" . TABLE_PANEL_CUSTOMERS . "` + WHERE `loginname`= :loginname + "); + Database::pexecute($stmt, [ + "loginname" => $loginname + ]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($row && $row['customer'] == $loginname) { + $table = "`" . TABLE_PANEL_CUSTOMERS . "`"; + $adminsession = '0'; + } else { + $stmt = Database::prepare(" + SELECT `loginname` AS `admin` FROM `" . TABLE_PANEL_ADMINS . "` + WHERE `loginname`= :loginname + "); + Database::pexecute($stmt, [ + "loginname" => $loginname + ]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($row && $row['admin'] == $loginname) { + $table = "`" . TABLE_PANEL_ADMINS . "`"; + $adminsession = '1'; + } else { + throw new Exception("Unknown user '" . $loginname . "'"); + } + } + + $userinfo_stmt = Database::prepare(" + SELECT * FROM $table + WHERE `loginname`= :loginname + "); + Database::pexecute($userinfo_stmt, [ + "loginname" => $loginname + ]); + $userinfo = $userinfo_stmt->fetch(PDO::FETCH_ASSOC); + $userinfo['adminsession'] = $adminsession; + + if ($userinfo['deactivated']) { + throw new Exception("User '" . $loginname . "' is currently deactivated"); + } + + return $userinfo; + } +}