protected/Web/Logs/*
protected/Web/Config/baculum.users
protected/Web/Config/hosts.conf
+protected/Web/Config/users.conf
+protected/Web/Config/roles.conf
protected/Web/Config/settings.conf
protected/Web/Config/session.dump
protected/API/Config/baculum.users
localeapilang = en pl pt
localewebdirsrc = $(datadir)/$(webdir)/Lang
localeapidirsrc = $(datadir)/$(apidir)/Lang
-localefile = messages.mo
+localemofile = messages.mo
+localepofile = messages.po
excluded_dirs = '.*/\(3rdParty\|tinymce-405\).*'
excluded_files = '.*\(\.htaccess\)$$'
for lang in $(localeweblang) ; do \
mkdir -p $(DESTDIR)$(LOCALEDIR)/$$lang/LC_MESSAGES ; \
mkdir -p $(DESTDIR)$(WWWDIR)/$(localewebdirsrc)/$$lang ; \
- install -m 644 $(localewebdirsrc)/$$lang/$(localefile) $(DESTDIR)$(LOCALEDIR)/$$lang/LC_MESSAGES/$(NAME)-web.mo ; \
- ln -s $(DESTDIR)$(LOCALEDIR)/$$lang/LC_MESSAGES/$(NAME)-web.mo $(DESTDIR)$(WWWDIR)/$(localewebdirsrc)/$$lang/$(localefile) ; \
+ install -m 644 $(localewebdirsrc)/$$lang/$(localemofile) $(DESTDIR)$(LOCALEDIR)/$$lang/LC_MESSAGES/$(NAME)-web.mo ; \
+ ln -s $(DESTDIR)$(LOCALEDIR)/$$lang/LC_MESSAGES/$(NAME)-web.mo $(DESTDIR)$(WWWDIR)/$(localewebdirsrc)/$$lang/$(localemofile) ; \
done
for lang in $(localeapilang) ; do \
mkdir -p $(DESTDIR)$(LOCALEDIR)/$$lang/LC_MESSAGES ; \
mkdir -p $(DESTDIR)$(WWWDIR)/$(localeapidirsrc)/$$lang ; \
- install -m 644 $(localeapidirsrc)/$$lang/$(localefile) $(DESTDIR)$(LOCALEDIR)/$$lang/LC_MESSAGES/$(NAME)-api.mo ; \
- ln -s $(DESTDIR)$(LOCALEDIR)/$$lang/LC_MESSAGES/$(NAME)-api.mo $(DESTDIR)$(WWWDIR)/$(localeapidirsrc)/$$lang/$(localefile) ; \
+ install -m 644 $(localeapidirsrc)/$$lang/$(localemofile) $(DESTDIR)$(LOCALEDIR)/$$lang/LC_MESSAGES/$(NAME)-api.mo ; \
+ ln -s $(DESTDIR)$(LOCALEDIR)/$$lang/LC_MESSAGES/$(NAME)-api.mo $(DESTDIR)$(WWWDIR)/$(localeapidirsrc)/$$lang/$(localemofile) ; \
done
prepare_samples:
sed -i -e "s#%CONFDIR#$(CONFDIR)#g" $(DESTDIR)$(UNITDIR)/$(NAME)-api-lighttpd.service
sed -i -e "s#%CONFDIR#$(CONFDIR)#g" $(DESTDIR)$(UNITDIR)/$(NAME)-web-lighttpd.service
find $(DESTDIR)/ -type f -name .gitignore -exec rm -f {} \;
+
+lang_mo:
+ for lang in $(localeapilang) ; do \
+ msgfmt -o $(localeapidirsrc)/$$lang/$(localemofile) $(localeapidirsrc)/$$lang/$(localepofile); \
+ done
+ for lang in $(localeweblang) ; do \
+ msgfmt -o $(localewebdirsrc)/$$lang/$(localemofile) $(localewebdirsrc)/$$lang/$(localepofile); \
+ done
url.rewrite-once = (
"^/themes/(.+)$" => "/themes/$1",
"^/assets/(.+)$" => "/assets/$1",
- "^/$" => "/index.php?web",
+ "^/$" => "/index.php/web",
"^/(.+)$" => "/index.php/$1"
)
url.rewrite-once = (
"^/themes/(.+)$" => "/themes/$1",
"^/assets/(.+)$" => "/assets/$1",
- "^/$" => "/index.php?web",
+ "^/$" => "/index.php/web",
"^/(.+)$" => "/index.php/$1"
)
url.rewrite-once = (
"^/themes/(.+)$" => "/themes/$1",
"^/assets/(.+)$" => "/assets/$1",
- "^/$" => "/index.php?web",
+ "^/$" => "/index.php/web",
"^/(.+)$" => "/index.php/$1"
)
url.rewrite-once = (
"^/themes/(.+)$" => "/themes/$1",
"^/assets/(.+)$" => "/assets/$1",
- "^/$" => "/index.php?web",
+ "^/$" => "/index.php/web",
"^/(.+)$" => "/index.php/$1"
)
-module baculum-web 1.0.0;
+module baculum-web 1.0.1;
require {
type httpd_t;
type unreserved_port_t;
type httpd_cache_t;
+ type ldap_port_t;
class tcp_socket { name_bind name_connect };
class dir { search read write create getattr };
class file { read write create };
#============= httpd_t ==============
allow httpd_t unreserved_port_t:tcp_socket { name_bind name_connect };
+allow httpd_t ldap_port_t:tcp_socket { name_connect };
allow httpd_t httpd_cache_t:dir { read create };
allow httpd_t httpd_cache_t:file { read write create };
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
/**
* Default application language
*/
- const DEFAULT_LANGUAGE = 'en';
+ const DEF_LANG = 'en';
/**
* API config file path
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
public function onPreInit($param) {
parent::onPreInit($param);
$config = $this->getModule('api_config')->getConfig('api');
- $lang = array_key_exists('lang', $config) ? $config['lang'] : APIConfig::DEFAULT_LANGUAGE;
+ $lang = array_key_exists('lang', $config) ? $config['lang'] : APIConfig::DEF_LANG;
$this->Application->getGlobalization()->Culture = $lang;
}
}
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
const USERS_FILE_NAME = 'Application.API.Config.baculum';
const USERS_FILE_EXTENSION = '.users';
- protected function getConfigPath() {
- return Prado::getPathOfNamespace(self::USERS_FILE_NAME, self::USERS_FILE_EXTENSION);
+ public function getConfigPath() {
+ // First check if custom config path is set, if not, then use default users file
+ return parent::getConfigPath() ?: Prado::getPathOfNamespace(self::USERS_FILE_NAME, self::USERS_FILE_EXTENSION);
}
}
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
$values[] = "{$oauth2_cfg[$ids[$i]]['client_id']} ({$oauth2_cfg[$ids[$i]]['name']})";
}
} elseif ($config['api']['auth_type'] === 'basic') {
- $api_user_cfg = $this->getModule('basic_apiuser')->getAllUsers();
+ $api_user_cfg = $this->getModule('basic_apiuser')->getUsers();
$values = $ids = array_keys($api_user_cfg);
}
$this->AuthParamsCombo->DataSource = array_combine($ids, $values);
private function getBasicUsers() {
$basic_users = array();
- $basic_cfg = $this->getModule('basic_apiuser')->getAllUsers();
+ $basic_cfg = $this->getModule('basic_apiuser')->getUsers();
foreach($basic_cfg as $user => $pwd) {
$basic_users[] = array('username' => $user);
}
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
$cfg_data['api']['auth_type'] = 'oauth2';
}
$cfg_data['api']['debug'] = isset($this->config['api']['debug']) ? $this->config['api']['debug'] : "0";
- $cfg_data['api']['lang'] = isset($_SESSION['language']) ? $_SESSION['language'] : APIConfig::DEFAULT_LANGUAGE;
+ $cfg_data['api']['lang'] = isset($_SESSION['language']) ? $_SESSION['language'] : APIConfig::DEF_LANG;
$cfg_data['db']['enabled'] = (integer)($this->DatabaseYes->Checked === true);
$cfg_data['db']['type'] = $this->DBType->SelectedValue;
$cfg_data['db']['name'] = $this->DBName->Text;
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.CommonModule');
+
+/**
+ * Cryptographic APR1-MD5 hashing function module.
+ * Module is responsible for providing APR1-MD5 support.
+ *
+ * @category Module
+ * @package Baculum Common
+ */
+class Apr1Md5 extends CommonModule {
+
+ /**
+ * Get hashed password using APR1-MD5 algorithm.
+ * This function is based on common sample using PHP implementation APR1-MD5.
+ * The original author is unknown.
+ * @see https://stackoverflow.com/questions/1038791/how-to-programmatically-build-an-apr1-md5-using-php
+ *
+ * @param string $password plain text password
+ * @return string hashed password
+ */
+ public function crypt($password) {
+ $salt = $this->getModule('crypto')->getRandomString(8);
+ $len = strlen($password);
+ $text = sprintf('%s$apr1$%s', $password, $salt);
+ $bin = pack('H32', md5($password . $salt . $password));
+ for ($i = $len; $i > 0; $i -= 16) {
+ $text .= substr($bin, 0, min(16, $i));
+ }
+ for ($i = $len; $i > 0; $i >>= 1) {
+ $text .= ($i & 1) ? chr(0) : $password[0];
+ }
+ $bin = pack('H32', md5($text));
+ for ($i = 0; $i < 1000; $i++) {
+ $new = ($i & 1) ? $password : $bin;
+ if ($i % 3) {
+ $new .= $salt;
+ }
+ if ($i % 7) {
+ $new .= $password;
+ }
+ $new .= ($i & 1) ? $bin : $password;
+ $bin = pack('H32', md5($new));
+ }
+ $tmp = null;
+ for ($i = 0; $i < 5; $i++) {
+ $k = $i + 6;
+ $j = $i + 12;
+ if ($j == 16) {
+ $j = 5;
+ }
+ $tmp = $bin[$i] . $bin[$k] . $bin[$j] . $tmp;
+ }
+ $tmp = chr(0) . chr(0) . $bin[11] . $tmp;
+ $str = strrev(substr(base64_encode($tmp), 2));
+ $tmp = strtr(
+ $str,
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
+ './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
+ );
+ return sprintf('$apr1$%s$%s', $salt, $tmp);
+ }
+}
+?>
*/
class BClientScript extends TClientScript {
- const SCRIPTS_VERSION = 6;
+ const SCRIPTS_VERSION = 7;
public function getScriptUrl()
{
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.CommonModule');
+
+/**
+ * Cryptographic BCrypt hashing function module
+ * Module is responsible for providing BCrypt support.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Common
+ */
+class BCrypt extends CommonModule {
+
+ // bcrypt uses not standard base64 alphabet
+ const BCRYPT_BASE64_CODE = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+
+ // bcrypt cost parameter - number of iterations during hashing
+ const BCRYPT_COST = 10;
+
+ /**
+ * Get hashed password using BCrypt algorithm and salt.
+ *
+ * @param string $password plain text password
+ * @return string hashed password
+ */
+ public function crypt($password) {
+ // Suffle string
+ $rand_string = str_shuffle(self::BCRYPT_BASE64_CODE);
+
+ // BCrypt salt - 22 characters
+ $salt_str = substr($rand_string, 0, 22);
+
+ $salt = sprintf(
+ '$2y$%d$%s$',
+ self::BCRYPT_COST,
+ $salt_str
+ );
+ return crypt($password, $salt);
+ }
+}
+?>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
}
/**
- * Log in as specific user.
+ * Get full main URL to log in with user and password.
+ * It is useful to work with Basic authentication (login or logout for example).
*
- * Note, usually after this method call there required is using exit() just
- * after method execution. Otherwise the HTTP redirection may be canceled on some
- * web servers.
- *
- * @access public
* @param string $user user name to log in
- * @param string $string plain text user's password
- * @return none
+ * @param string $password plain text user's password
+ * @return string full login URL
*/
- public function switchToUser($user, $password) {
- $http_protocol = isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ? 'https' : 'http';
+ public function getFullLoginUrl($user, $password) {
+ $protocol = isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ? 'https' : 'http';
$host = $_SERVER['SERVER_NAME'];
$port = $_SERVER['SERVER_PORT'];
- $url_prefix = $this->Application->getModule('url_manager')->getUrlPrefix();
+ $url_prefix = $this->getModule('url_manager')->getUrlPrefix();
$url_prefix = str_replace('/index.php', '', $url_prefix);
- $location = sprintf("%s://%s:%s@%s:%d%s", $http_protocol, $user, $password, $host, $port, $url_prefix);
-
- // Log in by header
- header("Location: $location");
+ $location = sprintf(
+ '%s://%s:%s@%s:%d%s',
+ $protocol,
+ $user,
+ $password,
+ $host,
+ $port,
+ $url_prefix
+ );
+ return $location;
}
public function setStyleSheetFiles(){
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
$this->setServiceUrlManager();
}
+ /**
+ * Get all pages for current service.
+ * Pages are taken directly from configuration file.
+ *
+ * @return array all pages for service.
+ */
+ public function getPages() {
+ $pages = [];
+ foreach ($this->_patterns as $pattern) {
+ $pages[] = $pattern->getServiceParameter();
+ }
+ return $pages;
+ }
+
private function getServiceID() {
$service_id = null;
$url = $this->getRequestedUrl();
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
* @category Module
* @package Baculum Common
*/
-abstract class BasicUserConfig extends CommonModule {
+class BasicUserConfig extends CommonModule {
+
+ /**
+ * Stores user config path.
+ */
+ protected $config_path;
/**
* User name allowed characters pattern
* Get config file path to store users' parameters.
* @return string config path
*/
- abstract protected function getConfigPath();
+ public function getConfigPath() {
+ return $this->config_path;
+ }
+
+ /**
+ * Set config file path.
+ *
+ * @param string $path path to config file
+ * @return none
+ */
+ public function setConfigPath($path) {
+ $this->config_path = $path;
+ }
/**
* Save user to users configuration file.
* @param string $password user's password
* @param boolean $clear_config determine if clear config before save
* @param mixed $old_user previous username before change
+ * @param array $opts setting user options
* @return boolean true if user saved successfully, otherwise false
*/
- public function setUsersConfig($user, $password, $clear_config = false, $old_user = null) {
+ public function setUsersConfig($user, $password, $clear_config = false, $old_user = null, $opts = []) {
if ($clear_config === true) {
$this->clearUsersConfig();
}
- $all_users = $this->getAllUsers();
- $password = $this->getModule('misc')->getHashedPassword($password);
+ $all_users = $this->getUsers();
+
+ $alg = key_exists('hash_alg', $opts) ? $opts['hash_alg'] : null;
+ $password = $this->getModule('crypto')->getHashedPassword($password, $alg);
- $userExists = array_key_exists($user, $all_users);
+ $user_exists = key_exists($user, $all_users);
- if ($userExists === true) {
+ if ($user_exists === true) {
// update user password;
$all_users[$user] = $password;
}
if (!is_null($old_user) && $old_user !== $user) {
// delete old username with password from configuration file
- if (array_key_exists($old_user, $all_users)) {
+ if (key_exists($old_user, $all_users)) {
unset($all_users[$old_user]);
}
}
// add new user if does not exist
- if ($userExists === false) {
+ if ($user_exists === false) {
$all_users[$user] = $password;
}
- $result = $this->saveUserConfig($all_users);
- return $result;
+ return $this->saveUserConfig($all_users);
}
/**
* and encrypted passwords as values.
*
* @access public
+ * @param string $patter regular expression pattern
* @return array users/passwords list
*/
- public function getAllUsers() {
- $all_users = array();
+ public function getUsers($pattern = '') {
+ $all_users = [];
if ($this->isUsersConfig() === true) {
$users = file($this->getConfigPath(), FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
-
for($i = 0; $i < count($users); $i++) {
if (preg_match("/^(?P<user>\S+)\:(?P<hash>\S+)$/", $users[$i], $match) === 1) {
+ if ($pattern && !fnmatch($pattern, $match['user'])) {
+ // wildcard pattern doesn't match, skip it
+ continue;
+ }
$all_users[$match['user']] = $match['hash'];
}
}
$usersToFile = implode("\n", $users);
$old_umask = umask(0);
umask(0077);
- $result = file_put_contents($this->getConfigPath(), $usersToFile) !== false;
+ $result = file_put_contents($this->getConfigPath(), $usersToFile, LOCK_EX) !== false;
umask($old_umask);
return $result;
}
*/
public function removeUser($username) {
$result = false;
- $all_users = $this->getAllUsers();
+ $all_users = $this->getUsers();
if (array_key_exists($username, $all_users)) {
unset($all_users[$username]);
$result = $this->saveUserConfig($all_users);
return $result;
}
+ /**
+ * Remove multiple user from users file.
+ *
+ * @param array $usernames user names to remove
+ * @return boolean true if users removed successfully, otherwise false
+ */
+ public function removeUsers(array $usernames) {
+ $result = false;
+ $all_users = $this->getUsers();
+ for ($i = 0; $i < count($usernames); $i++) {
+ if (key_exists($usernames[$i], $all_users)) {
+ unset($all_users[$usernames[$i]]);
+ }
+ }
+ return $this->saveUserConfig($all_users);
+ }
+
/**
* Check if users configuration file exists.
*
* @return boolean true if file cleared successfully, otherwise false
*/
public function clearUsersConfig() {
- $result = file_put_contents($this->getConfigPath(), '') !== false;
+ $result = file_put_contents($this->getConfigPath(), '', LOCK_EX) !== false;
return $result;
}
}
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.CommonModule');
+
+/**
+ * Cryptographic tools.
+ * Module is responsible for providing basic cryptograhic tool set.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Common
+ */
+class Crypto extends CommonModule {
+
+ /**
+ * Supported hash algorithms.
+ */
+ const HASH_ALG_APR1_MD5 = 'apr-md5';
+ const HASH_ALG_SHA1 = 'sha1';
+ const HASH_ALG_SSHA1 = 'ssha1';
+ const HASH_ALG_SHA256 = 'sha256';
+ const HASH_ALG_SHA512 = 'sha512';
+ const HASH_ALG_BCRYPT = 'bcrypt';
+
+ /**
+ * Get (pseudo)random string.
+ *
+ * Useful for log out user from HTTP Basic auth by providing random password.
+ *
+ * @access public
+ * @return string random string from range [a-zA-Z0-9]
+ */
+ public function getRandomString($length = null) {
+ $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ $rand_string = str_shuffle($characters);
+ if (is_int($length) && $length <= 62) {
+ $rand_string = substr($rand_string, 0, $length);
+ }
+ return $rand_string;
+ }
+
+ /**
+ * Get hashed password to use in web server auth.
+ * If no hash algorithm given, use APR1-MD5.
+ *
+ * @access public
+ * @param string $password plain text password
+ * @param string $hash_alg hash algorithm (apr1-md5|sha1)
+ * @return string hashed password
+ */
+ public function getHashedPassword($password, $hash_alg = null) {
+ $mod = '';
+ switch ($hash_alg) {
+ case self::HASH_ALG_BCRYPT: {
+ $mod = 'bcrypt';
+ break;
+ }
+ case self::HASH_ALG_APR1_MD5: {
+ $mod = 'apr1md5';
+ break;
+ }
+ case self::HASH_ALG_SHA1: {
+ $mod = 'sha1';
+ break;
+ }
+ case self::HASH_ALG_SSHA1: {
+ $mod = 'ssha1';
+ break;
+ }
+ case self::HASH_ALG_SHA256: {
+ $mod = 'sha256';
+ break;
+ }
+ case self::HASH_ALG_SHA512: {
+ $mod = 'sha512';
+ break;
+ }
+ default: {
+ $mod = 'apr1md5';
+ }
+ }
+ return $this->getModule($mod)->crypt($password);
+ }
+}
+?>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
public function getRequestHeaderValue($header);
}
+
+/**
+ * Defined user manager methods.
+ */
+interface UserManager {
+
+ public function init($config);
+
+ public function validateUser($username, $password);
+}
?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.CommonModule');
+
+/**
+ * The module supports basic operations on LDAP server.
+ * To work it uses php-ldap module.
+ * @see https://www.php.net/manual/en/book.ldap.php
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Common
+ */
+class Ldap extends CommonModule {
+
+ /**
+ * Authentication methods.
+ */
+ const AUTH_METHOD_ANON = 'anonymous';
+ const AUTH_METHOD_SIMPLE = 'simple';
+
+ /**
+ * LDAP protocol types.
+ */
+ const PROTOCOL_PLAIN = 'ldap';
+ const PROTOCOL_SSL = 'ldaps';
+
+ /**
+ * LDAP attributes.
+ */
+ const DN_ATTR = 'dn';
+
+ /**
+ * Stores LDAP connection parameters.
+ */
+ private $params = [];
+
+ /**
+ * Stores last error message if error occurs.
+ */
+ private $error = '';
+
+ /**
+ * Stores obligatory (required) parameters.
+ */
+ private $req_params = array(
+ 'address',
+ 'port',
+ 'protocol_ver',
+ 'auth_method'
+ );
+
+ /**
+ * Stores LDAP connection resource.
+ */
+ private $conn;
+
+ /**
+ * Connect to LDAP server.
+ * Note, it is used to initialize connection parameters, but itself it
+ * doesn't open any LDAP conection.
+ *
+ * @return resource|false LDAP connection resource or false if any error occurs
+ */
+ public function connect() {
+ $ldapuri = $this->getLdapUri();
+ $conn = @ldap_connect($ldapuri);
+ $this->conn = $conn;
+ if ($conn) {
+ $this->setConnectionOpts();
+ } else {
+ $this->setLdapError();
+ }
+ return $conn;
+ }
+
+ /**
+ * Binds to LDAP directory.
+ * Supported are anonymous bind and bind with credentials (RDN/password).
+ * Note, if an error occurs, the error message is available in
+ * the error property.
+ *
+ * @param string $rdn distinguished name
+ * @param string $password user password
+ * @return boolean true on successfull connection, false otherwise
+ */
+ public function bind($rdn = null, $password = null) {
+ $success = false;
+ if ($this->conn) {
+ $success = @ldap_bind($this->conn, $rdn, $password);
+ }
+ if (!$success) {
+ $this->setLdapError();
+ }
+ return $success;
+ }
+
+ /**
+ * Set LDAP parameters (both connection and attributes' parameters).
+ * Parameters are set after successful validation.
+ *
+ * @param array $param parameters to work with LDAP
+ * @return none
+ */
+ public function setParams(array $params) {
+ if ($this->validateConnectionParams($params)) {
+ $this->params = $params;
+ }
+ }
+
+ /**
+ * Set LDAP connection specific options.
+ *
+ * @return none
+ */
+ public function setConnectionOpts() {
+ ldap_set_option($this->conn, LDAP_OPT_PROTOCOL_VERSION, $this->params['protocol_ver']);
+ ldap_set_option($this->conn, LDAP_OPT_REFERRALS, 0);
+ }
+
+ /**
+ * Administrative bind action.
+ * This method should be used always to connect to LDAP (anonymous or with credentials).
+ * Note, if an error occurs, the error message is available in
+ * the error property.
+ *
+ * @return boolean true on successfull connection, false otherwise
+ */
+ public function adminBind() {
+ $success = false;
+ if ($this->connect()) {
+ if ($this->params['auth_method'] === self::AUTH_METHOD_ANON) {
+ $success = $this->bind();
+ } elseif ($this->params['auth_method'] === self::AUTH_METHOD_SIMPLE) {
+ $success = $this->bind($this->params['bind_dn'], $this->params['bind_password']);
+ }
+ }
+ return $success;
+ }
+
+ /**
+ * Login with username and password.
+ * Note, if an error occurs, the error message is available in
+ * the error property.
+ *
+ * @param string $username username to login
+ * @param string $password password to login
+ * @return boolean true if login finished successfully, false otherwise
+ */
+ public function login($username, $password) {
+ $success = false;
+ if ($this->adminBind()) {
+ $filter = sprintf('(%s=%s)', $this->params['user_attr'], $username);
+ $user = $this->search($this->params['base_dn'], $filter);
+ $cnt = ldap_count_entries($this->conn, $user);
+ if ($cnt === 1) {
+ $entry = ldap_first_entry($this->conn, $user);
+ if ($entry) {
+ $user_dn = ldap_get_dn($this->conn, $entry);
+ $success = $this->bind($user_dn, $password);
+ }
+ }
+ }
+ return $success;
+ }
+
+ /**
+ * Search in LDAP directory using filters.
+ * Note, if an error occurs, the error message is available in
+ * the error property.
+ *
+ * @param string $base_dn base distinguished name
+ * @param string $filter the search filter, ex. '(objectClass=inetOrgPerson)'
+ * @param array $attr required object attributes
+ * @return resource search result identifier or false otherwise
+ */
+ public function search($base_dn, $filter, $attr = array('*')) {
+ $search = @ldap_search($this->conn, $base_dn, $filter, $attr, 0, 0, 0);
+ if (!$search) {
+ $this->setLdapError();
+ }
+ return $search;
+ }
+
+ /**
+ * Find and get users by filter and attributes.
+ * Note, if an error occurs, the error message is available in
+ * the error property.
+ *
+ * @param string $filter the search filter, ex. '(objectClass=inetOrgPerson)'
+ * @param array $attr required object attributes
+ */
+ public function findUserAttr($filter, $attr = array('*')) {
+ $user_attr = [];
+ if ($this->adminBind()) {
+ $search = $this->search($this->params['base_dn'], $filter, $attr);
+ if ($search) {
+ $user_attr = ldap_get_entries($this->conn, $search);
+ }
+ }
+ return $user_attr;
+ }
+
+ /**
+ * Get LDAP URI used to connecting to LDAP server.
+ *
+ * @return string full LDAP URI (ex. ldap://host:port)
+ */
+ public function getLdapUri() {
+ $protocol = self::PROTOCOL_PLAIN;
+ if (key_exists('ldaps', $this->params) && $this->params['ldaps'] === 1) {
+ $protocol = self::PROTOCOL_SSL;
+ }
+ return sprintf(
+ '%s://%s:%d',
+ $protocol,
+ $this->params['address'],
+ $this->params['port']
+ );
+ }
+
+ /**
+ * Set LDAP errors in error property.
+ * Used to get the most information from LDAP server.
+ *
+ * @return none
+ */
+ public function setLdapError() {
+ ldap_get_option($this->conn, LDAP_OPT_DIAGNOSTIC_MESSAGE, $error);
+ $emsg = sprintf(
+ 'Error: %d: %s. %s',
+ ldap_errno($this->conn),
+ ldap_error($this->conn),
+ $error
+ );
+ $this->error = $emsg;
+ $this->getModule('logging')->log(
+ __FUNCTION__,
+ $emsg,
+ Logging::CATEGORY_EXTERNAL,
+ __FILE__,
+ __LINE__
+ );
+ }
+
+ /**
+ * Get LDAP error message.
+ * No error is represented by empty string.
+ *
+ * @return string error message
+ */
+ public function getLdapError() {
+ return $this->error;
+ }
+
+ /**
+ * Validate LDAP connection parameters.
+ *
+ * @param array $params connection parameters
+ * @return false if any of parameters is invalid
+ */
+ public function validateConnectionParams(array $params) {
+ $valid = true;
+ for ($i = 0; $i < count($this->req_params); $i++) {
+ if (!key_exists($this->req_params[$i], $params)) {
+ $valid = false;
+ }
+ }
+ return $valid;
+ }
+
+ /**
+ * Get simple key/value filter.
+ * Filters are used to search on LDAP server.
+ *
+ * @param string $key search key
+ * @param string $value search value
+ * @return formatted string in search filter
+ */
+ public function getFilter($key, $value) {
+ return sprintf('(%s=%s)', $key, $value);
+ }
+}
+?>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
* Bacula(R) is a registered trademark of Kern Sibbald.
*/
+
+/**
+ * Module with miscellaneous tools.
+ * Targetly it is meant to remove after splitting into smaller modules.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Common
+ */
class Miscellaneous extends TModule {
const RPATH_PATTERN = '/^b2\d+$/';
}
return $jobid;
}
-
- /**
- * Get (pseudo)random string.
- *
- * Useful for log out user from HTTP Basic auth by providing random password.
- *
- * @access public
- * @return string random 62 characters string from range [a-zA-Z0-9]
- */
- public function getRandomString($length = null) {
- $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
- $rand_string = str_shuffle($characters);
- if (is_int($length) && $length <= 62) {
- $rand_string = substr($rand_string, 0, $length);
- }
- return $rand_string;
- }
-
- /**
- * Get hashed password to use in web server auth.
- *
- * @access public
- * @param string $password plain text password
- * @return string hashed password
- */
- public function getHashedPassword($password) {
- return $this->cryptApr1Md5($password);
- }
-
- public function cryptApr1Md5($password) {
- $salt = $this->getRandomString(8);
- $len = strlen($password);
- $text = sprintf('%s$apr1$%s', $password, $salt);
- $bin = pack('H32', md5($password . $salt . $password));
- for ($i = $len; $i > 0; $i -= 16) {
- $text .= substr($bin, 0, min(16, $i));
- }
- for ($i = $len; $i > 0; $i >>= 1) {
- $text .= ($i & 1) ? chr(0) : $password[0];
- }
- $bin = pack('H32', md5($text));
- for ($i = 0; $i < 1000; $i++) {
- $new = ($i & 1) ? $password : $bin;
- if ($i % 3) {
- $new .= $salt;
- }
- if ($i % 7) {
- $new .= $password;
- }
- $new .= ($i & 1) ? $bin : $password;
- $bin = pack('H32', md5($new));
- }
- $tmp = null;
- for ($i = 0; $i < 5; $i++) {
- $k = $i + 6;
- $j = $i + 12;
- if ($j == 16) {
- $j = 5;
- }
- $tmp = $bin[$i] . $bin[$k] . $bin[$j] . $tmp;
- }
- $tmp = chr(0) . chr(0) . $bin[11] . $tmp;
- $str = strrev(substr(base64_encode($tmp), 2));
- $tmp = strtr(
- $str,
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
- './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
- );
- return sprintf('$apr1$%s$%s', $salt, $tmp);
- }
}
?>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
*/
class SessionRecord extends CommonModule implements SessionItem {
+ const SESS_FILE_PERM = 0600;
+
private static $lock = false;
private static $queue = 0;
private static function store($wouldblock = true) {
$c = get_called_class();
$sessfile = $c::getSessionFile();
- if (array_key_exists('sess', $GLOBALS)) {
+ if (key_exists('sess', $GLOBALS)) {
$content = serialize($GLOBALS['sess']);
+ if (file_exists($sessfile)) {
+ $perm = (fileperms($sessfile) & 0777);
+ if ($perm !== self::SESS_FILE_PERM) {
+ // Correct permissions to more restrictive if needed
+ chmod($sessfile, self::SESS_FILE_PERM);
+ }
+ }
+ $old_umask = umask(0);
+ $new_umask = (~(self::SESS_FILE_PERM) & 0777);
+ umask($new_umask);
$fp = fopen($sessfile, 'w');
if (flock($fp, LOCK_EX, $wouldblock)) {
fwrite($fp, $content);
);
}
fclose($fp);
+ umask($old_umask);
}
}
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.CommonModule');
+
+/**
+ * Cryptographic SHA-1 hashing function module
+ * Module is responsible for providing SHA-1 support.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Common
+ */
+class Sha1 extends CommonModule {
+
+ /**
+ * Get hashed password using SHA-1 algorithm.
+ *
+ * @param string $password plain text password
+ * @return string hashed password
+ */
+ public function crypt($password) {
+ $hash = sha1($password, true);
+ $bh = base64_encode($hash);
+ $ret = '{SHA}' . $bh;
+ return $ret;
+ }
+
+}
+?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.CommonModule');
+
+/**
+ * Cryptographic SHA-256 hashing function module
+ * Module is responsible for providing SHA-256 support.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Common
+ */
+class Sha256 extends CommonModule {
+
+ const SHA256_ROUNDS = 10000;
+
+ /**
+ * Get hashed password using SHA-256 algorithm and salt.
+ *
+ * @param string $password plain text password
+ * @return string hashed password
+ */
+ public function crypt($password) {
+ // Salt string - 16 characters for SHA-256
+ $salt_str = $this->getModule('crypto')->getRandomString(16);
+
+ $salt = sprintf(
+ '$5$rounds=%d$%s$',
+ self::SHA256_ROUNDS,
+ $salt_str
+ );
+ return crypt($password, $salt);
+ }
+}
+?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.CommonModule');
+
+/**
+ * Cryptographic SHA-512 hashing function module
+ * Module is responsible for providing SHA-512 support.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Common
+ */
+class Sha512 extends CommonModule {
+
+ const SHA512_ROUNDS = 10000;
+
+ /**
+ * Get hashed password using SHA-512 algorithm and salt.
+ *
+ * @param string $password plain text password
+ * @return string hashed password
+ */
+ public function crypt($password) {
+ // Salt string - 16 characters for SHA-512
+ $salt_str = $this->getModule('crypto')->getRandomString(16);
+
+ $salt = sprintf(
+ '$6$rounds=%d$%s$',
+ self::SHA512_ROUNDS,
+ $salt_str
+ );
+ return crypt($password, $salt);
+ }
+}
+?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.CommonModule');
+
+/**
+ * Cryptographic SHA-1 hashing function module.
+ * Module is responsible for providing SHA-1 support with
+ * using salt.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Common
+ */
+class Ssha1 extends CommonModule {
+
+ /**
+ * Get hashed password using SHA-1 algorithm and salt.
+ *
+ * @param string $password plain text password
+ * @param string $salt cryptographic salt
+ * @return string hashed password
+ */
+ public function crypt($password) {
+ // Salt string - 16 characters for SHA-256
+ $salt = $this->getModule('crypto')->getRandomString(4);
+
+ $hash = sha1($password . $salt, true);
+ $bh = base64_encode($hash . $salt);
+ $ret = '{SSHA}' . $bh;
+ return $ret;
+ }
+
+}
+?>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
$exists = false;
$config = $this->getModule('api_config')->getConfig();
if ($this->getAuthType() === 'basic') {
- $users = $this->getModule('basic_apiuser')->getAllUsers();
+ $users = $this->getModule('basic_apiuser')->getUsers();
if (!key_exists($this->APIBasicLogin->Text, $users)) {
$result = $this->getModule('basic_apiuser')->setUsersConfig(
$this->APIBasicLogin->Text,
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
$cached = null;
$ret = null;
if (is_null($host)) {
- if (isset($_SESSION['api_host'])) {
- $host = $_SESSION['api_host'];
- } else {
- $host = HostConfig::MAIN_CATALOG_HOST;
- }
+ $host = $this->User->getAPIHosts();
}
if ($use_cache === true) {
$cached = $this->getSessionCache($host, $params);
*/
public function set(array $params, array $options = array(), $host = null, $show_error = true) {
if (is_null($host)) {
- if (isset($_SESSION['api_host'])) {
- $host = $_SESSION['api_host'];
- } else {
- $host = HostConfig::MAIN_CATALOG_HOST;
- }
+ $host = $this->User->getAPIHosts();
}
$host_cfg = $this->getHostParams($host);
$uri = $this->getURIResource($host, $params);
*/
public function create(array $params, array $options, $host = null, $show_error = true) {
if (is_null($host)) {
- if (isset($_SESSION['api_host'])) {
- $host = $_SESSION['api_host'];
- } else {
- $host = HostConfig::MAIN_CATALOG_HOST;
- }
+ $host = $this->User->getAPIHosts();
}
$host_cfg = $this->getHostParams($host);
$uri = $this->getURIResource($host, $params);
*/
public function remove(array $params, $host = null, $show_error = true) {
if (is_null($host)) {
- if (isset($_SESSION['api_host'])) {
- $host = $_SESSION['api_host'];
- } else {
- $host = HostConfig::MAIN_CATALOG_HOST;
- }
+ $host = $this->User->getAPIHosts();
}
$host_cfg = $this->getHostParams($host);
$uri = $this->getURIResource($host, $params);
private function authToHost($host, $host_cfg) {
if (count($host_cfg) > 0 && $host_cfg['auth_type'] === 'oauth2') {
- $state = $this->getModule('misc')->getRandomString(16);
+ $state = $this->getModule('crypto')->getRandomString(16);
$params = array(
'response_type' => 'code',
'client_id' => $host_cfg['client_id'],
* Bacula(R) is a registered trademark of Kern Sibbald.
*/
-session_start();
-
Prado::using('Application.Web.Pages.Requirements');
Prado::using('Application.Common.Class.BaculumPage');
Prado::using('Application.Web.Init');
Prado::using('Application.Web.Class.WebConfig');
+Prado::using('Application.Web.Class.PageCategory');
/**
* Baculum Web page module.
public function onPreInit($param) {
parent::onPreInit($param);
$this->web_config = $this->getModule('web_config')->getConfig();
- $this->Application->getGlobalization()->Culture = $this->getLanguage();
if (count($this->web_config) === 0) {
- if (isset($_SERVER['PHP_AUTH_USER'])) {
- if ($this->Service->getRequestedPagePath() != 'WebConfigWizard') {
- $this->goToPage('WebConfigWizard');
- }
- // without config there is no way to call api below
- return;
- } else {
- self::accessDenied();
+ if ($this->Service->getRequestedPagePath() != 'WebConfigWizard') {
+ $this->goToPage('WebConfigWizard');
}
+ // without config there is no way to call api below
+ return;
}
Logging::$debug_enabled = (isset($this->web_config['baculum']['debug']) && $this->web_config['baculum']['debug'] == 1);
if (!$this->IsPostBack && !$this->IsCallBack) {
+ $this->postInitActions();
$this->getModule('api')->initSessionCache(true);
$this->setSessionUserVars();
}
- $this->checkPrivileges();
- }
-
- /**
- * Get curently set language short name (for example: en, pl).
- * If language short name is not set in session then the language value
- * is taken from Baculum config file, saved in session and returned.
- * If the language setting is set in session, then the value from
- * session is returned.
- *
- * @access public
- * @return string currently set language short name
- */
- public function getLanguage() {
- $language = null;
- if (isset($_SESSION['language']) && !empty($_SESSION['language'])) {
- $language = $_SESSION['language'];
- } else {
- if (isset($this->web_config['baculum']) && key_exists('lang', $this->web_config['baculum'])) {
- $language = $this->web_config['baculum']['lang'];
- }
- if (is_null($language)) {
- $language = WebConfig::DEFAULT_LANGUAGE;
- }
- $_SESSION['language'] = $language;
- }
- return $language;
}
/**
* @return none
*/
private function setSessionUserVars() {
- // NOTE. For oauth2 callback, the PHP_AUTH_USER is empty because no user/pass.
- if (count($this->web_config) > 0 && isset($_SERVER['PHP_AUTH_USER'])) {
- // Set administrator role
- $_SESSION['admin'] = ($_SERVER['PHP_AUTH_USER'] === $this->web_config['baculum']['login']);
-
- // Set api host for normal user
- if (!$_SESSION['admin'] && key_exists('users', $this->web_config) && array_key_exists($_SERVER['PHP_AUTH_USER'], $this->web_config['users'])) {
- $_SESSION['api_host'] = $this->web_config['users'][$_SERVER['PHP_AUTH_USER']];
- } elseif ($_SESSION['admin']) {
- $_SESSION['api_host'] = 'Main';
- }
- } else {
- $_SESSION['admin'] = false;
- }
-
// Set director
$directors = $this->getModule('api')->get(array('directors'), null, false);
- if ($directors->error === 0 && count($directors->output) > 0 && (!key_exists('director', $_SESSION) || $directors->output[0] != $_SESSION['director'])) {
+ if ($directors->error === 0 && count($directors->output) > 0 &&
+ (!key_exists('director', $_SESSION) || $directors->output[0] != $_SESSION['director'])) {
$_SESSION['director'] = $directors->output[0];
}
}
}
- private function checkPrivileges() {
- if (property_exists($this, 'admin') && $this->admin === true && !$_SESSION['admin']) {
- self::accessDenied();
+ /**
+ * Redirection to default page defined in application config.
+ *
+ * @access public
+ * @param array $params HTTP GET method parameters in associative array
+ * @return none
+ */
+ public function goToDefaultPage($params = null) {
+ $def_page = $this->Service->DefaultPage;
+ $manager = $this->getModule('users');
+ if (!$manager->isPageAllowed($this->User, $this->Service->DefaultPage)) {
+ // User hasn't access to default service page. Get first allowed page.
+ $def_page = $this->findDefaultPageForUser();
+
+ /**
+ * If page different than default for service, reset params because
+ * they will not work with different page.
+ */
+ $params = null;
}
+ if (!is_string($def_page)) {
+ $def_page = $this->getModule('auth')->getLoginPage();
+ }
+ $this->goToPage($def_page, $params);
}
- public static function accessDenied() {
- die('Access denied');
+ /**
+ * Find default page for an user.
+ * Useful to determine on which page direct user. It takes first one found
+ * that can be accessible by the user.
+ *
+ * @return mixed page path or null if no page for user found
+ */
+ private function findDefaultPageForUser() {
+ $manager = $this->getModule('users');
+ $user_role = $this->getModule('user_role');
+ $roles = $this->User->getRoles();
+ $pages = [];
+ for ($i = 0; $i < count($roles); $i++) {
+ $rpages = $user_role->getPagesByRole($roles[$i]);
+ for ($j = 0; $j < count($rpages); $j++) {
+ if (!in_array($rpages[$i], $pages) && $manager->isPageAllowed($this->User, $rpages[$i])) {
+ $pages[] = $rpages[$i];
+ }
+ }
+ }
+ return array_shift($pages);
+ }
+
+ /**
+ * Common actions which has to be done for each web page just after
+ * page pre-loading.
+ *
+ * @return none
+ */
+ private function postInitActions() {
+ /**
+ * If users config file doesn't exist, create it and populate
+ * using basic users file.
+ * Basic auth method is the main Baculum Web auth method. Before introducing
+ * users.conf file, it was the only one supported method.
+ */
+ $result = $this->getModule('user_config')->importUsers();
+ if ($result) {
+ $this->goToDefaultPage();
+ }
}
}
?>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
const USERS_FILE_NAME = 'Application.Web.Config.baculum';
const USERS_FILE_EXTENSION = '.users';
- protected function getConfigPath() {
- return Prado::getPathOfNamespace(self::USERS_FILE_NAME, self::USERS_FILE_EXTENSION);
+ public function getConfigPath() {
+ // First check if custom config path is set, if not, then use default users file
+ return parent::getConfigPath() ?: Prado::getPathOfNamespace(self::USERS_FILE_NAME, self::USERS_FILE_EXTENSION);
}
}
+?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.:Web.Class.HostConfig');
+Prado::using('Application.:Web.Class.WebModule');
+
+/**
+ * Page category module.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Web
+ */
+class PageCategory extends WebModule {
+
+ /**
+ * Pages allowed for user with native role 'normal'
+ */
+ const DASHBOARD = 'Dashboard';
+ const JOB_HISTORY_LIST = 'JobHistoryList';
+ const JOB_HISTORY_VIEW = 'JobHistoryView';
+ const JOB_LIST = 'JobList';
+ const JOB_VIEW = 'JobView';
+ const CLIENT_LIST = 'ClientList';
+ const CLIENT_VIEW = 'ClientView';
+ const RESTORE_WIZARD = 'RestoreWizard';
+ const GRAPHS = 'Graphs';
+
+ /**
+ * System pages - always allowed for authenticated users
+ */
+ const MONITOR = 'Monitor';
+ const BACULUM_ERROR = 'BaculumError';
+
+ /**
+ * Public pages - always allowed
+ */
+ const LOGIN_PAGE = 'LoginPage';
+ const OAUTH2_REDIRECT = 'OAuth2Redirect';
+
+ /**
+ * Pages available under conditions.
+ */
+ const WEB_CONFIG_WIZARD = 'WebConfigWizard';
+
+ /**
+ * Get page categories.
+ *
+ * @return array page categories
+ */
+ public function getCategories($with_sys_pub_pages = true) {
+ $pages = $this->getModule('url_manager')->getPages();
+ //some pages are double because they are defined for access by name or id, so do unique()
+ $pages = array_unique($pages);
+ if (!$with_sys_pub_pages) {
+ $system_pages = $this->getSystemCategories();
+ $public_pages = $this->getPublicCategories();
+ $pages = array_diff($pages, $system_pages, $public_pages);
+ }
+ return $pages;
+ }
+
+ public function isCategorySystem($category) {
+ $system_cats = $this->getSystemCategories();
+ return in_array($category, $system_cats);
+ }
+
+ private function getSystemCategories() {
+ return [
+ self::MONITOR,
+ self::BACULUM_ERROR
+ ];
+ }
+
+ public function isCategoryPublic($category) {
+ $public_cats = $this->getPublicCategories();
+ return in_array($category, $public_cats);
+ }
+
+ private function getPublicCategories() {
+ return [
+ self::OAUTH2_REDIRECT,
+ self::LOGIN_PAGE
+ ];
+ }
+
+ public function isCategoryConditional($category) {
+ $cond_cats = $this->getConditionalCategories();
+ return in_array($category, $cond_cats);
+ }
+
+ private function getConditionalCategories() {
+ $cond_cats = [];
+ $host_config = $this->getModule('host_config')->getConfig();
+ $first_run = (count($host_config) == 0 || !key_exists(HostConfig::MAIN_CATALOG_HOST, $host_config));
+ if ($first_run) {
+ $cond_cats[] = self::WEB_CONFIG_WIZARD;
+ }
+ return $cond_cats;
+ }
+}
+?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.Web.Class.WebModule');
+
+/**
+ * Web HTTP Basic user manager module.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Web
+ */
+class WebBasicUserManager extends WebModule implements UserManager {
+
+ public function init($config) {
+ }
+
+ public function validateUser($username, $password) {
+ /**
+ * Basic auth is realized by web server, so validating
+ * user/pass is always true here.
+ */
+ return true;
+ }
+}
+?>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
*/
Prado::using('Application.Common.Class.ConfigFileModule');
+Prado::using('Application.Common.Class.Crypto');
/**
* Manage webGUI configuration.
/**
* Default application language
*/
- const DEFAULT_LANGUAGE = 'en';
+ const DEF_LANG = 'en';
+
+ /**
+ * Default number of jobs visible in tables/
+ */
+ const DEF_MAX_JOBS = 15000;
+
+ /**
+ * Default size values unit.
+ */
+ const DEF_SIZE_VAL_UNIT = 'decimal';
+
+ /**
+ * Default value for showing time in job log.
+ */
+ const DEF_TIME_IN_JOB_LOG = 0;
+
+ /**
+ * Supported authentication methods.
+ */
+ const AUTH_METHOD_BASIC = 'basic';
+ const AUTH_METHOD_LDAP = 'ldap';
+
+ /**
+ * Default access options.
+ */
+ const DEF_ACCESS_NO_ACCESS = 'no_access';
+ const DEF_ACCESS_DEFAULT_SETTINGS = 'default_settings';
+
+ /**
+ * Stores web config content.
+ */
+ private $config = null;
/**
* These options are obligatory for web config.
*/
private $required_options = array(
- 'baculum' => array('login', 'debug', 'lang')
+ 'baculum' => array('debug', 'lang')
);
/**
- * Get (read) web config.
+ * Initialize module configuration.
+ *
+ * @param TXmlElement $config module configuration
+ * @return none
+ */
+ public function init($config) {
+ // add event handler to set page language
+ $this->Application->attachEventHandler(
+ 'onPreRunService',
+ [$this, 'setCulture']
+ );
+ }
+
+ /**
+ * Get web config.
*
* @access public
* @param string $section config section name
* @return array config
*/
public function getConfig($section = null) {
- $config = $this->readConfig(self::CONFIG_FILE_PATH, self::CONFIG_FILE_FORMAT);
- if ($this->validateConfig($config) === true) {
- if (!is_null($section)) {
- $config = array_key_exists($section, $this->required_options) ? $config[$section] : array();
+ $config = [];
+ $valid = true;
+ if (is_null($this->config)) {
+ $this->config = $this->readConfig(self::CONFIG_FILE_PATH, self::CONFIG_FILE_FORMAT);
+ $valid = $this->validateConfig($this->config);
+ }
+ if ($valid === true) {
+ if (is_string($section)) {
+ $config = key_exists($section, $this->config) ? $this->config[$section] : array();
+ } else {
+ $config = $this->config;
}
- } else {
- $config = array();
}
return $config;
}
/**
- * Set (save) web config.
+ * Set web config.
*
* @access public
* @param array $config config
$result = false;
if ($this->validateConfig($config) === true) {
$result = $this->writeConfig($config, self::CONFIG_FILE_PATH, self::CONFIG_FILE_FORMAT);
+ if ($result === true) {
+ $this->config = null;
+ }
}
return $result;
}
}
/**
- * Get web administrator name.
- * Web interface supports one admin and many normal users.
+ * Set default options for new config.
*
- * @return string web admin name
+ * @param array $opts custom options to add to default options
+ * @return true on success, otherwise false
*/
- public function getWebAdmin() {
+ public function setDefConfigOpts($opts = []) {
$config = $this->getConfig();
- $web_admin = $config['baculum']['login'];
- return $web_admin;
+
+ $baculum = [
+ 'debug' => 0,
+ 'lang' => self::DEF_LANG,
+ 'max_jobs' => self::DEF_MAX_JOBS,
+ 'size_values_unit' => self::DEF_SIZE_VAL_UNIT,
+ 'time_in_job_log' => self::DEF_TIME_IN_JOB_LOG
+ ];
+ if (key_exists('baculum', $config)) {
+ $config['baculum'] = array_merge($baculum, $config['baculum']);
+ } else {
+ $config['baculum'] = $baculum;
+ }
+
+ $user_file = $this->getModule('basic_webuser')->getConfigPath();
+ // basic options
+ $auth_basic = [
+ 'allow_manage_users' => 1,
+ 'user_file' => $user_file,
+ 'hash_alg' => Crypto::HASH_ALG_APR1_MD5
+ ];
+ if (key_exists('auth_basic', $config)) {
+ $config['auth_basic'] = array_merge($auth_basic, $config['auth_basic']);
+ } else {
+ $config['auth_basic'] = $auth_basic;
+ }
+
+ // security options
+ $security = [
+ 'auth_method' => self::AUTH_METHOD_BASIC,
+ 'def_access' => self::DEF_ACCESS_DEFAULT_SETTINGS,
+ 'def_role' => WebUserRoles::NORMAL,
+ 'def_api_host' => HostConfig::MAIN_CATALOG_HOST
+ ];
+ if (key_exists('security', $config)) {
+ $config['security'] = array_merge($security, $config['security']);
+ } else {
+ $config['security'] = $security;
+ }
+
+ if (count($opts) > 0) {
+ $config = array_replace_recursive($config, $opts);
+ }
+
+ // set default properties
+ $ret = $this->setConfig($config);
+ if ($ret !== true) {
+ $emsg = 'Error while saving auth basic config.';
+ $this->getModule('logging')->log(
+ __FUNCTION__,
+ $emsg,
+ Logging::CATEGORY_APPLICATION,
+ __FILE__,
+ __LINE__
+ );
+ }
+ return $ret;
}
/**
- * Get application language short name.
- * If no language set then returned is default language.
+ * Get authentication method.
*
- * @access public
- * @return string lanuage short name
+ * @return string authentication method
*/
- public function getLanguage() {
- $language = null;
+ public function getAuthMethod() {
$config = $this->getConfig();
- if (array_key_exists('baculum', $config) && array_key_exists('lang', $config['baculum'])) {
- $language = $config['baculum']['lang'];
+
+ $auth_method = self::AUTH_METHOD_BASIC; // Basic is default method
+ if (isset($config['security']['auth_method'])) {
+ $auth_method = $config['security']['auth_method'];
}
- if (is_null($language)) {
- $language = self::DEFAULT_LANGUAGE;
+ return $auth_method;
+ }
+
+ /**
+ * Check if current authentication method is set to Basic.
+ *
+ * @return boolean true if is set Basic auth, otherwise false
+ */
+ public function isAuthMethodBasic() {
+ return ($this->getAuthMethod() === self::AUTH_METHOD_BASIC);
+ }
+
+ /**
+ * Check if current authentication method is set to LDAP.
+ *
+ * @return boolean true if is set LDAP auth, otherwise false
+ */
+ public function isAuthMethodLdap() {
+ return ($this->getAuthMethod() === self::AUTH_METHOD_LDAP);
+ }
+
+ /**
+ * Check if current default access method for not existing users
+ * in configuration file is set to no access.
+ *
+ * @return boolean true if is set no access, otherwise false
+ */
+ public function isDefAccessNoAccess() {
+ $config = $this->getConfig();
+ return (isset($config['security']['def_access']) && $config['security']['def_access'] === self::DEF_ACCESS_NO_ACCESS);
+ }
+
+ /**
+ * Check if current default access method for not existing users
+ * in configuration file is set to default settings.
+ *
+ * @return boolean true if is set default settings, otherwise false
+ */
+ public function isDefAccessDefaultSettings() {
+ $config = $this->getConfig();
+ return (isset($config['security']['def_access']) && $config['security']['def_access'] === self::DEF_ACCESS_DEFAULT_SETTINGS);
+ }
+
+ /**
+ * Set culture for whole page.
+ * Uses currently set language settings.
+ *
+ * @return none
+ */
+ public function setCulture() {
+ $this->Application->getGlobalization()->Culture = $this->getLanguage();
+ }
+
+ /**
+ * Get curently set language short name (for example: en, pl).
+ * If language short name is not set in session then the language value
+ * is taken from Baculum config file, saved in session and returned.
+ * If the language setting is set in session, then the value from
+ * session is returned.
+ *
+ * @return string currently set language short name
+ */
+ public function getLanguage() {
+ $language = null;
+ if (isset($_SESSION['language']) && !empty($_SESSION['language'])) {
+ $language = $_SESSION['language'];
+ } else {
+ $config = $this->getConfig();
+ if (isset($config['baculum']) && key_exists('lang', $config['baculum'])) {
+ $language = $config['baculum']['lang'];
+ }
+ if (is_null($language)) {
+ $language = self::DEF_LANG;
+ }
+ $_SESSION['language'] = $language;
}
return $language;
}
+
+ /**
+ * Set language for current page.
+ * Note, it is done in session only.
+ *
+ * @return none
+ */
+ public function setLanguage($lang) {
+ $_SESSION['language'] = $lang;
+ }
}
?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.Web.Class.WebModule');
+
+/**
+ * Web LDAP user manager module.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Web
+ */
+class WebLdapUserManager extends WebModule implements UserManager {
+
+ private $ldap = null;
+
+ public function init($config) {
+ parent::init($config);
+ $web_config = $this->getModule('web_config')->getConfig();
+ if (key_exists('auth_ldap', $web_config)) {
+ $this->ldap = $this->getModule('ldap');
+ $this->ldap->setParams($web_config['auth_ldap']);
+ }
+ }
+
+ public function validateUser($username, $password) {
+ return $this->ldap->login($username, $password);
+ }
+}
+?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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 web role configuration.
+ * Module is responsible for get/set web role config data.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Web
+ */
+class WebRoleConfig extends ConfigFileModule {
+
+ /**
+ * Web role name allowed characters pattern
+ */
+ const ROLE_PATTERN = '[\w\@\-\.]+';
+
+ /**
+ * Web role config file path
+ */
+ const CONFIG_FILE_PATH = 'Application.Web.Config.roles';
+
+ /**
+ * Web role config file format
+ */
+ const CONFIG_FILE_FORMAT = 'ini';
+
+ /**
+ * These options are obligatory for web config.
+ */
+ private $role_required_options = [
+ 'long_name',
+ 'description',
+ 'enabled',
+ 'resources'
+ ];
+
+ /**
+ * Stores web roles config content.
+ */
+ private $config = null;
+
+ /**
+ * Get (read) web role config.
+ *
+ * @return array config
+ */
+ public function getConfig() {
+ if (is_null($this->config)) {
+ $this->config = $this->getConfigInternal();
+ }
+ return $this->config;
+ }
+
+ private function getConfigInternal() {
+ $roles_config = [];
+ $config = $this->readConfig(self::CONFIG_FILE_PATH, self::CONFIG_FILE_FORMAT);
+ // Web role config validation per single role
+ foreach ($config as $role => $role_config) {
+ if ($this->isRoleConfigValid([$role => $role_config]) === false) {
+ /**
+ * If config for one web role is invalid, don't add this role config
+ * but continue checking next role config sections.
+ * It is because during adding new role manually, admin can do a typo
+ * in new role config section. Validation errors are logged.
+ */
+ continue;
+ }
+ $role_config['role'] = $role;
+ $roles_config[$role] = $role_config;
+ }
+ return $roles_config;
+ }
+
+ /**
+ * Set web role config.
+ * Method is private, because this method saves whole web role config.
+ * To add (or modify) role in config, use method to save single role in config.
+ * @see setRoleConfig()
+ *
+ * @param array $config config
+ * @return boolean true if config saved successfully, otherwise false
+ */
+ public function setConfig(array $config) {
+ $result = false;
+ if ($this->isRoleConfigValid($config) === true) {
+ $result = $this->writeConfig($config, self::CONFIG_FILE_PATH, self::CONFIG_FILE_FORMAT);
+ if ($result === true) {
+ $this->config = null;;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Get single role config.
+ *
+ * @param $role role name
+ * @return array role config
+ */
+ public function getRoleConfig($role) {
+ $role_config = [];
+ $config = $this->getConfig();
+ if (key_exists($role, $config)) {
+ $config[$role]['role'] = $role;
+ $role_config = $config[$role];
+ }
+ return $role_config;
+ }
+
+ /**
+ * Set single role config.
+ *
+ * @param string $role role name
+ * @param array $role config
+ * @return boolean true if config saved successfully, otherwise false
+ */
+ public function setRoleConfig($role, array $role_config) {
+ $config = $this->getConfig();
+ $config[$role] = $role_config;
+ return $this->setConfig($config);
+ }
+
+ /**
+ * Validate role single role section in config.
+ *
+ * @param array $config role config section
+ * @return boolean true if config valid, otherwise false
+ */
+ private function isRoleConfigValid(array $config) {
+ $invalid = ['required' => []];
+ foreach ($config as $role => $role_config) {
+ for ($i = 0; $i < count($this->role_required_options); $i++) {
+ if (!key_exists($this->role_required_options[$i], $role_config)) {
+ $invalid['required'][] = [
+ 'role' => $role,
+ 'value' => $this->role_required_options[$i],
+ 'type' => 'option'
+ ];
+ }
+ }
+ }
+
+ $valid = true;
+ $required_len = count($invalid['required']);
+ if ($required_len > 0) {
+ $valid = false;
+ $emsg = '';
+ $path = $this->getConfigRealPath(self::CONFIG_FILE_PATH);
+ for ($i = 0; $i < $required_len; $i++) {
+ $emsg = "ERROR [$path] Required {$invalid['required'][$i]['type']} '{$invalid['required'][$i]['value']}' not found for role '{$invalid['required'][$i]['role']}.";
+ $this->Application->getModule('logging')->log(
+ __FUNCTION__,
+ $emsg,
+ Logging::CATEGORY_APPLICATION,
+ __FILE__,
+ __LINE__
+ );
+ }
+ }
+ return $valid;
+ }
+}
+?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.
+ */
+
+
+/**
+ * Web user module.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Web
+ */
+class WebUser extends TUser {
+
+ /**
+ * Saved in session user properties.
+ */
+ const LONG_NAME = 'LongName';
+ const EMAIL = 'Email';
+ const DESCRIPTION = 'Description';
+ const API_HOSTS = 'ApiHosts';
+ const IPS = 'Ips';
+ const ENABLED = 'Enabled';
+ const IN_CONFIG = 'InConfig';
+
+ /**
+ * Create single user instance.
+ * Used for authenticated users.
+ * If user doesn't exist in configuration then default access values can be taken into accout.
+ *
+ * @param string username user name
+ * @return WebUser user instance
+ */
+ public function createUser($username) {
+ $user = Prado::createComponent(__CLASS__, $this->getManager());
+ $user->setUsername($username);
+
+ $application = $this->getManager()->getApplication();
+ $user_config = $application->getModule('user_config')->getUserConfig($username);
+ $web_config = $application->getModule('web_config')->getConfig();
+
+ if (count($user_config) > 0) {
+ // User exists in Baculum Web users database
+ $user->setInConfig(true);
+ $user->setDescription($user_config['description']);
+ $user->setLongName($user_config['long_name']);
+ $user->setEmail($user_config['email']);
+ $user->setRoles($user_config['roles']);
+ $user->setAPIHosts($user_config['api_hosts']);
+ $user->setIps($user_config['ips']);
+ $user->setEnabled($user_config['enabled']);
+ } elseif (isset($web_config['security']['def_access'])) {
+ // User doesn't exist. Check if user can have access.
+ $user->setInConfig(false);
+ if ($web_config['security']['def_access'] === WebConfig::DEF_ACCESS_NO_ACCESS) {
+ // no access, nothing to do
+ } elseif ($web_config['security']['def_access'] === WebConfig::DEF_ACCESS_DEFAULT_SETTINGS) {
+ if (isset($web_config['security']['def_role'])) {
+ $user->setRoles($web_config['security']['def_role']);
+ }
+ if (isset($web_config['security']['def_api_host'])) {
+ $user->setAPIHosts($web_config['security']['def_api_host']);
+ }
+ }
+ }
+ return $user;
+ }
+
+ /**
+ * Username setter.
+ *
+ * @param string $username user name
+ * @return none
+ */
+ public function setUsername($username) {
+ $this->setName($username);
+ }
+
+ /**
+ * Username getter.
+ *
+ * @return string user name
+ */
+ public function getUsername() {
+ return $this->getName();
+ }
+
+ /**
+ * Long name setter.
+ *
+ * @param string $long_name long name
+ * @return none
+ */
+ public function setLongName($long_name) {
+ $this->setState(self::LONG_NAME, $long_name);
+ }
+
+ /**
+ * Long name getter.
+ *
+ * @return string long name (default empty string)
+ */
+ public function getLongName() {
+ return $this->getState(self::LONG_NAME, '');
+ }
+
+ /**
+ * E-mail address setter.
+ *
+ * @param string $email e-mail address
+ * @return none
+ */
+ public function setEmail($email) {
+ $this->setState(self::EMAIL, $email);
+ }
+
+ /**
+ * E-mail address getter.
+ *
+ * @return string e-mail address
+ */
+ public function getEmail() {
+ return $this->getState(self::EMAIL, '');
+ }
+
+ /**
+ * Description setter.
+ *
+ * @param string $desc description
+ * @return none
+ */
+ public function setDescription($desc) {
+ $this->setState(self::DESCRIPTION, $desc);
+ }
+
+ /**
+ * Description getter.
+ *
+ * @return string description
+ */
+ public function getDescription() {
+ return $this->getState(self::DESCRIPTION, '');
+ }
+
+ /**
+ * Set API hosts.
+ * So far is supported only one API host per user.
+ * In the future this method can support more API hosts per user.
+ *
+ * @param string $api_hosts API hosts
+ * @return none
+ */
+ public function setAPIHosts($api_hosts) {
+ $this->setState(self::API_HOSTS, $api_hosts);
+ }
+
+ /**
+ * API hosts getter.
+ *
+ * @return string API host
+ */
+ public function getAPIHosts() {
+ $hosts = $this->getState(self::API_HOSTS);
+ $hosts = explode(',', $hosts);
+ if (count($hosts) == 1 && !empty($hosts[0])) {
+ $api_hosts = $hosts[0];
+ } else {
+ // default API host
+ $api_hosts = HostConfig::MAIN_CATALOG_HOST;
+ }
+ return $api_hosts;
+ }
+
+ /**
+ * IP address restriction setter.
+ *
+ * @param string $ips comma separated IP addresses
+ * @return none
+ */
+ public function setIps($ips) {
+ $this->setState(self::IPS, $ips);
+ }
+
+ /**
+ * IP address restriction getter.
+ *
+ * @return string comma separated IP address list (default empty string)
+ */
+ public function getIps() {
+ return $this->getState(self::IPS, '');
+ }
+
+ /**
+ * Enabled setter
+ *
+ * @param boolean $enabled enabled flag state
+ * @return none
+ */
+ public function setEnabled($enabled) {
+ $enabled = TPropertyValue::ensureBoolean($enabled);
+ $this->setState(self::ENABLED, $enabled);
+ }
+
+ /**
+ * Enabled getter.
+ *
+ * @return string enabled flag state (default false)
+ */
+ public function getEnabled() {
+ return $this->getState(self::ENABLED, false);
+ }
+
+ /**
+ * Set if user exists in configuration file.
+ *
+ * @param boolean $in_config in config state value
+ * @return none
+ */
+ public function setInConfig($in_config) {
+ $in_config = TPropertyValue::ensureBoolean($in_config);
+ $this->setState(self::IN_CONFIG, $in_config);
+ }
+
+ /**
+ * In config getter.
+ *
+ * @return string in config state value (default false)
+ */
+ public function getInConfig() {
+ return $this->getState(self::IN_CONFIG, false);
+ }
+}
+?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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');
+Prado::using('Application.Web.Class.WebUserRoles');
+
+/**
+ * Manage web user configuration.
+ * Module is responsible for get/set web user config data.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Web
+ */
+class WebUserConfig extends ConfigFileModule {
+
+ /**
+ * Web user name allowed characters pattern
+ */
+ const USER_PATTERN = '[\w\@\-\.]+';
+
+ /**
+ * Regular expression to validate e-mail address.
+ * Note, there exists TEmailAddressValidator control but it doesn't allow
+ * local e-mail address, for example root@localhost or gani@darkstar.
+ *
+ * @see http://www.regular-expressions.info/email.html
+ */
+ const EMAIL_ADDRESS_PATTERN = '[a-zA-Z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.?)+';
+
+ /**
+ * Web user config file path
+ */
+ const CONFIG_FILE_PATH = 'Application.Web.Config.users';
+
+ /**
+ * Web user config file format
+ */
+ const CONFIG_FILE_FORMAT = 'ini';
+
+ /**
+ * Stores web user config content.
+ */
+ private $config = null;
+
+ /**
+ * These properties are obligatory for web config.
+ */
+ private $user_req_prop = [
+ 'long_name',
+ 'description',
+ 'email',
+ 'roles',
+ 'api_hosts',
+ 'enabled',
+ 'ips'
+ ];
+
+ /**
+ * Get web user config.
+ *
+ * @return array config
+ */
+ public function getConfig() {
+ $config = [];
+ if (is_null($this->config)) {
+ $this->config = $this->readConfig(self::CONFIG_FILE_PATH, self::CONFIG_FILE_FORMAT);
+ }
+ // Web user config validation per single user
+ foreach ($this->config as $username => $user_config) {
+ if ($this->isUserConfigValid([$username => $user_config]) === false) {
+ /**
+ * If config for one web user is invalid, don't add this user config
+ * but continue checking next user config sections.
+ * It is because during adding new user manually, admin can do a typo
+ * in new user config section. Validation errors are logged.
+ */
+ continue;
+ }
+ $user_config['username'] = $username;
+ $config[$username] = $user_config;
+ }
+ return $config;
+ }
+
+ /**
+ * Set web user config.
+ * Method is private, because this method saves whole web user config.
+ * To add (or modify) user in config, use method to save single user in config.
+ * @see setUserConfig()
+ *
+ * @param array $config config
+ * @return boolean true if config saved successfully, otherwise false
+ */
+ public function setConfig(array $config) {
+ $result = false;
+ if ($this->isUserConfigValid($config) === true) {
+ $result = $this->writeConfig($config, self::CONFIG_FILE_PATH, self::CONFIG_FILE_FORMAT);
+ if ($result === true) {
+ $this->config = null;
+ }
+ }
+ return $result;
+ }
+
+ public function configExists() {
+ return parent::isConfig(self::CONFIG_FILE_PATH);
+ }
+
+ /**
+ * Get single user config.
+ *
+ * @param $user user name
+ * @return array user config
+ */
+ public function getUserConfig($username) {
+ $user_config = [];
+ $config = $this->getConfig();
+ if (key_exists($username, $config)) {
+ $config[$username]['username'] = $username;
+ $user_config = $config[$username];
+ }
+ return $user_config;
+ }
+
+ /**
+ * Set single user config.
+ *
+ * @param string $username user name
+ * @param array $user_config user configuration
+ * @return boolean true if config saved successfully, otherwise false
+ */
+ public function setUserConfig($username, array $user_config) {
+ $config = $this->getConfig();
+ $config[$username] = $user_config;
+ if (key_exists('username', $user_config)) {
+ unset($user_config['username']);
+ }
+ return $this->setConfig($config);
+ }
+
+ /**
+ * Get user config properties.
+ * If custom properties provided, they are merged with required properties.
+ *
+ * @param array $prop custom user properties
+ * @return array user config properties
+ */
+ public function getUserConfigProps($prop = []) {
+ $req_prop = array_fill_keys($this->user_req_prop, '');
+ return array_merge($req_prop, $prop);
+ }
+
+
+ /**
+ * Import basic auth users to web user config.
+ * It can be done both if web user config hasn't been created yet
+ * and if web user config exists already with some users.
+ *
+ * @return boolean true if config with imported users saved successfully,
+ * otherwise false
+ */
+ public function importUsersToConfig() {
+ $basic_users = $this->getModule('basic_webuser')->getUsers();
+ $web_config = $this->getModule('web_config')->getConfig();
+ $users = array_keys($basic_users);
+ sort($users);
+ $users_list = [];
+ $is_users_config = (key_exists('users', $web_config) && is_array($web_config['users']));
+ $user_count = count($users);
+ for ($i = 0; $i < $user_count; $i++) {
+ $host = '';
+ if ($is_users_config && key_exists($users[$i], $web_config['users'])) {
+ $host = $web_config['users'][$users[$i]];
+ }
+ $role = '';
+ if ((isset($web_config['baculum']['login']) && $users[$i] === $web_config['baculum']['login']) || $user_count === 1) {
+ $role = WebUserRoles::ADMIN;
+ } else {
+ $role = WebUserRoles::NORMAL;
+ }
+ $users_list[$users[$i]] = $this->getUserConfigProps([
+ 'roles' => $role,
+ 'api_hosts' => $host,
+ 'enabled' => 1
+ ]);
+ }
+ /**
+ * Merge existing user configs if any.
+ * Already existing users overwrites basic user configs if the same users
+ * exists in basic user config and web user configuration.
+ */
+ $users_list = array_merge($users_list, $this->getConfig());
+ return $this->setConfig($users_list);
+ }
+
+ /**
+ * Import users config from basic auth file into users.conf file.
+ * If users config file doesn't exist, create it and populate
+ * using basic users file. It is one time operation.
+ * Basic auth method is the main Baculum Web auth method. Before introducing
+ * users.conf file, it was the only one supported method.
+ *
+ * @return boolean true on successfull import, otherwise false
+ */
+ public function importUsers() {
+ // import can take place only if user config file doesn't exist
+ if (parent::isConfig(self::CONFIG_FILE_PATH) === true) {
+ return false;
+ }
+
+ // import users
+ $ret = $this->importUsersToConfig();
+ if ($ret === true) {
+ $ret = $this->getModule('web_config')->setDefConfigOpts();
+ } else {
+ $ret = false;
+ $emsg = 'Error while importing basic users.';
+ $this->getModule('logging')->log(
+ __FUNCTION__,
+ $emsg,
+ Logging::CATEGORY_APPLICATION,
+ __FILE__,
+ __LINE__
+ );
+ }
+ return $ret;
+ }
+
+ /**
+ * Validate user single user section in config.
+ *
+ * @param array $config user config section
+ * @return boolean true if config valid, otherwise false
+ */
+ private function isUserConfigValid(array $config) {
+ $invalid = ['required' => []];
+ foreach ($config as $username => $user_config) {
+ for ($i = 0; $i < count($this->user_req_prop); $i++) {
+ if (!key_exists($this->user_req_prop[$i], $user_config)) {
+ $invalid['required'][] = [
+ 'username' => $username,
+ 'value' => $this->user_req_prop[$i],
+ 'type' => 'option'
+ ];
+ }
+ }
+ }
+
+ $valid = true;
+ $required_len = count($invalid['required']);
+ if ($required_len > 0) {
+ $valid = false;
+ $emsg = '';
+ $path = $this->getConfigRealPath(self::CONFIG_FILE_PATH);
+ for ($i = 0; $i < $required_len; $i++) {
+ $emsg = "ERROR [$path] Required {$invalid['required'][$i]['type']} '{$invalid['required'][$i]['value']}' not found for user '{$invalid['required'][$i]['username']}.";
+ $this->Application->getModule('logging')->log(
+ __FUNCTION__,
+ $emsg,
+ Logging::CATEGORY_APPLICATION,
+ __FILE__,
+ __LINE__
+ );
+ }
+ }
+ return $valid;
+ }
+}
+?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.Web.Class.WebUser');
+Prado::using('Application.Web.Class.WebModule');
+
+/**
+ * Web user manager module.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Web
+ */
+class WebUserManager extends WebModule implements IUserManager {
+
+ /**
+ * Authorized flag key.
+ * This flag is used to set authorization state.
+ */
+ const SET_AUTHROIZED_FLAG = 'AuthorizedFlag';
+
+ /**
+ * User class to represent single user instance
+ */
+ private $user_class = '';
+
+ /**
+ * Guest name.
+ */
+ private $guest_name = 'guest';
+
+ /**
+ * Stores object used to create users (factory)
+ */
+ private $user_factory;
+
+ /**
+ * Initialize module configuration.
+ *
+ * @param TXmlElement $config module configuration
+ * @return none
+ */
+ public function init($config) {
+ $this->user_factory = Prado::createComponent($this->user_class, $this);
+ $this->setAuthorizedFlag(null);
+ $this->getModule('auth')->attachEventHandler(
+ 'OnAuthenticate',
+ [$this, 'doAuthentication']
+ );
+ $this->Application->attachEventHandler(
+ 'onAuthorization',
+ [$this, 'doAuthorization']
+ );
+ $this->Application->attachEventHandler(
+ 'onAuthorizationComplete',
+ [$this, 'doAuthorizationComplete']
+ );
+ }
+
+ /**
+ * Create and get new user object.
+ *
+ * @param mixed $username username string or null for guests
+ */
+ public function getUser($username = null) {
+ $user = null;
+ if (is_null($username)) {
+ $user = Prado::createComponent($this->user_class, $this);
+ $user->setIsGuest(true);
+ } else {
+ $user = $this->user_factory->createUser($username);
+ $user->setIsGuest(false);
+ }
+ return $user;
+ }
+
+ /**
+ * Used for authentication.
+ * It does login try.
+ *
+ * @param string $username username
+ * @param string $password password
+ * @return boolean true if user and password are valid, false otherwise
+ */
+ public function validateUser($username, $password) {
+ $valid = false;
+ $manager_cls = $this->getUserManagerClass();
+ if (!empty($manager_cls)) {
+ $manager = Prado::createComponent($manager_cls);
+ $manager->init(null);
+ $valid = $manager->validateUser($username, $password);
+ }
+ return $valid;
+ }
+
+ /**
+ * User class getter.
+ *
+ * @return string user class name
+ */
+ public function getUserClass() {
+ return $this->user_class;
+ }
+
+ /**
+ * User class setter.
+ *
+ * @param string $cls user class name
+ * @return none
+ */
+ public function setUserClass($cls) {
+ $this->user_class = $cls;
+ }
+
+ /**
+ * Guest name getter.
+ *
+ * @return guest name
+ */
+ public function getGuestName() {
+ return $this->guest_name;
+ }
+
+ /**
+ * Guest name setter.
+ *
+ * @param string guest name
+ * @return none
+ */
+ public function setGuestName($name) {
+ $this->guest_name = $name;
+ }
+
+ /**
+ * Used for getting stored in cookie information about user.
+ * Useful to keep login between sessions.
+ *
+ * @param THttpCookie $cookie cookie object
+ * @return none
+ */
+ public function getUserFromCookie($cookie) {
+ // not implemented
+ }
+
+ /**
+ * Used for setting in cookie information about user.
+ * Useful to keep login between sessions.
+ *
+ * @param THttpCookie $cookie cookie object
+ * @return none
+ */
+ public function saveUserToCookie($cookie) {
+ // not implemented
+ }
+
+ /**
+ * Check if currently loading page is allowed for current user.
+ *
+ * @param WebUser $user user object
+ * @param string $page_path page path
+ */
+ public function isPageAllowed($user, $page_path) {
+ $allowed = false;
+ $page_roles = $this->getModule('user_role')->getRolesByPagePath($page_path);
+ $user_roles = $user->getRoles();
+ for ($i = 0; $i < count($user_roles); $i++) {
+ if (in_array($user_roles[$i], $page_roles)) {
+ $allowed = true;
+ break;
+ }
+ }
+ return $allowed;
+ }
+
+ /**
+ * Get user manager class.
+ * It is switcher between different authentication backends.
+ *
+ * @return string user manager class path
+ */
+ private function getUserManagerClass() {
+ $cls = null;
+ $auth_method = $this->getModule('web_config')->getAuthMethod();
+
+ switch ($auth_method) {
+ case WebConfig::AUTH_METHOD_BASIC:
+ $cls = 'Application.Web.Class.WebBasicUserManager';
+ break;
+ case WebConfig::AUTH_METHOD_LDAP:
+ $cls = 'Application.Web.Class.WebLdapUserManager';
+ break;
+ default:
+ $cls = 'Application.Web.Class.WebBasicUserManager';
+ }
+ return $cls;
+ }
+
+ /**
+ * Event handler attached to the application authentication event.
+ * It does auto-login for Basic authentication users and applies
+ * authorization rules before they are used in authorization phase.
+ *
+ * @param TApplication $application application object
+ * @return none
+ */
+ public function doAuthentication($application) {
+ if ($this->getModule('web_config')->isAuthMethodBasic() && $application->getUser()->IsGuest) {
+ /**
+ * For basic method the authentication is realized by web server.
+ * From this reason do log in autmatically.
+ */
+ // For basic auth take username from web server.
+ if (key_exists('PHP_AUTH_USER' , $_SERVER) && !empty($_SERVER['PHP_AUTH_USER'])) {
+ $username = $_SERVER['PHP_AUTH_USER'];
+ $password = '';
+ $this->getModule('auth')->login($username, $password);
+ $this->Response->redirect('/');
+ } else {
+ $emsg = 'Basic auth is enabled, but PHP_AUTH_USER is not set or is empty.';
+ $this->Application->getModule('logging')->log(
+ __FUNCTION__,
+ $emsg,
+ Logging::CATEGORY_SECURITY,
+ __FILE__,
+ __LINE__
+ );
+ }
+ }
+
+ $this->applyAuthorizationRules($application);
+ }
+
+ /**
+ * Apply authorization rules.
+ * It is important place because here are defined authorization rules for currently
+ * logged in user and currently visited page.
+ *
+ * @param TApplication $application application object
+ */
+ private function applyAuthorizationRules($application) {
+ $page_path = $this->getService()->getRequestedPagePath();
+ $page_roles = $this->getModule('user_role')->getRolesByPagePath($page_path, false);
+ $auth_rules = $this->getApplication()->getAuthorizationRules();
+ $web_config = $this->getModule('web_config');
+ $roles = implode(',', $page_roles);
+ $users = '';
+ $ips = $application->User->getIps();
+ $pc = $this->getModule('page_category');
+ $add_role = false;
+ if ($pc->isCategorySystem($page_path) || $pc->isCategoryConditional($page_path)) {
+ // allow system pages for all logged in users
+ $roles = '';
+ $users = '@';
+ $add_role = true;
+ }
+
+ if ($pc->isCategoryPublic($page_path)) {
+ // public pages are available for everybody
+ $roles = $ips = '';
+ $users = '*';
+ $add_role = true;
+ }
+
+ // remove authorization rules if any
+ $auth_rules->clear();
+
+ /**
+ * Rules for current page.
+ * NOTE: items order has meaning.
+ */
+ $allow_rule = [
+ 'action' => 'allow',
+ 'roles' => $roles,
+ 'users' => $users,
+ 'verb' => '',
+ 'ips' => $ips
+ ];
+
+ $deny_rule = [
+ 'action' => 'deny',
+ 'roles' => '',
+ 'users' => '*',
+ 'verb' => '',
+ 'ips' => ''
+ ];
+ $rules = [];
+
+ /**
+ * Add allow rule for enabled users, for special pages (system and public)
+ * and if user doesn't exist in user config and access by default setting
+ * is enabled.
+ */
+ if ($application->User->Enabled === true || $add_role === true || (!$application->User->InConfig && $web_config->isDefAccessDefaultSettings())) {
+ // Add allow rules for user with set enabled flag
+ $rules[] = $allow_rule;
+ }
+ // Deny everything else
+ $rules[] = $deny_rule;
+
+ // Add authorization rules
+ for ($i = 0; $i < count($rules); $i++) {
+ $rule = new TAuthorizationRule(
+ $rules[$i]['action'],
+ $rules[$i]['users'],
+ $rules[$i]['roles'],
+ $rules[$i]['verb'],
+ $rules[$i]['ips']
+ );
+ $auth_rules->insertAt($i, $rule);
+ }
+ }
+
+ /**
+ * Set authorization flag.
+ * This flag is to know if user finished authorization process successfully or not.
+ * The flag is set in temporary on authorization process and it is set to null
+ * just after the authorization finishes with success. If authorization fails
+ * then onAuthorizationComplete event isn't fired because all process page loading
+ * is stopped on onAuthorization event.
+ * NOTE: This flag has only informational character. All authorization work is
+ * done by the framework.
+ *
+ * @param string|null $state flag state
+ * @return none
+ */
+ public function setAuthorizedFlag($state) {
+ $this->Application->getSession()->add(self::SET_AUTHROIZED_FLAG, $state);
+ }
+
+ /**
+ * Get authorization flag.
+ *
+ * @return string|null flag state
+ */
+ public function getAuthorizedFlag() {
+ return $this->Application->getSession()->itemAt(self::SET_AUTHROIZED_FLAG);
+ }
+
+ /**
+ * Event handler attached to the application authorization event.
+ * It sets the authorization flag.
+ *
+ * @param TApplication $param application object
+ */
+ public function doAuthorization($application) {
+ $service = $this->Application->getService();
+ $auth = $this->getModule('auth');
+ $page = $service->getRequestedPagePath();
+ if (($service instanceof TPageService) && $page !== $auth->getLoginPage()) {
+ $this->setAuthorizedFlag($page);
+ }
+ }
+
+ /**
+ * Event handler attached to the application authorization event.
+ * It sets the authorization flag.
+ *
+ * @param TApplication $param application object
+ */
+ public function doAuthorizationComplete($application) {
+ $service = $this->Application->getService();
+ $auth = $this->getModule('auth');
+ $page = $service->getRequestedPagePath();
+ if (($service instanceof TPageService) && $page === $this->getAuthorizedFlag()) {
+ $this->setAuthorizedFlag(null);
+ }
+ }
+
+ /**
+ * Returns authorization state for current page request.
+ * Because the framework doesn't provide any public method to check
+ * whether authorization finished successfully, this method can
+ * be used for that purpose.
+ * NOTE: Use it after onAuthorizationComplete application event,
+ * not before.
+ *
+ * @return boolean true if authorization finished successfully, otherwise false
+ */
+ public function isAuthorized() {
+ $web_config = $this->getModule('web_config');
+ $user = $this->Application->getUser();
+ return ($this->getAuthorizedFlag() === null &&
+ (($user->InConfig && !$web_config->isDefAccessNoAccess()) ||
+ (!$user->InConfig && $web_config->isDefAccessDefaultSettings()))
+ );
+ }
+}
+?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.Web.Class.WebModule');
+Prado::using('Application.Web.Class.PageCategory');
+
+/**
+ * Web user roles module.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Web
+ */
+class WebUserRoles extends WebModule {
+
+ /**
+ * Pre-defined roles.
+ */
+ const ADMIN = 'admin';
+ const NORMAL = 'normal';
+
+ /**
+ * Single role properties.
+ */
+ private $role_prop = [
+ 'role',
+ 'long_name',
+ 'description',
+ 'enabled',
+ 'resources'
+ ];
+
+ /**
+ * Get pre-defined roles with available resources for them.
+ *
+ * @return array pre-defined roles
+ */
+ public function getPreDefinedRoles() {
+ $roles = [];
+
+ // Admin user resources
+ $res = $this->getModule('page_category')->getCategories(false);
+ $admin_res = array_values($res);
+ $roles[self::ADMIN] = array_combine($this->role_prop, [
+ self::ADMIN,
+ 'Administrator',
+ 'Role with full access',
+ '1',
+ implode(',', $admin_res)
+ ]);
+
+ // Normal user resources
+ $res = [
+ PageCategory::DASHBOARD,
+ PageCategory::JOB_HISTORY_LIST,
+ PageCategory::JOB_HISTORY_VIEW,
+ PageCategory::JOB_LIST,
+ PageCategory::JOB_VIEW,
+ PageCategory::CLIENT_LIST,
+ PageCategory::CLIENT_VIEW,
+ PageCategory::RESTORE_WIZARD,
+ PageCategory::GRAPHS
+ ];
+ $roles[self::NORMAL] = array_combine($this->role_prop, [
+ self::NORMAL,
+ 'Normal user',
+ 'Role with limitted access',
+ '1',
+ implode(',', $res)
+ ]);
+ return $roles;
+ }
+
+ /**
+ * Check if a role is predefined.
+ *
+ * @param string $role nazwa roli
+ * @return boolean true if role is predefined, otherwise false
+ */
+ public function isRolePreDefined($role) {
+ $roles = $this->getPreDefinedRoles();
+ return key_exists($role, $roles);
+ }
+
+ /**
+ * Get all roles pre-defined and defined in config.
+ * Pre-defined are merged together with defined.
+ *
+ * @return array all available roles
+ */
+ public function getRoles() {
+ $roles = $this->getModule('role_config')->getConfig();
+ return array_merge($this->getPreDefinedRoles(), $roles);
+ }
+
+ /**
+ * Get single role config.
+ *
+ * @param string $role role name
+ * @return array role config or empty array if role doesn't exist
+ */
+ public function getRole($role) {
+ $ret = [];
+ $roles = $this->getRoles();
+ if (key_exists($role, $roles)) {
+ $roles[$role]['role'] = $role;
+ $ret = $roles[$role];
+ }
+ return $ret;
+ }
+
+ /**
+ * Get all roles by specific page path.
+ * They are roles that have page defined in allowed resources.
+ *
+ * @param string $page_path page path
+ * @return array roles defined for a page
+ */
+ public function getRolesByPagePath($page_path, $with_disabled = true) {
+ $roles = [];
+ $all_roles = $this->getRoles();
+ foreach ($all_roles as $role => $prop) {
+ $rs = explode(',', $prop['resources']);
+ $enabled = (boolean)$prop['enabled'];
+ if (($enabled || $with_disabled) && in_array($page_path, $rs)) {
+ $roles[] = $role;
+ }
+ }
+ return $roles;
+ }
+
+ /**
+ * Get all pages for specific role.
+ */
+ public function getPagesByRole($role) {
+ $pages = [];
+ $role_cfg = $this->getRole($role);
+ return explode(',', $role_cfg['resources']);
+ }
+}
+?>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
date_default_timezone_set($timezone);
}
+/**
+ * Set time limit to 60 seconds.
+ * Please note that async requests default times out after 30 seconds
+ * if not set other value.
+ */
+set_time_limit(60);
+
/*
* Support for web servers (for example Lighttpd) which do not provide direct
* info about HTTP Basic auth to PHP superglobal $_SERVER array.
}
}
-var Users = {
- ids: {
- create_user: {
- add_user: 'add_user',
- add_user_btn: 'add_user_btn',
- newuser: 'newuser',
- newpwd: 'newpwd'
- },
- change_pwd: {
- rel_chpwd: 'chpwd',
- rel_chpwd_btn: 'chpwd_btn'
- },
- set_host: {
- rel_user_host: 'user_host_img'
- }
- },
- validators: {
- user_pattern: null
- },
- current_action: null,
- init: function() {
- this.setEvents();
- },
- setEvents: function() {
- document.getElementById(this.ids.create_user.add_user_btn).addEventListener('click', function(e) {
- $('#' + this.ids.create_user.add_user).show();
- $('#' + this.ids.create_user.newuser).focus();
- }.bind(this));
- document.getElementById(this.ids.create_user.newuser).addEventListener('keydown', function(e) {
- var target = e.target || e.srcElement;
- if (e.keyCode == 13) {
- this.addUser();
- } else if (e.keyCode == 27) {
- this.cancelAddUser();
- }
- return false;
- }.bind(this));
- document.getElementById(this.ids.create_user.newpwd).addEventListener('keydown', function(e) {
- var target = e.target || e.srcElement;
- if (e.keyCode == 13) {
- this.addUser();
- } else if (e.keyCode == 27) {
- this.cancelAddUser();
- }
- return false;
- }.bind(this));
- },
- userValidator: function(user) {
- user = user.replace(/\s/g, '');
- if (user == '') {
- alert(this.txt.enter_login);
- return false;
- }
- var valid = this.validators.user_pattern.test(user);
- if (valid === false) {
- alert(this.txt.invalid_login);
- return false;
- }
- return true;
- },
- pwdValidator: function(pwd) {
- var valid = pwd.length > 4;
- if (valid === false) {
- alert(this.txt.invalid_pwd);
- }
- return valid;
- },
- addUser: function() {
- var user = document.getElementById(this.ids.create_user.newuser);
- var pwd = document.getElementById(this.ids.create_user.newpwd);
- if (this.userValidator(user.value) === false) {
- return false;
- }
- if (this.pwdValidator(pwd.value) === false) {
- return false;
- }
-
- $('#' + this.ids.create_user.add_user).hide();
- this.action_callback('newuser', user.value, pwd.value);
- user.value = '';
- pwd.value = '';
- return true;
- },
- rmUser: function(user) {
- this.action_callback('rmuser', user);
- },
- showChangePwd: function(el) {
- $('a[rel=\'' + this.ids.change_pwd.rel_chpwd_btn + '\']').show();
- $('#' + el).hide();
- $('span[rel=\'' + this.ids.change_pwd.rel_chpwd + '\']').hide();
- $(el.nextElementSibling).show();
- $(el.nextElementSibling).find('input')[0].focus();
- },
- changePwd: function(el, user) {
- var pwd = el.value;
-
- if (this.pwdValidator(pwd) === false) {
- return false;
- }
-
- $(el.parentNode).hide();
- $(el.parentNode.previousElementSibling).show();
- this.action_callback('chpwd', user, pwd);
- return true;
- },
- set_host: function(user, select) {
- select.nextElementSibling.style.visibility = 'visible';
- this.action_callback('set_host', user, select.value);
- },
- hide_loader: function() {
- setTimeout(function() {
- if (this.current_action === 'set_host') {
- $('I[rel=\'' + this.ids.set_host.rel_user_host + '\']').css({visibility: 'hidden'});
- }
- }.bind(this), 300);
-
- },
- cancelAddUser: function() {
- $('#' + this.ids.create_user.add_user).hide();
- },
- cancelChangePwd: function(el) {
- $(el.parentNode.parentNode).hide();
- }
-
-};
-
var W3SideBar = {
ids: {
sidebar: 'sidebar',
return $.fn.dataTable.util.escapeRegex(value);
};
+/**
+ * Do validation comma separated list basing on regular expression
+ * for particular values.
+ */
+function validate_comma_separated_list(value, regex) {
+ var valid = true;
+ var vals = value.split(',');
+ var val;
+ for (var i = 0; i < vals.length; i++) {
+ val = vals[i].trim();
+ if (!val || (regex && !regex.test(val))) {
+ valid = false;
+ break;
+ }
+ }
+ return valid;
+}
function get_table_toolbar(table, actions, txt) {
- var table_toolbar = document.querySelector('div.table_toolbar');
+ var table_toolbar = document.querySelector('#' + table.table().node().id + '_wrapper div.table_toolbar');
table_toolbar.className += ' dt-buttons';
table_toolbar.style.display = 'none';
var title = document.createTextNode(txt.actions);
select.appendChild(option);
acts[actions[i].action] = actions[i];
}
+ if (actions.length == 1) {
+ select.selectedIndex = 1;
+ }
var btn = document.createElement('BUTTON');
btn.type = 'button';
btn.className = 'dt-button';
msgid "Not supported"
msgstr "Not supported"
-msgid "Authorization to Baculum API"
-msgstr "Authorization to Baculum API"
-
-msgid "Authorization to Baculum Web"
-msgstr "Authorization to Baculum Web"
-
msgid "Step 3 - authentication params to Baculum Web panel"
msgstr "Step 3 - authentication params to Baculum Web panel"
msgid "During restore there will be used following volumes:"
msgstr "During restore there will be used following volumes:"
-msgid "Use Ctrl + Mouse Click to change selection"
-msgstr "Use Ctrl + Mouse Click to change selection"
-
msgid "Go back"
msgstr "Go back"
msgid "Show time in job log:"
msgstr "Show time in job log:"
+
+msgid "Security"
+msgstr "Security"
+
+msgid "Roles"
+msgstr "Roles"
+
+msgid "Long name"
+msgstr "Long name"
+
+msgid "Edit user"
+msgstr "Edit user"
+
+msgid "Long name:"
+msgstr "Long name:"
+
+msgid "Description:"
+msgstr "Description:"
+
+msgid "E-mail:"
+msgstr "E-mail:"
+
+msgid "Roles:"
+msgstr "Roles:"
+
+msgid "Invalid e-mail address value."
+msgstr "Invalid e-mail address value."
+
+msgid "Invalid username value."
+msgstr "Invalid username value."
+
+msgid "Username"
+msgstr "Username"
+
+msgid "E-mail"
+msgstr "E-mail"
+
+msgid "Description"
+msgstr "Description"
+
+msgid "Add user"
+msgstr "Add user"
+
+msgid "Username field cannot be empty."
+msgstr "Username field cannot be empty."
+
+msgid "At least one role must be selected."
+msgstr "At least one role must be selected."
+
+msgid "Username with the given name already exists."
+msgstr "Username with the given name already exists."
+
+msgid "Edit role"
+msgstr "Edit role"
+
+msgid "Role with limitted access"
+msgstr "Role with limitted access"
+
+msgid "Role with full access"
+msgstr "Role with full access"
+
+msgid "Role:"
+msgstr "Role:"
+
+msgid "Resources:"
+msgstr "Resources:"
+
+msgid "Role name"
+msgstr "Role name"
+
+msgid "Resources"
+msgstr "Resources"
+
+msgid "Add new role"
+msgstr "Add new role"
+
+msgid "Role field cannot be empty."
+msgstr "Role field cannot be empty."
+
+msgid "At least one resource must be selected."
+msgstr "At least one resource must be selected."
+
+msgid "Role with the given name already exists."
+msgstr "Role with the given name already exists."
+
+msgid "This is native predefined role, that cannot be changed. For having custom roles please use the button to add new role."
+msgstr "This is native predefined role, that cannot be changed. For having custom roles please use the button to add new role."
+
+msgid "The following roles are predefined and cannot be removed: %predefined_roles"
+msgstr "The following roles are predefined and cannot be removed: %predefined_roles"
+
+msgid "The following roles are using by users and cannot be removed: %used_roles To remove them, please unassign all users from these roles."
+msgstr "The following roles are using by users and cannot be removed: %used_roles To remove them, please unassign all users from these roles."
+
+msgid "Add role"
+msgstr "Add role"
+
+msgid "Authentication method"
+msgstr "Authentication method"
+
+msgid "HTTP Basic authentication"
+msgstr "HTTP Basic authentication"
+
+msgid "LDAP authentication"
+msgstr "LDAP authentication"
+
+msgid "LDAP server options"
+msgstr "LDAP server options"
+
+msgid "SSL encryption (LDAPS):"
+msgstr "SSL encryption (LDAPS):"
+
+msgid "Protocol version:"
+msgstr "Protocol version:"
+
+msgid "LDAP version"
+msgstr "LDAP version"
+
+msgid "LDAP authentication method"
+msgstr "LDAP authentication method"
+
+msgid "Anonymous authentication"
+msgstr "Anonymous authentication"
+
+msgid "Simple authentication"
+msgstr "Simple authentication"
+
+msgid "Base DN:"
+msgstr "Base DN:"
+
+msgid "Attributes"
+msgstr "Attributes"
+
+msgid "Please enter port."
+msgstr "Please enter port."
+
+msgid "Please enter username."
+msgstr "Please enter username."
+
+msgid "Manager DN:"
+msgstr "Manager DN:"
+
+msgid "Please enter IP Address/Hostname."
+msgstr "Please enter IP Address/Hostname."
+
+msgid "Please enter username attribute."
+msgstr "Please enter username attribute."
+
+msgid "Test LDAP connection"
+msgstr "Test LDAP connection"
+
+msgid "Manage LDAP users"
+msgstr "Manage LDAP users"
+
+msgid "Please enter Base DN."
+msgstr "Please enter Base DN."
+
+msgid "LDAP user list"
+msgstr "LDAP user list"
+
+msgid "Import users"
+msgstr "Import users"
+
+msgid "Import all users"
+msgstr "Import all users"
+
+msgid "Import selected users"
+msgstr "Import selected users"
+
+msgid "Import users whose fulfill criteria"
+msgstr "Import users whose fulfill criteria"
+
+msgid "Import options:"
+msgstr "Import options:"
+
+msgid "Criteria filter:"
+msgstr "Criteria filter:"
+
+msgid "contains:"
+msgstr "contains:"
+
+msgid "Reload"
+msgstr "Reload"
+
+msgid "Please select users in table to import."
+msgstr "Please select users in table to import."
+
+msgid "Do not overwrite existing Baculum Web users, if they have the same username"
+msgstr "Do not overwrite existing Baculum Web users, if they have the same username"
+
+msgid "Default role for imported users:"
+msgstr "Default role for imported users:"
+
+msgid "Advanced options"
+msgstr "Advanced options"
+
+msgid "Default API host for imported users:"
+msgstr "Default API host for imported users:"
+
+msgid "Log in"
+msgstr "Log in"
+
+msgid "Invalid username or password"
+msgstr "Invalid username or password"
+
+msgid "IP address restrictions"
+msgstr "IP address restrictions"
+
+msgid "IP address restrictions:"
+msgstr "IP address restrictions:"
+
+msgid "Use CTRL + left-click to multiple item selection"
+msgstr "Use CTRL + left-click to multiple item selection"
+
+msgid "Comma separated IP addresses. Using asterisk character, there is also possible to provide subnet, for example: 192.168.1.*"
+msgstr "Comma separated IP addresses. Using asterisk character, there is also possible to provide subnet, for example: 192.168.1.*"
+
+msgid "Default IP address restrictions for imported users:"
+msgstr "Default IP address restrictions for imported users:"
+
+msgid "Set your IP address"
+msgstr "Set your IP address"
+
+msgid "Invalid IP address restrictions value. This field can have comma separated IP addresses only or subnet addresses like 192.168.1.*"
+msgstr "Invalid IP address restrictions value. This field can have comma separated IP addresses only or subnet addresses like 192.168.1.*"
+
+msgid "This type of authentication is realized by web server. To use it, the basic authentication has to be enabled in the Baculum Web web server configuration."
+msgstr "This type of authentication is realized by web server. To use it, the basic authentication has to be enabled in the Baculum Web web server configuration."
+
+msgid "General settings"
+msgstr "General settings"
+
+msgid "Default access setting for logged in users not defined in Baculum Web:"
+msgstr "Default access setting for logged in users not defined in Baculum Web:"
+
+msgid "No access"
+msgstr "No access"
+
+msgid "Access with default settings"
+msgstr "Access with default settings"
+
+msgid "Default role:"
+msgstr "Default role:"
+
+msgid "Default API host:"
+msgstr "Default API host:"
+
+msgid "Users file path:"
+msgstr "Users file path:"
+
+msgid "Hash algorithm:"
+msgstr "Hash algorithm:"
+
+msgid "Please enter users file path value."
+msgstr "Please enter users file path value."
+
+msgid "Test"
+msgstr "Test"
+
+msgid "The user file is not accessible."
+msgstr "The user file is not accessible."
+
+msgid "The user file is not readable by web server user."
+msgstr "The user file is not readable by web server user."
+
+msgid "The user file is readable but not writeable by web server user."
+msgstr "The user file is readable but not writeable by web server user."
+
+msgid "Allow Baculum Web to manage the Basic authentication users (add/remove users and change their passwords)"
+msgstr "Allow Baculum Web to manage the Basic authentication users (add/remove users and change their passwords)"
+
+msgid "Authorization failed. Please contact the Baculum administrator to grant permissions."
+msgstr "Authorization failed. Please contact the Baculum administrator to grant permissions."
+
+msgid "Try again"
+msgstr "Try again"
+
+msgid "Manage Basic users"
+msgstr "Manage Basic users"
+
+msgid "Empty user list"
+msgstr "Empty user list"
+
+msgid "Basic user list"
+msgstr "Basic user list"
+
+msgid "Access to Baculum API"
+msgstr "Access to Baculum API"
+
+msgid "Access to Baculum Web"
+msgstr "Access to Baculum Web"
+
+msgid "Requried support from web server side - e.g. %supported_web_server_list"
+msgstr "Requried support from web server side - e.g. %supported_web_server_list"
+
+msgid "Apache 2.4 with apr-util 1.5+"
+msgstr "Apache 2.4 with apr-util 1.5+"
+
+msgid "Apache, Lighttpd and Nginx on most UNIX platforms"
+msgstr "Apache, Lighttpd and Nginx on most UNIX platforms"
+
+msgid "Nginx on most UNIX platforms"
+msgstr "Nginx on most UNIX platforms"
msgid "Authentication"
msgstr "認証"
-msgid "Authorization to Baculum API"
-msgstr "Baculum API 設定"
-
-msgid "Authorization to Baculum Web"
-msgstr "Baculum Web 管理ユーザー設定"
-
msgid "Authorization to Baculum panel"
msgstr "Authorization to Baculum panel"
msgid "Update volumes"
msgstr "Update volumes"
-msgid "Use Ctrl + Mouse Click to change selection"
-msgstr "Use Ctrl + Mouse Click to change selection"
-
msgid "Use HTTP Basic authentication"
msgstr "ベーシック認証使用"
msgid "Show time in job log:"
msgstr "Show time in job log:"
+
+msgid "Security"
+msgstr "Security"
+
+msgid "Roles"
+msgstr "Roles"
+
+msgid "Long name"
+msgstr "Long name"
+
+msgid "Edit user"
+msgstr "Edit user"
+
+msgid "Long name:"
+msgstr "Long name:"
+
+msgid "Description:"
+msgstr "Description:"
+
+msgid "E-mail:"
+msgstr "E-mail:"
+
+msgid "Roles:"
+msgstr "Roles:"
+
+msgid "Invalid e-mail address value."
+msgstr "Invalid e-mail address value."
+
+msgid "Invalid username value."
+msgstr "Invalid username value."
+
+msgid "Username"
+msgstr "Username"
+
+msgid "E-mail"
+msgstr "E-mail"
+
+msgid "Description"
+msgstr "Description"
+
+msgid "Add user"
+msgstr "Add user"
+
+msgid "Username field cannot be empty."
+msgstr "Username field cannot be empty."
+
+msgid "At least one role must be selected."
+msgstr "At least one role must be selected."
+
+msgid "Username with the given name already exists."
+msgstr "Username with the given name already exists."
+
+msgid "Edit role"
+msgstr "Edit role"
+
+msgid "Role with limitted access"
+msgstr "Role with limitted access"
+
+msgid "Role with full access"
+msgstr "Role with full access"
+
+msgid "Role:"
+msgstr "Role:"
+
+msgid "Resources:"
+msgstr "Resources:"
+
+msgid "Role name"
+msgstr "Role name"
+
+msgid "Resources"
+msgstr "Resources"
+
+msgid "Add new role"
+msgstr "Add new role"
+
+msgid "Role field cannot be empty."
+msgstr "Role field cannot be empty."
+
+msgid "At least one resource must be selected."
+msgstr "At least one resource must be selected."
+
+msgid "Role with the given name already exists."
+msgstr "Role with the given name already exists."
+
+msgid "This is native predefined role, that cannot be changed. For having custom roles please use the button to add new role."
+msgstr "This is native predefined role, that cannot be changed. For having custom roles please use the button to add new role."
+
+msgid "The following roles are predefined and cannot be removed: %predefined_roles"
+msgstr "The following roles are predefined and cannot be removed: %predefined_roles"
+
+msgid "The following roles are using by users and cannot be removed: %used_roles To remove them, please unassign all users from these roles."
+msgstr "The following roles are using by users and cannot be removed: %used_roles To remove them, please unassign all users from these roles."
+
+msgid "Add role"
+msgstr "Add role"
+
+msgid "Authentication method"
+msgstr "Authentication method"
+
+msgid "HTTP Basic authentication"
+msgstr "HTTP Basic authentication"
+
+msgid "LDAP authentication"
+msgstr "LDAP authentication"
+
+msgid "LDAP server options"
+msgstr "LDAP server options"
+
+msgid "SSL encryption (LDAPS):"
+msgstr "SSL encryption (LDAPS):"
+
+msgid "Protocol version:"
+msgstr "Protocol version:"
+
+msgid "LDAP version"
+msgstr "LDAP version"
+
+msgid "LDAP authentication method"
+msgstr "LDAP authentication method"
+
+msgid "Anonymous authentication"
+msgstr "Anonymous authentication"
+
+msgid "Simple authentication"
+msgstr "Simple authentication"
+
+msgid "Base DN:"
+msgstr "Base DN:"
+
+msgid "Attributes"
+msgstr "Attributes"
+
+msgid "Please enter port."
+msgstr "Please enter port."
+
+msgid "Please enter username."
+msgstr "Please enter username."
+
+msgid "Manager DN:"
+msgstr "Manager DN:"
+
+msgid "Please enter IP Address/Hostname."
+msgstr "Please enter IP Address/Hostname."
+
+msgid "Please enter username attribute."
+msgstr "Please enter username attribute."
+
+msgid "Test LDAP connection"
+msgstr "Test LDAP connection"
+
+msgid "Manage LDAP users"
+msgstr "Manage LDAP users"
+
+msgid "Please enter Base DN."
+msgstr "Please enter Base DN."
+
+msgid "LDAP user list"
+msgstr "LDAP user list"
+
+msgid "Import users"
+msgstr "Import users"
+
+msgid "Import all users"
+msgstr "Import all users"
+
+msgid "Import selected users"
+msgstr "Import selected users"
+
+msgid "Import users whose fulfill criteria"
+msgstr "Import users whose fulfill criteria"
+
+msgid "Import options:"
+msgstr "Import options:"
+
+msgid "Criteria filter:"
+msgstr "Criteria filter:"
+
+msgid "contains:"
+msgstr "contains:"
+
+msgid "Reload"
+msgstr "Reload"
+
+msgid "Please select users in table to import."
+msgstr "Please select users in table to import."
+
+msgid "Do not overwrite existing Baculum Web users, if they have the same username"
+msgstr "Do not overwrite existing Baculum Web users, if they have the same username"
+
+msgid "Default role for imported users:"
+msgstr "Default role for imported users:"
+
+msgid "Advanced options"
+msgstr "Advanced options"
+
+msgid "Default API host for imported users:"
+msgstr "Default API host for imported users:"
+
+msgid "Log in"
+msgstr "Log in"
+
+msgid "Invalid username or password"
+msgstr "Invalid username or password"
+
+msgid "IP address restrictions"
+msgstr "IP address restrictions"
+
+msgid "IP address restrictions:"
+msgstr "IP address restrictions:"
+
+msgid "Use CTRL + left-click to multiple item selection"
+msgstr "Use CTRL + left-click to multiple item selection"
+
+msgid "Comma separated IP addresses. Using asterisk character, there is also possible to provide subnet, for example: 192.168.1.*"
+msgstr "Comma separated IP addresses. Using asterisk character, there is also possible to provide subnet, for example: 192.168.1.*"
+
+msgid "Default IP address restrictions for imported users:"
+msgstr "Default IP address restrictions for imported users:"
+
+msgid "Set your IP address"
+msgstr "Set your IP address"
+
+msgid "Invalid IP address restrictions value. This field can have comma separated IP addresses only or subnet addresses like 192.168.1.*"
+msgstr "Invalid IP address restrictions value. This field can have comma separated IP addresses only or subnet addresses like 192.168.1.*"
+
+msgid "This type of authentication is realized by web server. To use it, the basic authentication has to be enabled in the Baculum Web web server configuration."
+msgstr "This type of authentication is realized by web server. To use it, the basic authentication has to be enabled in the Baculum Web web server configuration."
+
+msgid "General settings"
+msgstr "General settings"
+
+msgid "Default access setting for logged in users not defined in Baculum Web:"
+msgstr "Default access setting for logged in users not defined in Baculum Web:"
+
+msgid "No access"
+msgstr "No access"
+
+msgid "Access with default settings"
+msgstr "Access with default settings"
+
+msgid "Default role:"
+msgstr "Default role:"
+
+msgid "Default API host:"
+msgstr "Default API host:"
+
+msgid "Users file path:"
+msgstr "Users file path:"
+
+msgid "Hash algorithm:"
+msgstr "Hash algorithm:"
+
+msgid "Please enter users file path value."
+msgstr "Please enter users file path value."
+
+msgid "Test"
+msgstr "Test"
+
+msgid "The user file is not accessible."
+msgstr "The user file is not accessible."
+
+msgid "The user file is not readable by web server user."
+msgstr "The user file is not readable by web server user."
+
+msgid "The user file is readable but not writeable by web server user."
+msgstr "The user file is readable but not writeable by web server user."
+
+msgid "Allow Baculum Web to manage the Basic authentication users (add/remove users and change their passwords)"
+msgstr "Allow Baculum Web to manage the Basic authentication users (add/remove users and change their passwords)"
+
+msgid "Authorization failed. Please contact the Baculum administrator to grant permissions."
+msgstr "Authorization failed. Please contact the Baculum administrator to grant permissions."
+
+msgid "Try again"
+msgstr "Try again"
+
+msgid "Manage Basic users"
+msgstr "Manage Basic users"
+
+msgid "Empty user list"
+msgstr "Empty user list"
+
+msgid "Basic user list"
+msgstr "Basic user list"
+
+msgid "Access to Baculum API"
+msgstr "Access to Baculum API"
+
+msgid "Access to Baculum Web"
+msgstr "Access to Baculum Web"
+
+msgid "Requried support from web server side - e.g. %supported_web_server_list"
+msgstr "Requried support from web server side - e.g. %supported_web_server_list"
+
+msgid "Apache 2.4 with apr-util 1.5+"
+msgstr "Apache 2.4 with apr-util 1.5+"
+
+msgid "Apache, Lighttpd and Nginx on most UNIX platforms"
+msgstr "Apache, Lighttpd and Nginx on most UNIX platforms"
+
+msgid "Nginx on most UNIX platforms"
+msgstr "Nginx on most UNIX platforms"
msgid "Not supported"
msgstr "Nie wspierane"
-msgid "Authorization to Baculum API"
-msgstr "Autoryzacja do Baculum API"
-
-msgid "Authorization to Baculum Web"
-msgstr "Autoryzacja do Baculum Web"
-
msgid "Step 3 - authentication params to Baculum Web panel"
msgstr "Step 3 - parametry autentykacji do panelu Baculum Web"
msgid "During restore there will be used following volumes:"
msgstr "Podczas zadania przywracania danych potrzebne będą następujące wolumeny:"
-msgid "Use Ctrl + Mouse Click to change selection"
-msgstr "Aby zmienić wybór, użyj Ctrl + klik myszy"
-
msgid "Go back"
msgstr "Powrót"
msgid "Show time in job log:"
msgstr "Pokaż czas w dzienniku zadania:"
+
+msgid "Security"
+msgstr "Bezpieczeństwo"
+
+msgid "Roles"
+msgstr "Role"
+
+msgid "Long name"
+msgstr "Długa nazwa"
+
+msgid "Edit user"
+msgstr "Edytuj użytkownika"
+
+msgid "Long name:"
+msgstr "Długa nazwa:"
+
+msgid "Description:"
+msgstr "Opis:"
+
+msgid "E-mail:"
+msgstr "E-mail:"
+
+msgid "Roles:"
+msgstr "Roles:"
+
+msgid "Invalid e-mail address value."
+msgstr "Niepoprawna wartość adresu e-mail."
+
+msgid "Invalid username value."
+msgstr "Niepoprawna nazwa użytkownika."
+
+msgid "Username"
+msgstr "Użytkownik"
+
+msgid "E-mail"
+msgstr "E-mail"
+
+msgid "Description"
+msgstr "Opis"
+
+msgid "Add user"
+msgstr "Dodaj użytkownika"
+
+msgid "Username field cannot be empty."
+msgstr "Pole użytkownika nie może być puste."
+
+msgid "At least one role must be selected."
+msgstr "Co najmniej jednak rola musi być wybrana."
+
+msgid "Username with the given name already exists."
+msgstr "Użytkownik z podaną nazwą już istnieje."
+
+msgid "Edit role"
+msgstr "Edytuj rolę"
+
+msgid "Role with limitted access"
+msgstr "Rola z ograniczonym dostępem"
+
+msgid "Role with full access"
+msgstr "Rola z pełnym dostępem"
+
+msgid "Role:"
+msgstr "Rola:"
+
+msgid "Resources:"
+msgstr "Zasoby:"
+
+msgid "Role name"
+msgstr "Rola"
+
+msgid "Resources"
+msgstr "Zasoby"
+
+msgid "Add new role"
+msgstr "Dodaj nową rolę"
+
+msgid "Role field cannot be empty."
+msgstr "Pole roli nie może być puste."
+
+msgid "At least one resource must be selected."
+msgstr "Musisz wybrać co najmniej jeden zasób."
+
+msgid "Role with the given name already exists."
+msgstr "Rola z podaną nazwą już istnieje."
+
+msgid "This is native predefined role, that cannot be changed. For having custom roles, please use the button to add new role."
+msgstr "To jest natywna predefiniowana rola, która nie może zostać zmieniona. Aby mieć własne niestandardowe role, proszę użyć przycisku do dodawania nowej roli."
+
+msgid "The following roles are predefined and cannot be removed: %predefined_roles"
+msgstr "Następujące role są natywne predefiniowane i nie mogą zostać usunięte: %predefined_roles"
+
+msgid "The following roles are using by users and cannot be removed: %used_roles To remove them, please unassign all users from these roles."
+msgstr "Następujące role są używane przez użytkowników i nie mogą zostać usunięte: %used_roles Aby je usunąć, wypisz wszystkich"
+
+msgid "Add role"
+msgstr "Add role"
+
+msgid "Authentication method"
+msgstr "Metoda uwierzytelniania"
+
+msgid "HTTP Basic authentication"
+msgstr "Uwierzytelnianie HTTP Basic"
+
+msgid "LDAP authentication"
+msgstr "Uwierzytelnianie LDAP"
+
+msgid "LDAP server options"
+msgstr "Opcje serwera LDAP"
+
+msgid "SSL encryption (LDAPS):"
+msgstr "Szyfrowanie SSL (LDAPS):"
+
+msgid "Protocol version:"
+msgstr "Wersja protokołu:"
+
+msgid "LDAP version"
+msgstr "LDAP wersja"
+
+msgid "LDAP authentication method"
+msgstr "Metoda uwierzytelniania LDAP"
+
+msgid "Anonymous authentication"
+msgstr "Uwierzytelnianie anonimowe"
+
+msgid "Simple authentication"
+msgstr "Proste uwierzytelnianie"
+
+msgid "Base DN:"
+msgstr "Bazowy DN:"
+
+msgid "Attributes"
+msgstr "Atrybuty"
+
+msgid "Please enter port."
+msgstr "Proszę wprowadzić port."
+
+msgid "Please enter username."
+msgstr "Proszę wprowadzić nazwę użytkownika."
+
+msgid "Manager DN:"
+msgstr "DN konta administratora:"
+
+msgid "Please enter IP Address/Hostname."
+msgstr "Proszę wprowadzić adres IP/nazwę hosta."
+
+msgid "Please enter username attribute."
+msgstr "Proszę wprowadzić atrybut użytkownika."
+
+msgid "Test LDAP connection"
+msgstr "Test połączenia LDAP"
+
+msgid "Manage LDAP users"
+msgstr "Zarządzaj użytkowników LDAP"
+
+msgid "Please enter Base DN."
+msgstr "Proszę wprowadzić Bazowy DN."
+
+msgid "LDAP user list"
+msgstr "Lista użytkowników LDAP"
+
+msgid "Import users"
+msgstr "Importuj użytkowników"
+
+msgid "Import all users"
+msgstr "Importuj wszystkich użytkowników"
+
+msgid "Import selected users"
+msgstr "Importuj wybranych użytkowników"
+
+msgid "Import users whose fulfill criteria"
+msgstr "Importuj użytkowników spełniających kryteria"
+
+msgid "Import options:"
+msgstr "Opcje importu:"
+
+msgid "Criteria filter:"
+msgstr "Filtr kryteriów:"
+
+msgid "contains:"
+msgstr "zawiera:"
+
+msgid "Reload"
+msgstr "Przeładuj"
+
+msgid "Please select users in table to import."
+msgstr "Proszę wybrać użytkowników w tabeli do zaimportowania."
+
+msgid "Do not overwrite existing Baculum Web users, if they have the same username"
+msgstr "Nie nadpisuj istniejących użytkowników Baculum Web, jeśli mają tą samą nazwę"
+
+msgid "Default role for imported users:"
+msgstr "Domyślna rola dla zaimportowanych użytkowników:"
+
+msgid "Advanced options"
+msgstr "Opcje zaawansowane"
+
+msgid "Default API host for imported users:"
+msgstr "Domyślny host API dla zaimportowanych użytkowników:"
+
+msgid "Log in"
+msgstr "Zaloguj"
+
+msgid "Invalid username or password"
+msgstr "Niepoprawna nazwa użytkownika lub hasło"
+
+msgid "IP address restrictions"
+msgstr "IP address restrictions"
+
+msgid "IP address restrictions:"
+msgstr "IP address restrictions:"
+
+msgid "Use CTRL + left-click to multiple item selection"
+msgstr "Aby zaznaczyć wiele elementów, użyj CTRL + lewy przycisk myszy"
+
+msgid "Comma separated IP addresses. Using asterisk character, there is also possible to provide subnet, for example: 192.168.1.*"
+msgstr "Adresy IP oddzielone przecinkami. Używając znaku gwiazdki, jest również możliwe d"
+
+msgid "Default IP address restrictions for imported users:"
+msgstr "Domyślne restrykcje adresu IP dla zaimportowanych użytkowników:"
+
+msgid "Set your IP address"
+msgstr "Ustaw twój adres IP"
+
+msgid "Invalid IP address restrictions value. This field can have comma separated IP addresses only or subnet addresses like 192.168.1.*"
+msgstr "Nieprawidłowa wartość restrykcji adresów IP. To pole może mieć tylko oddzielone przecinkami adresy IP lub adresy podsieci, takie jak 192.168.1.*"
+
+msgid "This type of authentication is realized by web server. To use it, the basic authentication has to be enabled in the Baculum Web web server configuration."
+msgstr "Ten typ uwierzytelniania jest realizowany przez serwer WWW. Aby z niego skorzystać, uwierzytelnianie 'basic' musi być włączone w konfiguracji serwera WWW."
+
+msgid "General settings"
+msgstr "Ustawienia ogólne"
+
+msgid "Default access setting for logged in users not defined in Baculum Web:"
+msgstr "Domyślne ustawienie dostępu dla zalogowanych użytkowników nie zdefiniowanych w Baculum Web:"
+
+msgid "No access"
+msgstr "Brak dostępu"
+
+msgid "Access with default settings"
+msgstr "Dostęp z domyślnymi ustawieniami"
+
+msgid "Default role:"
+msgstr "Domyślna rola:"
+
+msgid "Default API host:"
+msgstr "Domyślny host API:"
+
+msgid "Users file path:"
+msgstr "Lokalizacja pliku użytkowników:"
+
+msgid "Hash algorithm:"
+msgstr "Funkcja skrótu:"
+
+msgid "Please enter users file path value."
+msgstr "Proszę wprowadzić lokalizację pliku użytkowników."
+
+msgid "Test"
+msgstr "Test"
+
+msgid "The user file is not accessible."
+msgstr "Plik użytkownika nie jest dostępny."
+
+msgid "The user file is not readable by web server user."
+msgstr "Plik użytkowników nie jest możliwy do odczytania przez użytkownika serwera WWW."
+
+msgid "The user file is readable but not writeable by web server user."
+msgstr "Plik użytkowników jest możliwy do odczytania, ale nie jest możliwy do zapisania przez użytkownika serwera WWW."
+
+msgid "Allow Baculum Web to manage the Basic authentication users (add/remove users and change their passwords)"
+msgstr "Allow Baculum Web to manage the Basic authentication users (add/remove users and change their passwords)"
+
+msgid "Authorization failed. Please contact the Baculum administrator to grant permissions."
+msgstr "Błąd autoryzacji. Proszę skontaktować się administratorem Baculum w celu nadania uprawnień."
+
+msgid "Try again"
+msgstr "Spróbuj ponownie"
+
+msgid "Manage Basic users"
+msgstr "Zarządzaj użytkownikami Basic"
+
+msgid "Empty user list"
+msgstr "Pusta lista użytkowników"
+
+msgid "Basic user list"
+msgstr "Lista użytkowników Basic"
+
+msgid "Access to Baculum API"
+msgstr "Dostęp do Baculum API"
+
+msgid "Access to Baculum Web"
+msgstr "Dostęp do Baculum Web"
+
+msgid "Requried support from web server side - e.g. %supported_web_server_list"
+msgstr "Wymagane wsparcie ze strony serwera WWW - m.in. %supported_web_server_list"
+
+msgid "Apache 2.4 with apr-util 1.5+"
+msgstr "Apache 2.4 z apr-util 1.5+"
+
+msgid "Apache, Lighttpd and Nginx on most UNIX platforms"
+msgstr "Apache, Lighttpd i Nginx na większości platform UNIXowych"
+
+msgid "Nginx on most UNIX platforms"
+msgstr "Nginx na większości platform UNIXowych"
msgid "Not supported"
msgstr "Não suportado"
-msgid "Authorization to Baculum API"
-msgstr "Autorização para a API do Baculum"
-
-msgid "Authorization to Baculum Web"
-msgstr "Autorização para o Baculum Web"
-
msgid "Step 3 - authentication params to Baculum Web panel"
msgstr "Etapa 3 - Parâmetros de autenticação para o painel Web do Baculum"
msgid "During restore there will be used following volumes:"
msgstr "Durante a restauração, serão utilizados os seguintes volumes:"
-msgid "Use Ctrl + Mouse Click to change selection"
-msgstr "Use Ctrl + Mouse Click para alterar a seleção"
-
msgid "Go back"
msgstr "Voltar"
msgid "Show time in job log:"
msgstr "Show time in job log:"
+
+msgid "Security"
+msgstr "Security"
+
+msgid "Roles"
+msgstr "Roles"
+
+msgid "Long name"
+msgstr "Long name"
+
+msgid "Edit user"
+msgstr "Edit user"
+
+msgid "Long name:"
+msgstr "Long name:"
+
+msgid "Description:"
+msgstr "Description:"
+
+msgid "E-mail:"
+msgstr "E-mail:"
+
+msgid "Roles:"
+msgstr "Roles:"
+
+msgid "Invalid e-mail address value."
+msgstr "Invalid e-mail address value."
+
+msgid "Invalid username value."
+msgstr "Invalid username value."
+
+msgid "Username"
+msgstr "Username"
+
+msgid "E-mail"
+msgstr "E-mail"
+
+msgid "Description"
+msgstr "Description"
+
+msgid "Add user"
+msgstr "Add user"
+
+msgid "Username field cannot be empty."
+msgstr "Username field cannot be empty."
+
+msgid "At least one role must be selected."
+msgstr "At least one role must be selected."
+
+msgid "Username with the given name already exists."
+msgstr "Username with the given name already exists."
+
+msgid "Edit role"
+msgstr "Edit role"
+
+msgid "Role with limitted access"
+msgstr "Role with limitted access"
+
+msgid "Role with full access"
+msgstr "Role with full access"
+
+msgid "Role:"
+msgstr "Role:"
+
+msgid "Resources:"
+msgstr "Resources:"
+
+msgid "Role name"
+msgstr "Role name"
+
+msgid "Resources"
+msgstr "Resources"
+
+msgid "Add new role"
+msgstr "Add new role"
+
+msgid "Role field cannot be empty."
+msgstr "Role field cannot be empty."
+
+msgid "At least one resource must be selected."
+msgstr "At least one resource must be selected."
+
+msgid "Role with the given name already exists."
+msgstr "Role with the given name already exists."
+
+msgid "This is native predefined role, that cannot be changed. For having custom roles please use the button to add new role."
+msgstr "This is native predefined role, that cannot be changed. For having custom roles please use the button to add new role."
+
+msgid "The following roles are predefined and cannot be removed: %predefined_roles"
+msgstr "The following roles are predefined and cannot be removed: %predefined_roles"
+
+msgid "The following roles are using by users and cannot be removed: %used_roles To remove them, please unassign all users from these roles."
+msgstr "The following roles are using by users and cannot be removed: %used_roles To remove them, please unassign all users from these roles."
+
+msgid "Add role"
+msgstr "Add role"
+
+msgid "Authentication method"
+msgstr "Authentication method"
+
+msgid "HTTP Basic authentication"
+msgstr "HTTP Basic authentication"
+
+msgid "LDAP authentication"
+msgstr "LDAP authentication"
+
+msgid "LDAP server options"
+msgstr "LDAP server options"
+
+msgid "SSL encryption (LDAPS):"
+msgstr "SSL encryption (LDAPS):"
+
+msgid "Protocol version:"
+msgstr "Protocol version:"
+
+msgid "LDAP version"
+msgstr "LDAP version"
+
+msgid "LDAP authentication method"
+msgstr "LDAP authentication method"
+
+msgid "Anonymous authentication"
+msgstr "Anonymous authentication"
+
+msgid "Simple authentication"
+msgstr "Simple authentication"
+
+msgid "Base DN:"
+msgstr "Base DN:"
+
+msgid "Attributes"
+msgstr "Attributes"
+
+msgid "Please enter port."
+msgstr "Please enter port."
+
+msgid "Please enter username."
+msgstr "Please enter username."
+
+msgid "Manager DN:"
+msgstr "Manager DN:"
+
+msgid "Please enter IP Address/Hostname."
+msgstr "Please enter IP Address/Hostname."
+
+msgid "Please enter username attribute."
+msgstr "Please enter username attribute."
+
+msgid "Test LDAP connection"
+msgstr "Test LDAP connection"
+
+msgid "Manage LDAP users"
+msgstr "Manage LDAP users"
+
+msgid "Please enter Base DN."
+msgstr "Please enter Base DN."
+
+msgid "LDAP user list"
+msgstr "LDAP user list"
+
+msgid "Import users"
+msgstr "Import users"
+
+msgid "Import all users"
+msgstr "Import all users"
+
+msgid "Import selected users"
+msgstr "Import selected users"
+
+msgid "Import users whose fulfill criteria"
+msgstr "Import users whose fulfill criteria"
+
+msgid "Import options:"
+msgstr "Import options:"
+
+msgid "Criteria filter:"
+msgstr "Criteria filter:"
+
+msgid "contains:"
+msgstr "contains:"
+
+msgid "Reload"
+msgstr "Reload"
+
+msgid "Please select users in table to import."
+msgstr "Please select users in table to import."
+
+msgid "Do not overwrite existing Baculum Web users, if they have the same username"
+msgstr "Do not overwrite existing Baculum Web users, if they have the same username"
+
+msgid "Default role for imported users:"
+msgstr "Default role for imported users:"
+
+msgid "Advanced options"
+msgstr "Advanced options"
+
+msgid "Default API host for imported users:"
+msgstr "Default API host for imported users:"
+
+msgid "Log in"
+msgstr "Log in"
+
+msgid "Invalid username or password"
+msgstr "Invalid username or password"
+
+msgid "IP address restrictions"
+msgstr "IP address restrictions"
+
+msgid "IP address restrictions:"
+msgstr "IP address restrictions:"
+
+msgid "Use CTRL + left-click to multiple item selection"
+msgstr "Use CTRL + left-click to multiple item selection"
+
+msgid "Comma separated IP addresses. Using asterisk character, there is also possible to provide subnet, for example: 192.168.1.*"
+msgstr "Comma separated IP addresses. Using asterisk character, there is also possible to provide subnet, for example: 192.168.1.*"
+
+msgid "Default IP address restrictions for imported users:"
+msgstr "Default IP address restrictions for imported users:"
+
+msgid "Set your IP address"
+msgstr "Set your IP address"
+
+msgid "Invalid IP address restrictions value. This field can have comma separated IP addresses only or subnet addresses like 192.168.1.*"
+msgstr "Invalid IP address restrictions value. This field can have comma separated IP addresses only or subnet addresses like 192.168.1.*"
+
+msgid "This type of authentication is realized by web server. To use it, the basic authentication has to be enabled in the Baculum Web web server configuration."
+msgstr "This type of authentication is realized by web server. To use it, the basic authentication has to be enabled in the Baculum Web web server configuration."
+
+msgid "General settings"
+msgstr "General settings"
+
+msgid "Default access setting for logged in users not defined in Baculum Web:"
+msgstr "Default access setting for logged in users not defined in Baculum Web:"
+
+msgid "No access"
+msgstr "No access"
+
+msgid "Access with default settings"
+msgstr "Access with default settings"
+
+msgid "Default role:"
+msgstr "Default role:"
+
+msgid "Default API host:"
+msgstr "Default API host:"
+
+msgid "Users file path:"
+msgstr "Users file path:"
+
+msgid "Hash algorithm:"
+msgstr "Hash algorithm:"
+
+msgid "Please enter users file path value."
+msgstr "Please enter users file path value."
+
+msgid "Test"
+msgstr "Test"
+
+msgid "The user file is not accessible."
+msgstr "The user file is not accessible."
+
+msgid "The user file is not readable by web server user."
+msgstr "The user file is not readable by web server user."
+
+msgid "The user file is readable but not writeable by web server user."
+msgstr "The user file is readable but not writeable by web server user."
+
+msgid "Allow Baculum Web to manage the Basic authentication users (add/remove users and change their passwords)"
+msgstr "Allow Baculum Web to manage the Basic authentication users (add/remove users and change their passwords)"
+
+msgid "Authorization failed. Please contact the Baculum administrator to grant permissions."
+msgstr "Authorization failed. Please contact the Baculum administrator to grant permissions."
+
+msgid "Try again"
+msgstr "Try again"
+
+msgid "Manage Basic users"
+msgstr "Manage Basic users"
+
+msgid "Empty user list"
+msgstr "Empty user list"
+
+msgid "Basic user list"
+msgstr "Basic user list"
+
+msgid "Access to Baculum API"
+msgstr "Access to Baculum API"
+
+msgid "Access to Baculum Web"
+msgstr "Access to Baculum Web"
+
+msgid "Requried support from web server side - e.g. %supported_web_server_list"
+msgstr "Requried support from web server side - e.g. %supported_web_server_list"
+
+msgid "Apache 2.4 with apr-util 1.5+"
+msgstr "Apache 2.4 with apr-util 1.5+"
+
+msgid "Apache, Lighttpd and Nginx on most UNIX platforms"
+msgstr "Apache, Lighttpd and Nginx on most UNIX platforms"
+
+msgid "Nginx on most UNIX platforms"
+msgstr "Nginx on most UNIX platforms"
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.Params');
+
+/**
+ * Simple layout class.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Layout
+ * @package Baculum Web
+ */
+class Simple extends TTemplateControl {
+}
+?>
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+ <com:THead Title="Baculum - Bacula Web Interface">
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="icon" href="<%=$this->getPage()->getTheme()->getBaseUrl()%>/favicon.ico" type="image/x-icon" />
+ </com:THead>
+ <body class="w3-light-grey">
+ <com:TForm>
+ <com:BClientScript ScriptUrl=<%~ ../JavaScript/fontawesome.min.js %> />
+ <!-- Top container -->
+ <div class="w3-bar w3-top w3-black w3-large" style="z-index:4">
+ <span class="w3-bar-item w3-right">
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/logo.png" alt="" />
+ </span>
+ </div>
+ <!-- !PAGE CONTENT! -->
+ <com:TContentPlaceHolder ID="Main" />
+ </com:TForm>
+ </body>
+</html>
*/
class ApplicationSettings extends BaculumWebPage {
- protected $admin = true;
-
public function onInit($param) {
parent::onInit($param);
$this->DecimalBytes->Checked = true;
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
*/
class ConfigureHosts extends BaculumWebPage {
- protected $admin = true;
-
public function onInit($param) {
parent::onInit($param);
if ($this->IsPostBack || $this->IsCallBack) {
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
const USE_CACHE = true;
- protected $admin = true;
-
public $filesets = array();
public function onInit($param) {
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
const USE_CACHE = true;
- protected $admin = true;
-
const FILESET_NAME = 'FileSetName';
public function onInit($param) {
--- /dev/null
+<%@ MasterClass="Application.Web.Layouts.Simple" Theme="Baculum-v2"%>
+<com:TContent ID="Main">
+ <div style="width: 100%; height: 100%;">
+ <com:TPanel ID="LoginForm" CssClass="w3-display-middle w3-center" Style="width: 100%; max-width: 440px" DefaultButton="LoginBtn">
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/logo_xl.png" alt="Baculum - The Bacula web interface" class="w3-block" style="margin-bottom: 10px"/>
+ <com:TLabel ID="Msg" CssClass="error" Display="Hidden"><%[ Invalid username or password ]%></com:TLabel>
+ <div class="w3-section">
+ <label for="<%=$this->Username->ClientID%>" class="w3-show-inline-block" style="width: 95px"><%[ Username: ]%></label> <com:TTextBox ID="Username" CssClass="w3-input w3-border w3-show-inline-block" Style="width: 335px" />
+ </div>
+ <div class="w3-section">
+ <label for="<%=$this->Password->ClientID%>" class="w3-show-inline-block" style="width: 95px"><%[ Password: ]%></label> <com:TTextBox ID="Password" CssClass="w3-input w3-border w3-show-inline-block" TextMode="Password" Style="width: 335px" />
+ </div>
+ <div class="w3-section w3-center">
+ <com:TLinkButton ID="LoginBtn" OnClick="login" CssClass="w3-button w3-green">
+ <i class="fas fa-sign-in-alt"></i> <%[ Log in ]%></i>
+ </com:TLinkButton>
+ </div>
+ </com:TPanel>
+ <com:TPanel ID="AuthorizationError" CssClass="w3-display-middle w3-center" Style="width: 100%; max-width: 440px" Display="None">
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/logo_xl.png" alt="Baculum - The Bacula web interface" style="margin-bottom: 40px; width: 100%;"/>
+ <p class="w3-text-red"><%[ Authorization failed. Please contact the Baculum administrator to grant permissions. ]%></p>
+ <p class="w3-center">
+ <script>var login_form_reload_url = '<%=$this->reload_url%>';</script>
+ <com:TActiveLinkButton
+ OnClick="logout"
+ >
+ <prop:ClientSide.OnComplete>
+ if (!window.chrome || window.navigator.webdriver) {
+ window.location.href = main_side_bar_reload_url;
+ } else if (window.chrome) {
+ // For chrome this reload is required to show login Basic auth prompt
+ window.location.reload();
+ }
+ </prop:ClientSide.OnComplete>
+ <%[ Logout ]%>
+ </com:TActiveLinkButton>
+ <a href="<%=$this->getModule('auth')->getReturnUrl() ?: '/'%>"><%[ Try again ]%></a>
+ </p>
+ </com:TPanel>
+ </div>
+<script>
+ document.getElementById('<%=$this->Username->ClientID%>').focus();
+</script>
+</com:TContent>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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.Web.Class.BaculumWebPage');
+
+/**
+ * User login page.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Page
+ * @package Baculum Web
+ */
+class LoginPage extends BaculumWebPage {
+
+ /**
+ * Reload URL is used to refresh page after logout with Basic auth.
+ */
+ public $reload_url = '';
+
+ public function onInit($param) {
+ parent::onInit($param);
+ if ($this->getModule('web_config')->isAuthMethodBasic()) {
+ $fake_pwd = $this->getModule('crypto')->getRandomString();
+ $user = $_SERVER['PHP_AUTH_USER'] . '1'; // must be different than currently logged in Basic user
+
+ // do a login try with different user and password to logout current user
+ $this->reload_url = $this->getPage()->getFullLoginUrl($user, $fake_pwd);
+ }
+ }
+
+ public function onPreLoad($param) {
+ parent::onPreLoad($param);
+ $users = $this->getModule('users');
+
+ $authorized = $users->isAuthorized();
+
+ if ($this->User->getIsGuest() === false) {
+ // for authenticated users
+
+ $web_config = $this->getModule('web_config');
+ if ($web_config->isAuthMethodBasic()) {
+ /**
+ * Using default page here is because it is required for case if user doesn't
+ * have access to default service page. Then it is directed to first free page
+ * available for him.
+ */
+ if ($authorized || ($this->User->Enabled && $users->getAuthorizedFlag() === $this->Service->DefaultPage)) {
+ // Basic user authenticated and authorized
+ $this->goToDefaultPage();
+ } else {
+ // Basic - user authenticated but not authorized or no access by default
+ $this->LoginForm->Display = 'None';
+ $this->AuthorizationError->Display = 'Dynamic';
+ }
+ } else if ($web_config->isAuthMethodLdap() && !$authorized) {
+ // Ldap - user authenticated but not authorized
+ $this->LoginForm->Display = 'None';
+ $this->AuthorizationError->Display = 'Dynamic';
+ }
+ }
+ }
+
+ /**
+ * Login using login page form.
+ *
+ * @param TLinkButton $sender sender object
+ * @param mixed $param event parameter (in this case null)
+ */
+ public function login($sender, $param) {
+ $username = $this->Username->Text;
+ $password = $this->Password->Text;
+
+ if ($this->getModule('web_config')->isAuthMethodBasic() && !empty($_SERVER['PHP_AUTH_USER'])) {
+ // For basic auth take username from web server.
+ $username = $_SERVER['PHP_AUTH_USER'];
+ }
+
+ $success = $this->getModule('auth')->login($username, $password);
+ if ($success === true) {
+ $this->goToDefaultPage();
+ } else {
+ $this->Msg->Display = 'Fixed';
+ }
+ }
+
+ /**
+ * Logout button event handler.
+ * It is used for logout button visible after unsuccessfull authorization.
+ *
+ * @param TLinkButton $sender sender object
+ * @param mixed $param event parameter (in this case null)
+ */
+ public function logout($sender, $param) {
+ $this->getModule('auth')->logout();
+ if ($this->getModule('web_config')->isAuthMethodBasic()) {
+ /**
+ * This status code 401 is necessary to stop comming AJAX requests
+ * and to bring the login prompt on.
+ */
+ $this->Response->setStatusCode(401);
+ } else {
+ $this->goToDefaultPage();
+ }
+ }
+}
+?>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
$error = $result;
}
}
- if (!$error && $_SESSION['admin'] && key_exists('dbsize', $params)) {
+ if (!$error && key_exists('dbsize', $params)) {
$result = $this->getModule('api')->get(array('dbsize'));
if ($result->error === 0) {
$monitor_data['dbsize'] = $result->output;
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
*/
class NewJobWizard extends BaculumWebPage {
- protected $admin = true;
-
const PREV_STEP = 'PrevStep';
const JOBDEFS = 'JobDefs';
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
$this->setComponentType($component_type);
$this->setComponentName($component_name);
$this->setResourceType($resource_type);
- if (!$_SESSION['admin']) {
- // Non-admin can configure only host assigned to him
- $this->NewResource->setHost($_SESSION['api_host']);
- }
+ // Non-admin can configure only host assigned to him
+ $this->NewResource->setHost($this->User->getAPIHosts());
$this->NewResource->setComponentType($component_type);
$this->NewResource->setComponentName($component_name);
$this->NewResource->setResourceType($resource_type);
$config = $this->getModule('host_config')->getConfig();
$hosts = array('' => Prado::localize('Please select host'));
foreach ($config as $host => $vals) {
- if (!$_SESSION['admin'] && $host !== $_SESSION['api_host']) {
+ if ($host !== $this->User->getAPIHosts()) {
continue;
}
$item = "Host: $host, Address: {$vals['address']}, Port: {$vals['port']}";
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
* @package Baculum Web
*/
class PoolList extends BaculumWebPage {
-
- protected $admin = true;
}
?>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
const POOLID = 'PoolId';
const POOL_NAME = 'PoolName';
- protected $admin = true;
-
public $volumes_in_pool;
public function onInit($param) {
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
* translation engine initialization. From this reason all messages are not
* translated.
*/
- private $req_exts = array(
- array(
+ private $req_exts = [
+ [
'ext' => 'curl',
'help_msg' => 'Please install <b>PHP cURL module</b>.'
- )
- );
+ ],
+ [
+ 'ext' => 'ldap',
+ 'help_msg' => 'Please install <b>PHP LDAP module</b>.'
+ ]
+ ];
public function __construct($app_dir, $base_dir) {
parent::__construct($app_dir, $base_dir);
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
const USE_CACHE = true;
- protected $admin = true;
-
public $schedules = array();
public function onInit($param) {
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
const SCHEDULE_NAME = 'ScheduleName';
- protected $admin = true;
-
public function onInit($param) {
parent::onInit($param);
if ($this->IsPostBack || $this->IsCallBack) {
--- /dev/null
+<%@ MasterClass="Application.Web.Layouts.Main" Theme="Baculum-v2"%>
+<com:TContent ID="Main">
+ <!-- Header -->
+ <header class="w3-container">
+ <h5>
+ <b><i class="fa fa-lock"></i> <%[ Security ]%></b>
+ </h5>
+ </header>
+ <div class="w3-bar w3-green w3-margin-bottom">
+ <button id="btn_user_security" type="button" class="w3-bar-item w3-button tab_btn w3-grey" onclick="W3Tabs.open(this.id, 'user_security');"><%[ Settings ]%></button>
+ <button id="btn_user_list" type="button" class="w3-bar-item w3-button tab_btn" onclick="W3Tabs.open(this.id, 'user_list'); oUserList.table.responsive.recalc();"><%[ Users ]%></button>
+ <button id="btn_role_list" type="button" class="w3-bar-item w3-button tab_btn" onclick="W3Tabs.open(this.id, 'role_list'); oRoleList.table.responsive.recalc();"><%[ Roles ]%></button>
+ </div>
+
+ <div class="w3-container tab_item" id="user_security">
+ <h4><%[ General settings ]%></h4>
+ <p><%[ Default access setting for logged in users not defined in Baculum Web: ]%></p>
+ <div class="w3-container w3-row w3-padding opt_row">
+ <div class="w3-col" style="width: 40px">
+ <com:TRadioButton
+ ID="GeneralDefaultNoAccess"
+ GroupName="GeneralDefault"
+ CssClass="w3-radio"
+ Attributes.onclick="document.getElementById('general_options_default_access').style.display = 'none';"
+ />
+ </div>
+ <label for="<%=$this->GeneralDefaultNoAccess->ClientID%>"><%[ No access ]%></label>
+ </div>
+ <div class="w3-container w3-row w3-padding opt_row">
+ <div class="w3-col" style="width: 40px">
+ <com:TRadioButton
+ ID="GeneralDefaultAccess"
+ GroupName="GeneralDefault"
+ CssClass="w3-radio"
+ Attributes.onclick="document.getElementById('general_options_default_access').style.display = '';"
+ />
+ </div>
+ <label for="<%=$this->GeneralDefaultAccess->ClientID%>"><%[ Access with default settings ]%></label>
+ </div>
+ <div id="general_options_default_access" class="w3-container w3-margin-left" style="display: <%=$this->GeneralDefaultAccess->Checked ? 'block' : 'none'%>">
+ <div class="w3-half">
+ <div class="w3-container w3-row w3-padding w3-block">
+ <div class="w3-third w3-col">
+ <%[ Default role: ]%>
+ </div>
+ <div class="w3-twothird w3-col">
+ <com:TActiveDropDownList
+ ID="GeneralDefaultAccessRole"
+ CssClass="w3-input w3-border"
+ CausesValidation="false"
+ Width="90%"
+ />
+ </div>
+ </div>
+ <div class="w3-container w3-row w3-padding">
+ <div class="w3-third w3-col">
+ <%[ Default API host: ]%>
+ </div>
+ <div class="w3-twothird w3-col">
+ <com:TActiveDropDownList
+ ID="GeneralDefaultAccessAPIHost"
+ CssClass="w3-input w3-border"
+ CausesValidation="false"
+ Width="90%"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <h4><%[ Authentication method ]%></h4>
+ <div class="w3-container w3-row w3-padding opt_row">
+ <div class="w3-col" style="width: 40px">
+ <com:TRadioButton
+ ID="BasicAuth"
+ GroupName="AuthMethod"
+ CssClass="w3-radio"
+ Attributes.onclick="oUserSecurity.select_auth_method('basic');"
+ />
+ </div>
+ <label for="<%=$this->BasicAuth->ClientID%>"><%[ HTTP Basic authentication ]%></label>
+ </div>
+ <div id="authentication_method_basic" class="w3-container" style="display: none">
+ <%[ This type of authentication is realized by web server. To use it, the basic authentication has to be enabled in the Baculum Web web server configuration. ]%>
+ <div>
+ <div class="w3-container w3-row w3-padding opt_row">
+ <div class="w3-col" style="width: 40px">
+ <com:TCheckBox
+ ID="BasicAuthAllowManageUsers"
+ CssClass="w3-check"
+ />
+ </div>
+ <label for="<%=$this->BasicAuthAllowManageUsers->ClientID%>"><%[ Allow Baculum Web to manage the Basic authentication users (add/remove users and change their passwords) ]%></label>
+ </div>
+ <div class="w3-container w3-row w3-padding w3-block">
+ <div class="w3-col opt_row" style="width: 220px">
+ <%[ Users file path: ]%>
+ </div>
+ <div class="w3-twothird w3-col">
+ <com:TActiveTextBox
+ ID="BasicAuthUserFile"
+ CssClass="w3-input w3-border w3-show-inline-block"
+ CausesValidation="false"
+ Width="400px"
+ /><com:TActiveLinkButton
+ ValidationGroup="AuthMethodGroup"
+ CausesValidation="true"
+ CssClass="w3-button w3-green"
+ OnCallback="doBasicUserFileTest"
+ >
+ <i class="fas fa-play"></i> <%[ Test ]%>
+ <prop:ClientSide.OnLoading>
+ document.getElementById('basic_auth_user_file_test_ok').style.display = 'none';
+ document.getElementById('basic_auth_user_file_test_error').style.display = 'none';
+ document.getElementById('basic_auth_user_file_test_loading').style.visibility = 'visible';
+ document.getElementById('<%=$this->BasicAuthUserFileMsg->ClientID%>').style.display = 'none';
+ </prop:ClientSide.OnLoading>
+ <prop:ClientSide.OnComplete>
+ document.getElementById('basic_auth_user_file_test_loading').style.visibility = 'hidden';
+ document.getElementById('<%=$this->BasicAuthUserFileMsg->ClientID%>').style.display = '';
+ </prop:ClientSide.OnComplete>
+ </com:TActiveLinkButton>
+ <i id="basic_auth_user_file_test_loading" class="fas fa-sync w3-spin" style="visibility: hidden;"></i>
+ <span id="basic_auth_user_file_test_ok" class="w3-text-green" style="display: none;"><i class="fas fa-check w3-text-green"></i> <%[ OK ]%></span>
+ <div>
+ <i id="basic_auth_user_file_test_error" class="fas fa-times-circle w3-text-red" style="display: none;"></i>
+ <com:TActiveLabel
+ ID="BasicAuthUserFileMsg"
+ CssClass="error"
+ ActiveControl.EnableUpdate="true"
+ />
+ <com:TRequiredFieldValidator
+ ValidationGroup="AuthMethodGroup"
+ CssClass="validator-block"
+ ControlCssClass="field_invalid"
+ Display="Dynamic"
+ ControlToValidate="BasicAuthUserFile"
+ Text="<%[ Please enter users file path value. ]%>"
+ >
+ <prop:ClientSide.OnValidate>
+ var basic_auth_opt = $('#<%=$this->BasicAuth->ClientID%>')[0];
+ var user_file_opt = $('#<%=$this->BasicAuthAllowManageUsers->ClientID%>')[0];
+ sender.enabled = (basic_auth_opt.checked && user_file_opt.checked);
+ </prop:ClientSide.OnValidate>
+ </com:TRequiredFieldValidator>
+ </div>
+ </div>
+ </div>
+ <div class="w3-container w3-row w3-padding w3-block">
+ <div class="w3-col opt_row" style="width: 220px">
+ <%[ Hash algorithm: ]%>
+ </div>
+ <div class="w3-twothird w3-col">
+ <com:TActiveDropDownList
+ ID="BasicAuthHashAlgorithm"
+ CssClass="w3-select w3-border"
+ CausesValidation="false"
+ Width="180px"
+ Attributes.onchange="basic_auth_change_hash_alg(this.value);"
+ >
+ <com:TListItem Value="apr1-md5" Text="APR1-MD5" />
+ <com:TListItem Value="sha1" Text="SHA-1" />
+ <com:TListItem Value="sha256" Text="SHA-256" />
+ <com:TListItem Value="sha512" Text="SHA-512" />
+ <com:TListItem Value="ssha1" Text="SSHA (salted SHA-1)" />
+ <com:TListItem Value="bcrypt" Text="BCrypt" />
+ </com:TActiveDropDownList>
+ <span id="basic_auth_alg_supp_msg"></span>
+ </div>
+ <script>
+ function basic_auth_change_hash_alg(alg) {
+ var msg = '<%[ Requried support from web server side - e.g. %supported_web_server_list ]%>'
+ var hash_alg_supp = {
+ 'apr1-md5': [
+ 'Apache',
+ 'Lighttpd 1.4.13+',
+ 'Nginx 1.0.3+'
+ ],
+ 'sha1': [
+ 'Apache',
+ 'Lighttpd 1.4.33+',
+ 'Nginx 1.3.13+'
+ ],
+ 'sha256': [
+ '<%[ Apache, Lighttpd and Nginx on most UNIX platforms ]%>',
+ ],
+ 'sha512': [
+ '<%[ Apache, Lighttpd and Nginx on most UNIX platforms ]%>',
+ ],
+ 'ssha1': [
+ 'Nginx 1.0.3+'
+ ],
+ 'bcrypt': [
+ '<%[ Apache 2.4 with apr-util 1.5+ ]%>',
+ '<%[ Nginx on most UNIX platforms ]%>'
+ ]
+ };
+ msg = msg.replace('%supported_web_server_list', hash_alg_supp[alg].join(', '));
+ document.getElementById('basic_auth_alg_supp_msg').textContent = '( ' + msg + ' )';
+ }
+ var basic_auth_hash_alg_sel_val = document.getElementById('<%=$this->BasicAuthHashAlgorithm->ClientID%>').value;
+ basic_auth_change_hash_alg(basic_auth_hash_alg_sel_val);
+ </script>
+ </div>
+ </div>
+ <div class="w3-container">
+ <com:TActiveLinkButton
+ ValidationGroup="AuthMethodGroup"
+ CausesValidation="true"
+ OnCommand="getBasicUsers"
+ CommandParameter="load"
+ CssClass="w3-button w3-section w3-green w3-padding"
+ >
+ <i class="fa fa-users"></i> <%[ Manage Basic users ]%>
+ <prop:ClientSide.OnLoading>
+ document.getElementById('basic_get_users_ok').style.display = 'none';
+ document.getElementById('basic_get_users_error').style.display = 'none';
+ document.getElementById('basic_get_users_loading').style.visibility = 'visible';
+ oUserSecurity.show_user_selected_msg(false);
+ </prop:ClientSide.OnLoading>
+ <prop:ClientSide.OnComplete>
+ document.getElementById('basic_get_users_loading').style.visibility = 'hidden';
+ </prop:ClientSide.OnComplete>
+ </com:TActiveLinkButton>
+ <i id="basic_get_users_loading" class="fas fa-sync w3-spin" style="visibility: hidden;"></i>
+ <span id="basic_get_users_ok" class="w3-text-green" style="display: none;"><i class="fas fa-check w3-text-green"></i> <%[ OK ]%></span>
+ <i id="basic_get_users_error" class="fas fa-times-circle w3-text-red" style="display: none;"></i>
+ <com:TActiveLabel ID="TestBasicGetUsersMsg" CssClass="w3-text-red" Display="None" />
+ </div>
+ </div>
+ <div class="w3-container w3-row w3-padding opt_row">
+ <div class="w3-col" style="width: 40px">
+ <com:TRadioButton
+ ID="LdapAuth"
+ GroupName="AuthMethod"
+ CssClass="w3-radio"
+ Attributes.onclick="oUserSecurity.select_auth_method('ldap');"
+ />
+ </div>
+ <label for="<%=$this->LdapAuth->ClientID%>"><%[ LDAP authentication ]%></label>
+ </div>
+ <div id="authentication_method_ldap" class="w3-container w3-half w3-margin-left" style="display: none">
+ <h5><%[ LDAP server options ]%></h5>
+ <div class="w3-container w3-row w3-padding">
+ <div class="w3-third w3-col">
+ <%[ IP Address/Hostname: ]%>
+ </div>
+ <div class="w3-twothird w3-col">
+ <com:TActiveTextBox
+ ID="LdapAuthServerAddress"
+ CssClass="w3-input w3-border w3-show-inline-block"
+ CausesValidation="false"
+ Width="90%"
+ />
+ <i class="fas fa-asterisk w3-text-red opt_req"></i>
+ <com:TRequiredFieldValidator
+ ValidationGroup="AuthMethodGroup"
+ CssClass="validator-block"
+ Display="Dynamic"
+ ControlCssClass="field_invalid"
+ ControlToValidate="LdapAuthServerAddress"
+ Text="<%[ Please enter IP Address/Hostname. ]%>"
+ >
+ <prop:ClientSide.OnValidate>
+ sender.enabled = $('#<%=$this->LdapAuth->ClientID%>')[0].checked;
+ </prop:ClientSide.OnValidate>
+ </com:TRequiredFieldValidator>
+ </div>
+ </div>
+ <div class="w3-container w3-row w3-padding">
+ <div class="w3-third w3-col">
+ <%[ Port: ]%>
+ </div>
+ <div class="w3-col" style="width: 245px;">
+ <com:TActiveTextBox
+ ID="LdapAuthServerPort"
+ CssClass="w3-input w3-border w3-show-inline-block"
+ CausesValidation="false"
+ Width="70px"
+ Text="389"
+ />
+ <i class="fas fa-asterisk w3-text-red opt_req"></i><br />
+ <com:TRequiredFieldValidator
+ ValidationGroup="AuthMethodGroup"
+ CssClass="validator-block"
+ Display="Dynamic"
+ ControlCssClass="field_invalid"
+ ControlToValidate="LdapAuthServerPort"
+ Text="<%[ Please enter port. ]%>"
+ >
+ <prop:ClientSide.OnValidate>
+ sender.enabled = $('#<%=$this->LdapAuth->ClientID%>')[0].checked;
+ </prop:ClientSide.OnValidate>
+ </com:TRequiredFieldValidator>
+ </div>
+ </div>
+ <div class="w3-container w3-row w3-padding">
+ <div class="w3-third w3-col">
+ <%[ SSL encryption (LDAPS): ]%>
+ </div>
+ <div class="w3-col" style="width: 70px;">
+ <com:TActiveCheckBox
+ ID="LdapAuthServerLdaps"
+ CssClass="w3-check w3-border"
+ CausesValidation="false"
+ AutoPostBack="false"
+ />
+ </div>
+ </div>
+ <div class="w3-container w3-row w3-padding">
+ <div class="w3-third w3-col">
+ <%[ Protocol version: ]%>
+ </div>
+ <div class="w3-col" style="width: 160px">
+ <com:TActiveDropDownList
+ ID="LdapAuthServerProtocolVersion"
+ CssClass="w3-input w3-border"
+ CausesValidation="false"
+ AutoPostBack="false"
+ >
+ <com:TListItem Value="1" Text="LDAP version 1" />
+ <com:TListItem Value="2" Text="LDAP version 2" />
+ <com:TListItem Value="3" Text="LDAP version 3" Selected="true" />
+ </com:TActiveDropDownList>
+ </div>
+ </div>
+ <h5><%[ LDAP authentication method ]%></h5>
+ <div class="w3-container w3-row w3-padding opt_row">
+ <div class="w3-col" style="width: 40px">
+ <com:TRadioButton
+ ID="LdapAuthMethodAnonymous"
+ GroupName="LdapAuthMethod"
+ CssClass="w3-radio"
+ Attributes.onclick="oLdapUserSecurity.show_ldap_auth('anon');"
+ Checked="true"
+ />
+ </div>
+ <label for="<%=$this->LdapAuthMethodAnonymous->ClientID%>"><%[ Anonymous authentication ]%></label>
+ </div>
+ <div class="w3-container w3-row w3-padding opt_row">
+ <div class="w3-col" style="width: 40px">
+ <com:TRadioButton
+ ID="LdapAuthMethodSimple"
+ GroupName="LdapAuthMethod"
+ CssClass="w3-radio"
+ Attributes.onclick="oLdapUserSecurity.show_ldap_auth('simple');"
+ />
+ </div>
+ <label for="<%=$this->LdapAuthMethodSimple->ClientID%>"><%[ Simple authentication ]%></label>
+ </div>
+ <div id="authentication_method_ldap_auth_simple" class="w3-container w3-margin-left" style="display: <%=$this->LdapAuthMethodSimple->Checked ? 'block' : 'none'%>">
+ <div class="w3-container w3-row w3-padding">
+ <div class="w3-third w3-col">
+ <%[ Manager DN: ]%>
+ </div>
+ <div class="w3-twothird w3-col">
+ <com:TActiveTextBox
+ ID="LdapAuthMethodSimpleUsername"
+ CssClass="w3-input w3-border w3-show-inline-block"
+ CausesValidation="false"
+ Width="90%"
+ />
+ <i class="fas fa-asterisk w3-text-red opt_req"></i>
+ <com:TRequiredFieldValidator
+ ValidationGroup="AuthMethodGroup"
+ CssClass="validator-block"
+ Display="Dynamic"
+ ControlCssClass="field_invalid"
+ ControlToValidate="LdapAuthMethodSimpleUsername"
+ Text="<%[ Please enter username. ]%>"
+ >
+ <prop:ClientSide.OnValidate>
+ var is_ldap_auth = $('#<%=$this->LdapAuth->ClientID%>')[0].checked;
+ var is_auth_simple = $('#<%=$this->LdapAuthMethodSimple->ClientID%>')[0].checked;
+ sender.enabled = (is_ldap_auth && is_auth_simple);
+ </prop:ClientSide.OnValidate>
+ </com:TRequiredFieldValidator>
+ </div>
+ </div>
+ <div class="w3-container w3-row w3-padding">
+ <div class="w3-third w3-col">
+ <%[ Password: ]%>
+ </div>
+ <div class="w3-twothird w3-col">
+ <com:TActiveTextBox
+ ID="LdapAuthMethodSimplePassword"
+ CssClass="w3-input w3-border w3-show-inline-block"
+ CausesValidation="false"
+ TextMode="Password"
+ PersistPassword="true"
+ Width="90%"
+ />
+ <i class="fas fa-asterisk w3-text-red opt_req"></i>
+ <com:TRequiredFieldValidator
+ ValidationGroup="AuthMethodGroup"
+ CssClass="validator-block"
+ Display="Dynamic"
+ ControlCssClass="field_invalid"
+ ControlToValidate="LdapAuthMethodSimplePassword"
+ Text="<%[ Please enter password. ]%>"
+ >
+ <prop:ClientSide.OnValidate>
+ var is_ldap_auth = $('#<%=$this->LdapAuth->ClientID%>')[0].checked;
+ var is_auth_simple = $('#<%=$this->LdapAuthMethodSimple->ClientID%>')[0].checked;
+ sender.enabled = (is_ldap_auth && is_auth_simple);
+ </prop:ClientSide.OnValidate>
+ </com:TRequiredFieldValidator>
+ </div>
+ </div>
+ </div>
+ <div class="w3-container">
+ <com:TActiveLinkButton
+ ID="LdapAuthServerConnectionTest"
+ ValidationGroup="AuthMethodGroup"
+ CausesValidation="true"
+ OnCallback="testLdapConnection"
+ CssClass="w3-button w3-section w3-green w3-padding"
+ >
+ <i class="fa fa-plug"></i> <%[ Test LDAP connection ]%>
+ <prop:ClientSide.OnLoading>
+ document.getElementById('ldap_test_connection_ok').style.display = 'none';
+ document.getElementById('ldap_test_connection_error').style.display = 'none';
+ document.getElementById('ldap_test_connection_loading').style.visibility = 'visible';
+ </prop:ClientSide.OnLoading>
+ <prop:ClientSide.OnComplete>
+ document.getElementById('ldap_test_connection_loading').style.visibility = 'hidden';
+ </prop:ClientSide.OnComplete>
+ </com:TActiveLinkButton>
+ <i id="ldap_test_connection_loading" class="fas fa-sync w3-spin" style="visibility: hidden;"></i>
+ <span id="ldap_test_connection_ok" class="w3-text-green" style="display: none;"><i class="fas fa-check w3-text-green"></i> <%[ OK ]%></span>
+ <i id="ldap_test_connection_error" class="fas fa-times-circle w3-text-red" style="display: none;"></i>
+ <com:TActiveLabel ID="TestLdapConnectionMsg" CssClass="w3-text-red" Display="None" />
+ </div>
+ <div class="w3-container w3-row w3-padding">
+ <div class="w3-third w3-col">
+ <%[ Base DN: ]%>
+ </div>
+ <div class="w3-twothird w3-col">
+ <com:TActiveTextBox
+ ID="LdapAuthServerBaseDn"
+ CssClass="w3-input w3-border w3-show-inline-block"
+ CausesValidation="false"
+ Width="90%"
+ />
+ <i class="fas fa-asterisk w3-text-red opt_req"></i><br />
+ <com:TRequiredFieldValidator
+ ValidationGroup="AuthMethodGroup"
+ CssClass="validator-block"
+ Display="Dynamic"
+ ControlCssClass="field_invalid"
+ ControlToValidate="LdapAuthServerBaseDn"
+ Text="<%[ Please enter Base DN. ]%>"
+ >
+ <prop:ClientSide.OnValidate>
+ var is_test_con = (parameter && parameter.options.ID == '<%=$this->LdapAuthServerConnectionTest->ClientID%>');
+ var is_ldap_enabled = $('#<%=$this->LdapAuth->ClientID%>')[0].checked;
+ sender.enabled = (!is_test_con && is_ldap_enabled);
+ </prop:ClientSide.OnValidate>
+ </com:TRequiredFieldValidator>
+ </div>
+ </div>
+ <h5><%[ Attributes ]%></h5>
+ <div class="w3-container w3-row w3-padding">
+ <div class="w3-third w3-col">
+ <%[ Username: ]%>
+ </div>
+ <div class="w3-twothird w3-col">
+ <com:TActiveTextBox
+ ID="LdapAttributesUsername"
+ CssClass="w3-input w3-border w3-show-inline-block"
+ CausesValidation="false"
+ Text="uid"
+ Width="90%"
+ />
+ <i class="fas fa-asterisk w3-text-red opt_req"></i>
+ <com:TRequiredFieldValidator
+ ValidationGroup="AuthMethodGroup"
+ CssClass="validator-block"
+ Display="Dynamic"
+ ControlCssClass="field_invalid"
+ ControlToValidate="LdapAttributesUsername"
+ Text="<%[ Please enter username attribute. ]%>"
+ >
+ <prop:ClientSide.OnValidate>
+ sender.enabled = $('#<%=$this->LdapAuth->ClientID%>')[0].checked;
+ </prop:ClientSide.OnValidate>
+ </com:TRequiredFieldValidator>
+ </div>
+ </div>
+ <div class="w3-container w3-row w3-padding">
+ <div class="w3-third w3-col">
+ <%[ Long name: ]%>
+ </div>
+ <div class="w3-twothird w3-col">
+ <com:TActiveTextBox
+ ID="LdapAttributesLongName"
+ CssClass="w3-input w3-border"
+ CausesValidation="false"
+ Text="sn"
+ Width="90%"
+ />
+ </div>
+ </div>
+ <div class="w3-container w3-row w3-padding">
+ <div class="w3-third w3-col">
+ <%[ E-mail: ]%>
+ </div>
+ <div class="w3-twothird w3-col">
+ <com:TActiveTextBox
+ ID="LdapAttributesEmail"
+ CssClass="w3-input w3-border"
+ CausesValidation="false"
+ Text="mail"
+ Width="90%"
+ />
+ </div>
+ </div>
+ <div class="w3-container w3-row w3-padding">
+ <div class="w3-third w3-col">
+ <%[ Description: ]%>
+ </div>
+ <div class="w3-twothird w3-col">
+ <com:TActiveTextBox
+ ID="LdapAttributesDescription"
+ CssClass="w3-input w3-border"
+ CausesValidation="false"
+ Width="90%"
+ />
+ </div>
+ </div>
+ <div class="w3-container">
+ <com:TActiveLinkButton
+ ValidationGroup="AuthMethodGroup"
+ CausesValidation="true"
+ OnCommand="getLdapUsers"
+ CommandParameter="load"
+ CssClass="w3-button w3-section w3-green w3-padding"
+ >
+ <i class="fa fa-users"></i> <%[ Manage LDAP users ]%>
+ <prop:ClientSide.OnLoading>
+ document.getElementById('ldap_get_users_ok').style.display = 'none';
+ document.getElementById('ldap_get_users_error').style.display = 'none';
+ document.getElementById('ldap_get_users_loading').style.visibility = 'visible';
+ oUserSecurity.show_user_selected_msg(false);
+ </prop:ClientSide.OnLoading>
+ <prop:ClientSide.OnComplete>
+ document.getElementById('ldap_get_users_loading').style.visibility = 'hidden';
+ </prop:ClientSide.OnComplete>
+ </com:TActiveLinkButton>
+ <i id="ldap_get_users_loading" class="fas fa-sync w3-spin" style="visibility: hidden;"></i>
+ <span id="ldap_get_users_ok" class="w3-text-green" style="display: none;"><i class="fas fa-check w3-text-green"></i> <%[ OK ]%></span>
+ <i id="ldap_get_users_error" class="fas fa-times-circle w3-text-red" style="display: none;"></i>
+ <com:TActiveLabel ID="TestLdapGetUsersMsg" CssClass="w3-text-red" Display="None" />
+ </div>
+ </div>
+ <div class="w3-container w3-center">
+ <com:TActiveLinkButton
+ ID="AuthMethodSave"
+ ValidationGroup="AuthMethodGroup"
+ CausesValidation="true"
+ OnCallback="saveSecurityConfig"
+ CssClass="w3-button w3-section w3-green w3-padding"
+ >
+ <i class="fa fa-save"></i> <%[ Save ]%>
+ <prop:ClientSide.OnLoading>
+ document.getElementById('auth_method_save_ok').style.display = 'none';
+ document.getElementById('auth_method_save_error').style.display = 'none';
+ document.getElementById('auth_method_save_loading').style.visibility = 'visible';
+ </prop:ClientSide.OnLoading>
+ <prop:ClientSide.OnComplete>
+ document.getElementById('auth_method_save_loading').style.visibility = 'hidden';
+ </prop:ClientSide.OnComplete>
+ </com:TActiveLinkButton>
+ <i id="auth_method_save_loading" class="fas fa-sync w3-spin" style="visibility: hidden;"></i>
+ <span id="auth_method_save_ok" class="w3-text-green" style="display: none;"><i class="fas fa-check w3-text-green"></i> <%[ OK ]%></span>
+ <span id="auth_method_save_error" class="w3-text-red" style="display: none"><i class="fas fa-times-circle w3-text-red"></i><%[ Error ]%></span>
+ </div>
+
+ <div id="get_users_modal" class="w3-modal" style="display: none">
+ <div class="w3-modal-content w3-card-4 w3-animate-zoom" style="width: 990px">
+ <header class="w3-container w3-green">
+ <span onclick="oUserSecurity.show_user_modal(false);" class="w3-button w3-display-topright">×</span>
+ <h2 id="get_users_table_title"></h2>
+ </header>
+ <div class="w3-margin-left w3-margin-right" style="max-height: 645px; overflow-x: auto; margin: 10px auto;">
+ <table id="get_users_table" class="w3-table w3-striped w3-hoverable w3-white w3-margin-bottom" style="width: 100%;">
+ <thead>
+ <tr>
+ <th><%[ Username ]%></th>
+ <th class="w3-center"><%[ Long name ]%></th>
+ <th class="w3-center"><%[ Description ]%></th>
+ <th class="w3-center"><%[ E-mail ]%></th>
+ </tr>
+ </thead>
+ <tbody id="get_users_body"></tbody>
+ <tfoot>
+ <tr>
+ <th><%[ Username ]%></th>
+ <th class="w3-center"><%[ Long name ]%></th>
+ <th class="w3-center"><%[ Description ]%></th>
+ <th class="w3-center"><%[ E-mail ]%></th>
+ </tr>
+ </tfoot>
+ </table>
+ <p class="info w3-hide-medium w3-hide-small" style="margin: 4px 0;"><%[ Tip: Use left-click to select table row. Use CTRL + left-click to multiple row selection. Use SHIFT + left-click to add a range of rows to selection. ]%></p>
+ </div>
+ <footer class="w3-container w3-border-top">
+ <div style="padding-top: 10px">
+ <%[ Import options: ]%> <com:TActiveDropDownList
+ ID="GetUsersImportOptions"
+ CssClass="w3-select w3-border w3-show-inline-block"
+ AutoPostBack="false"
+ Width="250px"
+ >
+ <com:TListItem Value="0" Text="<%[ Import all users ]%>" />
+ <com:TListItem Value="1" Text="<%[ Import selected users ]%>" />
+ <com:TListItem Value="2" Text="<%[ Import users whose fulfill criteria ]%>" />
+ </com:TActiveDropDownList>
+ <div id="get_users_criteria" style="display: none;">
+ <%[ Criteria filter: ]%>
+ <com:TActiveDropDownList
+ ID="GetUsersCriteria"
+ CssClass="w3-select w3-border w3-show-inline-block"
+ AutoPostBack="false"
+ Width="150px"
+ >
+ <com:TListItem Attributes.id="get_users_criteria_filter_username" Value="0" Text="<%[ Username ]%>" />
+ <com:TListItem Attributes.id="get_users_criteria_filter_long_name" Value="1" Text="<%[ Long name ]%>" />
+ <com:TListItem Attributes.id="get_users_criteria_filter_desc" Value="2" Text="<%[ Description ]%>" />
+ <com:TListItem Attributes.id="get_users_criteria_filter_email" Value="3" Text="<%[ E-mail ]%>" />
+ </com:TActiveDropDownList>
+ <%[ contains: ]%>
+ <com:TActiveTextBox
+ ID="GetUsersCriteriaFilter"
+ CssClass="w3-input w3-border w3-show-inline-block"
+ Width="130px"
+ Attributes.onkeypress="oUserSecurity.on_reload_user_list(event);"
+ />
+ <com:TActiveLinkButton
+ ID="GetUsersCriteriaTest"
+ CssClass="w3-button w3-green w3-show-inline-block"
+ OnCallback="getUsers"
+ >
+ <i class="fa fa-sync-alt"></i> <%[ Reload ]%>
+ </com:TActiveLinkButton>
+ </div>
+ <div id="get_users_selected_msg" style="display: none;">
+ <%[ Please select users in table to import. ]%>
+ </div>
+ </div>
+ <i class="fas fa-wrench"></i> <a href="javascript:void(0)" onclick="$('#get_users_advanced_options').toggle('fast');"><%[ Advanced options ]%></a>
+ <div id="get_users_advanced_options" style="display: none">
+ <div>
+ <com:TActiveCheckBox
+ ID="GetUsersProtectOverwrite"
+ CssClass="w3-check"
+ Checked="true"
+ AutoPostBack="false"
+ />
+ <label for="<%=$this->GetUsersProtectOverwrite->ClientID%>"><%[ Do not overwrite existing Baculum Web users, if they have the same username ]%></label>
+ </div>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="GetUsersDefaultRole" Text="<%[ Default role for imported users: ]%>" /></div>
+ <div class="w3-half">
+ <com:TActiveListBox
+ ID="GetUsersDefaultRole"
+ SelectionMode="Multiple"
+ Rows="6"
+ CssClass="w3-select w3-border"
+ AutoPostBack="false"
+ />
+ <p class="w3-text-black" style="margin: 0 16px 0 0"><%[ Use CTRL + left-click to multiple item selection ]%></p>
+ <com:TRequiredFieldValidator
+ ValidationGroup="GetUsersGroup"
+ ControlToValidate="GetUsersDefaultRole"
+ ErrorMessage="<%[ At least one role must be selected. ]%>"
+ ControlCssClass="field_invalid"
+ />
+ </div> <i class="fa fa-asterisk w3-text-red opt_req"></i>
+ </div>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="GetUsersDefaultAPIHost" Text="<%[ Default API host for imported users: ]%>" /></div>
+ <div class="w3-half">
+ <com:TActiveDropDownList
+ ID="GetUsersDefaultAPIHost"
+ CssClass="w3-select w3-border"
+ AutoPostBack="false"
+ />
+ </div>
+ </div>
+ <div class="w3-row w3-section" title="<%[ Comma separated IP addresses. Using asterisk character, there is also possible to provide subnet, for example: 192.168.1.* ]%>">
+ <div class="w3-col w3-third"><com:TLabel ForControl="UserIps" Text="<%[ Default IP address restrictions for imported users: ]%>" /></div>
+ <div class="w3-half">
+ <com:TActiveTextBox
+ ID="GetUsersDefaultIps"
+ AutoPostBack="false"
+ MaxLength="500"
+ CssClass="w3-input w3-border"
+ />
+ <p class="w3-text-black"><a href="javascript:void(0)" onclick="document.getElementById('<%=$this->GetUsersDefaultIps->ClientID%>').value = '<%=$_SERVER['REMOTE_ADDR']%>';"><%[ Set your IP address ]%></a></p>
+ <com:TActiveCustomValidator
+ ID="GetUsersDefaultIpsValidator"
+ ValidationGroup="GetUsersGroup"
+ ControlToValidate="GetUsersDefaultIps"
+ OnServerValidate="validateIps"
+ Display="Dynamic"
+ ErrorMessage="<%[ Invalid IP address restrictions value. This field can have comma separated IP addresses only or subnet addresses like 192.168.1.* ]%>"
+ ControlCssClass="field_invalid"
+ />
+ </div>
+ </div>
+ </div>
+ <div class="w3-center w3-margin-top w3-margin-bottom w3-border-top" style="padding-top: 10px;">
+ <button type="button" class="w3-button w3-red" onclick="oUserSecurity.show_user_modal(false);"><i class="fa fa-times"></i> <%[ Close ]%></button>
+ <com:TActiveLinkButton
+ ID="GetUsersImport"
+ CssClass="w3-button w3-green"
+ CausesValidation="true"
+ ValidationGroup="GetUsersGroup"
+ OnCallback="importUsers"
+ Attributes.onclick="return oUserSecurity.prepare_import();"
+ >
+ <prop:ClientSide.OnLoading>
+ document.getElementById('get_users_loader').style.visibility = 'visible';
+ </prop:ClientSide.OnLoading>
+ <prop:ClientSide.OnComplete>
+ document.getElementById('get_users_loader').style.visibility = 'hidden';
+ </prop:ClientSide.OnComplete>
+ <i class="fas fa-download"></i> <%[ Import users ]%>
+ </com:TActiveLinkButton>
+ <i id="get_users_loader" class="fa fa-sync w3-spin w3-margin-left" style="visibility: hidden"></i>
+ </div>
+ </footer>
+ </div>
+ </div>
+<com:TCallback ID="SelectedUsersImport" OnCallback="TemplateControl.importUsers" />
+<script>
+var oUserSecurity = {
+ ids: {
+ auth_method_basic: 'authentication_method_basic',
+ auth_method_ldap: 'authentication_method_ldap',
+ get_users: 'get_users_table',
+ get_users_body: 'get_users_body',
+ get_users_modal: 'get_users_modal',
+ get_users_selected_msg: 'get_users_selected_msg',
+ get_users_criteria: 'get_users_criteria',
+ get_users_criteria_test: '<%=$this->GetUsersCriteriaTest->ClientID%>',
+ get_users_import_opts: '<%=$this->GetUsersImportOptions->ClientID%>',
+ filter_long_name: 'get_users_criteria_filter_long_name',
+ filter_desc: 'get_users_criteria_filter_desc',
+ filter_email: 'get_users_criteria_filter_email',
+ table_title: 'get_users_table_title'
+ },
+ import_options: {
+ all_users: <%=Security::IMPORT_OPT_ALL_USERS%>,
+ selected_users: <%=Security::IMPORT_OPT_SELECTED_USERS%>,
+ criteria: <%=Security::IMPORT_OPT_CRITERIA%>
+ },
+ data: [],
+ table: null,
+ sel_users_import_cb: <%=$this->SelectedUsersImport->ActiveControl->Javascript%>,
+ user_obj: null,
+ init: function() {
+ this.set_events();
+ },
+ select_auth_method: function(auth) {
+ var auth_basic = document.getElementById(this.ids.auth_method_basic);
+ var auth_ldap = document.getElementById(this.ids.auth_method_ldap);
+
+ // hide all auth containers
+ [auth_basic, auth_ldap].forEach(function(el) {
+ el.style.display = 'none';
+ });
+
+ switch (auth) {
+ case 'basic': {
+ auth_basic.style.display = 'block';
+ this.user_obj = oBasicUserSecurity;
+ break;
+ }
+ case 'ldap': {
+ auth_ldap.style.display = 'block';
+ this.user_obj = oLdapUserSecurity;
+ break;
+ }
+ }
+ },
+ set_events: function() {
+ document.getElementById(this.ids.get_users).addEventListener('click', function(e) {
+ $(function() {
+ if (import_opts.value == this.import_options.selected_users) {
+ var show = this.table.rows({selected: true}).data().length == 0;
+ this.show_user_selected_msg(show);
+ }
+ }.bind(this));
+ }.bind(this));
+ var import_opts = document.getElementById(this.ids.get_users_import_opts);
+ import_opts.addEventListener('change', function(e) {
+ if (import_opts.value == this.import_options.criteria) {
+ this.show_user_criteria(true);
+ } else {
+ this.show_user_criteria(false);
+ }
+
+ if (import_opts.value == this.import_options.selected_users && this.table.rows({selected: true}).data().length == 0) {
+ this.show_user_selected_msg(true);
+ } else {
+ this.show_user_selected_msg(false);
+ }
+ }.bind(this));
+ },
+ set_table: function() {
+ this.table = $('#' + this.ids.get_users).DataTable({
+ data: this.data,
+ deferRender: true,
+ dom: 'lBfrtip',
+ buttons: [
+ 'copy', 'csv', 'colvis'
+ ],
+ columns: [
+ {data: 'username'},
+ {
+ data: 'long_name',
+ visible: (this.user_obj.supported_fields.indexOf('long_name') !== -1)
+ },
+ {
+ data: 'description',
+ visible: (this.user_obj.supported_fields.indexOf('description') !== -1)
+ },
+ {
+ data: 'email',
+ visible: (this.user_obj.supported_fields.indexOf('email') !== -1)
+ }
+ ],
+ responsive: {
+ details: {
+ type: 'column'
+ }
+ },
+ columnDefs: [{
+ className: "dt-center",
+ targets: [ 1, 2, 3 ]
+ }],
+ select: {
+ style: 'os',
+ selector: 'td',
+ blurable: false
+ },
+ order: [1, 'asc']
+ });
+ },
+ destroy_table: function() {
+ if (this.table) {
+ this.table.destroy();
+ }
+ },
+ prepare_import: function() {
+ ret = true;
+ var import_opts = document.getElementById(this.ids.get_users_import_opts);
+ if (import_opts.value == this.import_options.selected_users) {
+ var users = this.table.rows({selected: true}).data().toArray();
+ this.sel_users_import_cb.setCallbackParameter(users);
+ this.sel_users_import_cb.dispatch();
+ ret = false; // to stop sending click event in original button
+ }
+ return ret;
+ },
+ set_user_table_cb: function(data, obj) {
+ oUserSecurity.data = data;
+ oUserSecurity.destroy_table();
+ oUserSecurity.set_table();
+ oUserSecurity.show_user_modal(true);
+ },
+ show_user_modal: function(show) {
+ var modal = document.getElementById(oUserSecurity.ids.get_users_modal);
+ if (show) {
+ modal.style.display = 'block';
+ if (this.user_obj) {
+ this.user_obj.show_user_modal();
+ }
+ } else {
+ modal.style.display = 'none';
+ }
+ },
+ show_user_criteria: function(show) {
+ document.getElementById(this.ids.get_users_criteria).style.display = (show ? 'inline-block' : 'none');
+ },
+ show_user_selected_msg: function(show) {
+ document.getElementById(this.ids.get_users_selected_msg).style.display = (show ? 'inline-block' : 'none');
+ },
+ on_reload_user_list: function(e) {
+ var x = e.which || e.keyCode;
+ if (x === 13) {
+ $('#' + this.ids.get_users_criteria_test).click();
+ }
+ }
+};
+var oBasicUserSecurity = {
+ ids: {},
+ name: 'basic',
+ table_title: '<%[ Basic user list ]%>',
+ supported_fields: ['username'],
+ enabled: <%=$this->BasicAuth->Checked ? 'true' : 'false'%>,
+ init: function() {
+ jQuery.extend(this.ids, oUserSecurity.ids);
+ if (this.enabled) {
+ oUserSecurity.select_auth_method(this.name);
+ }
+ },
+ show_user_modal: function() {
+ document.getElementById(this.ids.table_title).textContent = this.table_title;
+ document.getElementById(this.ids.filter_long_name).disabled = true;
+ document.getElementById(this.ids.filter_desc).disabled = true;
+ document.getElementById(this.ids.filter_email).disabled = true;
+ }
+};
+
+var oLdapUserSecurity = {
+ ids: {
+ auth_simple: 'authentication_method_ldap_auth_simple',
+ },
+ name: 'ldap',
+ table_title: '<%[ LDAP user list ]%>',
+ supported_fields: ['username', 'long_name', 'description', 'email'],
+ enabled: <%=$this->LdapAuth->Checked ? 'true' : 'false'%>,
+ init: function() {
+ jQuery.extend(this.ids, oUserSecurity.ids);
+ if (this.enabled) {
+ oUserSecurity.select_auth_method(this.name);
+ }
+ },
+ show_ldap_auth: function(auth) {
+ var auth_simple = document.getElementById(this.ids.auth_simple);
+ auth_simple.style.display = (auth === 'simple') ? 'block' : 'none';
+ },
+ show_user_modal: function() {
+ document.getElementById(this.ids.table_title).textContent = this.table_title;
+ document.getElementById(this.ids.filter_long_name).disabled = false;
+ document.getElementById(this.ids.filter_desc).disabled = false;
+ document.getElementById(this.ids.filter_email).disabled = false;
+ }
+};
+
+function validate_ips(sender, parameter) {
+ return validate_comma_separated_list(parameter);
+}
+oUserSecurity.init();
+oBasicUserSecurity.init();
+oLdapUserSecurity.init();
+</script>
+ </div>
+ <div class="w3-container tab_item" id="user_list" style="display: none">
+ <div class="w3-panel">
+ <button type="button" id="add_user_btn" class="w3-button w3-green" onclick="oUsers.load_user_window()"><i class="fa fa-plus"></i> <%[ Add new user ]%></a>
+ </div>
+ <table id="user_list_table" class="w3-table w3-striped w3-hoverable w3-white w3-margin-bottom" style="width: 100%">
+ <thead>
+ <tr>
+ <th></th>
+ <th><%[ Username ]%></th>
+ <th class="w3-center"><%[ Long name ]%></th>
+ <th class="w3-center"><%[ Description ]%></th>
+ <th class="w3-center"><%[ E-mail ]%></th>
+ <th class="w3-center"><%[ Roles ]%></th>
+ <th class="w3-center"><%[ API host ]%></th>
+ <th class="w3-center"><%[ IP address restrictions ]%></th>
+ <th class="w3-center"><%[ Enabled ]%></th>
+ <th class="w3-center"><%[ Action ]%></th>
+ </tr>
+ </thead>
+ <tbody id="user_list_body"></tbody>
+ <tfoot>
+ <tr>
+ <th></th>
+ <th><%[ Username ]%></th>
+ <th class="w3-center"><%[ Long name ]%></th>
+ <th class="w3-center"><%[ Description ]%></th>
+ <th class="w3-center"><%[ E-mail ]%></th>
+ <th class="w3-center"><%[ Roles ]%></th>
+ <th class="w3-center"><%[ API host ]%></th>
+ <th class="w3-center"><%[ IP address restrictions ]%></th>
+ <th class="w3-center"><%[ Enabled ]%></th>
+ <th class="w3-center"><%[ Action ]%></th>
+ </tr>
+ </tfoot>
+ </table>
+ <p class="info w3-hide-medium w3-hide-small"><%[ Tip: Use left-click to select table row. Use CTRL + left-click to multiple row selection. Use SHIFT + left-click to add a range of rows to selection. ]%></p>
+<com:TCallback ID="UserList" OnCallback="TemplateControl.setUserList" />
+<com:TCallback ID="LoadUser" OnCallback="TemplateControl.loadUserWindow" />
+<com:TCallback ID="RemoveUsersAction" OnCallback="TemplateControl.removeUsers" />
+<script>
+var oUserList = {
+ ids: {
+ user_list: 'user_list_table',
+ user_list_body: 'user_list_body'
+ },
+ actions: [
+ {
+ action: 'remove',
+ label: '<%[ Remove ]%>',
+ value: 'username',
+ callback: <%=$this->RemoveUsersAction->ActiveControl->Javascript%>
+ }
+ ],
+ data: [],
+ table: null,
+ table_toolbar: null,
+ init: function() {
+ if (!this.table) {
+ this.set_table();
+ this.set_bulk_actions();
+ this.set_events();
+ } else {
+ var page = this.table.page();
+ this.table.clear().rows.add(this.data).draw();
+ this.table.page(page).draw(false);
+ oUserList.set_filters(this.table);
+ this.table_toolbar.style.display = 'none';
+ }
+ },
+ set_events: function() {
+ document.getElementById(this.ids.user_list).addEventListener('click', function(e) {
+ $(function() {
+ this.table_toolbar.style.display = this.table.rows({selected: true}).data().length > 0 ? '' : 'none';
+ }.bind(this));
+ }.bind(this));
+ },
+ set_table: function() {
+ this.table = $('#' + this.ids.user_list).DataTable({
+ data: this.data,
+ deferRender: true,
+ dom: 'lB<"table_toolbar">frtip',
+ stateSave: true,
+ buttons: [
+ 'copy', 'csv', 'colvis'
+ ],
+ columns: [
+ {
+ className: 'details-control',
+ orderable: false,
+ data: null,
+ defaultContent: '<button type="button" class="w3-button w3-blue"><i class="fa fa-angle-down"></i></button>'
+ },
+ {data: 'username'},
+ {data: 'long_name'},
+ {
+ data: 'description',
+ visible: false
+ },
+ {
+ data: 'email',
+ visible: false
+ },
+ {data: 'roles'},
+ {data: 'api_hosts'},
+ {
+ data: 'ips',
+ visible: false
+ },
+ {
+ data: 'enabled',
+ render: function(data, type, row) {
+ var ret;
+ if (type == 'display') {
+ ret = '';
+ if (data == 1) {
+ 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) {
+ var btn_edit = document.createElement('BUTTON');
+ btn_edit.className = 'w3-button w3-green';
+ btn_edit.type = 'button';
+ var i_edit = document.createElement('I');
+ i_edit.className = 'fa fa-list-ul';
+ var label_edit = document.createTextNode(' <%[ Edit ]%>');
+ btn_edit.appendChild(i_edit);
+ btn_edit.innerHTML += ' ';
+ btn_edit.style.marginRight = '8px';
+ btn_edit.appendChild(label_edit);
+ btn_edit.setAttribute('onclick', 'oUsers.load_user_window(\'' + data + '\')');
+ return btn_edit.outerHTML;
+ }
+ }
+ ],
+ responsive: {
+ details: {
+ type: 'column'
+ }
+ },
+ columnDefs: [{
+ className: 'control',
+ orderable: false,
+ targets: 0
+ },
+ {
+ className: 'action_col',
+ orderable: false,
+ targets: [ 9 ]
+ },
+ {
+ className: "dt-center",
+ targets: [ 5, 6, 8, 9 ]
+ }],
+ select: {
+ style: 'os',
+ selector: 'td:not(:last-child):not(:first-child)',
+ blurable: false
+ },
+ order: [1, 'asc'],
+ initComplete: function () {
+ oUserList.set_filters(this.api());
+ }
+ });
+ },
+ set_filters: function(api) {
+ api.columns([5, 6, 8]).every(function () {
+ var column = this;
+ var select = $('<select><option value=""></option></select>')
+ .appendTo($(column.footer()).empty())
+ .on('change', function () {
+ var val = dtEscapeRegex(
+ $(this).val()
+ );
+ column
+ .search(val ? '^' + val + '$' : '', true, false)
+ .draw();
+ });
+ if (column[0][0] == 8) { // Enabled column
+ column.data().unique().sort().each(function (d, j) {
+ var ds = '';
+ if (d === '1') {
+ ds = '<%[ Enabled ]%>';
+ } else if (d === '0') {
+ ds = '<%[ Disabled ]%>';
+ }
+ if (column.search() == '^' + dtEscapeRegex(d) + '$') {
+ select.append('<option value="' + d + '" title="' + ds + '" selected>' + ds + '</option>');
+ } else {
+ select.append('<option value="' + d + '" title="' + ds + '">' + ds + '</option>');
+ }
+ });
+ } else {
+ column.cells('', column[0]).render('display').unique().sort().each(function(d, j) {
+ if (column.search() == '^' + dtEscapeRegex(d) + '$') {
+ select.append('<option value="' + d + '" selected>' + d + '</option>');
+ } else if(d) {
+ select.append('<option value="' + d + '">' + d + '</option>');
+ }
+ });
+ }
+ });
+ },
+ set_bulk_actions: function() {
+ this.table_toolbar = get_table_toolbar(this.table, this.actions, {
+ actions: '<%[ Actions ]%>',
+ ok: '<%[ OK ]%>'
+ });
+ }
+};
+
+var oUsers = {
+ load_user_window: function(username) {
+ var title_add = document.getElementById('user_window_title_add');
+ var title_edit = document.getElementById('user_window_title_edit');
+ var user_field_name = document.getElementById('<%=$this->UserName->ClientID%>');
+ var user_field_req = document.getElementById('user_window_required');
+ var user_win_type = document.getElementById('<%=$this->UserWindowType->ClientID%>');
+ // callback is sent both for new and edit user because there is realized
+ // checking if password is allowed to set or not
+ var cb = <%=$this->LoadUser->ActiveControl->Javascript%>;
+ cb.setCallbackParameter(username);
+ cb.dispatch();
+ if (username) {
+ title_add.style.display = 'none';
+ title_edit.style.display = 'inline-block';
+ user_field_name.setAttribute('readonly', '');
+ user_field_req.style.display = 'none';
+ user_win_type.value = 'edit';
+ } else {
+ title_add.style.display = 'inline-block';
+ title_edit.style.display = 'none';
+ this.clear_user_window();
+ if (user_field_name.hasAttribute('readonly')) {
+ user_field_name.removeAttribute('readonly');
+ }
+ user_field_req.style.display = 'inline';
+ user_win_type.value = 'add';
+ }
+ document.getElementById('user_window').style.display = 'block';
+ user_field_name.focus();
+ },
+ clear_user_window: function() {
+ [
+ '<%=$this->UserName->ClientID%>',
+ '<%=$this->UserLongName->ClientID%>',
+ '<%=$this->UserDescription->ClientID%>',
+ '<%=$this->UserEmail->ClientID%>',
+ '<%=$this->UserPassword->ClientID%>',
+ '<%=$this->UserRoles->ClientID%>',
+ '<%=$this->UserAPIHost->ClientID%>',
+ '<%=$this->UserIps->ClientID%>'
+ ].forEach(function(id) {
+ document.getElementById(id).value = '';
+ });
+ document.getElementById('<%=$this->UserEnabled->ClientID%>').checked = true;
+ },
+ load_user_list: function() {
+ var cb = <%=$this->UserList->ActiveControl->Javascript%>;
+ cb.dispatch();
+ },
+ load_user_list_cb: function(list) {
+ oUserList.data = list;
+ oUserList.init();
+ },
+ save_user_cb: function() {
+ document.getElementById('user_window').style.display = 'none';
+ }
+}
+
+$(function() {
+ oUsers.load_user_list();
+});
+</script>
+ <div id="user_window" class="w3-modal">
+ <div class="w3-modal-content w3-animate-top w3-card-4">
+ <header class="w3-container w3-teal">
+ <span onclick="document.getElementById('user_window').style.display = 'none';" class="w3-button w3-display-topright">×</span>
+ <h2 id="user_window_title_add" style="display: none"><%[ Add user ]%></h2>
+ <h2 id="user_window_title_edit" style="display: none"><%[ Edit user ]%></h2>
+ </header>
+ <div class="w3-container w3-margin-left w3-margin-right w3-text-teal">
+ <com:TValidationSummary
+ CssClass="field_invalid-summary"
+ ValidationGroup="UserGroup"
+ AutoUpdate="true"
+ Display="Dynamic"
+ />
+ <span id="user_window_username_exists" class="error" style="display: none"><ul><li><%[ Username with the given name already exists. ]%></li></ul></span>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="UserName" Text="<%[ Username: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveTextBox
+ ID="UserName"
+ AutoPostBack="false"
+ MaxLength="100"
+ CssClass="w3-input w3-border"
+ />
+ <com:TRequiredFieldValidator
+ ValidationGroup="UserGroup"
+ ControlToValidate="UserName"
+ ErrorMessage="<%[ Username field cannot be empty. ]%>"
+ ControlCssClass="field_invalid"
+ Display="None"
+ />
+ <com:TRegularExpressionValidator
+ ValidationGroup="UserGroup"
+ RegularExpression="<%=WebUserConfig::USER_PATTERN%>"
+ ControlToValidate="UserName"
+ ErrorMessage="<%[ Invalid username value. ]%>"
+ ControlCssClass="field_invalid"
+ Display="None"
+ />
+ </div> <i id="user_window_required" class="fa fa-asterisk w3-text-red opt_req" style="display none"></i>
+ </div>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="UserLongName" Text="<%[ Long name: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveTextBox
+ ID="UserLongName"
+ AutoPostBack="false"
+ MaxLength="100"
+ CssClass="w3-input w3-border"
+ />
+ </div>
+ </div>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="UserDescription" Text="<%[ Description: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveTextBox
+ ID="UserDescription"
+ TextMode="MultiLine"
+ Rows="3"
+ AutoPostBack="false"
+ MaxLength="500"
+ CssClass="w3-input w3-border"
+ />
+ </div>
+ </div>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="UserEmail" Text="<%[ E-mail: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveTextBox
+ ID="UserEmail"
+ AutoPostBack="false"
+ MaxLength="500"
+ CssClass="w3-input w3-border"
+ />
+ <com:TRegularExpressionValidator
+ ValidationGroup="UserGroup"
+ RegularExpression="<%=WebUserConfig::EMAIL_ADDRESS_PATTERN%>"
+ ControlToValidate="UserEmail"
+ ErrorMessage="<%[ Invalid e-mail address value. ]%>"
+ ControlCssClass="field_invalid"
+ Display="None"
+ />
+ </div>
+ </div>
+ <div id="user_window_password" class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="UserPassword" Text="<%[ Password: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveTextBox
+ ID="UserPassword"
+ TextMode="Password"
+ AutoPostBack="false"
+ MaxLength="1000"
+ CssClass="w3-input w3-border"
+ />
+ </div>
+ </div>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="UserRoles" Text="<%[ Roles: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveListBox
+ ID="UserRoles"
+ SelectionMode="Multiple"
+ Rows="6"
+ CssClass="w3-select w3-border"
+ AutoPostBack="false"
+ />
+ <com:TRequiredFieldValidator
+ ValidationGroup="UserGroup"
+ ControlToValidate="UserRoles"
+ ErrorMessage="<%[ At least one role must be selected. ]%>"
+ ControlCssClass="field_invalid"
+ Display="None"
+ />
+ <p class="w3-text-black" style="margin: 0 16px 0 0"><%[ Use CTRL + left-click to multiple item selection ]%></p>
+ </div> <i class="fa fa-asterisk w3-text-red opt_req"></i>
+ </div>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="UserAPIHost" Text="<%[ API host: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveDropDownList
+ ID="UserAPIHost"
+ CssClass="w3-select w3-border"
+ AutoPostBack="false"
+ />
+ </div>
+ </div>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="UserEnabled" Text="<%[ Enabled: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveCheckBox
+ ID="UserEnabled"
+ CssClass="w3-check"
+ AutoPostBack="false"
+ />
+ </div>
+ </div>
+ <i class="fas fa-wrench w3-text-black"></i> <a href="javascript:void(0)" onclick="$('#user_window_advanced_options').toggle('fast');" class="w3-text-black"><%[ Advanced options ]%></a>
+ <div id="user_window_advanced_options" style="display: none">
+ <div class="w3-row w3-section" title="<%[ Comma separated IP addresses. Using asterisk character, there is also possible to provide subnet, for example: 192.168.1.* ]%>">
+ <div class="w3-col w3-third"><com:TLabel ForControl="UserIps" Text="<%[ IP address restrictions: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveTextBox
+ ID="UserIps"
+ AutoPostBack="false"
+ MaxLength="500"
+ CssClass="w3-input w3-border"
+ />
+ <p class="w3-text-black"><a href="javascript:void(0)" onclick="document.getElementById('<%=$this->UserIps->ClientID%>').value = '<%=$_SERVER['REMOTE_ADDR']%>';"><%[ Set your IP address ]%></a></p>
+ <com:TActiveCustomValidator
+ ID="UserIpsValidator"
+ ValidationGroup="UserGroup"
+ ControlToValidate="UserIps"
+ OnServerValidate="validateIps"
+ Display="Dynamic"
+ ErrorMessage="<%[ Invalid IP address restrictions value. This field can have comma separated IP addresses only or subnet addresses like 192.168.1.* ]%>"
+ ControlCssClass="field_invalid"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ <footer class="w3-container w3-center">
+ <button type="button" class="w3-button w3-red" onclick="document.getElementById('user_window').style.display = 'none';"><i class="fas fa-times"></i> <%[ Cancel ]%></button>
+ <com:TActiveLinkButton
+ ID="UserSave"
+ ValidationGroup="UserGroup"
+ CausesValidation="true"
+ OnCallback="saveUser"
+ CssClass="w3-button w3-section w3-teal w3-padding"
+ >
+ <i class="fa fa-save"></i> <%[ Save ]%>
+ </com:TActiveLinkButton>
+ <i id="status_command_loading" class="fa fa-sync w3-spin" style="visibility: hidden;"></i>
+ </footer>
+ </div>
+ <com:TActiveHiddenField ID="UserWindowType" />
+ </div>
+ </div>
+ <div class="w3-container tab_item" id="role_list" style="display: none;">
+ <div class="w3-panel">
+ <button type="button" id="add_role_btn" class="w3-button w3-green" onclick="oRoles.load_role_window()"><i class="fa fa-plus"></i> <%[ Add new role ]%></a>
+ </div>
+ <table id="role_list_table" class="w3-table w3-striped w3-hoverable w3-white w3-margin-bottom" style="width: 100%">
+ <thead>
+ <tr>
+ <th></th>
+ <th><%[ Role name ]%></th>
+ <th class="w3-center"><%[ Long name ]%></th>
+ <th class="w3-center"><%[ Description ]%></th>
+ <th class="w3-center"># <%[ Users ]%></th>
+ <th class="w3-center"><%[ Resources ]%></th>
+ <th class="w3-center"><%[ Enabled ]%></th>
+ <th class="w3-center"><%[ Action ]%></th>
+ </tr>
+ </thead>
+ <tbody id="role_list_body"></tbody>
+ <tfoot>
+ <tr>
+ <th></th>
+ <th><%[ Role name ]%></th>
+ <th class="w3-center"><%[ Long name ]%></th>
+ <th class="w3-center"><%[ Description ]%></th>
+ <th class="w3-center"># <%[ Users ]%></th>
+ <th class="w3-center"><%[ Resources ]%></th>
+ <th class="w3-center"><%[ Enabled ]%></th>
+ <th class="w3-center"><%[ Action ]%></th>
+ </tr>
+ </tfoot>
+ </table>
+ <p class="info w3-hide-medium w3-hide-small"><%[ Tip: Use left-click to select table row. Use CTRL + left-click to multiple row selection. Use SHIFT + left-click to add a range of rows to selection. ]%></p>
+<com:TCallback ID="RoleList" OnCallback="TemplateControl.setRoleList" />
+<com:TCallback ID="LoadRole" OnCallback="TemplateControl.loadRoleWindow" />
+<script>
+var oRoleList = {
+ ids: {
+ role_list: 'role_list_table',
+ role_list_body: 'role_list_body'
+ },
+ actions: [
+ {
+ action: 'remove',
+ label: '<%[ Remove ]%>',
+ value: 'role',
+ callback: <%=$this->RemoveRolesAction->ActiveControl->Javascript%>,
+ validate: function(selected) {
+ var used_roles = [];
+ var predefined_roles = <%=json_encode(array_keys($this->getModule('user_role')->getPreDefinedRoles()))%>;
+ var predef_roles = [];
+ selected.each(function(v, k) {
+ if (predefined_roles.indexOf(v.role) !== -1) {
+ predef_roles.push(' - ' + v.role);
+ } else if (v.user_count > 0) {
+ used_roles.push(' - ' + v.role);
+ }
+ });
+ var emsg = '', msg;
+ if (predef_roles.length > 0) {
+ msg = '<%[ The following roles are predefined and cannot be removed: %predefined_roles ]%>';
+ emsg += msg.replace('%predefined_roles', '<hr />' + predef_roles.join('<br />') + '<hr />');
+ }
+ if (used_roles.length > 0) {
+ msg = '<%[ The following roles are using by users and cannot be removed: %used_roles To remove them, please unassign all users from these roles. ]%>';
+ emsg += msg.replace('%used_roles', '<hr />' + used_roles.join('<br />') + '<hr />');
+ }
+ if (emsg) {
+ oBulkActionsModal.set_error(emsg);
+ return false;
+ }
+ return true;
+ }
+ }
+ ],
+ data: [],
+ table: null,
+ table_toolbar: null,
+ init: function() {
+ if (!this.table) {
+ this.set_table();
+ this.set_bulk_actions();
+ this.set_events();
+ } else {
+ var page = this.table.page();
+ this.table.clear().rows.add(oRoleList.data).draw();
+ this.table.page(page).draw(false);
+ this.set_filters(this.table);
+ this.table_toolbar.style.display = 'none';
+ }
+ },
+ set_events: function() {
+ document.getElementById(this.ids.role_list).addEventListener('click', function(e) {
+ $(function() {
+ this.table_toolbar.style.display = this.table.rows({selected: true}).data().length > 0 ? '' : 'none';
+ }.bind(this));
+ }.bind(this));
+ },
+ set_table: function() {
+ this.table = $('#' + this.ids.role_list).DataTable({
+ data: this.data,
+ deferRender: true,
+ dom: 'lB<"table_toolbar">frtip',
+ stateSave: true,
+ buttons: [
+ 'copy', 'csv', 'colvis'
+ ],
+ columns: [
+ {
+ className: 'details-control',
+ orderable: false,
+ data: null,
+ defaultContent: '<button type="button" class="w3-button w3-blue"><i class="fa fa-angle-down"></i></button>'
+ },
+ {data: 'role'},
+ {data: 'long_name'},
+ {
+ data: 'description',
+ visible: false
+ },
+ {data: 'user_count'},
+ {
+ data: 'resources',
+ render: function(data, type, row) {
+ ret = data;
+ if (type == 'display') {
+ var span = document.createElement('SPAN');
+ span.title = data;
+ if (data.length > 40) {
+ span.textContent = data.substring(0, 40) + '...';
+ } else {
+ span.textContent = data;
+ }
+ ret = span.outerHTML;
+ } else {
+ ret = data;
+ }
+ return ret;
+ }
+ },
+ {
+ data: 'enabled',
+ render: function(data, type, row) {
+ var ret;
+ if (type == 'display') {
+ ret = '';
+ if (data == 1) {
+ var check = document.createElement('I');
+ check.className = 'fas fa-check';
+ ret = check.outerHTML;
+ }
+ } else {
+ ret = data;
+ }
+ return ret;
+ }
+ },
+ {
+ data: 'role',
+ render: function (data, type, row) {
+ var btn_edit = document.createElement('BUTTON');
+ btn_edit.className = 'w3-button w3-green';
+ btn_edit.type = 'button';
+ var i_edit = document.createElement('I');
+ i_edit.className = 'fa fa-list-ul';
+ var label_edit = document.createTextNode(' <%[ Edit ]%>');
+ btn_edit.appendChild(i_edit);
+ btn_edit.innerHTML += ' ';
+ btn_edit.style.marginRight = '8px';
+ btn_edit.appendChild(label_edit);
+ btn_edit.setAttribute('onclick', 'oRoles.load_role_window(\'' + data + '\')');
+ return btn_edit.outerHTML;
+ }
+ }
+ ],
+ responsive: {
+ details: {
+ type: 'column'
+ }
+ },
+ columnDefs: [{
+ className: 'control',
+ orderable: false,
+ targets: 0
+ },
+ {
+ className: 'action_col',
+ orderable: false,
+ targets: [ 7 ]
+ },
+ {
+ className: "dt-center",
+ targets: [ 2, 3, 4, 5, 6, 7 ]
+ }],
+ select: {
+ style: 'os',
+ selector: 'td:not(:last-child):not(:first-child)',
+ blurable: false
+ },
+ order: [1, 'asc'],
+ initComplete: function () {
+ oRoleList.set_filters(this.api());
+ }
+ });
+ },
+ set_filters: function(api) {
+ api.columns([6]).every(function () {
+ var column = this;
+ var select = $('<select><option value=""></option></select>')
+ .appendTo($(column.footer()).empty())
+ .on('change', function () {
+ var val = dtEscapeRegex(
+ $(this).val()
+ );
+ column
+ .search(val ? '^' + val + '$' : '', true, false)
+ .draw();
+ });
+ if (column[0][0] == 6) { // Enabled column
+ column.data().unique().sort().each(function (d, j) {
+ var ds = '';
+ if (d === '1') {
+ ds = '<%[ Enabled ]%>';
+ } else if (d === '0') {
+ ds = '<%[ Disabled ]%>';
+ }
+ if (column.search() == '^' + dtEscapeRegex(d) + '$') {
+ select.append('<option value="' + d + '" title="' + ds + '" selected>' + ds + '</option>');
+ } else if (ds) {
+ select.append('<option value="' + d + '" title="' + ds + '">' + ds + '</option>');
+ }
+ });
+ }
+ });
+ },
+ set_bulk_actions: function() {
+ this.table_toolbar = get_table_toolbar(this.table, this.actions, {
+ actions: '<%[ Actions ]%>',
+ ok: '<%[ OK ]%>'
+ });
+ }
+};
+
+var oRoles = {
+ load_role_window: function(role) {
+ var title_add = document.getElementById('role_window_title_add');
+ var title_edit = document.getElementById('role_window_title_edit');
+ var role_field_name = document.getElementById('<%=$this->Role->ClientID%>');
+ var role_field_req = document.getElementById('role_window_required');
+ var role_win_type = document.getElementById('<%=$this->RoleWindowType->ClientID%>');
+ var cb = <%=$this->LoadRole->ActiveControl->Javascript%>;
+ cb.setCallbackParameter(role);
+ cb.dispatch();
+ if (role) {
+ title_add.style.display = 'none';
+ title_edit.style.display = 'inline-block';
+ role_field_name.setAttribute('readonly', '');
+ role_field_req.style.display = 'none';
+ role_win_type.value = 'edit';
+ } else {
+ title_add.style.display = 'inline-block';
+ title_edit.style.display = 'none';
+ this.clear_role_window();
+ if (role_field_name.hasAttribute('readonly')) {
+ role_field_name.removeAttribute('readonly');
+ }
+ role_field_req.style.display = 'inline';
+ role_win_type.value = 'add';
+ }
+ document.getElementById('role_window_role_exists').style.display = 'none';
+ document.getElementById('role_window').style.display = 'block';
+ if (!role) {
+ role_field_name.focus();
+ }
+ },
+ clear_role_window: function() {
+ [
+ '<%=$this->Role->ClientID%>',
+ '<%=$this->RoleLongName->ClientID%>',
+ '<%=$this->RoleDescription->ClientID%>',
+ '<%=$this->RoleResources->ClientID%>',
+ ].forEach(function(id) {
+ document.getElementById(id).value = '';
+ });
+ document.getElementById('<%=$this->RoleEnabled->ClientID%>').checked = true;
+ },
+ load_role_list: function() {
+ var cb = <%=$this->RoleList->ActiveControl->Javascript%>;
+ cb.dispatch();
+ },
+ load_role_list_cb: function(list) {
+ oRoleList.data = list;
+ oRoleList.init();
+ },
+ save_role_cb: function() {
+ document.getElementById('role_window').style.display = 'none';
+ }
+}
+
+$(function() {
+ oRoles.load_role_list();
+});
+</script>
+ <div id="role_window" class="w3-modal">
+ <div class="w3-modal-content w3-animate-top w3-card-4">
+ <header class="w3-container w3-teal">
+ <span onclick="document.getElementById('role_window').style.display = 'none';" class="w3-button w3-display-topright">×</span>
+ <h2 id="role_window_title_add" style="display: none"><%[ Add role ]%></h2>
+ <h2 id="role_window_title_edit" style="display: none"><%[ Edit role ]%></h2>
+ </header>
+ <div class="w3-container w3-margin-left w3-margin-right w3-text-teal">
+ <com:TValidationSummary
+ CssClass="field_invalid-summary"
+ ValidationGroup="RoleGroup"
+ AutoUpdate="true"
+ Display="Dynamic"
+ />
+ <span id="role_window_role_exists" class="error" style="display: none"><ul><li><%[ Role with the given name already exists. ]%></li></ul></span>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="Role" Text="<%[ Role: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveTextBox
+ ID="Role"
+ AutoPostBack="false"
+ MaxLength="100"
+ CssClass="w3-input w3-border"
+ />
+ <com:TRequiredFieldValidator
+ ValidationGroup="RoleGroup"
+ ControlToValidate="Role"
+ ErrorMessage="<%[ Role field cannot be empty. ]%>"
+ ControlCssClass="field_invalid"
+ Display="None"
+ />
+ <com:TRegularExpressionValidator
+ ValidationGroup="RoleGroup"
+ RegularExpression="<%=WebRoleConfig::ROLE_PATTERN%>"
+ ControlToValidate="Role"
+ ErrorMessage="<%[ Invalid role value. ]%>"
+ ControlCssClass="field_invalid"
+ Display="None"
+ />
+ </div> <i id="role_window_required" class="fa fa-asterisk w3-text-red opt_req" style="display none"></i>
+ </div>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="RoleLongName" Text="<%[ Long name: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveTextBox
+ ID="RoleLongName"
+ AutoPostBack="false"
+ MaxLength="100"
+ CssClass="w3-input w3-border"
+ />
+ </div>
+ </div>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="RoleDescription" Text="<%[ Description: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveTextBox
+ ID="RoleDescription"
+ TextMode="MultiLine"
+ Rows="3"
+ AutoPostBack="false"
+ MaxLength="500"
+ CssClass="w3-input w3-border"
+ />
+ </div>
+ </div>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="RoleResources" Text="<%[ Resources: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveListBox
+ ID="RoleResources"
+ SelectionMode="Multiple"
+ Rows="6"
+ CssClass="w3-select w3-border"
+ AutoPostBack="false"
+ />
+ <com:TRequiredFieldValidator
+ ValidationGroup="RoleGroup"
+ ControlToValidate="RoleResources"
+ ErrorMessage="<%[ At least one resource must be selected. ]%>"
+ ControlCssClass="field_invalid"
+ Display="None"
+ />
+ <p class="w3-text-black" style="margin: 0 16px 0 0"><%[ Use CTRL + left-click to multiple item selection ]%></p>
+ </div> <i class="fa fa-asterisk w3-text-red opt_req"></i>
+
+ </div>
+ <div class="w3-row w3-section">
+ <div class="w3-col w3-third"><com:TLabel ForControl="RoleEnabled" Text="<%[ Enabled: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-half">
+ <com:TActiveCheckBox
+ ID="RoleEnabled"
+ CssClass="w3-check"
+ AutoPostBack="false"
+ />
+ </div>
+ </div>
+ </div>
+ <com:TActiveLabel
+ ID="PreDefinedRoleMsg"
+ CssClass="w3-margin-left"
+ Display="None"
+ >
+ <%[ This is native predefined role, that cannot be changed. For having custom roles please use the button to add new role. ]%>
+ </com:TActiveLabel>
+ <footer class="w3-container w3-center w3-padding">
+ <button type="button" class="w3-button w3-red" onclick="document.getElementById('role_window').style.display = 'none';"><i class="fas fa-times"></i> <%[ Cancel ]%></button>
+ <com:TActiveLinkButton
+ ID="RoleSave"
+ ValidationGroup="RoleGroup"
+ CausesValidation="true"
+ OnCallback="saveRole"
+ CssClass="w3-button w3-section w3-teal"
+ >
+ <i class="fa fa-save"></i> <%[ Save ]%>
+ </com:TActiveLinkButton>
+ <i id="status_command_loading" class="fa fa-sync w3-spin" style="visibility: hidden;"></i>
+ </footer>
+ </div>
+ <com:TActiveHiddenField ID="RoleWindowType" />
+ </div>
+ </div>
+<script>
+/**
+ * Defne bulk actions output id here because expression tags (< % = % >) cannot
+ * be defined in the TCallback ClientSide properties.
+ */
+var bulk_actions_output_id = '<%=$this->SourceTemplateControl->BulkActions->BulkActionsOutput->ClientID%>';
+</script>
+ <com:TCallback ID="RemoveRolesAction" OnCallback="TemplateControl.removeRoles" />
+ <com:Application.Web.Portlets.BulkActionsModal ID="BulkActions" />
+</com:TContent>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2020 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('System.Web.UI.ActiveControls.TActiveCheckBox');
+Prado::using('System.Web.UI.ActiveControls.TActiveCustomValidator');
+Prado::using('System.Web.UI.ActiveControls.TActiveDropDownList');
+Prado::using('System.Web.UI.ActiveControls.TActiveHiddenField');
+Prado::using('System.Web.UI.ActiveControls.TActiveLabel');
+Prado::using('System.Web.UI.ActiveControls.TActiveLinkButton');
+Prado::using('System.Web.UI.ActiveControls.TActiveListBox');
+Prado::using('System.Web.UI.ActiveControls.TActiveTextBox');
+Prado::using('System.Web.UI.ActiveControls.TCallback');
+Prado::using('System.Web.UI.WebControls.TCheckBox');
+Prado::using('System.Web.UI.WebControls.TLabel');
+Prado::using('System.Web.UI.WebControls.TListItem');
+Prado::using('System.Web.UI.WebControls.TRadioButton');
+Prado::using('System.Web.UI.WebControls.TRegularExpressionValidator');
+Prado::using('System.Web.UI.WebControls.TRequiredFieldValidator');
+Prado::using('System.Web.UI.WebControls.TValidationSummary');
+Prado::using('Application.Common.Class.Ldap');
+Prado::using('Application.Web.Class.BaculumWebPage');
+
+/**
+ * Security page (auth methods, users, roles...).
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Page
+ * @package Baculum Web
+ */
+class Security extends BaculumWebPage {
+
+ /**
+ * Modal window types for users and roles.
+ */
+ const TYPE_ADD_WINDOW = 'add';
+ const TYPE_EDIT_WINDOW = 'edit';
+
+ /**
+ * Options for import users.
+ */
+ const IMPORT_OPT_ALL_USERS = 0;
+ const IMPORT_OPT_SELECTED_USERS = 1;
+ const IMPORT_OPT_CRITERIA = 2;
+
+ /**
+ * Options for import criteria.
+ */
+ const IMPORT_CRIT_USERNAME = 0;
+ const IMPORT_CRIT_LONG_NAME = 1;
+ const IMPORT_CRIT_DESCRIPTION = 2;
+ const IMPORT_CRIT_EMAIL = 3;
+
+
+ /**
+ * Store web user config.
+ */
+ private $user_config = [];
+
+ /**
+ * Initialize page.
+ *
+ * @param mixed $param oninit event parameter
+ * @return none
+ */
+ public function onInit($param) {
+ parent::onInit($param);
+ if ($this->IsCallBack || $this->IsPostBack) {
+ return;
+ }
+ $this->initDefAccessForm();
+ $this->initAuthForm();
+ $this->initUserWindow();
+ $this->initRoleWindow();
+ $this->setBasicAuthConfig();
+ }
+
+ /**
+ * Initialize form with default access settings.
+ *
+ * @return none
+ */
+ public function initDefAccessForm() {
+ $this->setRoles(
+ $this->GeneralDefaultAccessRole,
+ WebUserRoles::NORMAL
+ );
+
+ $this->setAPIHosts(
+ $this->GeneralDefaultAccessAPIHost,
+ HostConfig::MAIN_CATALOG_HOST
+ );
+ if (isset($this->web_config['security']['def_access'])) {
+ if ($this->web_config['security']['def_access'] === WebConfig::DEF_ACCESS_NO_ACCESS) {
+ $this->GeneralDefaultNoAccess->Checked = true;
+ } elseif ($this->web_config['security']['def_access'] === WebConfig::DEF_ACCESS_DEFAULT_SETTINGS) {
+ $this->GeneralDefaultAccess->Checked = true;
+ }
+ if (isset($this->web_config['security']['def_role'])) {
+ $this->GeneralDefaultAccessRole->SelectedValue = $this->web_config['security']['def_role'];
+ }
+ if (isset($this->web_config['security']['def_api_host'])) {
+ $this->GeneralDefaultAccessAPIHost->SelectedValue = $this->web_config['security']['def_api_host'];
+ }
+ } else {
+ $this->GeneralDefaultAccess->Checked = true;
+ }
+ }
+
+ /**
+ * Initialize form with authentication method settings.
+ *
+ * @return none
+ */
+ public function initAuthForm() {
+ if (isset($this->web_config['security']['auth_method'])) {
+ if ($this->web_config['security']['auth_method'] === WebConfig::AUTH_METHOD_BASIC) {
+ $this->BasicAuth->Checked = true;
+ } elseif ($this->web_config['security']['auth_method'] === WebConfig::AUTH_METHOD_LDAP) {
+ $this->LdapAuth->Checked = true;
+ }
+
+ // Fill LDAP auth fileds
+ if (key_exists('auth_ldap', $this->web_config)) {
+ $this->LdapAuthServerAddress->Text = $this->web_config['auth_ldap']['address'];
+ $this->LdapAuthServerPort->Text = $this->web_config['auth_ldap']['port'];
+ $this->LdapAuthServerLdaps->Checked = ($this->web_config['auth_ldap']['ldaps'] == 1);
+ $this->LdapAuthServerProtocolVersion->Text = $this->web_config['auth_ldap']['protocol_ver'];
+ $this->LdapAuthServerBaseDn->Text = $this->web_config['auth_ldap']['base_dn'];
+ if ($this->web_config['auth_ldap']['auth_method'] === Ldap::AUTH_METHOD_ANON) {
+ $this->LdapAuthMethodAnonymous->Checked = true;
+ } elseif ($this->web_config['auth_ldap']['auth_method'] === Ldap::AUTH_METHOD_SIMPLE) {
+ $this->LdapAuthMethodSimple->Checked = true;
+ }
+ $this->LdapAuthMethodSimpleUsername->Text = $this->web_config['auth_ldap']['bind_dn'];
+ $this->LdapAuthMethodSimplePassword->Text = $this->web_config['auth_ldap']['bind_password'];
+ $this->LdapAuthServerBaseDn->Text = $this->web_config['auth_ldap']['base_dn'];
+ $this->LdapAttributesUsername->Text = $this->web_config['auth_ldap']['user_attr'];
+ $this->LdapAttributesLongName->Text = $this->web_config['auth_ldap']['long_name_attr'];
+ $this->LdapAttributesEmail->Text = $this->web_config['auth_ldap']['email_attr'];
+ $this->LdapAttributesDescription->Text = $this->web_config['auth_ldap']['desc_attr'];
+ }
+ // Fill Basic auth fields
+ if (key_exists('auth_basic', $this->web_config)) {
+ $this->BasicAuthAllowManageUsers->Checked = ($this->web_config['auth_basic']['allow_manage_users'] == 1);
+ $this->BasicAuthUserFile->Text = $this->web_config['auth_basic']['user_file'];
+ $this->BasicAuthHashAlgorithm->SelectedValue = $this->web_config['auth_basic']['hash_alg'];
+ }
+ } else {
+ // Default set to Basic auth method
+ $this->BasicAuth->Checked = true;
+ }
+ }
+
+ /**
+ * Initialize values in user modal window.
+ *
+ * @return none
+ */
+ public function initUserWindow() {
+ // set API hosts
+ $this->setAPIHosts($this->UserAPIHost);
+
+ // set roles
+ $this->setRoles($this->UserRoles);
+ }
+
+ /**
+ * Set role list control.
+ *
+ * @param object $control control which contains role list
+ * @param mixed $def_val default value or null if no default value to set
+ * @return none
+ */
+ private function setRoles($control, $def_val = null) {
+ // set roles
+ $roles = $this->getModule('user_role')->getRoles();
+ $role_items = [];
+ foreach ($roles as $role_name => $role) {
+ $role_items[$role_name] = $role['long_name'] ?: $role_name;
+ }
+ $control->DataSource = $role_items;
+ if ($def_val) {
+ $control->SelectedValue = $def_val;
+ }
+ $control->dataBind();
+ }
+
+ /**
+ * Set API host list control.
+ *
+ * @param object $control control which contains API host list
+ * @param mixed $def_val default value or null if no default value to set
+ * @return none
+ */
+ private function setAPIHosts($control, $def_val = null) {
+ $api_hosts = array_keys($this->getModule('host_config')->getConfig());
+ array_unshift($api_hosts, '');
+ $control->DataSource = array_combine($api_hosts, $api_hosts);
+ if ($def_val) {
+ $control->SelectedValue = $def_val;
+ }
+ $control->dataBind();
+ }
+
+ /**
+ * Set and load user list.
+ *
+ * @param TCallback $sender sender object
+ * @param TCallbackEventParameter callback parameter
+ * @return none
+ */
+ public function setUserList($sender, $param) {
+ $config = $this->getModule('user_config')->getConfig();
+ $this->getCallbackClient()->callClientFunction('oUsers.load_user_list_cb', [
+ array_values($config)
+ ]);
+ $this->user_config = $config;
+ }
+
+ /**
+ * Load data in user modal window.
+ *
+ * @param TCallback $sender sender object
+ * @param TCallbackEventParameter $param callback parameter
+ * @return none
+ */
+ public function loadUserWindow($sender, $param) {
+ //$this->getModule('user_config')->importBasicUsers();
+ $username = $param->getCallbackParameter();
+ $config = $this->getModule('user_config')->getUserConfig($username);
+ if (count($config) > 0) {
+ // It is done only for existing users
+ $this->UserName->Text = $config['username'];
+ $this->UserLongName->Text = $config['long_name'];
+ $this->UserDescription->Text = $config['description'];
+ $this->UserEmail->Text = $config['email'];
+ $this->UserPassword->Text = '';
+ $selected_indices = [];
+ $roles = explode(',', $config['roles']);
+ for ($i = 0; $i < $this->UserRoles->getItemCount(); $i++) {
+ if (in_array($this->UserRoles->Items[$i]->Value, $roles)) {
+ $selected_indices[] = $i;
+ }
+ }
+ $this->UserRoles->setSelectedIndices($selected_indices);
+ $this->UserAPIHost->SelectedValue = $config['api_hosts'];
+ $this->UserIps->Text = $config['ips'];
+ $this->UserEnabled->Checked = ($config['enabled'] == 1);
+ }
+
+ // It is done both for new user and for edit user
+ if ($this->isManageUsersAvail()) {
+ $this->getCallbackClient()->show('user_window_password');
+ } else {
+ $this->getCallbackClient()->hide('user_window_password');
+ }
+ }
+
+ /**
+ * Save user.
+ * It works both for new users and for edited users.
+ * Saves values from modal popup.
+ *
+ * @param TCallback $sender sender object
+ * @param TCallbackEventParameter $param callback parameter
+ * @return none
+ */
+ public function saveUser($sender, $param) {
+ if (!$this->UserIps->IsValid) {
+ // invalid IP restriction value
+ return;
+ }
+ $user_win_type = $this->UserWindowType->Value;
+ $username = $this->UserName->Text;
+ $this->getCallbackClient()->hide('user_window_username_exists');
+ if ($user_win_type === self::TYPE_ADD_WINDOW) {
+ $config = $this->getModule('user_config')->getUserConfig($username);
+ if (count($config) > 0) {
+ $this->getCallbackClient()->show('user_window_username_exists');
+ return;
+ }
+ }
+
+ $config = [];
+ $config['long_name'] = $this->UserLongName->Text;
+ $config['description'] = $this->UserDescription->Text;
+ $config['email'] = $this->UserEmail->Text;
+
+ $selected_indices = $this->UserRoles->getSelectedIndices();
+ $roles = [];
+ foreach ($selected_indices as $indice) {
+ for ($i = 0; $i < $this->UserRoles->getItemCount(); $i++) {
+ if ($i === $indice) {
+ $roles[] = $this->UserRoles->Items[$i]->Value;
+ }
+ }
+ }
+ $config['roles'] = implode(',', $roles);
+ $config['api_hosts'] = $this->UserAPIHost->SelectedValue;
+ $config['ips'] = $this->trimIps($this->UserIps->Text);
+ $config['enabled'] = $this->UserEnabled->Checked ? 1 : 0;
+ $result = $this->getModule('user_config')->setUserConfig($username, $config);
+
+ // Set password if auth method supports it
+ if ($result === true && !empty($this->UserPassword->Text) && $this->isManageUsersAvail()) {
+ // Set Basic auth users password
+ if ($this->getModule('web_config')->isAuthMethodBasic() &&
+ isset($this->web_config['auth_basic']['user_file'])) {
+
+ $opts = [];
+ if (isset($this->web_config['auth_basic']['hash_alg'])) {
+ $opts['hash_alg'] = $this->web_config['auth_basic']['hash_alg'];
+ }
+
+ // Setting basic users works both for adding and editing users
+ $basic = $this->getModule('basic_webuser');
+ $basic->setUsersConfig(
+ $username,
+ $this->UserPassword->Text,
+ false,
+ null,
+ $opts
+ );
+ }
+ }
+
+ $this->setUserList(null, null);
+ $this->setRoleList(null, null);
+ $this->getCallbackClient()->callClientFunction('oUsers.save_user_cb');
+ }
+
+ /**
+ * Remove users action.
+ * Here is possible to remove one user or many.
+ * This action is linked with table bulk actions.
+ *
+ * @param TCallback $sender sender object
+ * @param TCallbackEventParameter $param callback parameter
+ * @return none
+ */
+ public function removeUsers($sender, $param) {
+ $usernames = explode('|', $param->getCallbackParameter());
+ $config = $this->getModule('user_config')->getConfig();
+ for ($i = 0; $i < count($usernames); $i++) {
+ if (key_exists($usernames[$i], $config)) {
+ unset($config[$usernames[$i]]);
+ }
+ }
+ $result = $this->getModule('user_config')->setConfig($config);
+
+ if ($result === true && $this->isManageUsersAvail() &&
+ $this->getModule('web_config')->isAuthMethodBasic() &&
+ isset($this->web_config['auth_basic']['user_file'])) {
+ // Remove basic auth users too
+ $basic = $this->getModule('basic_webuser');
+ $basic->removeUsers($usernames);
+ }
+
+ // refresh user list
+ $this->setUserList(null, null);
+
+ // refresh role list
+ $this->setRoleList(null, null);
+ }
+
+ /**
+ * Initialize values in role modal window.
+ *
+ * @return none
+ */
+ public function initRoleWindow() {
+ // set role resources
+ $resources = $this->getModule('page_category')->getCategories(false);
+ $this->RoleResources->DataSource = array_combine($resources, $resources);
+ $this->RoleResources->dataBind();
+ }
+
+ /**
+ * Set and load role list.
+ *
+ * @param TCallback $sender sender object
+ * @param TCallbackEventParameter callback parameter
+ * @return none
+ */
+ public function setRoleList($sender, $param) {
+ $config = $this->getModule('user_role')->getRoles();
+ $this->addUserStatsToRoles($config);
+ $this->getCallbackClient()->callClientFunction('oRoles.load_role_list_cb', [
+ array_values($config)
+ ]);
+ }
+
+ /**
+ * Load data in role modal window.
+ *
+ * @param TCallback $sender sender object
+ * @param TCallbackEventParameter $param callback parameter
+ * @return none
+ */
+ public function loadRoleWindow($sender, $param) {
+ $role = $param->getCallbackParameter();
+ $config = $this->getModule('user_role')->getRole($role);
+ if (count($config) > 0) {
+ // Edit role window
+ $this->Role->Text = $config['role'];
+ $this->RoleLongName->Text = $config['long_name'];
+ $this->RoleDescription->Text = $config['description'];
+ $selected_indices = [];
+ $resources = explode(',', $config['resources']);
+ for ($i = 0; $i < $this->RoleResources->getItemCount(); $i++) {
+ if (in_array($this->RoleResources->Items[$i]->Value, $resources)) {
+ $selected_indices[] = $i;
+ }
+ }
+ $this->RoleResources->setSelectedIndices($selected_indices);
+ $this->RoleEnabled->Checked = ($config['enabled'] == 1);
+ if ($this->getModule('user_role')->isRolePreDefined($role)) {
+ $this->RoleSave->Display = 'None';
+ $this->PreDefinedRoleMsg->Display = 'Dynamic';
+ } else {
+ $this->RoleSave->Display = 'Dynamic';
+ $this->PreDefinedRoleMsg->Display = 'None';
+ }
+ } else {
+ // New role window
+ $this->RoleSave->Display = 'Dynamic';
+ $this->PreDefinedRoleMsg->Display = 'None';
+ }
+ }
+
+ /**
+ * Save role.
+ * It works both for new roles and for edited roles.
+ * Saves values from modal popup.
+ *
+ * @param TCallback $sender sender object
+ * @param TCallbackEventParameter $param callback parameter
+ * @return none
+ */
+ public function saveRole($sender, $param) {
+ $role_win_type = $this->RoleWindowType->Value;
+ $role = $this->Role->Text;
+ $this->getCallbackClient()->hide('role_window_role_exists');
+ if ($role_win_type === self::TYPE_ADD_WINDOW) {
+ $config = $this->getModule('user_role')->getRole($role);
+ if (count($config) > 0) {
+ $this->getCallbackClient()->show('role_window_role_exists');
+ return;
+ }
+ }
+ if ($this->getModule('user_role')->isRolePreDefined($role)) {
+ // Predefined roles cannot be saved
+ return;
+ }
+ $config = [];
+ $config['long_name'] = $this->RoleLongName->Text;
+ $config['description'] = $this->RoleDescription->Text;
+
+ $selected_indices = $this->RoleResources->getSelectedIndices();
+ $resources = [];
+ foreach ($selected_indices as $indice) {
+ for ($i = 0; $i < $this->RoleResources->getItemCount(); $i++) {
+ if ($i === $indice) {
+ $resources[] = $this->RoleResources->Items[$i]->Value;
+ }
+ }
+ }
+ $config['resources'] = implode(',', $resources);
+ $config['enabled'] = $this->RoleEnabled->Checked ? 1 : 0;
+ $this->getModule('role_config')->setRoleConfig($role, $config);
+ $this->setRoleList(null, null);
+ if ($role_win_type === self::TYPE_ADD_WINDOW) {
+ // refresh user window for new role
+ $this->initUserWindow();
+ }
+ $this->getCallbackClient()->callClientFunction('oRoles.save_role_cb');
+ }
+
+ /**
+ * Remove roles action.
+ * Here is possible to remove one role or many.
+ * This action is linked with table bulk actions.
+ *
+ * @param TCallback $sender sender object
+ * @param TCallbackEventParameter $param callback parameter
+ * @return none
+ */
+ public function removeRoles($sender, $param) {
+ $roles = explode('|', $param->getCallbackParameter());
+ $config = $this->getModule('role_config')->getConfig();
+ $user_role = $this->getModule('user_role');
+ for ($i = 0; $i < count($roles); $i++) {
+ if (key_exists($roles[$i], $config)) {
+ if ($user_role->isRolePreDefined($roles[$i])) {
+ // Predefined roles cannot be saved
+ continue;
+ }
+ unset($config[$roles[$i]]);
+ }
+ }
+ $this->getModule('role_config')->setConfig($config);
+ $this->setRoleList(null, null);
+ // refresh user window to now show removed roles
+ $this->initUserWindow();
+ }
+
+ /**
+ * Add user statistics to roles.
+ * It adds user count to information about roles.
+ *
+ * @param array $role_config role config (note, passing by reference)
+ * @return none
+ */
+ private function addUserStatsToRoles(&$role_config) {
+ $config = [];
+ if (count($this->user_config) > 0) {
+ $config = $this->user_config;
+ } else {
+ $config = $this->getModule('user_config')->getConfig();
+ }
+ $user_roles = [];
+ foreach ($role_config as $role => $prop) {
+ $user_roles[$role] = 0;
+ }
+ foreach ($config as $username => $prop) {
+ $roles = explode(',', $prop['roles']);
+ for ($i = 0; $i < count($roles); $i++) {
+ $user_roles[$roles[$i]]++;
+ }
+ }
+ foreach ($role_config as $role => $prop) {
+ $role_config[$role]['user_count'] = $user_roles[$role];
+ }
+ }
+
+ /**
+ * Set basic authentication user file.
+ *
+ * @return none
+ */
+ private function setBasicAuthConfig() {
+ if ($this->isManageUsersAvail() && isset($this->web_config['auth_basic']['user_file'])) {
+ $this->getModule('basic_webuser')->setConfigPath($this->web_config['auth_basic']['user_file']);
+ }
+ }
+
+ /**
+ * Get basic users and provide them to template.
+ *
+ * @param TActiveLinkButton $sender sender
+ * @param TCommandEventParameter $param event parameter object
+ * @return none
+ */
+ public function getBasicUsers($sender, $param) {
+ if ($param instanceof Prado\Web\UI\TCommandEventParameter && $param->getCommandParameter() === 'load') {
+ // reset criteria filters when modal is open
+ $this->GetUsersImportOptions->SelectedValue = self::IMPORT_OPT_ALL_USERS;
+ $this->GetUsersCriteria->SelectedValue = self::IMPORT_CRIT_USERNAME;
+ $this->GetUsersCriteriaFilter->Text = '';
+ $this->getCallbackClient()->hide('get_users_criteria');
+ $this->getCallbackClient()->hide('get_users_advanced_options');
+
+ // set role resources
+ $this->setRoles($this->GetUsersDefaultRole, WebUserRoles::NORMAL);
+
+ // set API hosts
+ $this->setAPIHosts($this->GetUsersDefaultAPIHost, HostConfig::MAIN_CATALOG_HOST);
+ }
+
+ $params = $this->getBasicParams();
+
+ // add additional parameters
+ $this->addBasicExtraParams($params);
+
+ $pattern = '';
+ if (!empty($params['filter_val'])) {
+ $pattern = '*' . $params['filter_val'] . '*';
+ }
+
+ $basic = $this->getModule('basic_webuser');
+ // set path from input because user can have unsaved changes
+ $basic->setConfigPath($this->BasicAuthUserFile->Text);
+ $users = $basic->getUsers($pattern);
+ $users = array_keys($users);
+ $user_list = $this->convertBasicUsers($users);
+ $this->getCallbackClient()->callClientFunction('oUserSecurity.set_user_table_cb', [
+ $user_list
+ ]);
+ if (count($users) > 0) {
+ // Success
+ $this->TestBasicGetUsersMsg->Text = '';
+ $this->TestBasicGetUsersMsg->Display = 'None';
+ $this->getCallbackClient()->hide('basic_get_users_error');
+ $this->getCallbackClient()->show('basic_get_users_ok');
+ } else {
+ // Error
+ $this->getCallbackClient()->show('basic_get_users_error');
+ $this->TestBasicGetUsersMsg->Text = Prado::localize('Empty user list');
+ $this->TestBasicGetUsersMsg->Display = 'Dynamic';
+ }
+ }
+
+ /**
+ * Convert basic users from simple username list into full form.
+ * There is option to return user list in config file form or data table form.
+ *
+ * @param array $users simple user list
+ * @param boolean $config_form_result if true, sets the list in config file form
+ * @return array user list
+ */
+ private function convertBasicUsers(array $users, $config_form_result = false) {
+ $user_list = [];
+ for ($i = 0; $i < count($users); $i++) {
+ $user = [
+ 'username' => $users[$i],
+ 'long_name' => '',
+ 'email' => '',
+ 'description' => ''
+ ];
+ if ($config_form_result) {
+ $user_list[$users[$i]] = $user;
+ } else {
+ $user_list[] = $user;
+ }
+ }
+ return $user_list;
+ }
+
+ /**
+ * Get basic auth specific parameters with form values.
+ *
+ * @return array array basic auth parameters
+ */
+ private function getBasicParams() {
+ $params = [];
+ $params['allow_manage_users'] = $this->BasicAuthAllowManageUsers->Checked ? 1 : 0;
+ $params['user_file'] = $this->BasicAuthUserFile->Text;
+ $params['hash_alg'] = $this->BasicAuthHashAlgorithm->SelectedValue;
+ return $params;
+ }
+
+ /**
+ * Add to basic auth params additional parameters.
+ * Note, extra parameters are not set in config.
+ *
+ * @param array $params basic auth parameters (passing by reference)
+ * @return none
+ */
+ private function addBasicExtraParams(&$params) {
+ if ($this->GetUsersImportOptions->SelectedValue == self::IMPORT_OPT_CRITERIA) {
+ $params['filter_val'] = $this->GetUsersCriteriaFilter->Text;
+ }
+ }
+
+ /**
+ * Prepare basic users to import.
+ *
+ * @param TCallback $sender sender object
+ * @param TCallbackEventParameter $param event parameter object
+ * @return array web users list to import
+ */
+ public function prepareBasicUsers($sender, $param) {
+ $users_web = [];
+ $import_opt = (integer)$this->GetUsersImportOptions->SelectedValue;
+ $basic_webuser = $this->getModule('basic_webuser');
+ switch ($import_opt) {
+ case self::IMPORT_OPT_ALL_USERS: {
+ $users_web = $basic_webuser->getUsers();
+ $users_web = array_keys($users_web);
+ $users_web = $this->convertBasicUsers($users_web, true);
+ break;
+ }
+ case self::IMPORT_OPT_SELECTED_USERS: {
+ if ($param instanceof Prado\Web\UI\ActiveControls\TCallbackEventParameter) {
+ $cb_param = $param->getCallbackParameter();
+ if (is_array($cb_param)) {
+ for ($i = 0; $i < count($cb_param); $i++) {
+ $val = (array)$cb_param[$i];
+ $users_web[$val['username']] = $val;
+ }
+ }
+ }
+ break;
+ }
+ case self::IMPORT_OPT_CRITERIA: {
+ $params = $this->getBasicParams();
+ // add additional parameters
+ $this->addBasicExtraParams($params);
+ if (!empty($params['filter_val'])) {
+ $pattern = '*' . $params['filter_val'] . '*';
+ $users_web = $basic_webuser->getUsers($pattern);
+ $users_web = array_keys($users_web);
+ $users_web = $this->convertBasicUsers($users_web, true);
+
+ }
+ break;
+ }
+ }
+ return $users_web;
+ }
+
+ /**
+ * Test basic user file.
+ *
+ * @param TActiveLinkButton $sender sender object
+ * @param TCallbackEventParameter $param event parameter object
+ * @return none
+ */
+ public function doBasicUserFileTest($sender, $param) {
+ $user_file = $this->BasicAuthUserFile->Text;
+ $msg = '';
+ $valid = true;
+ if (!file_exists($user_file)) {
+ $valid = false;
+ $msg = Prado::localize('The user file is not accessible.');
+ } else if (!is_readable($user_file)) {
+ $valid = false;
+ $msg = Prado::localize('The user file is not readable by web server user.');
+ } else if (!is_writeable($user_file)) {
+ $valid = false;
+ $msg = Prado::localize('The user file is readable but not writeable by web server user.');
+ }
+ $this->BasicAuthUserFileMsg->Text = $msg;
+ if ($valid) {
+ $this->getCallbackClient()->show('basic_auth_user_file_test_ok');
+ $this->BasicAuthUserFileMsg->Display = 'None';
+ } else {
+ $this->getCallbackClient()->show('basic_auth_user_file_test_error');
+ $this->BasicAuthUserFileMsg->Display = 'Dynamic';
+ }
+ }
+
+ /**
+ * Get LDAP users and provide them to template.
+ *
+ * @param TActiveLinkButton $sender sender
+ * @param TCommandEventParameter $param event parameter object
+ * @return none
+ */
+ public function getLdapUsers($sender, $param) {
+ if ($param instanceof Prado\Web\UI\TCommandEventParameter && $param->getCommandParameter() === 'load') {
+ // reset criteria filters when modal is open
+ $this->GetUsersImportOptions->SelectedValue = self::IMPORT_OPT_ALL_USERS;
+ $this->GetUsersCriteria->SelectedValue = self::IMPORT_CRIT_USERNAME;
+ $this->GetUsersCriteriaFilter->Text = '';
+ $this->getCallbackClient()->hide('get_users_criteria');
+ $this->getCallbackClient()->hide('get_users_advanced_options');
+
+ // set role resources
+ $this->setRoles($this->GetUsersDefaultRole, WebUserRoles::NORMAL);
+
+ // set API hosts
+ $this->setAPIHosts($this->GetUsersDefaultAPIHost, HostConfig::MAIN_CATALOG_HOST);
+ }
+
+ $ldap = $this->getModule('ldap');
+ $params = $this->getLdapParams();
+ $ldap->setParams($params);
+
+ // add additional parameters
+ $this->addLdapExtraParams($params);
+
+ $filter = $ldap->getFilter($params['user_attr'], '*');
+ if (!empty($params['filter_attr']) && !empty($params['filter_val'])) {
+ $filter = $ldap->getFilter(
+ $params['filter_attr'],
+ '*' . $params['filter_val'] . '*'
+ );
+ }
+
+ $users = $ldap->findUserAttr($filter, $params['attrs']);
+ $user_list = $this->convertLdapUsers($users, $params);
+ $this->getCallbackClient()->callClientFunction('oUserSecurity.set_user_table_cb', [
+ $user_list
+ ]);
+
+ if (key_exists('count', $users)) {
+ // Success
+ $this->TestLdapGetUsersMsg->Text = '';
+ $this->TestLdapGetUsersMsg->Display = 'None';
+ $this->getCallbackClient()->show('ldap_get_users_ok');
+ } else {
+ // Error
+ $this->getCallbackClient()->show('ldap_get_users_error');
+ $this->TestLdapGetUsersMsg->Text = $ldap->getLdapError();
+ $this->TestLdapGetUsersMsg->Display = 'Dynamic';
+ }
+ }
+
+ /**
+ * Convert LDAP users from simple username list into full form.
+ * There is option to return user list in config file form or data table form.
+ *
+ * @param array $users simple user list
+ * @param array $params LDAP specific parameters (@see getLdapParams)
+ * @param boolean $config_form_result if true, sets the list in config file form
+ * @return array user list
+ */
+ private function convertLdapUsers(array $users, array $params, $config_form_result = false) {
+ $user_list = [];
+ for ($i = 0; $i < $users['count']; $i++) {
+ if (!key_exists($params['user_attr'], $users[$i])) {
+ $emsg = "User attribute '{$params['user_attr']}' doesn't exist in LDAP response.";
+ $this->getModule('logging')->log(
+ __FUNCTION__,
+ $emsg,
+ Logging::CATEGORY_EXTERNAL,
+ __FILE__,
+ __LINE__
+ );
+ continue;
+ }
+ $username = $long_name = $email = $desc = '';
+ if ($params['user_attr'] !== Ldap::DN_ATTR && $users[$i][$params['user_attr']]['count'] != 1) {
+ $emsg = "Invalid user attribute count for '{$params['user_attr']}'. Is {$users[$i][$params['user_attr']]['count']}, should be 1.";
+ $this->getModule('logging')->log(
+ __FUNCTION__,
+ $emsg,
+ Logging::CATEGORY_EXTERNAL,
+ __FILE__,
+ __LINE__
+ );
+ continue;
+
+ }
+ $username = $users[$i][$params['user_attr']];
+ if ($params['user_attr'] !== Ldap::DN_ATTR) {
+ $username = $users[$i][$params['user_attr']][0];
+ }
+
+ if (key_exists($params['long_name_attr'], $users[$i])) {
+ if ($params['long_name_attr'] === Ldap::DN_ATTR) {
+ $long_name = $users[$i][$params['long_name_attr']];
+ } else if($users[$i][$params['long_name_attr']]['count'] === 1) {
+ $long_name = $users[$i][$params['long_name_attr']][0];
+ }
+ }
+
+ if (key_exists($params['email_attr'], $users[$i])) {
+ if ($params['email_attr'] === Ldap::DN_ATTR) {
+ $email = $users[$i][$params['email_attr']];
+ } else if ($users[$i][$params['email_attr']]['count'] === 1) {
+ $email = $users[$i][$params['email_attr']][0];
+ }
+ }
+
+ if (key_exists($params['desc_attr'], $users[$i])) {
+ if ($params['desc_attr'] === Ldap::DN_ATTR) {
+ $desc = $users[$i][$params['desc_attr']];
+ } else if ($users[$i][$params['desc_attr']]['count'] === 1) {
+ $desc = $users[$i][$params['desc_attr']][0];
+ }
+ }
+
+ if ($config_form_result) {
+ $user_list[$username] = [
+ 'long_name' => $long_name,
+ 'email' => $email,
+ 'description' => $desc
+ ];
+ } else {
+ $user_list[] = [
+ 'username' => $username,
+ 'long_name' => $long_name,
+ 'email' => $email,
+ 'description' => $desc
+ ];
+ }
+ }
+ return $user_list;
+ }
+
+
+ /**
+ * Get LDAP auth specific parameters with form values.
+ *
+ * @return array array LDAP auth parameters
+ */
+ private function getLdapParams() {
+ $params = [];
+ $params['address'] = $this->LdapAuthServerAddress->Text;
+ $params['port'] = $this->LdapAuthServerPort->Text;
+ $params['ldaps'] = $this->LdapAuthServerLdaps->Checked ? 1 : 0;
+ $params['protocol_ver'] = $this->LdapAuthServerProtocolVersion->SelectedValue;
+ $params['base_dn'] = $this->LdapAuthServerBaseDn->Text;
+ if ($this->LdapAuthMethodAnonymous->Checked) {
+ $params['auth_method'] = Ldap::AUTH_METHOD_ANON;
+ } elseif ($this->LdapAuthMethodSimple->Checked) {
+ $params['auth_method'] = Ldap::AUTH_METHOD_SIMPLE;
+ }
+ $params['bind_dn'] = $this->LdapAuthMethodSimpleUsername->Text;
+ $params['bind_password'] = $this->LdapAuthMethodSimplePassword->Text;
+ $params['user_attr'] = $this->LdapAttributesUsername->Text;
+ $params['long_name_attr'] = $this->LdapAttributesLongName->Text;
+ $params['desc_attr'] = $this->LdapAttributesDescription->Text;
+ $params['email_attr'] = $this->LdapAttributesEmail->Text;
+ return $params;
+ }
+
+ /**
+ * Add to LDAP auth params additional parameters.
+ * Note, extra parameters are not set in config.
+ *
+ * @param array $params LDAP auth parameters (passing by reference)
+ * @return none
+ */
+ private function addLdapExtraParams(&$params) {
+ $params['attrs'] = [$params['user_attr']]; // user attribute is obligatory
+ if (key_exists('long_name_attr', $params) && !empty($params['long_name_attr'])) {
+ $params['attrs'][] = $params['long_name_attr'];
+ }
+ if (key_exists('email_attr', $params) && !empty($params['email_attr'])) {
+ $params['attrs'][] = $params['email_attr'];
+ }
+ if (key_exists('desc_attr', $params) && !empty($params['desc_attr'])) {
+ $params['attrs'][] = $params['desc_attr'];
+ }
+ if ($this->GetUsersImportOptions->SelectedValue == self::IMPORT_OPT_CRITERIA) {
+ $crit = intval($this->GetUsersCriteria->SelectedValue);
+ switch ($crit) {
+ case self::IMPORT_CRIT_USERNAME: $params['filter_attr'] = $params['user_attr']; break;
+ case self::IMPORT_CRIT_LONG_NAME: $params['filter_attr'] = $params['long_name_attr']; break;
+ case self::IMPORT_CRIT_DESCRIPTION: $params['filter_attr'] = $params['desc_attr']; break;
+ case self::IMPORT_CRIT_EMAIL: $params['filter_attr'] = $params['email_attr']; break;
+ }
+ $params['filter_val'] = $this->GetUsersCriteriaFilter->Text;
+ }
+ }
+
+
+ /**
+ * Prepare LDAP users to import.
+ *
+ * @param TCallback $sender sender object
+ * @param TCallbackEventParameter $param event parameter object
+ * @return array web users list to import
+ */
+ private function prepareLdapUsers($sender, $param) {
+ $ldap = $this->getModule('ldap');
+ $params = $this->getLdapParams();
+ $ldap->setParams($params);
+
+ // add additional parameters
+ $this->addLdapExtraParams($params);
+
+ $import_opt = (integer)$this->GetUsersImportOptions->SelectedValue;
+
+ $users_web = [];
+ switch ($import_opt) {
+ case self::IMPORT_OPT_ALL_USERS: {
+ $filter = $ldap->getFilter($params['user_attr'], '*');
+ $users_ldap = $ldap->findUserAttr($filter, $params['attrs']);
+ $users_web = $this->convertLdapUsers($users_ldap, $params, true);
+ break;
+ }
+ case self::IMPORT_OPT_SELECTED_USERS: {
+ if ($param instanceof Prado\Web\UI\ActiveControls\TCallbackEventParameter) {
+ $cb_param = $param->getCallbackParameter();
+ if (is_array($cb_param)) {
+ for ($i = 0; $i < count($cb_param); $i++) {
+ $val = (array)$cb_param[$i];
+ $users_web[$val['username']] = $val;
+ unset($users_web[$val['username']]['username']);
+ }
+ }
+ }
+ break;
+ }
+ case self::IMPORT_OPT_CRITERIA: {
+ if (!empty($params['filter_attr']) && !empty($params['filter_val'])) {
+ $filter = $ldap->getFilter(
+ $params['filter_attr'],
+ '*' . $params['filter_val'] . '*'
+ );
+ $users_ldap = $ldap->findUserAttr($filter, $params['attrs']);
+ $users_web = $this->convertLdapUsers($users_ldap, $params, true);
+
+ }
+ break;
+ }
+ }
+ return $users_web;
+ }
+
+ /**
+ * Test LDAP connection.
+ *
+ * @param TActiveLinkButton $sender sender object
+ * @param TCallbackEventParameter $param event object parameter
+ * @return none
+ */
+ public function testLdapConnection($sender, $param) {
+ $ldap = $this->getModule('ldap');
+ $params = $this->getLdapParams();
+ $ldap->setParams($params);
+
+ if ($ldap->adminBind()) {
+ $this->TestLdapConnectionMsg->Text = '';
+ $this->TestLdapConnectionMsg->Display = 'None';
+ $this->getCallbackClient()->show('ldap_test_connection_ok');
+ } else {
+ $this->getCallbackClient()->show('ldap_test_connection_error');
+ $this->TestLdapConnectionMsg->Text = $ldap->getLdapError();
+ $this->TestLdapConnectionMsg->Display = 'Dynamic';
+ }
+ }
+
+ /**
+ * Main method to import users.
+ * Supported are basic auth and LDAP auth user imports.
+ *
+ * @param TActiveLinkButton $sender sender object
+ * @param TCallbackEventParameter $param event object parameter
+ * @return none
+ */
+ public function importUsers($sender, $param) {
+ if (!$this->GetUsersDefaultIps->IsValid) {
+ // invalid IP restriction value
+ return;
+ }
+
+ $users_web = [];
+ if ($this->BasicAuth->Checked) {
+ $users_web = $this->prepareBasicUsers($sender, $param);
+ } elseif ($this->LdapAuth->Checked) {
+ $users_web = $this->prepareLdapUsers($sender, $param);
+ }
+
+ // Get default roles for imported users
+ $def_roles = $this->GetUsersDefaultRole->getSelectedIndices();
+ $role_list = [];
+ foreach ($def_roles as $indice) {
+ for ($i = 0; $i < $this->GetUsersDefaultRole->getItemCount(); $i++) {
+ if ($i === $indice) {
+ $role_list[] = $this->GetUsersDefaultRole->Items[$i]->Value;
+ }
+ }
+ }
+ $roles = implode(',', $role_list);
+
+ // Get default API hosts for imported users
+ $api_hosts = $this->GetUsersDefaultAPIHost->SelectedValue;
+
+ // Get default IP address restrictions for imported users
+ $ips = $this->trimIps($this->GetUsersDefaultIps->Text);
+
+ // fill missing default values
+ $add_def_user_params = function (&$user, $idx) use ($roles, $api_hosts, $ips) {
+ $user['roles'] = $roles;
+ $user['api_hosts'] = $api_hosts;
+ $user['ips'] = $ips;
+ $user['enabled'] = '1';
+ };
+ array_walk($users_web, $add_def_user_params);
+
+ $user_mod = $this->getModule('user_config');
+ $users = $user_mod->getConfig();
+
+ $users_cfg = [];
+ if ($this->GetUsersProtectOverwrite->Checked) {
+ $users_cfg = array_merge($users_web, $users);
+ } else {
+ $users_cfg = array_merge($users, $users_web);
+ }
+ $user_mod->setConfig($users_cfg);
+
+ // refresh user list
+ $this->setUserList(null, null);
+
+ // refresh role list
+ $this->setRoleList(null, null);
+
+ $this->getCallbackClient()->callClientFunction('oUserSecurity.show_user_modal', [
+ false
+ ]);
+ }
+
+ /**
+ * Get users and provide them to template.
+ *
+ * @param TActiveLinkButton $sender sender object
+ * @param TCallbackEventParameter $param event object parameter
+ * @return none
+ */
+ public function getUsers($sender, $param) {
+ if ($this->BasicAuth->Checked) {
+ $this->getBasicUsers($sender, $param);
+ } elseif ($this->LdapAuth->Checked) {
+ $this->getLdapUsers($sender, $param);
+ }
+ }
+
+ /**
+ * Save security config.
+ *
+ * @param TActiveLinkButton $sender sender object
+ * @param TCallbackEventParameter $param event object parameter
+ * @return none
+ */
+ public function saveSecurityConfig($sender, $param) {
+ $config = $this->web_config;
+ if (!key_exists('security', $config)) {
+ $config['security'] = [];
+ }
+ if ($this->GeneralDefaultNoAccess->Checked) {
+ $config['security']['def_access'] = WebConfig::DEF_ACCESS_NO_ACCESS;
+ } elseif ($this->GeneralDefaultAccess->Checked) {
+ $config['security']['def_access'] = WebConfig::DEF_ACCESS_DEFAULT_SETTINGS;
+ $config['security']['def_role'] = $this->GeneralDefaultAccessRole->SelectedValue;
+ $config['security']['def_api_host'] = $this->GeneralDefaultAccessAPIHost->SelectedValue;
+ }
+ if ($this->BasicAuth->Checked) {
+ $config['security']['auth_method'] = WebConfig::AUTH_METHOD_BASIC;
+ $config['auth_basic'] = $this->getBasicParams();
+ } else if ($this->LdapAuth->Checked) {
+ $config['security']['auth_method'] = WebConfig::AUTH_METHOD_LDAP;
+ $config['auth_ldap'] = $this->getLdapParams();
+ }
+ $ret = $this->getModule('web_config')->setConfig($config);
+ if ($ret === true) {
+ $this->getCallbackClient()->hide('auth_method_save_error');
+ $this->getCallbackClient()->show('auth_method_save_ok');
+ } else {
+ $this->getCallbackClient()->hide('auth_method_save_ok');
+ $this->getCallbackClient()->show('auth_method_save_error');
+ }
+ }
+
+ /**
+ * Determines if user management is enabled.
+ * This checking bases on selected auth method and permission to manage users.
+ *
+ * @return boolean true if managing users is enabled, otherwise false
+ */
+ private function isManageUsersAvail() {
+ $is_basic = $this->getModule('web_config')->isAuthMethodBasic();
+ $allow_manage_users = (isset($this->web_config['auth_basic']['allow_manage_users']) &&
+ $this->web_config['auth_basic']['allow_manage_users'] == 1);
+ return ($is_basic && $allow_manage_users);
+ }
+
+ /**
+ * Validate IP restriction address value.
+ *
+ * @param TActiveCustomValidator $sender sender object
+ * @param TServerValidateEventParameter $param event object parameter
+ * @return none
+ */
+ public function validateIps($sender, $param) {
+ $valid = true;
+ $val = trim($param->Value);
+ if (!empty($val)) {
+ $ips = explode(',', $val);
+ for ($i = 0; $i < count($ips); $i++) {
+ $ip = trim($ips[$i]);
+ if (!filter_var($ip, FILTER_VALIDATE_IP) && !(strpos($ip, '*') !== false && preg_match('/^[\da-f:.*]+$/i', $ip) === 1)) {
+ $valid = false;
+ break;
+ }
+ }
+ }
+ $param->IsValid = $valid;
+ }
+
+ /**
+ * Simple helper that trims IP restriction address values.
+ *
+ * @param string $ips IP restriction address values
+ * @return string trimmed addresses
+ */
+ public function trimIps($ips) {
+ $ips = trim($ips);
+ if (!empty($ips)) {
+ $ips = explode(',', $ips);
+ $ips = array_map('trim', $ips);
+ $ips = implode(',', $ips);
+ }
+ return $ips;
+ }
+}
+?>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
const USE_CACHE = true;
- protected $admin = true;
-
public $statistics = array();
public function onInit($param) {
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
const COMPONENT_TYPE = 'ComponentType';
const STATISTICS_NAME = 'StatisticsName';
- protected $admin = true;
-
public function onInit($param) {
parent::onInit($param);
if ($this->IsPostBack || $this->IsCallBack) {
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
const USE_CACHE = true;
- protected $admin = true;
-
public $storages;
public function onInit($param) {
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
const USE_CACHE = true;
- protected $admin = true;
-
public function onInit($param) {
parent::onInit($param);
if ($this->IsPostBack || $this->IsCallBack) {
+++ /dev/null
-<%@ MasterClass="Application.Web.Layouts.Main" Theme="Baculum-v2"%>
-<com:TContent ID="Main">
- <!-- Header -->
- <header class="w3-container">
- <h5>
- <b><i class="fa fa-users"></i> <%[ Users ]%></b>
- </h5>
- </header>
- <div class="w3-panel">
- <a class="w3-button w3-green" href="javascript:void(0)" id="add_user_btn"><i class="fa fa-plus"></i> <%[ Add new user ]%></a>
- </div>
-<div id="add_user" style="display: none" class="w3-margin">
- <div class="w3-third">
- <div class="w3-quarter">
- <p><%[ Username: ]%></p>
- </div>
- <div class="w3-rest w3-margin-right">
- <input id="newuser" type="text" class="w3-input w3-border" />
- </div>
- </div>
- <div class="w3-third">
- <div class="w3-quarter">
- <p><%[ Password: ]%></p>
- </div>
- <div class="w3-rest w3-margin-right">
- <input id="newpwd" type="password" class="w3-input w3-border w3-margin-right" />
- </div>
- </div>
- <div class="w3-third">
- <a href="javascript:void(0)" onclick="Users.addUser()" class="w3-button w3-green">
- <i class="fa fa-save"></i> <%[ Save ]%>
- </a>
- <a href="javascript:void(0)" onclick="Users.cancelAddUser()" class="w3-button w3-red">
- <i class="fa fa-times"></i> <%[ Close ]%>
- </a>
- </div>
-</div>
-<com:TActiveRepeater ID="UsersList" OnLoad="setUsers" ActiveControl.EnableUpdate="false">
- <prop:HeaderTemplate>
- <table id="users_list" class="w3-table w3-striped w3-hoverable w3-white w3-margin-bottom">
- <thead>
- <tr>
- <th><%[ User name ]%></th>
- <th><%[ Role ]%></th>
- <th><%[ API host ]%></th>
- <th><%[ Actions ]%></th>
- </tr>
- </thead>
- </prop:HeaderTemplate>
- <prop:ItemTemplate>
- <tr class="slide-window-element">
- <td><%=$this->Data['user']%></td>
- <td><%=$this->Data['admin'] ? Prado::localize('Administrator') : Prado::localize('Normal user')%></td>
- <td>
- <com:TPanel Visible="<%=$this->Data['admin']%>" Style="line-height: 29px">
- Main
- </com:TPanel>
- <com:TPanel Visible="<%=!$this->Data['admin']%>">
- <select rel="user_host" onchange="Users.set_host('<%=$this->Data['user']%>', this);" class="w3-select w3-border" style="width: 85%; float: left;">
- <com:TRepeater OnInit="SourceTemplateControl.initHosts">
- <prop:HeaderTemplate>
- <option value=""><%[ Select host ]%></option>
- </prop:HeaderTemplate>
- <prop:ItemTemplate>
- <option value="<%=$this->Data%>" <%=$this->SourceTemplateControl->isSelectedHost($this->Parent->Parent->Parent->Data['user'], $this->Data) ? 'selected' : ''%>><%=$this->Data%></option>
- </prop:ItemTemplate>
- </com:TRepeater>
- </select>
- <i class="fa fa-sync w3-spin" rel="user_host_img" style="visibility: hidden" />
- </com:TPanel>
- </td>
- <td>
- <a href="javascript:void(0)" class="w3-button w3-green" <%=$this->Data['admin'] ? 'style="visibility: hidden"' : ''%> onclick="Users.rmUser('<%=$this->Data['user']%>')">
- <i class="fa fa-trash-alt"></i> <%[ Remove user ]%>
- </a>
- <a href="javascript:void(0)" class="w3-button w3-green" onclick="Users.showChangePwd(this)" rel="chpwd_btn">
- <i class="fa fa-key"></i> <%[ Change password ]%>
- </a>
- <span style="display: none;" rel="chpwd">
- <div class="w3-threequarter w3-section">
- <%[ Password: ]%>
- <input type="password" onkeydown="event.keyCode == 13 ? Users.changePwd(this, '<%=$this->Data['user']%>') : (event.keyCode == 27 ? Users.cancelChangePwd(this.nextElementSibling.nextElementSibling) : '');" class="w3-input w3-border" />
- <a href="javascript:void(0)" onclick="Users.changePwd(this.previousElementSibling, '<%=$this->Data['user']%>')" class="w3-button w3-green w3-margin-top">
- <i class="fa fa-save"></i> <%[ Save ]%>
- </a>
- <a href="javascript:void(0)" onclick="Users.cancelChangePwd(this)" class="w3-button w3-red w3-margin-top">
- <i class="fa fa-times"></i> <%[ Close ]%>
- </a>
- </div>
- </span>
- </td>
- </tr>
- </prop:ItemTemplate>
- <prop:FooterTemplate>
- </table>
- </prop:FooterTemplate>
-</com:TActiveRepeater>
-<com:TCallback ID="UserAction" OnCallback="TemplateControl.userAction" ClientSide.OnComplete="Users.hide_loader();" />
-<script type="text/javascript">
- var send_user_action = function(action, param, value) {
- Users.current_action = action;
- if (!value) {
- value = '';
- }
- var user_action_callback = <%=$this->UserAction->ActiveControl->Javascript%>;
- user_action_callback.setCallbackParameter([action, param, value].join(';'));
- user_action_callback.dispatch();
- };
- Users.txt = {
- enter_login: '<%[ Please enter login. ]%>',
- invalid_login: '<%[ Invalid login value. Login may contain a-z A-Z 0-9 characters. ]%>',
- invalid_pwd: '<%[ Password must be longer than 4 chars. ]%>'
- };
- Users.action_callback = send_user_action;
- Users.validators = { user_pattern: new RegExp('^<%=BasicUserConfig::USER_PATTERN%>$') };
- Users.init();
-</script>
-
- <div id="debug_confirm" class="w3-modal" style="display: none">
- <div class="w3-modal-content w3-card-4 w3-animate-zoom w3-padding" style="max-width: 600px">
- <span onclick="document.getElementById('debug_confirm').style.display='none'" class="w3-button w3-xlarge w3-hover-red w3-display-topright">×</span>
- <h4><%[ Enable debug ]%></h4>
- <div class="w3-center">
- <button class="w3-button w3-red" type="button" onclick="document.getElementById('debug_confirm').style.display='none'"><i class="fa fa-times"></i> <%[ No ]%></button>
- <button class="w3-button w3-green" type="button" onclick="document.getElementById('debug_confirm').style.display='none'"><i class="fa fa-check"></i> <%[ Yes ]%></button>
- </div>
- </div>
- </div>
-</com:TContent>
+++ /dev/null
-<?php
-/*
- * Bacula(R) - The Network Backup Solution
- * Baculum - Bacula web interface
- *
- * Copyright (C) 2013-2020 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('System.Web.UI.ActiveControls.TActiveLinkButton');
-Prado::using('System.Web.UI.ActiveControls.TActiveRepeater');
-Prado::using('System.Web.UI.ActiveControls.TCallback');
-Prado::using('Application.Web.Class.BaculumWebPage');
-
-/**
- * Users page.
- *
- * @author Marcin Haba <marcin.haba@bacula.pl>
- * @category Page
- * @package Baculum Web
- */
-class Users extends BaculumWebPage {
-
- protected $admin = true;
-
- public function setUsers() {
- $all_users = $this->getModule('basic_webuser')->getAllUsers();
- $users = array_keys($all_users);
- sort($users);
- $users_list = array();
- $users_feature = (array_key_exists('users', $this->web_config) && is_array($this->web_config['users']));
- for ($i = 0; $i < count($users); $i++) {
- $host = null;
- if ($users_feature && array_key_exists($users[$i], $this->web_config['users'])) {
- $host = $this->web_config['users'][$users[$i]];
- }
- $users_list[] = array(
- 'user' => $users[$i],
- 'host' => $host,
- 'admin' => ($users[$i] === $this->web_config['baculum']['login'])
- );
- }
- $this->UsersList->dataSource = $users_list;
- $this->UsersList->dataBind();
- }
-
- public function initHosts($sender, $param) {
- $api_hosts = array_keys($this->getModule('host_config')->getConfig());
- $sender->DataSource = array_combine($api_hosts, $api_hosts);
- $sender->dataBind();
- }
-
- public function userAction($sender, $param) {
- $this->UsersList->ActiveControl->EnableUpdate = true;
- list($action, $user, $value) = explode(';', $param->CallbackParameter, 3);
- switch($action) {
- case 'newuser':
- case 'chpwd': {
- $this->getModule('basic_webuser')->setUsersConfig($user, $value);
- if ($user === $this->web_config['baculum']['login']) {
- // if admin password changed then try to auto-login by async request
- $http_protocol = isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ? 'https' : 'http';
- $this->switchToUser($user, $value);
- exit();
- } else {
- // if normal user's password changed then update users grid
- $this->setUsers();
- }
- }
- break;
- case 'rmuser': {
- if ($user != $_SERVER['PHP_AUTH_USER']) {
- $this->getModule('basic_webuser')->removeUser($user);
- if (array_key_exists('users', $this->web_config) && array_key_exists($user, $this->web_config['users'])) {
- unset($this->web_config['users'][$user]);
- }
- $this->getModule('web_config')->setConfig($this->web_config);
- $this->setUsers();
- }
- break;
- }
- case 'set_host': {
- if (empty($value) && array_key_exists($user, $this->web_config['users'])) {
- unset($this->web_config['users'][$user]);
- } else {
- $this->web_config['users'][$user] = $value;
- }
- $this->getModule('web_config')->setConfig($this->web_config);
- break;
- }
- }
- }
-
- public function isSelectedHost($user, $host) {
- if (!key_exists('users', $this->web_config)) {
- return;
- }
- return (key_exists($user, $this->web_config['users']) && $this->web_config['users'][$user] === $host);
- }
-}
-?>
const USE_CACHE = true;
- protected $admin = true;
-
public $volumes;
public function onInit($param) {
if ($this->IsPostBack || $this->IsCallBack) {
return;
}
- if ($_SESSION['admin']) {
- $this->setVolumes();
- }
+ $this->setVolumes();
}
public function setVolumes() {
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
const MEDIAID = 'MediaId';
const VOLUME_NAME = 'VolumeName';
- protected $admin = true;
-
public $jobs_on_volume;
private $volstatus_by_dir = array('Recycle', 'Purged', 'Error', 'Busy');
NavigationStyle.CssClass="navigation"
UseDefaultLayout="false"
ShowSideBar="false"
+ OnPreviousButtonClick="previousStep"
+ OnNextButtonClick="nextStep"
OnCancelButtonClick="wizardStop"
OnCompleteButtonClick="wizardCompleted"
>
<prop:HeaderTemplate>
- <div class="w3-quarter w3-padding-16">
+ <div class="<%=$this->SourceTemplateControl->first_run ? 'w3-quarter' : 'w3-third'%> w3-padding-16">
<div class="step w3-padding w3-text-white w3-margin-right <%=($this->Parent->ActiveStepIndex === 0 ? 'w3-light-green' : 'w3-green')%>">
<div class="w3-left"><i class="fa fa-language w3-xxxlarge"></i></div>
<div class="w3-clear"></div>
<h4><com:TTranslate Text="Language" /></h4>
</div>
</div>
- <div class="w3-quarter w3-padding-16">
+ <div class="<%=$this->SourceTemplateControl->first_run ? 'w3-quarter' : 'w3-third'%> w3-padding-16">
<div class="step w3-padding w3-text-white w3-margin-right <%=($this->Parent->ActiveStepIndex === 1 ? 'w3-light-green' : 'w3-green')%>">
<div class="w3-left"><i class="fa fa-plus-square w3-xxxlarge"></i></div>
<div class="w3-clear"></div>
<h4><com:TTranslate Text="Add APIs" /></h4>
</div>
</div>
- <div class="w3-quarter w3-padding-16">
+ <div class="<%=$this->SourceTemplateControl->first_run ? 'w3-quarter' : 'w3-third'%> w3-padding-16"<%=!$this->SourceTemplateControl->first_run ? ' style="display: none"' : ''%>>
<div class="step w3-padding w3-text-white w3-margin-right <%=($this->Parent->ActiveStepIndex === 2 ? 'w3-light-green' : 'w3-green')%>">
<div class="w3-left"><i class="fa fa-key w3-xxxlarge"></i></div>
<div class="w3-clear"></div>
<h4><com:TTranslate Text="Authentication" /></h4>
</div>
</div>
- <div class="w3-quarter w3-padding-16">
+ <div class="<%=$this->SourceTemplateControl->first_run ? 'w3-quarter' : 'w3-third'%> w3-padding-16">
<div class="step w3-padding w3-text-white w3-margin-right <%=($this->Parent->ActiveStepIndex === 3 ? 'w3-light-green' : 'w3-green')%>">
<div class="w3-left"><i class="fa fa-check-square w3-xxxlarge"></i></div>
<div class="w3-clear"></div>
</com:TWizardStep>
<com:TWizardStep ID="Step4" Title="<%[ Step 4 - Finish ]%>" StepType="Finish">
<fieldset>
- <legend><%[ Authorization to Baculum API ]%></legend>
+ <legend><%[ Access to Baculum API ]%></legend>
<div class="w3-container w3-section">
<div class="w3-third"><%[ Protocol: ]%></div>
<div class="w3-third bold"><%=$this->AddNewHost->APIProtocol->SelectedValue%></div>
</div>
</div>
</fieldset>
- <fieldset>
- <legend><%[ Authorization to Baculum Web ]%></legend>
+ <fieldset<%=!$this->SourceTemplateControl->first_run ? ' style="display: none"' : ''%>>
+ <legend><%[ Access to Baculum Web ]%></legend>
<div class="w3-container w3-section">
<div class="w3-third"><%[ Administration login: ]%></div>
<div class="w3-third bold"><%=$this->WebLogin->Text%></div>
*/
class WebConfigWizard extends BaculumWebPage
{
-
- protected $admin = false;
-
public $first_run;
public $host_config;
public function onInit($param) {
parent::onInit($param);
- $this->Lang->SelectedValue = $this->getLanguage();
$this->host_config = $this->getModule('host_config')->getConfig();
$this->first_run = (count($this->host_config) == 0 || !key_exists(HostConfig::MAIN_CATALOG_HOST, $this->host_config));
Logging::$debug_enabled = Logging::$debug_enabled ?: $this->first_run;
- if($this->first_run === false && !$_SESSION['admin']) {
- parent::accessDenied();
- }
}
public function onLoad($param) {
$this->AddNewHost->APIOAuth2RedirectURI->Text = $this->host_config[$host]['redirect_uri'];
$this->AddNewHost->APIOAuth2Scope->Text = $this->host_config[$host]['scope'];
}
- $this->WebLogin->Text = $this->web_config['baculum']['login'];
} else {
$this->AddNewHost->APIProtocol->SelectedValue = 'http';
$this->AddNewHost->APIAddress->Text = 'localhost';
}
}
- public function NextStep($sender, $param) {
+ public function onPreRender($param) {
+ parent::onPreRender($param);
+ if($this->IsPostBack || $this->IsCallBack) {
+ return;
+ }
+ $this->Lang->SelectedValue = $this->getModule('web_config')->getLanguage();
+ }
+
+ public function nextStep($sender, $param) {
+ if ($param->CurrentStepIndex === 1 && !$this->first_run) {
+ $this->InstallWizard->ActiveStepIndex = 3;
+ }
}
- public function PreviousStep($sender, $param) {
+ public function previousStep($sender, $param) {
}
public function wizardStop($sender, $param) {
$host_config[$host] = $cfg_host;
$ret = $this->getModule('host_config')->setConfig($host_config);
if($ret === true) {
- $cfg_web = array('baculum' => array(), 'users' => array());
- if (count($this->web_config) > 0) {
- $cfg_web = $this->web_config;
- }
- $cfg_web['baculum']['login'] = $this->WebLogin->Text;
- $cfg_web['baculum']['debug'] = 0;
- $cfg_web['baculum']['lang'] = $this->Lang->SelectedValue;
- if (array_key_exists('users', $cfg_web) && array_key_exists($this->WebLogin->Text, $cfg_web)) {
- // Admin shoudn't be added to users section, only regular users
- unset($cfg_web['users'][$this->WebLogin->Text]);
- }
- $ret = $this->getModule('web_config')->setConfig($cfg_web);
- if($ret && $this->getModule('basic_webuser')->isUsersConfig() === true) {
- $previous_user = $this->first_run ? parent::DEFAULT_AUTH_USER : $this->web_config['baculum']['login'];
- $this->getModule('basic_webuser')->setUsersConfig(
- $cfg_web['baculum']['login'],
+ // complete new Baculum main settings
+ $web_config = $this->getModule('web_config');
+ $ret = $web_config->setDefConfigOpts([
+ 'baculum' => [
+ 'lang' => $this->Lang->SelectedValue
+ ]
+ ]);
+
+ $basic_webuser = $this->getModule('basic_webuser');
+ if($this->first_run && $ret && $web_config->isAuthMethodBasic()) {
+ // set new user on first wizard run
+ $previous_user = parent::DEFAULT_AUTH_USER;
+ $ret = $basic_webuser->setUsersConfig(
+ $this->WebLogin->Text,
$this->WebPassword->Text,
false,
$previous_user
);
+ } else {
+ $emsg = 'Error while saving basic user config.';
+ $this->getModule('logging')->log(
+ __FUNCTION__,
+ $emsg,
+ Logging::CATEGORY_APPLICATION,
+ __FILE__,
+ __LINE__
+ );
}
+
+ if ($this->first_run && $ret) {
+ // create new Baculum user on first wizard run
+ $user_config = $this->getModule('user_config');
+ $new_user_prop = $user_config->getUserConfigProps([
+ 'username' => $this->WebLogin->Text,
+ 'roles' => WebUserRoles::ADMIN,
+ 'enabled' => 1
+ ]);
+ $ret = $user_config->setUserConfig($this->WebLogin->Text, $new_user_prop);
+ if (!$ret) {
+ $emsg = 'Error while saving user config.';
+ $this->getModule('logging')->log(
+ __FUNCTION__,
+ $emsg,
+ Logging::CATEGORY_APPLICATION,
+ __FILE__,
+ __LINE__
+ );
+ }
+
+ // Login user with new parameters
+ $this->getModule('auth')->login($this->WebLogin->Text, $this->WebPassword->Text);
+ }
+
+ // Go to default user page
$this->goToDefaultPage();
+ } else {
+ $emsg = 'Error while saving auth host config.';
+ $this->getModule('logging')->log(
+ __FUNCTION__,
+ $emsg,
+ Logging::CATEGORY_APPLICATION,
+ __FILE__,
+ __LINE__
+ );
}
}
}
public function setLang($sender, $param) {
- $_SESSION['language'] = $sender->SelectedValue;
+ $this->getModule('web_config')->setLanguage($sender->SelectedValue);
}
public function validateAdministratorPassword($sender, $param) {
<!-- config modules -->
<module id="web_config" class="Application.Web.Class.WebConfig" />
<module id="host_config" class="Application.Web.Class.HostConfig" />
+ <module id="user_config" class="Application.Web.Class.WebUserConfig" />
+ <module id="role_config" class="Application.Web.Class.WebRoleConfig" />
<!-- data modules -->
<module id="api" class="Application.Web.Class.BaculumAPIClient" />
<module id="data_desc" class="Application.Web.Class.DataDescription" />
<module id="log_parser" class="Application.Web.Class.LogParser" />
<!-- auth modules -->
<module id="basic_webuser" class="Application.Web.Class.BasicWebUserConfig" />
+ <module id="page_category" class="Application.Web.Class.PageCategory" />
+ <module id="user_role" class="Application.Web.Class.WebUserRoles" />
+ <module id="auth" class="System.Security.TAuthManager" UserManager="users" LoginPage="LoginPage" />
+ <module id="users" class="Application.Web.Class.WebUserManager" UserClass="Application.Web.Class.WebUser" />
</modules>
</configuration>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
public $config;
public function loadConfig($sender, $param) {
- if(!$_SESSION['admin']) {
- return;
- }
$this->config = $this->getModule('host_config')->getConfig();
$hosts = array_keys($this->config);
$this->RepeaterHosts->DataSource = $hosts;
}
public function removeHost($sender, $param) {
- if(!$_SESSION['admin']) {
- return;
- }
$host = $param->getCommandParameter();
if (!empty($host)) {
$host_config = $this->getModule('host_config');
Text="Field required."
Enabled="<%=$this->getRequired() && $this->getShow()%>"
/>
- <p class="w3-row w3-padding"><%[ Use Ctrl + Mouse Click to change selection ]%></p>
+ <p class="w3-row w3-padding"><%[ Use CTRL + left-click to multiple item selection ]%></p>
</div>
</div>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2020 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
*/
class MainSideBar extends Portlets {
+ /**
+ * Reload URL is used to refresh page after logout with Basic auth.
+ */
+ public $reload_url = '';
+
+ public function onInit($param) {
+ parent::onInit($param);
+ if ($this->getModule('web_config')->isAuthMethodBasic()) {
+ $fake_pwd = $this->getModule('crypto')->getRandomString();
+ $user = $_SERVER['PHP_AUTH_USER'] . '1'; // must be different than currently logged in Basic user
+
+ // do a login try with different user and password to logout current user
+ $this->reload_url = $this->getPage()->getFullLoginUrl($user, $fake_pwd);
+ }
+ }
+
public function logout($sender, $param) {
- $fake_pwd = $this->getModule('misc')->getRandomString();
- $this->getPage()->switchToUser($_SERVER['PHP_AUTH_USER'], $fake_pwd);
- exit();
+ $this->getModule('auth')->logout();
+ if ($this->getModule('web_config')->isAuthMethodBasic()) {
+ /**
+ * This status code 401 is necessary to stop comming AJAX requests
+ * and to bring the login prompt on.
+ */
+ $this->Response->setStatusCode(401);
+ } else {
+ $this->getPage()->goToDefaultPage();
+ }
}
}
?>
<img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/avatar2.png" class="w3-circle w3-margin-right" style="width:46px" />
</div>
<div class="w3-col s8 w3-bar">
- <span><%[ Welcome ]%>, <strong><%=$_SERVER['PHP_AUTH_USER']%></strong></span><br>
+ <span><%[ Welcome ]%>, <strong><%=$this->User->getUsername()%></strong></span><br>
+ <script>var main_side_bar_reload_url = '<%=$this->reload_url%>';</script>
<com:TActiveLinkButton
ID="Logout"
- OnCommand="logout"
+ OnClick="logout"
CssClass="w3-bar-item w3-button"
ToolTip="<%[ Logout ]%>"
>
+ <prop:ClientSide.OnComplete>
+ if (!window.chrome || window.navigator.webdriver) {
+ window.location.href = main_side_bar_reload_url;
+ } else if (window.chrome) {
+ // For chrome this reload is required to show login Basic auth prompt
+ window.location.reload();
+ }
+ </prop:ClientSide.OnComplete>
<i class="fa fa-power-off"></i>
</com:TActiveLinkButton>
- <a href="<%=$this->Service->constructUrl('Console')%>" class="w3-bar-item w3-button" title="<%[ Console ]%>"><i class="fa fa-terminal"></i></a>
- <a href="<%=$this->Service->constructUrl('ApplicationSettings')%>" class="w3-bar-item w3-button<%=!$_SESSION['admin'] ? ' hide' : ''%>" title="<%[ Application settings ]%>"><i class="fa fa-cog"></i></a>
+ <a href="<%=$this->Service->constructUrl('Console')%>" class="w3-bar-item w3-button<%=$this->getModule('users')->isPageAllowed($this->User, 'Console') ? '' : ' hide'%>" title="<%[ Console ]%>"><i class="fa fa-terminal"></i></a>
+ <a href="<%=$this->Service->constructUrl('ApplicationSettings')%>" class="w3-bar-item w3-button<%=$this->getModule('users')->isPageAllowed($this->User, 'ApplicationSettings') ? '' : ' hide'%>" title="<%[ Application settings ]%>"><i class="fa fa-cog"></i></a>
</div>
</div>
<hr />
- <div class="w3-container">
+ <div class="w3-container w3-black">
<h5>Baculum Menu</h5>
</div>
<div class="w3-bar-block" style="margin-bottom: 45px;">
- <a href="#" class="w3-bar-item w3-button w3-padding-16 w3-black w3-hover-black" onclick="W3SideBar.close(); return false;" title="close menu"> <%[ Close Menu ]%> <i class="fa fa-window-close fa-fw w3-right w3-xlarge"></i></a>
- <a href="<%=$this->Service->constructUrl('Dashboard')%>" class="w3-bar-item w3-button w3-padding<%=$this->Service->getRequestedPagePath() == 'Dashboard' ? ' w3-blue': ''%>"><i class="fa fa-tachometer-alt fa-fw"></i> <%[ Dashboard ]%></a>
- <a href="<%=$this->Service->constructUrl('JobHistoryList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('JobHistoryList', 'JobHistoryView')) ? ' w3-blue': ''%>"><i class="fa fa-history fa-fw"></i> <%[ Job history ]%></a>
- <a href="<%=$this->Service->constructUrl('JobList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('JobList', 'JobView')) ? ' w3-blue': ''%>"><i class="fa fa-tasks fa-fw"></i> <%[ Jobs ]%></a>
- <a href="<%=$this->Service->constructUrl('ClientList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('ClientList', 'ClientView')) ? ' w3-blue': ''%>"><i class="fa fa-desktop fa-fw"></i> <%[ Clients ]%></a>
- <a href="<%=$this->Service->constructUrl('StorageList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('StorageList', 'StorageView', 'DeviceView')) ? ' w3-blue': ''%><%=!$_SESSION['admin'] ? ' hide' : ''%>"><i class="fa fa-database fa-fw"></i> <%[ Storages ]%></a>
- <a href="<%=$this->Service->constructUrl('PoolList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('PoolList', 'PoolView')) ? ' w3-blue': ''%><%=!$_SESSION['admin'] ? ' hide' : ''%>"><i class="fa fa-tape fa-fw"></i> <%[ Pools ]%></a>
- <a href="<%=$this->Service->constructUrl('VolumeList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('VolumeList', 'VolumeView')) ? ' w3-blue': ''%><%=!$_SESSION['admin'] ? ' hide' : ''%>"><i class="fa fa-hdd fa-fw"></i> <%[ Volumes ]%></a>
- <a href="<%=$this->Service->constructUrl('FileSetList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('FileSetList', 'FileSetView')) ? ' w3-blue': ''%><%=!$_SESSION['admin'] ? ' hide' : ''%>"><i class="fa fa-copy fa-fw"></i> <%[ FileSets ]%></a>
- <a href="<%=$this->Service->constructUrl('ScheduleList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('ScheduleList', 'ScheduleView')) ? ' w3-blue': ''%><%=!$_SESSION['admin'] ? ' hide' : ''%>"><i class="fa fa-clock fa-fw"></i> <%[ Schedules ]%></a>
- <a href="<%=$this->Service->constructUrl('ConfigureHosts')%>" class="w3-bar-item w3-button w3-padding<%=$this->Service->getRequestedPagePath() == 'ConfigureHosts' ? ' w3-blue': ''%><%=!$_SESSION['admin'] ? ' hide' : ''%>"><i class="fa fa-cog fa-fw"></i> <%[ Configure ]%></a>
- <a href="<%=$this->Service->constructUrl('RestoreWizard')%>" class="w3-bar-item w3-button w3-padding<%=$this->Service->getRequestedPagePath() == 'RestoreWizard' ? ' w3-blue': ''%>"><i class="fa fa-reply fa-fw"></i> <%[ Restore wizard ]%></a>
- <a href="<%=$this->Service->constructUrl('Graphs')%>" class="w3-bar-item w3-button w3-padding<%=$this->Service->getRequestedPagePath() == 'Graphs' ? ' w3-blue': ''%>"><i class="fa fa-chart-pie fa-fw"></i> <%[ Graphs ]%></a>
- <a href="<%=$this->Service->constructUrl('StatisticsList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('StatisticsList', 'StatisticsView')) ? ' w3-blue': ''%><%=!$_SESSION['admin'] ? ' hide' : ''%>"><i class="fas fa-chart-line fa-fw"></i> <%[ Statistics ]%></a>
- <a href="<%=$this->Service->constructUrl('WebConfigWizard')%>" class="w3-bar-item w3-button w3-padding<%=$this->Service->getRequestedPagePath() == 'WebConfigWizard' ? ' w3-blue': ''%><%=!$_SESSION['admin'] ? ' hide' : ''%>"><i class="fa fa-wrench fa-fw"></i> <%[ Settings ]%></a>
- <a href="<%=$this->Service->constructUrl('Users')%>" class="w3-bar-item w3-button w3-padding<%=$this->Service->getRequestedPagePath() == 'Users' ? ' w3-blue': ''%><%=!$_SESSION['admin'] ? ' hide' : ''%>"><i class="fa fa-users fa-fw"></i> <%[ Users ]%></a>
+ <!--a href="#" class="w3-bar-item w3-button w3-padding-16 w3-black w3-hover-black w3-hide-large" onclick="W3SideBar.close(); return false;" title="close menu"> <%[ Close Menu ]%> <i class="fa fa-window-close fa-fw w3-right w3-xlarge"></i></a-->
+ <div class="w3-black" style="height: 3px"></div>
+ <a href="<%=$this->Service->constructUrl('Dashboard')%>" class="w3-bar-item w3-button w3-padding<%=$this->Service->getRequestedPagePath() == 'Dashboard' ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'Dashboard') ? '' : ' hide'%>"><i class="fa fa-tachometer-alt fa-fw"></i> <%[ Dashboard ]%></a>
+ <a href="<%=$this->Service->constructUrl('JobHistoryList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('JobHistoryList', 'JobHistoryView')) ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'JobHistoryList') ? '' : ' hide'%>"><i class="fa fa-history fa-fw"></i> <%[ Job history ]%></a>
+ <a href="<%=$this->Service->constructUrl('JobList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('JobList', 'JobView')) ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'JobList') ? '' : ' hide'%>"><i class="fa fa-tasks fa-fw"></i> <%[ Jobs ]%></a>
+ <a href="<%=$this->Service->constructUrl('ClientList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('ClientList', 'ClientView')) ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'ClientList') ? '' : ' hide'%>"><i class="fa fa-desktop fa-fw"></i> <%[ Clients ]%></a>
+ <a href="<%=$this->Service->constructUrl('StorageList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('StorageList', 'StorageView', 'DeviceView')) ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'StorageList') ? '' : ' hide'%>"><i class="fa fa-database fa-fw"></i> <%[ Storages ]%></a>
+ <a href="<%=$this->Service->constructUrl('PoolList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('PoolList', 'PoolView')) ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'PoolList') ? '' : ' hide'%>"><i class="fa fa-tape fa-fw"></i> <%[ Pools ]%></a>
+ <a href="<%=$this->Service->constructUrl('VolumeList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('VolumeList', 'VolumeView')) ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'VolumeList') ? '' : ' hide'%>"><i class="fa fa-hdd fa-fw"></i> <%[ Volumes ]%></a>
+ <a href="<%=$this->Service->constructUrl('FileSetList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('FileSetList', 'FileSetView')) ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'FileSetList') ? '' : ' hide'%>"><i class="fa fa-copy fa-fw"></i> <%[ FileSets ]%></a>
+ <a href="<%=$this->Service->constructUrl('ScheduleList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('ScheduleList', 'ScheduleView')) ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'ScheduleList') ? '' : ' hide'%>"><i class="fa fa-clock fa-fw"></i> <%[ Schedules ]%></a>
+ <a href="<%=$this->Service->constructUrl('ConfigureHosts')%>" class="w3-bar-item w3-button w3-padding<%=$this->Service->getRequestedPagePath() == 'ConfigureHosts' ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'ConfigureHosts') ? '' : ' hide'%>"><i class="fa fa-cog fa-fw"></i> <%[ Configure ]%></a>
+ <a href="<%=$this->Service->constructUrl('RestoreWizard')%>" class="w3-bar-item w3-button w3-padding<%=$this->Service->getRequestedPagePath() == 'RestoreWizard' ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'RestoreWizard') ? '' : ' hide'%>"><i class="fa fa-reply fa-fw"></i> <%[ Restore wizard ]%></a>
+ <a href="<%=$this->Service->constructUrl('Graphs')%>" class="w3-bar-item w3-button w3-padding<%=$this->Service->getRequestedPagePath() == 'Graphs' ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'Graphs') ? '' : ' hide'%>"><i class="fa fa-chart-pie fa-fw"></i> <%[ Graphs ]%></a>
+ <a href="<%=$this->Service->constructUrl('StatisticsList')%>" class="w3-bar-item w3-button w3-padding<%=in_array($this->Service->getRequestedPagePath(), array('StatisticsList', 'StatisticsView')) ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'StatisticsList') ? '' : ' hide'%>"><i class="fas fa-chart-line fa-fw"></i> <%[ Statistics ]%></a>
+ <a href="<%=$this->Service->constructUrl('WebConfigWizard')%>" class="w3-bar-item w3-button w3-padding<%=$this->Service->getRequestedPagePath() == 'WebConfigWizard' ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'WebConfigWizard') ? '' : ' hide'%>"><i class="fa fa-wrench fa-fw"></i> <%[ Settings ]%></a>
+ <a href="<%=$this->Service->constructUrl('Security')%>" class="w3-bar-item w3-button w3-padding<%=$this->Service->getRequestedPagePath() == 'Security' ? ' w3-blue': ''%><%=$this->getModule('users')->isPageAllowed($this->User, 'Security') ? '' : ' hide'%>"><i class="fa fa-lock fa-fw"></i> <%[ Security ]%></a>
</div>
</nav>
$priority = $jobdata->priorjobid;
}
$this->Priority->Text = $priority;
+ $this->Estimate->Enabled = false;
}
public function selectJobValues($sender, $param) {
+++ /dev/null
-<?php
-/*
- * Bacula(R) - The Network Backup Solution
- * Baculum - Bacula web interface
- *
- * Copyright (C) 2013-2019 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('System.Web.UI.ActiveControls.TActiveRepeater');
-Prado::using('Application.Web.Portlets.Portlets');
-
-/**
- * Users control.
- *
- * @author Marcin Haba <marcin.haba@bacula.pl>
- * @category Control
- * @package Baculum Web
- */
-class Users extends Portlets {
-
- public $web_config;
-
- public function __construct() {
- parent::__construct();
- $this->web_config = $this->getModule('web_config')->getConfig();
- }
-
- public function setUsers() {
- if(!$_SESSION['admin']) {
- return;
- }
- $all_users = $this->getModule('basic_webuser')->getAllUsers();
- $users = array_keys($all_users);
- sort($users);
- $users_list = array();
- $users_feature = (array_key_exists('users', $this->web_config) && is_array($this->web_config['users']));
- for ($i = 0; $i < count($users); $i++) {
- $host = null;
- if ($users_feature && array_key_exists($users[$i], $this->web_config['users'])) {
- $host = $this->web_config['users'][$users[$i]];
- }
- $users_list[] = array(
- 'user' => $users[$i],
- 'host' => $host,
- 'admin' => ($users[$i] === $this->web_config['baculum']['login'])
- );
- }
- $this->UsersList->dataSource = $users_list;
- $this->UsersList->dataBind();
- }
-
- public function initHosts($sender, $param) {
- $api_hosts = array_keys($this->getModule('host_config')->getConfig());
- $sender->DataSource = array_combine($api_hosts, $api_hosts);
- $sender->dataBind();
- }
-
- public function userAction($sender, $param) {
- if(!$_SESSION['admin']) {
- return;
- }
- $this->UsersList->ActiveControl->EnableUpdate = true;
- list($action, $user, $value) = explode(';', $param->CallbackParameter, 3);
- switch($action) {
- case 'newuser':
- case 'chpwd': {
- $admin = false;
- $valid = true;
- if ($user === $this->web_config['baculum']['login']) {
- $this->web_config['baculum']['password'] = $value;
- $valid = $this->getModule('web_config')->setConfig($this->web_config);
- $admin = true;
- }
- if ($valid === true) {
- $this->getModule('basic_webuser')->setUsersConfig($user, $value);
- }
- if ($admin === true) {
- // if admin password changed then try to auto-login by async request
- $http_protocol = isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ? 'https' : 'http';
- $this->switchToUser($user, $value);
- exit();
- } else {
- // if normal user's password changed then update users grid
- $this->setUsers();
- }
- }
- break;
- case 'rmuser': {
- if ($user != $_SERVER['PHP_AUTH_USER']) {
- $this->getModule('basic_webuser')->removeUser($user);
- if (array_key_exists('users', $this->web_config) && array_key_exists($user, $this->web_config['users'])) {
- unset($this->web_config['users'][$user]);
- }
- $this->getModule('web_config')->setConfig($this->web_config);
- $this->setUsers();
- }
- break;
- }
- case 'set_host': {
- if (empty($value) && array_key_exists($user, $this->web_config['users'])) {
- unset($this->web_config['users'][$user]);
- } else {
- $this->web_config['users'][$user] = $value;
- }
- $this->getModule('web_config')->setConfig($this->web_config);
- break;
- }
- }
- }
-}
+++ /dev/null
-<a class="big" href="javascript:void(0)" id="add_user_btn"><img src="/themes/Baculum-v1/add.png" alt="Add"><%[ Add new user ]%></a>
-<div id="add_user" style="display: none">
- <p><%[ Username: ]%><input id="newuser" type="text" /><%[ Password: ]%><input id="newpwd" type="password" />
- <a href="javascript:void(0)" onclick="Users.addUser()">
- <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/icon_ok.png" alt="<%[ Save ]%>" title="<%[ Save ]%>"/>
- </a>
- <a href="javascript:void(0)" onclick="Users.cancelAddUser()">
- <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/icon_err.png" alt="<%[ Close ]%>" title="<%[ Close ]%>" />
- </a></p>
-</div>
-<com:TActiveRepeater ID="UsersList" OnLoad="setUsers" ActiveControl.EnableUpdate="false">
- <prop:HeaderTemplate>
- <table id="users_list" class="window-section-detail-smallrow">
- <tr>
- <th><%[ User name ]%></th>
- <th><%[ Role ]%></th>
- <th><%[ API host ]%></th>
- <th><%[ Actions ]%></th>
- </tr>
- </prop:HeaderTemplate>
- <prop:ItemTemplate>
- <tr class="slide-window-element">
- <td><%=$this->Data['user']%></td>
- <td><%=$this->Data['admin'] ? Prado::localize('Administrator') : Prado::localize('Normal user')%></td>
- <td>
- <com:TPanel Visible="<%=$this->Data['admin']%>" Style="line-height: 29px">
- Main
- </com:TPanel>
- <com:TPanel Visible="<%=!$this->Data['admin']%>">
- <select rel="user_host" onchange="Users.set_host('<%=$this->Data['user']%>', this);">
- <com:TRepeater OnInit="SourceTemplateControl.initHosts">
- <prop:HeaderTemplate>
- <option value=""><%[ Select host ]%></option>
- </prop:HeaderTemplate>
- <prop:ItemTemplate>
- <option value="<%=$this->Data%>" <%=(array_key_exists('users', $this->SourceTemplateControl->web_config) && array_key_exists($this->Parent->Parent->Parent->Data['user'], $this->SourceTemplateControl->web_config['users']) && $this->SourceTemplateControl->web_config['users'][$this->Parent->Parent->Parent->Data['user']] === $this->Data) ? 'selected' : ''%>><%=$this->Data%></option>
- </prop:ItemTemplate>
- </com:TRepeater>
- </select>
- <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/ajax-loader-arrows.gif" rel="user_host_img" alt="" style="visibility: hidden" />
- </com:TPanel>
- </td>
- <td>
- <a href="javascript:void(0)" <%=$this->Data['admin'] ? 'style="visibility: hidden"' : ''%> onclick="Users.rmUser('<%=$this->Data['user']%>')"><img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/user-del.png"> <%[ Remove user ]%></a>
- <a href="javascript:void(0)" onclick="Users.showChangePwd(this)" rel="chpwd_btn">
- <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/key.png" alt="" />
- <%[ Change password ]%>
- </a>
- <span style="display: none;" rel="chpwd">
- <input type="password" onkeydown="event.keyCode == 13 ? Users.changePwd(this, '<%=$this->Data['user']%>') : (event.keyCode == 27 ? Users.cancelChangePwd(this.nextElementSibling.nextElementSibling) : '');" />
- <a href="javascript:void(0)" onclick="Users.changePwd(this.prevousElementSibling, '<%=$this->Data['user']%>')">
- <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/icon_ok.png" alt="<%[ Save ]%>" title="<%[ Save ]%>"/>
- </a>
- <a href="javascript:void(0)" onclick="Users.cancelChangePwd(this)">
- <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/icon_err.png" alt="<%[ Close ]%>" title="<%[ Close ]%>" />
- </a>
- </span>
- </td>
- </tr>
- </prop:ItemTemplate>
- <prop:FooterTemplate>
- </table>
- </prop:FooterTemplate>
-</com:TActiveRepeater>
-<com:TCallback ID="UserAction" OnCallback="TemplateControl.userAction" ClientSide.OnComplete="Users.hide_loader();" />
-<script type="text/javascript">
- var send_user_action = function(action, param, value) {
- Users.current_action = action;
- if (!value) {
- value = '';
- }
- var user_action_callback = <%=$this->UserAction->ActiveControl->Javascript%>;
- user_action_callback.setCallbackParameter([action, param, value].join(';'));
- user_action_callback.dispatch();
- };
- Users.txt = {
- enter_login: '<%[ Please enter login. ]%>',
- invalid_login: '<%[ Invalid login value. Login may contain a-z A-Z 0-9 characters. ]%>',
- invalid_pwd: '<%[ Password must be longer than 4 chars. ]%>'
- };
- Users.action_callback = send_user_action;
- Users.validators = { user_pattern: new RegExp('^<%=BasicUserConfig::USER_PATTERN%>$') };
- Users.init();
-</script>
<urls>
<!-- webGUI endpoints -->
<url ServiceParameter="Dashboard" pattern="web/" />
+ <url ServiceParameter="LoginPage" pattern="web/login/" />
<url ServiceParameter="JobHistoryList" pattern="web/job/history/" />
<url ServiceParameter="JobHistoryView" pattern="web/job/history/{jobid}/" parameters.jobid="\d+" />
<url ServiceParameter="JobList" pattern="web/job/" />
<url ServiceParameter="Graphs" pattern="web/graphs/" />
<url ServiceParameter="Console" pattern="web/console/" />
<url ServiceParameter="ApplicationSettings" pattern="web/settings/" />
- <url ServiceParameter="Users" pattern="web/users/" />
+ <url ServiceParameter="Security" pattern="web/security/" />
<url ServiceParameter="ConfigureHosts" pattern="web/configure/" />
<url ServiceParameter="NewResource" pattern="web/new/{component_type}/{component_name}/{resource_type}/" parameters.component_type="\w+" parameters.component_name="[a-zA-Z0-9:.\-_ ]+" parameters.resource_type="\w+" />
<url ServiceParameter="NewResource" pattern="web/new/{host}/{component_type}/{component_name}/{resource_type}/" parameters.host="[a-zA-Z0-9:.\-_ ]+" parameters.component_type="\w+" parameters.component_name="[a-zA-Z0-9:.\-_ ]+" parameters.resource_type="\w+" />
<!-- authentication and authorization modules -->
<module id="auth_basic" class="Application.Common.Class.AuthBasic" />
<module id="auth_oauth2" class="Application.Common.Class.AuthOAuth2" />
+ <module id="ldap" class="Application.Common.Class.Ldap" />
+ <!-- cryptographic modules -->
+ <module id="crypto" class="Application.Common.Class.Crypto" />
+ <module id="apr1md5" class="Application.Common.Class.Apr1Md5" />
+ <module id="bcrypt" class="Application.Common.Class.BCrypt" />
+ <module id="sha1" class="Application.Common.Class.Sha1" />
+ <module id="ssha1" class="Application.Common.Class.Ssha1" />
+ <module id="sha256" class="Application.Common.Class.Sha256" />
+ <module id="sha512" class="Application.Common.Class.Sha512" />
<!-- communication modules -->
<module id="request" class="THttpRequest" UrlManager="url_manager" UrlFormat="HiddenPath" />
<module id="url_manager" class="Application.Common.Class.BaculumUrlMapping" EnableCustomUrl="true" />
.info {
color: #333;
}
+
+.error {
+ color: red;
+}
+
+/* Option row */
+.opt_row {
+ line-height: 36px;
+}
+
+/* Required option */
+.opt_req {
+ line-height: 40px;
+ display: inline;
+ margin-left: 2px;
+}
+
+.field_invalid {
+ border: 1px solid red !important;
+}