From 1100446451db866850ce1ec185db74ac9e41a4cc Mon Sep 17 00:00:00 2001 From: Marcin Haba Date: Thu, 31 Dec 2015 09:15:58 +0100 Subject: [PATCH] baculum: Tweak add comments --- gui/baculum/index.php | 6 + gui/baculum/protected/Class/API.php | 148 +++++++++++++++-- .../protected/Class/ConfigurationManager.php | 155 +++++++++++++----- gui/baculum/protected/Init.php | 18 +- gui/baculum/protected/application.xml | 2 +- 5 files changed, 265 insertions(+), 64 deletions(-) diff --git a/gui/baculum/index.php b/gui/baculum/index.php index 683acb7efc..d70df07e08 100644 --- a/gui/baculum/index.php +++ b/gui/baculum/index.php @@ -19,11 +19,17 @@ * * Bacula(R) is a registered trademark of Kern Sibbald. */ + +/* + * Constant is used to localize always valid document root directory + * Using for placing Baculum files in document root subdirectory + */ define('APPLICATION_DIRECTORY', __DIR__); require_once('./protected/Init.php'); require_once('./framework/prado.php'); +// Start application $application=new TApplication; $application->run(); ?> diff --git a/gui/baculum/protected/Class/API.php b/gui/baculum/protected/Class/API.php index b04df45407..9f631fd1f0 100644 --- a/gui/baculum/protected/Class/API.php +++ b/gui/baculum/protected/Class/API.php @@ -22,29 +22,58 @@ Prado::using('Application.Class.Errors'); +/** + * Internal API client module. + * + * @author Marcin Haba + */ class API extends TModule { + /** + * API version (used in HTTP header) + */ const API_VERSION = '0.1'; + /** + * Store configuration data from settings file + * @access protected + */ protected $appCfg; + /** + * These errors are allowed in API response and they do not cause + * disturb application working (no direction to error page) + * @access private + */ private $allowedErrors = array( GenericError::ERROR_NO_ERRORS, BconsoleError::ERROR_INVALID_COMMAND, PoolError::ERROR_NO_VOLUMES_IN_POOL_TO_UPDATE ); - private function getConnection() { + /** + * Get connection request handler. + * For data requests is used cURL interface. + * @access public + * @return resource connection handler on success, false on errors + */ + public function getConnection() { $ch = curl_init(); + $userpwd = sprintf('%s:%s', $this->appCfg['baculum']['login'], $this->appCfg['baculum']['password']); + curl_setopt($ch, CURLOPT_USERPWD, $userpwd); + curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_COOKIE, 'PHPSESSID=' . md5(session_id())); - curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - curl_setopt($ch, CURLOPT_USERPWD, $this->appCfg['baculum']['login'] . ':' . $this->appCfg['baculum']['password']); return $ch; } + /** + * Get API specific headers used in HTTP requests. + * @access private + * @return API specific headers + */ private function getAPIHeaders() { $headers = array( 'X-Baculum-API: ' . self::API_VERSION, @@ -55,29 +84,61 @@ class API extends TModule { return $headers; } + /** + * Initializes API module (framework module constructor) + * @access public + * @param TXmlElement $config API module configuration + */ public function init($config) { $this->initSessionCache(); $this->appCfg = $this->Application->getModule('configuration')->getApplicationConfig(); } + /** + * Get URL to use by internal API client's request. + * @access private + * @return string URL to internal API server + */ private function getURL() { $protocol = !empty($_SERVER['HTTPS']) ? 'https' : 'http'; $host = $_SERVER['SERVER_NAME']; $port = $_SERVER['SERVER_PORT']; + + // support for document root subdirectory $urlPrefix = $this->Application->getModule('friendly-url')->getUrlPrefix(); + $url = sprintf('%s://%s:%d%s/', $protocol, $host, $port, $urlPrefix); return $url; } - private function setParamsToUrl(&$url) { - $url .= (preg_match('/\?/', $url) === 1 ? '&' : '?' ) . 'director=' . ((array_key_exists('director', $_SESSION)) ? $_SESSION['director'] : ''); - $this->Application->getModule('logging')->log(__FUNCTION__, PHP_EOL . PHP_EOL . 'EXECUTE URL ==> ' . $url . ' <==' . PHP_EOL . PHP_EOL, Logging::CATEGORY_APPLICATION, __FILE__, __LINE__); + /** + * Set URL parameters and prepare URL to request send. + * @access private + * @param string &$url reference to URL string variable + */ + private function setUrlParams(&$url) { + $url .= (preg_match('/\?/', $url) === 1 ? '&' : '?'); + $url .= 'director='; + if (array_key_exists('director', $_SESSION)) { + $url .= $_SESSION['director']; + } + + $this->Application->getModule('logging')->log( + __FUNCTION__, + PHP_EOL . PHP_EOL . 'EXECUTE URL ==> ' . $url . ' <==' . PHP_EOL . PHP_EOL, + Logging::CATEGORY_APPLICATION, + __FILE__, + __LINE__ + ); } /** - * API REQUESTS METHODS (get, set, create, delete) + * Internal API GET request. + * @access public + * @param array $params GET params to send in request + * @param bool $use_cache if true then try to use session cache, if false then always use fresh data + * @return object stdClass with request result as two properties: 'output' and 'error' */ - public function get(array $params, $use_cache = false) { $cached = null; $ret = null; @@ -88,7 +149,7 @@ class API extends TModule { $ret = $cached; } else { $url = $this->getURL() . implode('/', $params); - $this->setParamsToUrl($url); + $this->setUrlParams($url); $ch = $this->getConnection(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, $this->getAPIHeaders()); @@ -102,14 +163,24 @@ class API extends TModule { return $ret; } + /** + * Internal API SET request. + * @access public + * @param array $params GET params to send in request + * @param array $options POST params to send in request + * @return object stdClass with request result as two properties: 'output' and 'error' + */ public function set(array $params, array $options) { $url = $this->getURL() . implode('/', $params); - $this->setParamsToUrl($url); + $this->setUrlParams($url); $data = http_build_query(array('update' => $options)); $ch = $this->getConnection(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); - curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge($this->getAPIHeaders(), array('X-HTTP-Method-Override: PUT', 'Content-Length: ' . strlen($data), 'Expect:'))); + curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge( + $this->getAPIHeaders(), + array('X-HTTP-Method-Override: PUT', 'Content-Length: ' . strlen($data), 'Expect:') + )); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $result = curl_exec($ch); @@ -117,9 +188,16 @@ class API extends TModule { return $this->preParseOutput($result); } + /** + * Internal API CREATE request. + * @access public + * @param array $params GET params to send in request + * @param array $options POST params to send in request + * @return object stdClass with request result as two properties: 'output' and 'error' + */ public function create(array $params, array $options) { $url = $this->getURL() . implode('/', $params); - $this->setParamsToUrl($url); + $this->setUrlParams($url); $data = http_build_query(array('create' => $options)); $ch = $this->getConnection(); curl_setopt($ch, CURLOPT_URL, $url); @@ -131,9 +209,15 @@ class API extends TModule { return $this->preParseOutput($result); } + /** + * Internal API REMOVE request. + * @access public + * @param array $params GET params to send in request + * @return object stdClass with request result as two properties: 'output' and 'error' + */ public function remove(array $params) { $url = $this->getURL() . implode('/', $params); - $this->setParamsToUrl($url); + $this->setUrlParams($url); $ch = $this->getConnection(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); @@ -143,10 +227,28 @@ class API extends TModule { return $this->preParseOutput($result); } + /** + * Initially parse and prepare every Internal API response. + * If a error occurs then redirect to appropriate error page. + * @access private + * @param string $result response output as JSON string (not object yet) + * @return object stdClass parsed response with two top level properties 'output' and 'error' + */ private function preParseOutput($result) { - $this->Application->getModule('logging')->log(__FUNCTION__, $result, Logging::CATEGORY_APPLICATION, __FILE__, __LINE__); + // first write log with that what comes + $this->Application->getModule('logging')->log( + __FUNCTION__, + $result, + Logging::CATEGORY_APPLICATION, + __FILE__, + __LINE__ + ); + + // decode JSON to object $resource = json_decode($result); + $error = null; + if(is_object($resource) && property_exists($resource, 'error')) { if(!in_array($resource->error, $this->allowedErrors)) { $error = $resource->error; @@ -155,10 +257,24 @@ class API extends TModule { $error = AuthorizationError::ERROR_AUTHORIZATION_TO_WEBGUI_PROBLEM; } - $this->Application->getModule('logging')->log(__FUNCTION__, $resource, Logging::CATEGORY_APPLICATION, __FILE__, __LINE__); + $this->Application->getModule('logging')->log( + __FUNCTION__, + $resource, + Logging::CATEGORY_APPLICATION, + __FILE__, + __LINE__ + ); + + // if other than allowed errors exist then show error page (redirect) if(!is_null($error)) { // Note! Redirection to error page takes place here. - $this->Response->redirect($this->Service->constructUrl('BaculumError',array('error' => $error), false)); + $this->Response->redirect( + $this->Service->constructUrl( + 'BaculumError', + array('error' => $error), + false + ) + ); } return $resource; diff --git a/gui/baculum/protected/Class/ConfigurationManager.php b/gui/baculum/protected/Class/ConfigurationManager.php index caa1bdd6f6..b5692817bb 100644 --- a/gui/baculum/protected/Class/ConfigurationManager.php +++ b/gui/baculum/protected/Class/ConfigurationManager.php @@ -22,11 +22,19 @@ Prado::using('Application.Class.Miscellaneous'); +/** + * Manage application configuration. + * Module is responsible for get/set application config data like: + * read/write application config and usersfiles, get application language + * and others. + * + * @author Marcin Haba + */ class ConfigurationManager extends TModule { /** - * Location o application configuration file. + * Application config file path */ const CONFIG_FILE = 'Application.Data.settings'; @@ -36,32 +44,39 @@ class ConfigurationManager extends TModule const USERS_FILE = 'Application.Data.baculum'; /** - * PostgreSQL default params. + * PostgreSQL default params */ const PGSQL = 'pgsql'; const PGSQL_NAME = 'PostgreSQL'; const PGSQL_PORT = 5432; /** - * MySQL default params. + * MySQL default params */ const MYSQL = 'mysql'; const MYSQL_NAME = 'MySQL'; const MYSQL_PORT = 3306; /** - * SQLite default params. + * SQLite default params */ const SQLITE = 'sqlite'; const SQLITE_NAME = 'SQLite'; const SQLITE_PORT = null; /** - * Default language for application. + * Default application language */ const DEFAULT_LANGUAGE = 'en'; + /** + * Get database name by database type (short name). + * @access public + * @param string $type database type ('pgsql', 'mysql' ...) + * @return mixed database name or null if database name not found + */ public function getDbNameByType($type) { + $type = (string) $type; switch($type) { case self::PGSQL: $dbName = self::PGSQL_NAME; break; case self::MYSQL: $dbName = self::MYSQL_NAME; break; @@ -71,34 +86,46 @@ class ConfigurationManager extends TModule return $dbName; } - public function getPostgreSQLType() { - return self::PGSQL; - } - - public function getMySQLType() { - return self::MYSQL; - } - - public function getSQLiteType() { - return self::SQLITE; - } - + /** + * Check if given database type is PostgreSQL type. + * @access public + * @param string $type database type ('pgsql', 'mysql' ...) + * @return boolean true if database type is PostgreSQL, otherwise false + */ public function isPostgreSQLType($type) { return ($type === self::PGSQL); } + /** + * Check if given database type is MySQL type. + * @access public + * @param string $type database type ('pgsql', 'mysql' ...) + * @return boolean true if database type is MySQL, otherwise false + */ public function isMySQLType($type) { return ($type === self::MYSQL); } + /** + * Check if given database type is SQLite type. + * @access public + * @param string $type database type ('sqlite', 'mysql' ...) + * @return boolean true if database type is SQLite, otherwise false + */ public function isSQLiteType($type) { return ($type === self::SQLITE); } + /** + * Get currently set application language short name. + * If no language set then default language is taken. + * @access public + * @return string lanuage short name + */ public function getLanguage() { $language = self::DEFAULT_LANGUAGE; if ($this->isApplicationConfig() === true) { - $config = $this->getApplicationConfig(); + $config = self::getApplicationConfig(); if (array_key_exists('lang', $config['baculum'])) { $language = $config['baculum']['lang']; } @@ -106,13 +133,8 @@ class ConfigurationManager extends TModule return $language; } - public function setLanguage($language) { - - } - /** - * Saving application configuration. - * + * Save application configuration. * @access public * @param array $config structure of config file params * @return boolean true if config save is successfully, false if config save is failure @@ -123,8 +145,7 @@ class ConfigurationManager extends TModule } /** - * Getting application configuration. - * + * Get application configuration. * @access public * @return array application configuration */ @@ -134,8 +155,7 @@ class ConfigurationManager extends TModule } /** - * Checking if application configuration file exists. - * + * Check if application configuration file exists. * @access public * @return boolean true if file exists, otherwise false */ @@ -143,20 +163,20 @@ class ConfigurationManager extends TModule return file_exists(Prado::getPathOfNamespace(self::CONFIG_FILE, '.conf')); } + /** + * Get encrypted password to use in HTTP Basic auth. + * + * @access public + * @param string $password plain text password + * @return string encrypted password + */ public function getCryptedPassword($password) { $enc_pwd = crypt($password, base64_encode($password)); return $enc_pwd; } /** - * Saving user to users configuration file. - * - * NOTE! - * So far by webGUI is possible to set one user. - * For more users and restricted consoles, there is need to modify - * users and passwords file. - * - * TODO: Support for more than one user setting on webGUI. + * Save user to users configuration file. * * @access public * @param string $user username @@ -166,13 +186,13 @@ class ConfigurationManager extends TModule * @return boolean true if user saved successfully, otherwise false */ public function setUsersConfig($user, $password, $firstUsage = false, $oldUser = null) { - $allUsers = $this->getAllUsers(); - $password = $this->getCryptedPassword($password); - if($firstUsage === true) { $this->clearUsersConfig(); } + $allUsers = $this->getAllUsers(); + $password = $this->getCryptedPassword($password); + $userExists = array_key_exists($user, $allUsers); @@ -197,6 +217,14 @@ class ConfigurationManager extends TModule return $result; } + /** + * Read all users from HTTP Basic users file. + * Returned value is associative array with usernames as keys + * and encrypted passwords as values. + * + * @access public + * @return array users/passwords list + */ public function getAllUsers() { $allUsers = array(); if ($this->isUsersConfig() === true) { @@ -212,6 +240,15 @@ class ConfigurationManager extends TModule return $allUsers; } + /** + * Save HTTP Basic users file. + * Given parameter is associative array with usernames as keys + * and encrypted passwords as values. + * + * @access public + * @param array $allUsers users/passwords list + * @return boolean true if users file saved successfully, otherwise false + */ public function saveUserConfig($allUsers) { $users = array(); foreach ($allUsers as $user => $pwd) { @@ -226,6 +263,15 @@ class ConfigurationManager extends TModule return $result; } + /** + * Remove single user from HTTP Basic users file. + * Note, this method saves config file if username was existed + * before removing. + * + * @access public + * @param string $username user name to remove + * @return boolean true if users file saved successfully, otherwise false + */ public function removeUser($username) { $result = false; $allUsers = $this->getAllUsers(); @@ -237,8 +283,7 @@ class ConfigurationManager extends TModule } /** - * Checking if users configuration file exists. - * + * Check if users configuration file exists. * @access public * @return boolean true if file exists, otherwise false */ @@ -248,22 +293,44 @@ class ConfigurationManager extends TModule /** * Clear all content of users file. - * - * @access private + * @access public * @return boolean true if file cleared successfully, otherwise false */ - private function clearUsersConfig() { + public function clearUsersConfig() { $usersFile = Prado::getPathOfNamespace(self::USERS_FILE, '.users'); $result = file_put_contents($usersFile, '') !== false; return $result; } + /** + * Log in as specific user. + * + * 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 $http_protocol 'http' or 'https' value + * @param string $host hostname without port, for example: my.own.host or localhost + * @param integer $port port number on which listens web server + * @param string $user user name to log in + * @param string $string plain text user's password + * @return none + */ public function switchToUser($http_protocol, $host, $port, $user, $password) { $urlPrefix = $this->Application->getModule('friendly-url')->getUrlPrefix(); $location = sprintf("%s://%s:%s@%s:%d%s", $http_protocol, $user, $password, $host, $port, $urlPrefix); header("Location: $location"); } + /** + * 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() { $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; $rand_string = str_shuffle($characters); diff --git a/gui/baculum/protected/Init.php b/gui/baculum/protected/Init.php index d3627030a2..f7a5f1c679 100644 --- a/gui/baculum/protected/Init.php +++ b/gui/baculum/protected/Init.php @@ -31,11 +31,23 @@ if (!ini_get('date.timezone')) { date_default_timezone_set($timezone); } -// Support for web servers which do not provide direct info about HTTP Basic auth to PHP superglobal $_SERVER array. -if(!isset($_SERVER['PHP_AUTH_USER']) && !isset($_SERVER['PHP_AUTH_PW']) && isset($_SERVER['HTTP_AUTHORIZATION'])) { - list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6))); +/* + * Support for web servers (for example Lighttpd) which do not provide direct + * info about HTTP Basic auth to PHP superglobal $_SERVER array. + */ +if (!isset($_SERVER['PHP_AUTH_USER']) && !isset($_SERVER['PHP_AUTH_PW']) && isset($_SERVER['HTTP_AUTHORIZATION'])) { + /* + * Substring 'Basic ' from HTTP authorization header + * Example 'Basic YWRtaW46YWRtaW4=' becomes 'YWRtaW46YWRtaW4=' + */ + $encoded_credentials = substr($_SERVER['HTTP_AUTHORIZATION'], 6); + $decoded_credentials = base64_decode($encoded_credentials); + + // initialize required auth superglobal $_SERVER array + list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':', $decoded_credentials); } +// Check requirements and if are some needed then show requirements page require_once('./protected/Pages/Requirements.php'); new Requirements(dirname(__DIR__)); diff --git a/gui/baculum/protected/application.xml b/gui/baculum/protected/application.xml index c1aea2b934..7f4c77477b 100644 --- a/gui/baculum/protected/application.xml +++ b/gui/baculum/protected/application.xml @@ -1,9 +1,9 @@ - + -- 2.39.2