From: Marcin Haba Date: Fri, 12 Nov 2021 03:20:47 +0000 (+0100) Subject: baculum: Add capability to assign dedicated bconsole config file to API basic users X-Git-Tag: Release-11.0.6~38 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ae7fe83ea349e56597ade4d9abb6b51354df720c;p=thirdparty%2Fbacula.git baculum: Add capability to assign dedicated bconsole config file to API basic users --- diff --git a/gui/baculum/protected/API/Class/BaculumAPIServer.php b/gui/baculum/protected/API/Class/BaculumAPIServer.php index 4713b6b25..d4acd0462 100644 --- a/gui/baculum/protected/API/Class/BaculumAPIServer.php +++ b/gui/baculum/protected/API/Class/BaculumAPIServer.php @@ -99,6 +99,12 @@ abstract class BaculumAPIServer extends TPage { $config = $this->getModule('api_config')->getConfig('api'); if ($config['auth_type'] === 'basic' && $this->getModule('auth_basic')->isAuthRequest()) { $is_auth = true; + $username = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null; + if ($username) { + $props = $this->getModule('basic_config')->getConfig($username); + $this->initAuthParams($props); + } + } elseif ($config['auth_type'] === 'oauth2' && $this->getModule('auth_oauth2')->isAuthRequest()) { $is_auth = $this->authorize(); } @@ -234,7 +240,7 @@ abstract class BaculumAPIServer extends TPage { */ private function initAuthParams(array $auth) { // if client has own bconsole config, assign it here - if (array_key_exists('bconsole_cfg_path', $auth) && !empty($auth['bconsole_cfg_path'])) { + if (key_exists('bconsole_cfg_path', $auth) && !empty($auth['bconsole_cfg_path'])) { Bconsole::setCfgPath($auth['bconsole_cfg_path'], true); } } diff --git a/gui/baculum/protected/API/Class/BasicConfig.php b/gui/baculum/protected/API/Class/BasicConfig.php new file mode 100644 index 000000000..b41ecc902 --- /dev/null +++ b/gui/baculum/protected/API/Class/BasicConfig.php @@ -0,0 +1,213 @@ + + * @category Authorization + * @package Baculum API + */ +class BasicConfig extends ConfigFileModule { + + /** + * Basic user config file path + */ + const CONFIG_FILE_PATH = 'Application.API.Config.basic'; + + /** + * Basic user config file format + */ + const CONFIG_FILE_FORMAT = 'ini'; + + /** + * These options are obligatory for Basic config. + */ + private $required_options = []; + + /** + * Stores basic user config content. + */ + private $config = null; + + /** + * Get (read) Basic user config. + * + * @access public + * @param string $section config section name + * @return array config + */ + public function getConfig($section = null) { + $config = []; + if (is_null($this->config)) { + $this->config = $this->readConfig(self::CONFIG_FILE_PATH, self::CONFIG_FILE_FORMAT); + } + $is_valid = true; + if (!is_null($section)) { + $config = key_exists($section, $this->config) ? $this->config[$section] : []; + $is_valid = $this->validateConfig($config); + } else { + foreach ($this->config as $username => $value) { + if ($this->validateConfig($value) === false) { + $is_valid = false; + break; + } + $config[$username] = $value; + } + } + if ($is_valid === false) { + // no validity, no config + $config = []; + } + return $config; + } + + /** + * Set (save) Basic user config. + * + * @access public + * @param array $config config + * @return boolean true if config saved successfully, otherwise false + */ + public function setConfig(array $config) { + $result = $this->writeConfig($config, self::CONFIG_FILE_PATH, self::CONFIG_FILE_FORMAT); + if ($result === true) { + $this->config = null; + } + return $result; + } + + /** + * Validate API config. + * Config validation should be used as early as config data is available. + * Validation is done in read config method. + * + * @access private + * @param array $config config + * @return boolean true if config valid, otherwise false + */ + private function validateConfig(array $config = array()) { + $is_valid = true; + /** + * Don't use validation from parent class because it logs to file in + * case errors and it could cause save to log a private auth params. + */ + for ($i = 0; $i < count($this->required_options); $i++) { + if (!key_exists($this->required_options[$i], $config)) { + $is_valid = false; + $emsg = 'Invalid Basic user config. Missing ' . $this->required_options[$i] . ' option.'; + $this->getModule('logging')->log( + __FUNCTION__, + $emsg, + Logging::CATEGORY_APPLICATION, + __FILE__, + __LINE__ + ); + break; + } + } + return $is_valid; + } + + /** + * Add single basic user to config. + * NOTE: Basic password hashes are stored in separate file. + * @see BasicAPIUserConfig + * + * @param string $username user name + * @param string $password password + * @param array $params user properties + * @return boolean true on success, otherwise false + */ + public function addUser($username, $password, array $props) { + $success = false; + $config = $this->getConfig(); + if (!key_exists($username, $config)) { + $config[$username] = $props; + $success = $this->setConfig($config); + } + if ($success) { + // Set password in the password file + $success = $this->getModule('basic_apiuser')->setUsersConfig( + $username, + $password + ); + } + // TODO: Add rollback and locking + return $success; + } + + /** + * Edit single basic user. + * + * @param string $username user name + * @param string $password password + * @param array $params user properties + * @return boolean true on success, otherwise false + */ + public function editUser($username, $password, array $props = []) { + $success = false; + $config = $this->getConfig(); + if (key_exists($username, $config)) { + // User exists, so edit him + $config[$username] = array_merge($config[$username], $props); + $success = $this->setConfig($config); + } else { + // User does not exists, so add him. + // NOTE: Not all users with password defined are in config file. + $config[$username] = $props; + $success = $this->setConfig($config); + } + if ($success && !empty($password)) { + // Update password in the password file + $success = $this->getModule('basic_apiuser')->setUsersConfig( + $username, + $password + ); + } + // TODO: Add rollback and locking + return $success; + } + + /** + * Remove single basic user. + * + * @param string $username user name + * @return boolean true on success, otherwise false + */ + public function removeUser($username) { + $success = false; + $config = $this->getConfig(); + if (key_exists($username, $config)) { + unset($config[$username]); + $success = $this->setConfig($config); + } + if ($success) { + $success = $this->getModule('basic_apiuser')->removeUser($username); + } + return $success; + } +} +?> diff --git a/gui/baculum/protected/API/Pages/API/config.xml b/gui/baculum/protected/API/Pages/API/config.xml index 568c92f61..a44502c1a 100644 --- a/gui/baculum/protected/API/Pages/API/config.xml +++ b/gui/baculum/protected/API/Pages/API/config.xml @@ -10,6 +10,9 @@ + + + diff --git a/gui/baculum/protected/API/Pages/Panel/APIBasicUsers.php b/gui/baculum/protected/API/Pages/Panel/APIBasicUsers.php index e4688b03c..d9c6f278a 100644 --- a/gui/baculum/protected/API/Pages/Panel/APIBasicUsers.php +++ b/gui/baculum/protected/API/Pages/Panel/APIBasicUsers.php @@ -76,17 +76,24 @@ class APIBasicUsers extends BaculumAPIPage { private function getBasicUsers() { $basic_users = array(); - $basic_cfg = $this->getModule('basic_apiuser')->getUsers(); - foreach($basic_cfg as $user => $pwd) { - $basic_users[] = ['username' => $user]; + $basic_apiuser = $this->getModule('basic_apiuser')->getUsers(); + $basic_config = $this->getModule('basic_config')->getConfig(); + foreach($basic_apiuser as $user => $pwd) { + $bconsole_cfg_path = ''; + if (key_exists($user, $basic_config) && key_exists('bconsole_cfg_path', $basic_config[$user])) { + $bconsole_cfg_path = $basic_config[$user]['bconsole_cfg_path']; + } + $basic_users[] = [ + 'username' => $user, + 'bconsole_cfg_path' => $bconsole_cfg_path + ]; } return $basic_users; } public function deleteBasicUser($sender, $param) { - $config = $this->getModule('basic_apiuser'); $username = $param->getCallbackParameter(); - $config->removeUser($username); + $this->getModule('basic_config')->removeUser($username); $this->loadBasicUsers(null, null); } } diff --git a/gui/baculum/protected/API/Pages/Panel/APIBasicUsers.tpl b/gui/baculum/protected/API/Pages/Panel/APIBasicUsers.tpl index f71ee2ce4..5ecbba64d 100644 --- a/gui/baculum/protected/API/Pages/Panel/APIBasicUsers.tpl +++ b/gui/baculum/protected/API/Pages/Panel/APIBasicUsers.tpl @@ -14,6 +14,7 @@ <%[ Username ]%> + <%[ Dedicated Bconsole config ]%> <%[ Actions ]%> @@ -22,6 +23,7 @@ <%[ Username ]%> + <%[ Dedicated Bconsole config ]%> <%[ Actions ]%> @@ -61,6 +63,23 @@ var oBasicUserList = { defaultContent: '' }, {data: 'username'}, + { + data: 'bconsole_cfg_path', + render: function(data, type, row) { + var ret; + if (type == 'display') { + ret = ''; + if (data) { + var check = document.createElement('I'); + check.className = 'fas fa-check'; + ret = check.outerHTML; + } + } else { + ret = data; + } + return ret; + } + }, { data: 'username', render: function(data, type, row) { @@ -110,10 +129,20 @@ var oBasicUserList = { }, { className: "dt-center", - targets: [ 2 ] + targets: [ 2, 3 ] }], order: [1, 'asc'], }); + }, + get_user_props: function(username) { + var props = {}; + for (var i = 0; i < this.data.length; i++) { + if (this.data[i].username === username) { + props = this.data[i]; + break; + } + } + return props; } }; @@ -197,9 +226,8 @@ var oAPIBasicUsers = { }, edit_user: function(username) { this.edit_obj.clear_basic_fields(); - this.edit_obj.set_basic_props({ - username: username - }); + var props = oBasicUserList.get_user_props(username); + this.edit_obj.set_basic_props(props); this.show_edit_user_window(true); }, delete_user: function(username) { diff --git a/gui/baculum/protected/API/Pages/Panel/config.xml b/gui/baculum/protected/API/Pages/Panel/config.xml index dfc9b44e7..44cd6f41d 100644 --- a/gui/baculum/protected/API/Pages/Panel/config.xml +++ b/gui/baculum/protected/API/Pages/Panel/config.xml @@ -22,6 +22,7 @@ + diff --git a/gui/baculum/protected/Common/Class/OAuth2.php b/gui/baculum/protected/Common/Class/OAuth2.php index 0b55fe45e..c4da2cfed 100644 --- a/gui/baculum/protected/Common/Class/OAuth2.php +++ b/gui/baculum/protected/Common/Class/OAuth2.php @@ -84,10 +84,10 @@ abstract class OAuth2 extends CommonModule { /** * Expiration time in seconds for access token. * - * Temportary set to 15 minutes for testst purposes. - * In production the value SHOULD BE changed. + * It is 1 hour by default. + * In production the value can be changed. */ - const ACCESS_TOKEN_EXPIRES_TIME = 120; + const ACCESS_TOKEN_EXPIRES_TIME = 3600; /** * Scope pattern. diff --git a/gui/baculum/protected/Common/Portlets/NewAuthClient.php b/gui/baculum/protected/Common/Portlets/NewAuthClient.php index 409fdd017..961138f43 100644 --- a/gui/baculum/protected/Common/Portlets/NewAuthClient.php +++ b/gui/baculum/protected/Common/Portlets/NewAuthClient.php @@ -62,22 +62,27 @@ class NewAuthClient extends PortletTemplate { $result = false; $exists = false; - $config = $this->getModule('api_config')->getConfig(); if ($this->getAuthType() === self::AUTH_TYPE_BASIC) { + $basic_config = $this->getModule('basic_config'); + $props = [ + 'bconsole_cfg_path' => $this->APIBasicBconsoleCfgPath->Text + ]; if ($this->Mode == self::MODE_TYPE_ADD) { $users = $this->getModule('basic_apiuser')->getUsers(); if (!key_exists($this->APIBasicLogin->Text, $users)) { - $result = $this->getModule('basic_apiuser')->setUsersConfig( + $result = $basic_config->addUser( $this->APIBasicLogin->Text, - $this->APIBasicPassword->Text + $this->APIBasicPassword->Text, + $props ); } else { $exists = true; } } elseif ($this->Mode === self::MODE_TYPE_EDIT) { - $result = $this->getModule('basic_apiuser')->setUsersConfig( + $result = $basic_config->editUser( $this->APIBasicLoginHidden->Value, - $this->APIBasicPassword->Text + $this->APIBasicPassword->Text, + $props ); } } elseif ($this->getAuthType() === self::AUTH_TYPE_OAUTH2) { diff --git a/gui/baculum/protected/Common/Portlets/NewAuthClient.tpl b/gui/baculum/protected/Common/Portlets/NewAuthClient.tpl index f9e4bd13c..13fbe95b3 100644 --- a/gui/baculum/protected/Common/Portlets/NewAuthClient.tpl +++ b/gui/baculum/protected/Common/Portlets/NewAuthClient.tpl @@ -6,8 +6,8 @@
-
-
+
+
+ />
-
-
+
+
+ > + + sender.enabled = <%=$this->Mode == 'add' ? 'true': 'false'%>; + +
-
-
+
+
+ > + + sender.enabled = <%=$this->Mode == 'add' ? 'true': 'false'%>; + +
+
+
+
+ <%[ (optional) ]%> +
+
@@ -266,7 +285,8 @@ var <%=$this->ClientID%>oNewAuthClient = { username: '<%=$this->APIBasicLogin->ClientID%>', username_hidden: '<%=$this->APIBasicLoginHidden->ClientID%>', password: '<%=$this->APIBasicPassword->ClientID%>', - password_retype: '<%=$this->RetypeAPIBasicPassword->ClientID%>' + password_retype: '<%=$this->RetypeAPIBasicPassword->ClientID%>', + bconsole_cfg_path: '<%=$this->APIBasicBconsoleCfgPath->ClientID%>' }, oauth2: { client_id: '<%=$this->APIOAuth2ClientId->ClientID%>', @@ -289,6 +309,9 @@ var <%=$this->ClientID%>oNewAuthClient = { if (props.hasOwnProperty('username')) { document.getElementById(this.ids.basic.username).value = props.username; document.getElementById(this.ids.basic.username_hidden).value = props.username; + if (props.hasOwnProperty('bconsole_cfg_path')) { + document.getElementById(this.ids.basic.bconsole_cfg_path).value = props.bconsole_cfg_path; + } } }, set_oauth2_props: function(props) { @@ -320,6 +343,7 @@ var <%=$this->ClientID%>oNewAuthClient = { document.getElementById(this.ids.basic.username_hidden).value = ''; document.getElementById(this.ids.basic.password).value = ''; document.getElementById(this.ids.basic.password_retype).value = ''; + document.getElementById(this.ids.basic.bconsole_cfg_path).value = ''; }, clear_oauth2_fields: function() { document.getElementById(this.ids.oauth2.client_id).value = '';