]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
Add endpoint to list AWS cloud buckets
authorMarcin Haba <marcin.haba@bacula.pl>
Wed, 10 Jan 2024 10:47:37 +0000 (11:47 +0100)
committerMarcin Haba <marcin.haba@bacula.pl>
Thu, 18 Jan 2024 09:22:37 +0000 (10:22 +0100)
gui/baculum/protected/API/Modules/AWSCliTool.php [new file with mode: 0644]
gui/baculum/protected/API/Pages/API/AWSListBuckets.php [new file with mode: 0644]
gui/baculum/protected/API/Pages/API/config.xml
gui/baculum/protected/API/Pages/API/endpoints.xml
gui/baculum/protected/Common/Modules/Errors/AWSToolError.php [new file with mode: 0644]
gui/baculum/protected/Common/Modules/Miscellaneous.php

diff --git a/gui/baculum/protected/API/Modules/AWSCliTool.php b/gui/baculum/protected/API/Modules/AWSCliTool.php
new file mode 100644 (file)
index 0000000..d450eb0
--- /dev/null
@@ -0,0 +1,207 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2024 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+namespace Baculum\API\Modules;
+
+use Baculum\Common\Modules\Errors\AWSToolError;
+use Baculum\Common\Modules\Logging;
+
+/**
+ * Module responsible for managing AWS CLI tool.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum API
+ */
+class AWSCliTool extends APIModule {
+
+       /**
+        * SUDO command.
+        */
+       const SUDO = 'sudo';
+
+       /**
+        * AWS CLI tool command pattern.
+        */
+       const AWS_TOOL_COMMAND_PATTERN = 'LANG=C AWS_ACCESS_KEY_ID="%access_key" AWS_SECRET_ACCESS_KEY="%secret_key" %use_sudo%bin %options 2>&1';
+
+       /**
+        * Check if AWS CLI tool is enabled.
+        *
+        * @return bool true if it is enabled, otherwise false
+        */
+       private function isToolEnabled() {
+               // temporary set staticaly to enabled
+               return true;
+       }
+
+       /**
+        * Prepare AWS CLI tool results.
+        *
+        * @param mixed $output result output
+        * @param integer $error error number
+        * @return array output and error
+        */
+       public function prepareResult($output, $error) {
+               $result = [
+                       'output' => $output,
+                       'error' => $error
+               ];
+               return $result;
+       }
+
+       /**
+        * Prepare and check AWS CLI tool output.
+        *
+        * @param array $output command output
+        * @return mixed array with results or null if output is unable to parse
+        */
+       private function prepareOutput(array $output) {
+               $output_txt = implode('', $output);
+               $out = json_decode($output_txt, true);
+               if (!is_array($out)) {
+                       $this->getModule('logging')->log(
+                               Logging::CATEGORY_EXTERNAL,
+                               "Parse output error: $output_txt"
+                       );
+                       $out = null;
+               }
+               return $out;
+       }
+
+       /**
+        * Get sudo command.
+        *
+        * @param bool $use_sudo true to use sudo, false otherwise
+        * @return string sudo command
+        */
+       private function getSudo($use_sudo = false) {
+               $sudo = '';
+               if ($use_sudo === true) {
+                       $sudo = self::SUDO . ' ';
+               }
+               return $sudo;
+       }
+
+       /**
+        * Execute AWS CLI command.
+        *
+        * @param array $params command parameters
+        * @return mixed array with results or null if AWS CLI is disabled.
+        */
+       public function execCommand($params = []) {
+               $result = null;
+               if ($this->isToolEnabled() === true) {
+                       $tool = $this->getTool();
+                       $result = $this->execTool(
+                               $tool['bin'],
+                               $tool['use_sudo'],
+                               $params
+                       );
+                       $output = $result['output'];
+                       $error = $result['error'];
+                       if ($error === 0) {
+                               $output = $this->prepareOutput($output);
+                               if (is_null($output)) {
+                                       $output = AWSToolError::MSG_ERROR_AWS_TOOL_UNABLE_TO_PARSE_OUTPUT;
+                                       $error = AWSToolError::ERROR_AWS_TOOL_UNABLE_TO_PARSE_OUTPUT;
+                               }
+                       } else {
+                               $emsg = PHP_EOL . ' Output:' . implode(PHP_EOL, $output);
+                               $output = AWSToolError::MSG_ERROR_WRONG_EXITCODE . $emsg;
+                               $error = AWSToolError::ERROR_WRONG_EXITCODE;
+                       }
+                       $result = $this->prepareResult($output, $error);
+               } else {
+                       $output = AWSToolError::MSG_ERROR_AWS_TOOL_DISABLED;
+                       $error = AWSToolError::ERROR_AWS_TOOL_DISABLED;
+                       $result = $this->prepareResult($output, $error);
+               }
+               return $result;
+       }
+
+       /**
+        * Get AWS CLI tool binary.
+        *
+        * @return array AWS CLI binary parameters
+        */
+       private function getTool() {
+               /**
+                * Parameters set staticaly because of the temporary AWS CLI implementation.
+                */
+               return [
+                       'bin' => 'aws',
+                       'cfg' => '',
+                       'use_sudo' => false
+               ];
+       }
+
+       /**
+        * Execute AWS CLI tool (internal method)
+        *
+        * @param string $bin tool binary path
+        * @param boolean use_sudo true to use sudo, false otherwise
+        * @return array output and exitcode results
+        */
+       private function execTool($bin, $use_sudo, $params = []) {
+               $sudo = $this->getSudo($use_sudo);
+               $cmd = $this->getCmd($sudo, $bin, $params);
+               exec($cmd, $output, $exitcode);
+               $this->getModule('logging')->log(
+                       Logging::CATEGORY_EXECUTE,
+                       Logging::prepareOutput($cmd, $output)
+               );
+               $result = $this->prepareResult($output, $exitcode);
+               return $result;
+       }
+
+       /**
+        * Get AWS CLi tool command.
+        *
+        * @param string $bin tool binary path
+        * @param bool $use_sudo use sudo with tool
+        * @param array $params command parameters
+        * @return string command
+        */
+       private function getCmd($bin, $use_sudo, $params) {
+               // Default command pattern
+               $pattern = self::AWS_TOOL_COMMAND_PATTERN;
+               $access_key = '';
+               if (key_exists('access_key', $params)) {
+                       $access_key = $params['access_key'];
+               }
+               $secret_key = '';
+               if (key_exists('secret_key', $params)) {
+                       $secret_key = $params['secret_key'];
+               }
+               $options = '';
+               if (key_exists('options', $params) && is_array($params['options']) && count($params['options']) > 0) {
+                       $options = '"' . implode('" "', $params['options']) . '"';
+               }
+               $pattern = str_replace('%access_key', $access_key, $pattern);
+               $pattern = str_replace('%secret_key', $secret_key, $pattern);
+               $pattern = str_replace('%bin', $bin, $pattern);
+               $pattern = str_replace('%use_sudo', $use_sudo, $pattern);
+               $pattern = str_replace('%options', $options, $pattern);
+               return $pattern;
+       }
+}
diff --git a/gui/baculum/protected/API/Pages/API/AWSListBuckets.php b/gui/baculum/protected/API/Pages/API/AWSListBuckets.php
new file mode 100644 (file)
index 0000000..11a78fd
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2024 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+use Baculum\Common\Modules\Errors\AWSToolError;
+use Baculum\API\Modules\BaculumAPIServer;
+
+/**
+ * AWS cloud list buckets.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category API
+ * @package Baculum API
+ */
+class AWSListBuckets extends BaculumAPIServer {
+
+       public function get() {
+               $misc = $this->getModule('misc');
+               $access_key = $this->Request->contains('access_key') && $misc->isValidLogin($this->Request['access_key']) ? $this->Request['access_key'] : '';
+               $secret_key = $this->Request->contains('secret_key') && $misc->isValidSecret($this->Request['secret_key']) ? $this->Request['secret_key'] : '';
+               $endpoint = $this->Request->contains('endpoint') && $misc->isValidURL($this->Request['endpoint']) ? $this->Request['endpoint'] : '';
+               $region = $this->Request->contains('region') && $misc->isValidName($this->Request['region']) ? $this->Request['region'] : '';
+               if (empty($access_key)) {
+                       $this->error = AWSToolError::ERROR_AWS_MISSING_ACCESS_KEY;
+                       $this->output = AWSToolError::MSG_ERROR_AWS_MISSING_ACCESS_KEY;
+                       return;
+               }
+               if (empty($secret_key)) {
+                       $this->error = AWSToolError::ERROR_AWS_MISSING_SECRET_KEY;
+                       $this->output = AWSToolError::MSG_ERROR_AWS_MISSING_SECRET_KEY;
+                       return;
+               }
+               $params['access_key'] = $access_key;
+               $params['secret_key'] = $secret_key;
+               $params['options'] = [
+                       's3api',
+                       'list-buckets'
+               ];
+               if (!empty($endpoint)) {
+                        $params['options'][] = '--endpoint-url=' . $endpoint;
+               }
+               if (!empty($region)) {
+                        $params['options'][] = '--region=' . $region;
+               }
+               $result = $this->getModule('aws_tool')->execCommand($params);
+               $this->output = $result['output'];
+               $this->error = $result['error'];
+       }
+}
index 4f14a0ac63a738f1d2cfe60c0f74354304619c09..c1f8442a76c38ad39827777adec4f4babe718210 100644 (file)
@@ -37,6 +37,7 @@
                <module id="json_tools" class="Baculum\API\Modules\JSONTools" />
                <module id="bvfs" class="Baculum\API\Modules\BVFS" />
                <module id="blstat" class="Baculum\API\Modules\BLStat" />
+               <module id="aws_tool" class="Baculum\API\Modules\AWSCliTool" />
                <!-- config modules -->
                <module id="api_config" class="Baculum\API\Modules\APIConfig" />
                <module id="bacula_config" class="Baculum\API\Modules\BaculaConfig" />
index 0e261779eed912c6c91baef6bdccf8a3dfc47cdf..f3cef97c81ec10d21bbf0d6eabd8f759e47849a0 100644 (file)
        <!-- Basic user endpoints -->
        <url ServiceParameter="BasicUsers" pattern="api/v2/basic/users/" />
        <url ServiceParameter="BasicUser" pattern="api/v2/basic/users/{id}/" parameters.id="[a-zA-Z0-9]+" />
+       <!-- cloud provider endpoints -->
+       <!-- Amazon (AWS) -->
+       <url ServiceParameter="AWSListBuckets" pattern="api/v2/cloud/aws/buckets/list" />
+
        <!-- Plugins -->
        <url ServiceParameter="ClientPluginNames" pattern="api/v2/plugins/client/names/" />
        <url ServiceParameter="PluginCoreDirFileList" pattern="api/v2/plugins/core/{id}/storage/ls/" parameters.id="\d+" />
diff --git a/gui/baculum/protected/Common/Modules/Errors/AWSToolError.php b/gui/baculum/protected/Common/Modules/Errors/AWSToolError.php
new file mode 100644 (file)
index 0000000..68f5dc6
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2024 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+namespace Baculum\Common\Modules\Errors;
+
+/**
+ * AWS tool error class.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Errors
+ * @package Baculum Common
+ */
+class AWSToolError extends GenericError {
+
+       const ERROR_AWS_TOOL_DISABLED = 540;
+       const ERROR_AWS_MISSING_ACCESS_KEY = 541;
+       const ERROR_AWS_MISSING_SECRET_KEY = 542;
+       const ERROR_AWS_TOOL_UNABLE_TO_PARSE_OUTPUT = 543;
+
+       const MSG_ERROR_AWS_TOOL_DISABLED = 'AWS tool support is disabled.';
+       const MSG_ERROR_AWS_MISSING_ACCESS_KEY = 'Missing access key.';
+       const MSG_ERROR_AWS_MISSING_SECRET_KEY = 'Missing secret key.';
+       const MSG_ERROR_AWS_TOOL_UNABLE_TO_PARSE_OUTPUT = 'Unable to parse output.';
+}
index 35216c5f12404b8c903808b1062466ae5f4d30ce..229ad29c3492e27c82ec43b4d6de10512c82d3f8 100644 (file)
@@ -256,6 +256,14 @@ class Miscellaneous extends TModule {
                return (preg_match('/^[\w\-]+$/', $state) === 1);
        }
 
+       public function isValidLogin($login) {
+               return (preg_match('/^\w+$/', $login) === 1);
+       }
+
+       public function isValidSecret($secret) {
+               return (preg_match('/^[\s\S]+$/u', $secret) === 1);
+       }
+
        public function isValidInteger($num) {
                return (preg_match('/^\d+$/', $num) === 1);
        }
@@ -328,6 +336,9 @@ class Miscellaneous extends TModule {
                return filter_var($email, FILTER_VALIDATE_EMAIL);
        }
 
+       public function isValidURL($url) {
+               return filter_var($url, FILTER_VALIDATE_URL);
+       }
        public function isValidColumn($column) {
                return (preg_match('/^[\w+.]+$/i', $column) === 1);
        }