$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();
}
*/
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);
}
}
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2021 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.
+ */
+
+Prado::using('Application.Common.Class.ConfigFileModule');
+
+/**
+ * Manage Basic user configuration.
+ * Module is responsible for get/set Basic user config data.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @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;
+ }
+}
+?>
<module id="oauth2_authid" class="Application.API.Class.OAuth2.AuthIdManager" />
<module id="oauth2_token" class="Application.API.Class.OAuth2.TokenManager" />
+ <!-- Basic user config -->
+ <module id="basic_config" class="Application.API.Class.BasicConfig" />
+
<!-- API Server modules -->
<module id="api_server_v1" class="Application.API.Class.APIServerV1" />
<module id="api_server_v2" class="Application.API.Class.APIServerV2" />
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);
}
}
<tr>
<th></th>
<th><%[ Username ]%></th>
+ <th><%[ Dedicated Bconsole config ]%></th>
<th><%[ Actions ]%></th>
</tr>
</thead>
<tr>
<th></th>
<th><%[ Username ]%></th>
+ <th><%[ Dedicated Bconsole config ]%></th>
<th><%[ Actions ]%></th>
</tr>
</tfoot>
defaultContent: '<button type="button" class="w3-button w3-blue"><i class="fa fa-angle-down"></i></button>'
},
{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) {
},
{
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;
}
};
</script>
},
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) {
</module>
<!-- auth modules -->
<module id="basic_apiuser" class="Application.API.Class.BasicAPIUserConfig" />
+ <module id="basic_config" class="Application.API.Class.BasicConfig" />
<module id="oauth2_config" class="Application.API.Class.OAuth2.OAuth2Config" />
<!-- component actions modules -->
<module id="comp_actions" class="Application.API.Class.ComponentActions" />
/**
* 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.
$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) {
<div class="w3-container">
<div class="w3-padding" style="display: <%=($this->getAuthType() == 'basic' ? '' : 'none')%>">
<div class="w3-row w3-section">
- <div class="w3-col w3-quarter"><com:TLabel ForControl="APIBasicLogin" Text="<%[ API Login: ]%>" /></div>
- <div class="w3-col w3-threequarter">
+ <div class="w3-col w3-third"><com:TLabel ForControl="APIBasicLogin" Text="<%[ API Login: ]%>" /></div>
+ <div class="w3-col w3-twothird">
<com:TActiveTextBox
ID="APIBasicLogin"
CssClass="w3-input w3-border"
ControlToValidate="APIBasicLogin"
ValidationGroup="<%=$this->ClientID%>Basic"
Text="<%[ Please enter API login. ]%>"
- />
+ />
<com:TRegularExpressionValidator
ValidationGroup="<%=$this->ClientID%>Basic"
ControlToValidate="APIBasicLogin"
</div>
</div>
<div class="w3-row w3-section">
- <div class="w3-col w3-quarter"><com:TLabel ForControl="APIBasicPassword" Text="<%[ API Password: ]%>" /></div>
- <div class="w3-col w3-threequarter">
+ <div class="w3-col w3-third"><com:TLabel ForControl="APIBasicPassword" Text="<%[ API Password: ]%>" /></div>
+ <div class="w3-col w3-twothird">
<com:TActiveTextBox
ID="APIBasicPassword"
TextMode="Password"
ControlToValidate="APIBasicPassword"
ValidationGroup="<%=$this->ClientID%>Basic"
Text="<%[ Please enter API password. ]%>"
- />
+ >
+ <prop:ClientSide.OnValidate>
+ sender.enabled = <%=$this->Mode == 'add' ? 'true': 'false'%>;
+ </prop:ClientSide.OnValidate>
+ </com:TRequiredFieldValidator>
<com:TRegularExpressionValidator
CssClass="validator-block"
Display="Dynamic"
</div>
</div>
<div class="w3-row w3-section">
- <div class="w3-col w3-quarter"><com:TLabel ForControl="RetypeAPIBasicPassword" Text="<%[ Retype password: ]%>" /></div>
- <div class="w3-col w3-threequarter">
+ <div class="w3-col w3-third"><com:TLabel ForControl="RetypeAPIBasicPassword" Text="<%[ Retype password: ]%>" /></div>
+ <div class="w3-col w3-twothird">
<com:TActiveTextBox
ID="RetypeAPIBasicPassword"
CssClass="w3-input w3-border"
ControlToValidate="RetypeAPIBasicPassword"
ValidationGroup="<%=$this->ClientID%>Basic"
Text="<%[ Please enter retype password. ]%>"
- />
+ >
+ <prop:ClientSide.OnValidate>
+ sender.enabled = <%=$this->Mode == 'add' ? 'true': 'false'%>;
+ </prop:ClientSide.OnValidate>
+ </com:TRequiredFieldValidator>
<com:TRegularExpressionValidator
CssClass="validator-block"
Display="Dynamic"
/>
</div>
</div>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="APIBasicBconsoleCfgPath" Text="<%[ Dedicated Bconsole config file path: ]%>" /></div>
+ <div class="w3-col w3-twothird">
+ <com:TTextBox
+ ID="APIBasicBconsoleCfgPath"
+ CssClass="w3-input w3-border"
+ Style="width: 70%"
+ CausesValidation="false"
+ /> <%[ (optional) ]%>
+ </div>
+ </div>
</div>
<div style="display: <%=($this->getAuthType() == 'oauth2' ? '' : 'none')%>">
<div class="w3-row w3-section">
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%>',
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) {
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 = '';