You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
961 lines
26 KiB
961 lines
26 KiB
1 year ago
|
<?php declare(strict_types = 0);
|
||
|
/*
|
||
|
** Zabbix
|
||
|
** Copyright (C) 2001-2023 Zabbix SIA
|
||
|
**
|
||
|
** This program is free software; you can redistribute it and/or modify
|
||
|
** it under the terms of the GNU General Public License as published by
|
||
|
** the Free Software Foundation; either version 2 of the License, or
|
||
|
** (at your option) any later version.
|
||
|
**
|
||
|
** This program is distributed in the hope that it will be useful,
|
||
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
** GNU General Public License for more details.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program; if not, write to the Free Software
|
||
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
**/
|
||
|
|
||
|
|
||
|
use Zabbix\Core\CModule;
|
||
|
|
||
|
use CController as CAction;
|
||
|
|
||
|
require_once dirname(__FILE__).'/CAutoloader.php';
|
||
|
|
||
|
class ZBase {
|
||
|
|
||
|
const EXEC_MODE_DEFAULT = 'default';
|
||
|
const EXEC_MODE_SETUP = 'setup';
|
||
|
const EXEC_MODE_API = 'api';
|
||
|
|
||
|
/**
|
||
|
* An instance of the current APP object.
|
||
|
*
|
||
|
* @var APP
|
||
|
*/
|
||
|
protected static $instance;
|
||
|
|
||
|
/**
|
||
|
* The absolute path to the root directory.
|
||
|
*
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $root_dir;
|
||
|
|
||
|
/**
|
||
|
* @var array of config data from zabbix config file
|
||
|
*/
|
||
|
protected $config = [];
|
||
|
|
||
|
/**
|
||
|
* @var CVault
|
||
|
*/
|
||
|
protected $vault;
|
||
|
|
||
|
/**
|
||
|
* @var CAutoloader
|
||
|
*/
|
||
|
protected $autoloader;
|
||
|
|
||
|
/**
|
||
|
* @var CComponentRegistry
|
||
|
*/
|
||
|
private $component_registry;
|
||
|
|
||
|
/**
|
||
|
* Application mode.
|
||
|
*
|
||
|
* @var string
|
||
|
*/
|
||
|
private $mode;
|
||
|
|
||
|
private CModuleManager $module_manager;
|
||
|
|
||
|
private ?CView $view = null;
|
||
|
|
||
|
/**
|
||
|
* Returns the current instance of APP.
|
||
|
*
|
||
|
* @static
|
||
|
*
|
||
|
* @return APP
|
||
|
*/
|
||
|
public static function getInstance(): APP {
|
||
|
if (self::$instance === null) {
|
||
|
self::$instance = new static;
|
||
|
}
|
||
|
|
||
|
return self::$instance;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get component registry.
|
||
|
*
|
||
|
* @return CComponentRegistry
|
||
|
*/
|
||
|
public static function Component(): CComponentRegistry {
|
||
|
return self::getInstance()->component_registry;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get module manager.
|
||
|
*
|
||
|
* @return CModuleManager
|
||
|
*/
|
||
|
public static function ModuleManager(): CModuleManager {
|
||
|
return self::getInstance()->module_manager;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return CView|null
|
||
|
*/
|
||
|
public static function View(): ?CView {
|
||
|
return self::getInstance()->view;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Init modules required to run frontend.
|
||
|
*/
|
||
|
protected function init() {
|
||
|
$this->root_dir = $this->findRootDir();
|
||
|
$this->initAutoloader();
|
||
|
$this->component_registry = new CComponentRegistry;
|
||
|
|
||
|
// initialize API classes
|
||
|
$apiServiceFactory = new CApiServiceFactory();
|
||
|
|
||
|
$client = new CLocalApiClient();
|
||
|
$client->setServiceFactory($apiServiceFactory);
|
||
|
$wrapper = new CFrontendApiWrapper($client);
|
||
|
$wrapper->setProfiler(CProfiler::getInstance());
|
||
|
API::setWrapper($wrapper);
|
||
|
API::setApiServiceFactory($apiServiceFactory);
|
||
|
|
||
|
// system includes
|
||
|
require_once 'include/debug.inc.php';
|
||
|
require_once 'include/gettextwrapper.inc.php';
|
||
|
require_once 'include/defines.inc.php';
|
||
|
require_once 'include/func.inc.php';
|
||
|
require_once 'include/html.inc.php';
|
||
|
require_once 'include/perm.inc.php';
|
||
|
require_once 'include/js.inc.php';
|
||
|
require_once 'include/users.inc.php';
|
||
|
require_once 'include/validate.inc.php';
|
||
|
require_once 'include/locales.inc.php';
|
||
|
require_once 'include/db.inc.php';
|
||
|
require_once 'vendor/autoload.php';
|
||
|
|
||
|
// page specific includes
|
||
|
require_once 'include/actions.inc.php';
|
||
|
require_once 'include/discovery.inc.php';
|
||
|
require_once 'include/draw.inc.php';
|
||
|
require_once 'include/events.inc.php';
|
||
|
require_once 'include/graphs.inc.php';
|
||
|
require_once 'include/hostgroups.inc.php';
|
||
|
require_once 'include/hosts.inc.php';
|
||
|
require_once 'include/httptest.inc.php';
|
||
|
require_once 'include/images.inc.php';
|
||
|
require_once 'include/items.inc.php';
|
||
|
require_once 'include/maps.inc.php';
|
||
|
require_once 'include/sounds.inc.php';
|
||
|
require_once 'include/triggers.inc.php';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initializes the application.
|
||
|
*
|
||
|
* @param string $mode Application initialization mode.
|
||
|
*
|
||
|
* @throws Exception
|
||
|
*/
|
||
|
public function run($mode) {
|
||
|
$this->mode = $mode;
|
||
|
|
||
|
$this->init();
|
||
|
|
||
|
$this->setMaintenanceMode();
|
||
|
|
||
|
ini_set('display_errors', 'Off');
|
||
|
set_error_handler('zbx_err_handler');
|
||
|
|
||
|
switch ($mode) {
|
||
|
case self::EXEC_MODE_DEFAULT:
|
||
|
$file = basename($_SERVER['SCRIPT_NAME']);
|
||
|
$action_name = ($file === 'zabbix.php') ? getRequest('action', '') : $file;
|
||
|
|
||
|
if ($action_name === 'notifications.get') {
|
||
|
CWebUser::disableSessionExtension();
|
||
|
}
|
||
|
|
||
|
$this->loadConfigFile();
|
||
|
$this->initVault();
|
||
|
$this->initDB();
|
||
|
$this->setServerAddress();
|
||
|
$this->authenticateUser();
|
||
|
|
||
|
$this->initMessages();
|
||
|
$this->setLayoutModeByUrl();
|
||
|
$this->initComponents();
|
||
|
$this->initModuleManager();
|
||
|
|
||
|
/** @var CRouter $router */
|
||
|
$router = $this->component_registry->get('router');
|
||
|
$router->addActions($this->module_manager->getActions());
|
||
|
|
||
|
$validator = new CNewValidator(['action' => $action_name], ['action' => 'fatal|required|string']);
|
||
|
$errors = $validator->getAllErrors();
|
||
|
|
||
|
if ($errors) {
|
||
|
CCookieHelper::set('system-message-details', base64_encode(json_encode(
|
||
|
['type' => 'error', 'messages' => $errors]
|
||
|
)));
|
||
|
|
||
|
redirect('zabbix.php?action=system.warning');
|
||
|
}
|
||
|
|
||
|
$router->setAction($action_name);
|
||
|
|
||
|
$this->component_registry->get('menu.main')
|
||
|
->setSelectedByAction($action_name, $_REQUEST,
|
||
|
CViewHelper::loadSidebarMode() != ZBX_SIDEBAR_VIEW_MODE_COMPACT
|
||
|
);
|
||
|
|
||
|
$this->component_registry->get('menu.user')
|
||
|
->setSelectedByAction($action_name, $_REQUEST,
|
||
|
CViewHelper::loadSidebarMode() != ZBX_SIDEBAR_VIEW_MODE_COMPACT
|
||
|
);
|
||
|
|
||
|
CProfiler::getInstance()->start();
|
||
|
|
||
|
$this->processRequest($router);
|
||
|
break;
|
||
|
|
||
|
case self::EXEC_MODE_API:
|
||
|
$this->loadConfigFile();
|
||
|
$this->initVault();
|
||
|
$this->initDB();
|
||
|
$this->setServerAddress();
|
||
|
$this->initLocales('en_us');
|
||
|
break;
|
||
|
|
||
|
case self::EXEC_MODE_SETUP:
|
||
|
try {
|
||
|
// try to load config file, if it exists we need to init db and authenticate user to check permissions
|
||
|
$this->loadConfigFile();
|
||
|
$this->initVault();
|
||
|
$this->initDB();
|
||
|
$this->authenticateUser();
|
||
|
$this->initComponents();
|
||
|
}
|
||
|
catch (ConfigFileException $e) {
|
||
|
if ($e->getCode() == CConfigFile::CONFIG_VAULT_ERROR) {
|
||
|
echo (new CView('general.warning', [
|
||
|
'header' => _('Vault connection failed.'),
|
||
|
'messages' => [$e->getMessage()],
|
||
|
'theme' => ZBX_DEFAULT_THEME
|
||
|
]))->getOutput();
|
||
|
|
||
|
session_write_close();
|
||
|
exit;
|
||
|
}
|
||
|
else {
|
||
|
$session = new CCookieSession();
|
||
|
$sessionid = $session->extractSessionId() ?: CEncryptHelper::generateKey();
|
||
|
|
||
|
if (!$session->session_start($sessionid)) {
|
||
|
throw new Exception(_('Session initialization error.'));
|
||
|
}
|
||
|
|
||
|
CSessionHelper::set('sessionid', $sessionid);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the application mode.
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function getMode(): string {
|
||
|
return self::getInstance()->mode;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the absolute path to the root dir.
|
||
|
*/
|
||
|
public static function getRootDir(): string {
|
||
|
return self::getInstance()->root_dir;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the path to the frontend's root dir.
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
private function findRootDir() {
|
||
|
return realpath(dirname(__FILE__).'/../../..');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* An array of directories to add to the autoloader include paths.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private function getIncludePaths() {
|
||
|
return [
|
||
|
$this->root_dir.'/include/classes/api',
|
||
|
$this->root_dir.'/include/classes/api/services',
|
||
|
$this->root_dir.'/include/classes/api/helpers',
|
||
|
$this->root_dir.'/include/classes/api/item_types',
|
||
|
$this->root_dir.'/include/classes/api/managers',
|
||
|
$this->root_dir.'/include/classes/api/clients',
|
||
|
$this->root_dir.'/include/classes/api/wrappers',
|
||
|
$this->root_dir.'/include/classes/core',
|
||
|
$this->root_dir.'/include/classes/data',
|
||
|
$this->root_dir.'/include/classes/mvc',
|
||
|
$this->root_dir.'/include/classes/db',
|
||
|
$this->root_dir.'/include/classes/debug',
|
||
|
$this->root_dir.'/include/classes/validators',
|
||
|
$this->root_dir.'/include/classes/validators/schema',
|
||
|
$this->root_dir.'/include/classes/validators/string',
|
||
|
$this->root_dir.'/include/classes/validators/object',
|
||
|
$this->root_dir.'/include/classes/validators/hostgroup',
|
||
|
$this->root_dir.'/include/classes/validators/host',
|
||
|
$this->root_dir.'/include/classes/validators/hostprototype',
|
||
|
$this->root_dir.'/include/classes/validators/event',
|
||
|
$this->root_dir.'/include/classes/export',
|
||
|
$this->root_dir.'/include/classes/export/writers',
|
||
|
$this->root_dir.'/include/classes/export/elements',
|
||
|
$this->root_dir.'/include/classes/graph',
|
||
|
$this->root_dir.'/include/classes/graphdraw',
|
||
|
$this->root_dir.'/include/classes/import',
|
||
|
$this->root_dir.'/include/classes/import/converters',
|
||
|
$this->root_dir.'/include/classes/import/importers',
|
||
|
$this->root_dir.'/include/classes/import/preprocessors',
|
||
|
$this->root_dir.'/include/classes/import/readers',
|
||
|
$this->root_dir.'/include/classes/import/validators',
|
||
|
$this->root_dir.'/include/classes/items',
|
||
|
$this->root_dir.'/include/classes/triggers',
|
||
|
$this->root_dir.'/include/classes/server',
|
||
|
$this->root_dir.'/include/classes/screens',
|
||
|
$this->root_dir.'/include/classes/services',
|
||
|
$this->root_dir.'/include/classes/sysmaps',
|
||
|
$this->root_dir.'/include/classes/helpers',
|
||
|
$this->root_dir.'/include/classes/helpers/trigger',
|
||
|
$this->root_dir.'/include/classes/macros',
|
||
|
$this->root_dir.'/include/classes/html',
|
||
|
$this->root_dir.'/include/classes/html/svg',
|
||
|
$this->root_dir.'/include/classes/html/widgets',
|
||
|
$this->root_dir.'/include/classes/html/widgets/fields',
|
||
|
$this->root_dir.'/include/classes/html/interfaces',
|
||
|
$this->root_dir.'/include/classes/parsers',
|
||
|
$this->root_dir.'/include/classes/parsers/results',
|
||
|
$this->root_dir.'/include/classes/controllers',
|
||
|
$this->root_dir.'/include/classes/routing',
|
||
|
$this->root_dir.'/include/classes/json',
|
||
|
$this->root_dir.'/include/classes/user',
|
||
|
$this->root_dir.'/include/classes/setup',
|
||
|
$this->root_dir.'/include/classes/regexp',
|
||
|
$this->root_dir.'/include/classes/ldap',
|
||
|
$this->root_dir.'/include/classes/pagefilter',
|
||
|
$this->root_dir.'/include/classes/xml',
|
||
|
$this->root_dir.'/include/classes/vaults',
|
||
|
$this->root_dir.'/local/app/controllers',
|
||
|
$this->root_dir.'/app/controllers'
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* An array of available themes.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public static function getThemes() {
|
||
|
return [
|
||
|
'blue-theme' => _('Blue'),
|
||
|
'dark-theme' => _('Dark'),
|
||
|
'hc-light' => _('High-contrast light'),
|
||
|
'hc-dark' => _('High-contrast dark')
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if maintenance mode is enabled.
|
||
|
*
|
||
|
* @throws Exception
|
||
|
*/
|
||
|
protected function setMaintenanceMode() {
|
||
|
require_once 'conf/maintenance.inc.php';
|
||
|
|
||
|
if (defined('ZBX_DENY_GUI_ACCESS')) {
|
||
|
if (!isset($ZBX_GUI_ACCESS_IP_RANGE) || !in_array(CWebUser::getIp(), $ZBX_GUI_ACCESS_IP_RANGE)) {
|
||
|
throw new Exception($_REQUEST['warning_msg']);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Load zabbix config file.
|
||
|
*/
|
||
|
protected function loadConfigFile(): void {
|
||
|
$configFile = $this->root_dir.CConfigFile::CONFIG_FILE_PATH;
|
||
|
|
||
|
$config = new CConfigFile($configFile);
|
||
|
|
||
|
$this->config = $config->load();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize classes autoloader.
|
||
|
*/
|
||
|
protected function initAutoloader() {
|
||
|
// Register base directory path for 'include' and 'require' functions.
|
||
|
set_include_path(get_include_path().PATH_SEPARATOR.$this->root_dir);
|
||
|
$autoloader = new CAutoloader;
|
||
|
$autoloader->addNamespace('', $this->getIncludePaths());
|
||
|
$autoloader->addNamespace('Zabbix\\Core', [$this->root_dir.'/include/classes/core']);
|
||
|
$autoloader->addNamespace('Zabbix\\Widgets', [$this->root_dir.'/include/classes/widgets']);
|
||
|
$autoloader->register();
|
||
|
$this->autoloader = $autoloader;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Vault provider initialisation if it exists in configuration file.
|
||
|
*/
|
||
|
protected function initVault(): void {
|
||
|
if (!array_key_exists('VAULT', $this->config['DB'])) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch ($this->config['DB']['VAULT']) {
|
||
|
case CVaultCyberArk::NAME:
|
||
|
$this->vault = new CVaultCyberArk($this->config['DB']['VAULT_URL'],
|
||
|
$this->config['DB']['VAULT_DB_PATH'], $this->config['DB']['VAULT_CERT_FILE'],
|
||
|
$this->config['DB']['VAULT_KEY_FILE']
|
||
|
);
|
||
|
break;
|
||
|
|
||
|
case CVaultHashiCorp::NAME:
|
||
|
$this->vault = new CVaultHashiCorp($this->config['DB']['VAULT_URL'],
|
||
|
$this->config['DB']['VAULT_DB_PATH'], $this->config['DB']['VAULT_TOKEN']
|
||
|
);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if frontend can connect to DB.
|
||
|
*
|
||
|
* @throws DBException
|
||
|
*/
|
||
|
protected function initDB(): void {
|
||
|
global $DB;
|
||
|
|
||
|
$error = null;
|
||
|
|
||
|
if ($this->vault !== null) {
|
||
|
$db_user = $this->config['DB']['VAULT_CACHE'] ? CDataCacheHelper::getValue('db_user', '') : '';
|
||
|
$db_password = $this->config['DB']['VAULT_CACHE'] ? CDataCacheHelper::getValue('db_password', '') : '';
|
||
|
|
||
|
if ($db_user === '' || $db_password === '') {
|
||
|
$db_credentials = $this->vault->getCredentials();
|
||
|
|
||
|
if ($db_credentials === null) {
|
||
|
throw new DBException(_('Unable to load database credentials from Vault.'));
|
||
|
}
|
||
|
|
||
|
['user' => $db_user, 'password' => $db_password] = $db_credentials;
|
||
|
}
|
||
|
|
||
|
if ($this->config['DB']['VAULT_CACHE'] && $db_user !== '' && $db_password !== '') {
|
||
|
CDataCacheHelper::setValueArray([
|
||
|
'db_user' => $db_user,
|
||
|
'db_password' => $db_password
|
||
|
]);
|
||
|
}
|
||
|
else {
|
||
|
CDataCacheHelper::clearValues(['db_user', 'db_password']);
|
||
|
}
|
||
|
|
||
|
$this->config['DB']['USER'] = $db_user;
|
||
|
$this->config['DB']['PASSWORD'] = $db_password;
|
||
|
|
||
|
$DB = $this->config['DB'];
|
||
|
}
|
||
|
|
||
|
if (!DBconnect($error)) {
|
||
|
CDataCacheHelper::clearValues(['db_user', 'db_password']);
|
||
|
|
||
|
throw new DBException($error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize translations, set up translated date and time constants.
|
||
|
*
|
||
|
* @param string|null $language Locale variant prefix like en_US, ru_RU etc.
|
||
|
*/
|
||
|
public function initLocales(?string $language): void {
|
||
|
if (!setupLocale($language, $error) && $error !== '') {
|
||
|
error($error);
|
||
|
}
|
||
|
|
||
|
require_once $this->root_dir.'/include/translateDefines.inc.php';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set messages received in cookies.
|
||
|
*/
|
||
|
private function initMessages(): void {
|
||
|
if (CCookieHelper::has('system-message-ok')) {
|
||
|
CMessageHelper::setSuccessTitle(CCookieHelper::get('system-message-ok'));
|
||
|
CCookieHelper::unset('system-message-ok');
|
||
|
}
|
||
|
if (CCookieHelper::has('system-message-error')) {
|
||
|
CMessageHelper::setErrorTitle(CCookieHelper::get('system-message-error'));
|
||
|
CCookieHelper::unset('system-message-error');
|
||
|
}
|
||
|
if (CCookieHelper::has('system-message-details')) {
|
||
|
$details = json_decode(base64_decode(CCookieHelper::get('system-message-details')), true);
|
||
|
if ($details['type'] === 'success') {
|
||
|
foreach ($details['messages'] as $message) {
|
||
|
CMessageHelper::addSuccess($message);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
foreach ($details['messages'] as $message) {
|
||
|
CMessageHelper::addError($message);
|
||
|
}
|
||
|
}
|
||
|
CCookieHelper::unset('system-message-details');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Authenticate user, apply some user-specific settings.
|
||
|
*
|
||
|
* @throws Exception
|
||
|
*/
|
||
|
protected function authenticateUser(): void {
|
||
|
$session = new CEncryptedCookieSession();
|
||
|
|
||
|
if (!CWebUser::checkAuthentication($session->extractSessionId() ?: '')) {
|
||
|
CWebUser::setDefault();
|
||
|
}
|
||
|
|
||
|
$this->initLocales(CWebUser::$data['lang']);
|
||
|
|
||
|
if (!$session->session_start(CWebUser::$data['sessionid'])) {
|
||
|
throw new Exception(_('Session initialization error.'));
|
||
|
}
|
||
|
|
||
|
CSessionHelper::set('sessionid', CWebUser::$data['sessionid']);
|
||
|
|
||
|
// Set the authentication token for the API.
|
||
|
API::getWrapper()->auth = [
|
||
|
'type' => CJsonRpc::AUTH_TYPE_COOKIE,
|
||
|
'auth' => CWebUser::$data['sessionid']
|
||
|
];
|
||
|
|
||
|
// Enable debug mode in the API.
|
||
|
API::getWrapper()->debug = CWebUser::getDebugMode();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Process request and generate response.
|
||
|
*
|
||
|
* @param CRouter $router CRouter class instance.
|
||
|
*/
|
||
|
private function processRequest(CRouter $router): void {
|
||
|
$action_name = $router->getAction();
|
||
|
$action_class = $router->getController();
|
||
|
|
||
|
try {
|
||
|
if ($action_class === null) {
|
||
|
throw new Exception(_('Page not found'));
|
||
|
}
|
||
|
|
||
|
if (!class_exists($action_class)) {
|
||
|
$namespace_parts = explode('\\', $action_class);
|
||
|
|
||
|
if (count($namespace_parts) > 1) {
|
||
|
$action_class_fallback = end($namespace_parts);
|
||
|
|
||
|
if (!class_exists($action_class_fallback)) {
|
||
|
throw new Exception(_s('Class %1$s not found for action %2$s.', $action_class, $action_name));
|
||
|
}
|
||
|
|
||
|
$action_class = $action_class_fallback;
|
||
|
}
|
||
|
else {
|
||
|
throw new Exception(_s('Class %1$s not found for action %2$s.', $action_class, $action_name));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$action = new $action_class();
|
||
|
|
||
|
if (!is_subclass_of($action, CAction::class)) {
|
||
|
throw new Exception(_s('Action class %1$s must extend %2$s class.', $action_class, CAction::class));
|
||
|
}
|
||
|
|
||
|
$action->setAction($action_name);
|
||
|
$this->module_manager->setActionName($action_name);
|
||
|
|
||
|
$modules = $this->module_manager->getModules();
|
||
|
|
||
|
$action_module = $this->module_manager->getActionModule();
|
||
|
|
||
|
if ($action_module !== null) {
|
||
|
$modules = array_replace([$action_module->getId() => $action_module], $modules);
|
||
|
|
||
|
if ($action_module->getType() === CModule::TYPE_WIDGET) {
|
||
|
CView::registerDirectory($this->root_dir.'/'.$action_module->getRelativePath().'/views');
|
||
|
CPartial::registerDirectory($this->root_dir.'/'.$action_module->getRelativePath().'/partials');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach (array_reverse($modules) as $module) {
|
||
|
if ($module->getType() === CModule::TYPE_MODULE) {
|
||
|
CView::registerDirectory($this->root_dir.'/'.$module->getRelativePath().'/views');
|
||
|
CPartial::registerDirectory($this->root_dir.'/'.$module->getRelativePath().'/partials');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
register_shutdown_function(function() use ($action) {
|
||
|
$this->module_manager->publishEvent($action, 'onTerminate');
|
||
|
});
|
||
|
|
||
|
$this->module_manager->publishEvent($action, 'onBeforeAction');
|
||
|
|
||
|
$action->run();
|
||
|
|
||
|
if (!($action instanceof CLegacyAction)) {
|
||
|
$this->processResponseFinal($router, $action);
|
||
|
}
|
||
|
}
|
||
|
catch (CAccessDeniedException $e) {
|
||
|
$this->denyPageAccess($router);
|
||
|
}
|
||
|
catch (Exception $e) {
|
||
|
self::terminateWithError($router, $e->getMessage());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private function processResponseFinal(CRouter $router, CAction $action): void {
|
||
|
$response = $action->getResponse();
|
||
|
|
||
|
// Controller returned redirect to another page?
|
||
|
if ($response instanceof CControllerResponseRedirect) {
|
||
|
header('Content-Type: text/html; charset=UTF-8');
|
||
|
|
||
|
filter_messages();
|
||
|
|
||
|
$response->redirect();
|
||
|
}
|
||
|
// Controller returned fatal error?
|
||
|
elseif ($response instanceof CControllerResponseFatal) {
|
||
|
header('Content-Type: text/html; charset=UTF-8');
|
||
|
|
||
|
filter_messages();
|
||
|
|
||
|
CMessageHelper::addError('Controller: '.$router->getAction());
|
||
|
ksort($_REQUEST);
|
||
|
foreach ($_REQUEST as $key => $value) {
|
||
|
if ($key !== 'sid') {
|
||
|
CMessageHelper::addError(is_scalar($value) ? $key.': '.$value : $key.': '.gettype($value));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$response->redirect();
|
||
|
}
|
||
|
// Action has layout?
|
||
|
if ($router->getLayout() !== null) {
|
||
|
if (!($response instanceof CControllerResponseData)) {
|
||
|
throw new Exception(_s('Unexpected response for action %1$s.', $router->getAction()));
|
||
|
}
|
||
|
|
||
|
$layout_data_defaults = [
|
||
|
'page' => [
|
||
|
'title' => $response->getTitle(),
|
||
|
'file' => $response->getFileName()
|
||
|
],
|
||
|
'controller' => [
|
||
|
'action' => $router->getAction()
|
||
|
],
|
||
|
'main_block' => '',
|
||
|
'javascript' => [
|
||
|
'files' => []
|
||
|
],
|
||
|
'stylesheet' => [
|
||
|
'files' => []
|
||
|
],
|
||
|
'web_layout_mode' => ZBX_LAYOUT_NORMAL,
|
||
|
'config' => [
|
||
|
'server_check_interval' => CSettingsHelper::get(CSettingsHelper::SERVER_CHECK_INTERVAL),
|
||
|
'x_frame_options' => CSettingsHelper::get(CSettingsHelper::X_FRAME_OPTIONS)
|
||
|
]
|
||
|
];
|
||
|
|
||
|
if ($router->getView() !== null && $response->isViewEnabled()) {
|
||
|
$this->view = new CView($router->getView(), $response->getData());
|
||
|
|
||
|
$module = $this->module_manager->getActionModule();
|
||
|
|
||
|
if ($module !== null) {
|
||
|
$this->view->setAssetsPath($module->getRelativePath().'/assets');
|
||
|
}
|
||
|
|
||
|
$layout_data = array_replace($layout_data_defaults, [
|
||
|
'main_block' => $this->view->getOutput(),
|
||
|
'javascript' => [
|
||
|
'files' => $this->view->getJsFiles()
|
||
|
],
|
||
|
'stylesheet' => [
|
||
|
'files' => $this->view->getCssFiles()
|
||
|
],
|
||
|
'web_layout_mode' => $this->view->getLayoutMode()
|
||
|
]);
|
||
|
}
|
||
|
else {
|
||
|
$layout_data = array_replace_recursive($layout_data_defaults, $response->getData());
|
||
|
}
|
||
|
|
||
|
echo (new CView($router->getLayout(), $layout_data))->getOutput();
|
||
|
}
|
||
|
|
||
|
session_write_close();
|
||
|
exit();
|
||
|
}
|
||
|
|
||
|
private static function denyPageAccess(CRouter $router): void {
|
||
|
$request_url = (new CUrl(array_key_exists('request', $_REQUEST) ? $_REQUEST['request'] : ''))
|
||
|
->removeArgument(CCsrfTokenHelper::CSRF_TOKEN_NAME)
|
||
|
->toString();
|
||
|
|
||
|
if (CAuthenticationHelper::get(CAuthenticationHelper::HTTP_LOGIN_FORM) == ZBX_AUTH_FORM_HTTP
|
||
|
&& CAuthenticationHelper::get(CAuthenticationHelper::HTTP_AUTH_ENABLED) == ZBX_AUTH_HTTP_ENABLED
|
||
|
&& (!CWebUser::isLoggedIn() || CWebUser::isGuest())) {
|
||
|
redirect(
|
||
|
(new CUrl('index_http.php'))
|
||
|
->setArgument('request', $request_url)
|
||
|
->toString()
|
||
|
);
|
||
|
}
|
||
|
|
||
|
$view = [
|
||
|
'messages' => [],
|
||
|
'buttons' => [],
|
||
|
'theme' => getUserTheme(CWebUser::$data)
|
||
|
];
|
||
|
|
||
|
if (CWebUser::isLoggedIn()) {
|
||
|
$view['header'] = _('Access denied');
|
||
|
$view['messages'][] = _s('You are logged in as "%1$s".', CWebUser::$data['username']).' '.
|
||
|
_('You have no permissions to access this page.');
|
||
|
}
|
||
|
else {
|
||
|
$view['header'] = _('You are not logged in');
|
||
|
$view['messages'][] = _('You must login to view this page.');
|
||
|
$view['messages'][] = _('Possibly the session has expired or the password was changed.');
|
||
|
}
|
||
|
|
||
|
$view['messages'][] = _('If you think this message is wrong, please consult your administrators about getting the necessary permissions.');
|
||
|
|
||
|
if (!CWebUser::isLoggedIn() || CWebUser::isGuest()) {
|
||
|
$view['buttons'][] = (new CButton('login', _('Login')))
|
||
|
->setAttribute('data-login-url',
|
||
|
(new CUrl('index.php'))
|
||
|
->setArgument('request', $request_url)
|
||
|
->toString()
|
||
|
)
|
||
|
->onClick('document.location = this.dataset.loginUrl;');
|
||
|
}
|
||
|
|
||
|
if (CWebUser::isLoggedIn()) {
|
||
|
$view['buttons'][] = (new CButton('back', _s('Go to "%1$s"', CMenuHelper::getFirstLabel())))
|
||
|
->setAttribute('data-home-url', CMenuHelper::getFirstUrl())
|
||
|
->onClick('document.location = this.dataset.homeUrl;');
|
||
|
}
|
||
|
|
||
|
switch ($router->getLayout()) {
|
||
|
case 'layout.json':
|
||
|
case 'layout.widget':
|
||
|
echo (new CView('layout.json', [
|
||
|
'main_block' => json_encode([
|
||
|
'error' => [
|
||
|
'title' => $view['header'],
|
||
|
'messages' => $view['messages']
|
||
|
]
|
||
|
])
|
||
|
]))->getOutput();
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
echo (new CView('general.warning', $view))->getOutput();
|
||
|
}
|
||
|
|
||
|
session_write_close();
|
||
|
exit();
|
||
|
}
|
||
|
|
||
|
private static function terminateWithError(CRouter $router, string $error): void {
|
||
|
switch ($router->getLayout()) {
|
||
|
case 'layout.json':
|
||
|
case 'layout.widget':
|
||
|
$layout = 'layout.json';
|
||
|
break;
|
||
|
|
||
|
case null:
|
||
|
if ((array_key_exists('CONTENT_TYPE', $_SERVER) && $_SERVER['CONTENT_TYPE'] === 'application/json')
|
||
|
|| (array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER)
|
||
|
&& strcasecmp($_SERVER['HTTP_X_REQUESTED_WITH'], 'XMLHttpRequest') == 0)) {
|
||
|
$layout = 'layout.json';
|
||
|
}
|
||
|
else {
|
||
|
$layout = 'general.warning';
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
$layout = 'general.warning';
|
||
|
}
|
||
|
|
||
|
switch ($layout) {
|
||
|
case 'layout.json':
|
||
|
echo (new CView('layout.json', [
|
||
|
'main_block' => json_encode([
|
||
|
'error' => [
|
||
|
'title' => $error
|
||
|
]
|
||
|
])
|
||
|
]))->getOutput();
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
echo (new CView('general.warning', [
|
||
|
'header' => $error,
|
||
|
'messages' => [],
|
||
|
'theme' => getUserTheme(CWebUser::$data)
|
||
|
]))->getOutput();
|
||
|
}
|
||
|
|
||
|
session_write_close();
|
||
|
exit();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set layout mode using URL parameters.
|
||
|
*/
|
||
|
private function setLayoutModeByUrl() {
|
||
|
if (hasRequest('kiosk')) {
|
||
|
CViewHelper::saveLayoutMode(getRequest('kiosk') === '1' ? ZBX_LAYOUT_KIOSKMODE : ZBX_LAYOUT_NORMAL);
|
||
|
|
||
|
// Remove $_GET arguments to prevent CUrl from generating URL with 'kiosk' arguments.
|
||
|
unset($_GET['kiosk']);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize menu for main navigation. Register instance as component with 'menu.main' key.
|
||
|
*/
|
||
|
private function initComponents(): void {
|
||
|
$this->component_registry->register('router', new CRouter());
|
||
|
$this->component_registry->register('menu.main', CMenuHelper::getMainMenu());
|
||
|
$this->component_registry->register('menu.user', CMenuHelper::getUserMenu());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize module manager and load all enabled and allowed modules according to user role settings.
|
||
|
*/
|
||
|
private function initModuleManager(): void {
|
||
|
$this->module_manager = new CModuleManager($this->root_dir);
|
||
|
|
||
|
$db_modules = API::getApiService('module')->get([
|
||
|
'output' => ['moduleid', 'id', 'relative_path', 'config'],
|
||
|
'filter' => ['status' => MODULE_STATUS_ENABLED],
|
||
|
'sortfield' => 'relative_path'
|
||
|
], false);
|
||
|
|
||
|
$modules_missing = [];
|
||
|
|
||
|
foreach ($db_modules as $db_module) {
|
||
|
if (!CWebUser::checkAccess('modules.module.'.$db_module['moduleid'])) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$manifest = $this->module_manager->addModule($db_module['relative_path'], $db_module['moduleid'],
|
||
|
$db_module['id'], $db_module['config']
|
||
|
);
|
||
|
|
||
|
if (!$manifest) {
|
||
|
$modules_missing[] = $db_module['relative_path'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($modules_missing) {
|
||
|
error(_n('Cannot load module at: %1$s.', 'Cannot load modules at: %1$s.', implode(', ', $modules_missing),
|
||
|
count($modules_missing)
|
||
|
));
|
||
|
}
|
||
|
|
||
|
foreach ($this->module_manager->getNamespaces() as $namespace => $paths) {
|
||
|
$this->autoloader->addNamespace($namespace, $paths);
|
||
|
}
|
||
|
|
||
|
$this->module_manager->initModules();
|
||
|
|
||
|
array_map('error', $this->module_manager->getErrors());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check for High availability override to standalone mode, set server to use for system information checks.
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
private function setServerAddress(): void {
|
||
|
global $ZBX_SERVER, $ZBX_SERVER_PORT;
|
||
|
|
||
|
if ($ZBX_SERVER !== null) {
|
||
|
$ZBX_SERVER_PORT = $ZBX_SERVER_PORT !== null ? (int) $ZBX_SERVER_PORT : ZBX_SERVER_PORT_DEFAULT;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$ha_nodes = API::getApiService('hanode')->get([
|
||
|
'output' => ['address', 'port', 'status'],
|
||
|
'sortfield' => 'lastaccess',
|
||
|
'sortorder' => 'DESC'
|
||
|
], false);
|
||
|
|
||
|
$active_node = null;
|
||
|
|
||
|
if (count($ha_nodes) == 1) {
|
||
|
$active_node = $ha_nodes[0];
|
||
|
}
|
||
|
else {
|
||
|
foreach ($ha_nodes as $node) {
|
||
|
if ($node['status'] == ZBX_NODE_STATUS_ACTIVE) {
|
||
|
$active_node = $node;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($active_node !== null) {
|
||
|
$ZBX_SERVER = $active_node['address'];
|
||
|
$ZBX_SERVER_PORT = $active_node['port'];
|
||
|
}
|
||
|
|
||
|
if ($ZBX_SERVER_PORT !== null) {
|
||
|
$ZBX_SERVER_PORT = (int) $ZBX_SERVER_PORT;
|
||
|
}
|
||
|
}
|
||
|
}
|