. */ namespace SP\Modules\Web\Controllers; defined('APP_ROOT') || die(); use SP\Account\AccountUtil; use SP\Controller\ControllerBase; use SP\Core\Acl\ActionsInterface; use SP\Core\DiFactory; use SP\Core\Exceptions\SPException; use SP\Core\Init; use SP\Core\Language; use SP\Core\Plugin\PluginUtil; use SP\Core\SessionFactory; use SP\Core\SessionUtil; use SP\Core\Task; use SP\Core\Upgrade\Check; use SP\Html\DataGrid\DataGridAction; use SP\Html\Html; use SP\Http\Request; use SP\Mgmt\Notices\Notice; use SP\Mvc\View\Template; use SP\Util\Checks; use SP\Util\Util; /** * Clase encargada de mostrar el interface principal de la aplicación * e interfaces que requieren de un documento html completo * * @package Controller */ class MainController extends ControllerBase implements ActionsInterface { /** * Constructor * * @param Template $template Template con instancia de plantilla * @param string $page El nombre de página para la clase del body * @param bool $initialize Si es una inicialización completa * @throws \Psr\Container\ContainerExceptionInterface */ public function __construct(Template $template = null, $page = '', $initialize = true) { parent::__construct($template); $this->setPage($page); if ($initialize === true) { $this->initialize(); } } /** * Establecer la variable de página de la vista * * @param $page */ protected function setPage($page) { $this->view->assign('page', $page); } /** * Inicializar las variables para la vista principal de la aplicación * * @throws \Psr\Container\ContainerExceptionInterface */ protected function initialize() { $this->view->assign('startTime', microtime()); $this->view->addTemplate('header'); $this->view->addTemplate('body-start'); $this->view->assign('useLayout', true); $this->view->assign('isInstalled', $this->configData->isInstalled()); $this->view->assign('sk', SessionUtil::getSessionKey(true)); $this->view->assign('appInfo', Util::getAppInfo()); $this->view->assign('appVersion', Util::getVersionString()); $this->view->assign('isDemoMode', $this->configData->isDemoEnabled()); $this->view->assign('icons', $this->theme->getIcons()); $this->view->assign('logoIcon', Init::$WEBURI . '/public/images/logo_icon.png'); $this->view->assign('logoNoText', Init::$WEBURI . '/public/images/logo_icon.svg'); $this->view->assign('logo', Init::$WEBURI . '/public/images/logo_full_bg.png'); $this->view->assign('logonobg', Init::$WEBURI . '/public/images/logo_full_nobg.png'); $this->view->assign('httpsEnabled', Checks::httpsEnabled()); $this->setLoggedIn(Util::isLoggedIn($this->session)); $this->view->assign('lang', $this->loggedIn ? Language::$userLang : Language::$globalLang); $this->view->assign('loadApp', $this->session->getAuthCompleted()); try { // Cargar la clave pública en la sesión SessionUtil::loadPublicKey(); } catch (SPException $e) { debugLog($e->getMessage(), true); } $this->getResourcesLinks(); $this->setResponseHeaders(); } /** * Obtener los datos para la cabcera de la página */ public function getResourcesLinks() { $version = Util::getVersionStringNormalized(); $jsVersionHash = md5($version); $this->view->append('jsLinks', Init::$WEBROOT . '/public/js/js.php?v=' . $jsVersionHash); $this->view->append('jsLinks', Init::$WEBROOT . '/public/js/js.php?g=1&v=' . $jsVersionHash); $themeInfo = $this->theme->getThemeInfo(); if (isset($themeInfo['js'])) { $themeJsBase = urlencode($this->theme->getThemePath() . DIRECTORY_SEPARATOR . 'js'); $themeJsFiles = urlencode(implode(',', $themeInfo['js'])); $this->view->append('jsLinks', Init::$WEBROOT . '/public/js/js.php?f=' . $themeJsFiles . '&b=' . $themeJsBase . '&v=' . $jsVersionHash); } $userPreferences = $this->session->getUserPreferences(); if ($this->loggedIn && $userPreferences->getUserId() > 0) { $resultsAsCards = $userPreferences->isResultsAsCards(); } else { $resultsAsCards = $this->configData->isResultsAsCards(); } $cssVersionHash = md5($version . $resultsAsCards); $this->view->append('cssLinks', Init::$WEBROOT . '/public/css/css.php?v=' . $cssVersionHash); if (isset($themeInfo['css'])) { if ($resultsAsCards) { $themeInfo['css'][] = 'search-card.min.css'; } else { $themeInfo['css'][] = 'search-grid.min.css'; } if ($this->configData->isDokuwikiEnabled()) { $themeInfo['css'][] = 'styles-wiki.min.css'; } $themeCssBase = urlencode($this->theme->getThemePath() . DIRECTORY_SEPARATOR . 'css'); $themeCssFiles = urlencode(implode(',', $themeInfo['css'])); $this->view->append('cssLinks', Init::$WEBROOT . '/public/css/css.php?f=' . $themeCssFiles . '&b=' . $themeCssBase . '&v=' . $jsVersionHash); } // Cargar los recursos de los plugins foreach (PluginUtil::getLoadedPlugins() as $Plugin) { $base = str_replace(BASE_PATH, '', $Plugin->getBase()); $jsResources = $Plugin->getJsResources(); $cssResources = $Plugin->getCssResources(); if (count($jsResources) > 0) { $this->view->append('jsLinks', Init::$WEBROOT . '/public/js/js.php?f=' . urlencode(implode(',', $jsResources)) . '&b=' . urlencode($base . DIRECTORY_SEPARATOR . 'js') . '&v=' . $jsVersionHash); } if (count($cssResources) > 0) { $this->view->append('cssLinks', Init::$WEBROOT . '/public/css/css.php?f=' . urlencode(implode(',', $cssResources)) . '&b=' . urlencode($base . DIRECTORY_SEPARATOR . 'css') . '&v=' . $jsVersionHash); } } } /** * Establecer las cabeceras HTTP */ private function setResponseHeaders() { // UTF8 Headers header('Content-Type: text/html; charset=UTF-8'); // Cache Control header('Cache-Control: public, no-cache, max-age=0, must-revalidate'); header('Pragma: public; max-age=0'); } /** * @throws SPException * @throws \Psr\Container\ContainerExceptionInterface */ public function indexAction() { $this->initialize(); $this->getMain(); } /** * Obtener los datos para el interface principal de sysPass * * @throws \SP\Core\Exceptions\SPException */ public function getMain() { $this->setPage('main'); $this->getSessionBar(); $this->getMenu(); $this->view->addTemplate('body-content'); $this->view->addTemplate('body-footer'); $this->view->addTemplate('body-end'); } /** * Obtener los datos para la mostrar la barra de sesión * * @throws \SP\Core\Exceptions\SPException */ private function getSessionBar() { $this->view->addTemplate('sessionbar'); $userType = null; if ($this->userData->isIsAdminApp()) { $userType = $this->icons->getIconAppAdmin(); } elseif ($this->userData->isIsAdminAcc()) { $userType = $this->icons->getIconAccAdmin(); } $this->view->assign('userType', $userType); $this->view->assign('userId', $this->userData->getId()); $this->view->assign('userLogin', mb_strtoupper($this->userData->getLogin())); $this->view->assign('userName', $this->userData->getName() ?: mb_strtoupper($this->view->userLogin)); $this->view->assign('userGroup', $this->userData->getUserGroupName()); $this->view->assign('showPassIcon', !($this->configData->isLdapEnabled() && $this->userData->isIsLdap())); $this->view->assign('userNotices', count(Notice::getItem()->getAllActiveForUser())); } /** * Obtener los datos para mostrar el menú de acciones */ private function getMenu() { $this->view->addTemplate('body-header-menu'); $ActionSearch = new DataGridAction(); $ActionSearch->setId(self::ACCOUNT_SEARCH); $ActionSearch->setTitle(__('Buscar')); $ActionSearch->setIcon($this->icons->getIconSearch()); $ActionSearch->setData(['historyReset' => 1, 'view' => 'search']); $this->view->append('actions', $ActionSearch); if ($this->acl->checkUserAccess(self::ACCOUNT_CREATE)) { $ActionNew = new DataGridAction(); $ActionNew->setId(self::ACCOUNT_CREATE); $ActionNew->setTitle(__('Nueva Cuenta')); $ActionNew->setIcon($this->icons->getIconAdd()); $ActionNew->setData(['historyReset' => 0, 'view' => 'account']); $this->view->append('actions', $ActionNew); } if ($this->acl->checkUserAccess(self::ACCESS_MANAGE)) { $ActionUsr = new DataGridAction(); $ActionUsr->setId(self::ACCESS_MANAGE); $ActionUsr->setTitle(__('Usuarios y Accesos')); $ActionUsr->setIcon($this->icons->getIconAccount()); $ActionUsr->setData(['historyReset' => 0, 'view' => 'datatabs']); $this->view->append('actions', $ActionUsr); } if ($this->acl->checkUserAccess(self::ITEMS_MANAGE)) { $ActionMgm = new DataGridAction(); $ActionMgm->setId(self::ITEMS_MANAGE); $ActionMgm->setTitle(__('Elementos y Personalización')); $ActionMgm->setIcon($this->icons->getIconGroup()); $ActionMgm->setData(['historyReset' => 0, 'view' => 'datatabs']); $this->view->append('actions', $ActionMgm); } if ($this->acl->checkUserAccess(self::CONFIG)) { $ActionConfig = new DataGridAction(); $ActionConfig->setId(self::CONFIG); $ActionConfig->setTitle(__('Configuración')); $ActionConfig->setIcon($this->icons->getIconSettings()); $ActionConfig->setData(['historyReset' => 1, 'view' => 'config']); $this->view->append('actions', $ActionConfig); } if ($this->acl->checkUserAccess(self::EVENTLOG) && $this->configData->isLogEnabled()) { $ActionEventlog = new DataGridAction(); $ActionEventlog->setId(self::EVENTLOG); $ActionEventlog->setTitle(__('Registro de Eventos')); $ActionEventlog->setIcon($this->icons->getIconHeadline()); $ActionEventlog->setData(['historyReset' => 1, 'view' => 'eventlog']); $this->view->append('actions', $ActionEventlog); } } /** * Obtener los datos para el interface de login */ public function getLogin() { $this->setPage('login'); if (SessionFactory::getLoggedOut() === true) { SessionFactory::setLoggedOut(false); $this->view->assign('loggedOut', 1); } else { $this->view->assign('loggedOut', 0); } $this->view->addTemplate('login'); $this->view->addTemplate('body-footer'); $this->view->addTemplate('body-end'); $this->view->assign('useLayout', false); $this->view->assign('mailEnabled', $this->configData->isMailEnabled()); $this->view->assign('updated', SessionFactory::getAppUpdated()); SessionFactory::setAppUpdated(false); $getParams = []; // Comprobar y parsear los parámetros GET para pasarlos como POST en los inputs if (count($_GET) > 0) { foreach ($_GET as $param => $value) { $getParams['g_' . Html::sanitizeFull($param)] = Html::sanitizeFull($value); } } $this->view->assign('getParams', $getParams); $this->view(); exit(); } /** * Obtener los datos para el interface de logout */ public function getLogout() { $this->setPage('logout'); $this->view->addTemplate('logout'); $this->view->addTemplate('body-footer'); $this->view->addTemplate('body-end'); $this->view(); exit(); } /** * Obtener los datos para el interface del instalador */ public function getInstaller() { $this->setPage('install'); $this->view->addTemplate('body-header'); $errors = []; if (!Checks::checkPhpVersion()) { $errors[] = [ 'type' => SPException::SP_CRITICAL, 'description' => __('Versión de PHP requerida >= ') . ' 5.6.0 <= 7.0', 'hint' => __('Actualice la versión de PHP para que la aplicación funcione correctamente') ]; } $modules = Checks::checkModules(); if (count($modules) > 0) { foreach ($modules as $module) { $error[] = [ 'type' => SPException::SP_WARNING, 'description' => sprintf('%s (%s)', __('Módulo no disponible'), $module), 'hint' => __('Sin este módulo la aplicación puede no funcionar correctamente.') ]; } } if (@file_exists(__FILE__ . "\0Nullbyte")) { $errors[] = [ 'type' => SPException::SP_WARNING, 'description' => __('La version de PHP es vulnerable al ataque NULL Byte (CVE-2006-7243)'), 'hint' => __('Actualice la versión de PHP para usar sysPass de forma segura')]; } if (!Checks::secureRNGIsAvailable()) { $errors[] = [ 'type' => SPException::SP_WARNING, 'description' => __('No se encuentra el generador de números aleatorios.'), 'hint' => __('Sin esta función un atacante puede utilizar su cuenta al resetear la clave')]; } $this->view->assign('errors', $errors); $this->view->assign('langsAvailable', Language::getAvailableLanguages()); $this->view->assign('langBrowser', Language::$globalLang); $this->view->addTemplate('install'); $this->view->addTemplate('body-footer'); $this->view->addTemplate('body-end'); } /** * Obtener los datos para el interface de error */ public function getError() { $this->setPage('error'); if (!Checks::isAjax()) { $this->view->addTemplate('body-header'); $this->view->addTemplate('error'); $this->view->addTemplate('body-footer'); } else { $this->view->addTemplate('error'); } $this->view(); exit(); } /** * Obtener los datos para el interface de actualización de componentes * * @param $version */ public function getUpgrade($version) { $this->setPage('upgrade'); $this->view->addTemplate('body-header'); $this->view->addTemplate('upgrade'); $this->view->addTemplate('body-footer'); $this->view->addTemplate('body-end'); $action = Request::analyze('a'); $type = Request::analyze('type'); $this->view->assign('action', $action); $this->view->assign('type', $type); $this->view->assign('version', $version); $this->view->assign('upgradeVersion', Util::getVersionStringNormalized()); $this->view->assign('taskId', Task::genTaskId('masterpass')); if (Util::checkVersion($version, '130.16011001')) { $this->view->assign('checkConstraints', Check::checkConstraints()); $constraints = []; foreach ($this->view->checkConstraints as $key => $val) { if ($val > 0) { $constraints[] = sprintf('%s : %s', $key, $val); } } $this->view->assign('constraints', $constraints); } if (Util::checkVersion($version, '210.17022601')) { $this->view->assign('numAccounts', AccountUtil::getTotalNumAccounts()); } $this->view(); exit(); } /** * Obtener los datos para el interface de comprobación de actualizaciones * * @throws \Psr\Container\ContainerExceptionInterface */ public function getCheckUpdates() { $this->view->addTemplate('update'); $this->view->assign('hasUpdates', false); $this->view->assign('updateStatus', null); if ($this->configData->isCheckUpdates()) { $updates = Util::checkUpdates(); if (is_array($updates)) { $description = nl2br($updates['description']); $version = $updates['version']; $this->view->assign('hasUpdates', true); $this->view->assign('title', $updates['title']); $this->view->assign('url', $updates['url']); $this->view->assign('description', sprintf('%s - %s

%s', __('Descargar nueva versión'), $version, $description)); } else { $this->view->assign('updateStatus', $updates); } } if ($this->configData->isChecknotices()) { $notices = Util::checkNotices(); $numNotices = count($notices); $noticesTitle = ''; if ($notices !== false && $numNotices > 0) { $noticesTitle = __('Avisos de sysPass') . '
'; foreach ($notices as $notice) { $noticesTitle .= '
' . $notice[0]; } } $this->view->assign('numNotices', $numNotices); $this->view->assign('noticesTitle', $noticesTitle); } } /** * Realizar las acciones del controlador * * @param mixed $type Tipo de acción */ public function doAction($type = null) { $this->setPage($type); try { switch ($type) { case 'prelogin.passreset': $this->getPassReset(); break; } DiFactory::getEventDispatcher()->notifyEvent('main.' . $type, $this); } catch (SPException $e) { $this->showError(self::ERR_EXCEPTION); } } /** * Obtener los datos para el interface de restablecimiento de clave de usuario */ public function getPassReset() { $this->setPage('passreset'); $this->view->addTemplate('body-header'); if ($this->configData->isMailEnabled() || Request::analyze('f', 0) === 1) { $this->view->addTemplate('passreset'); $this->view->assign('login', Request::analyze('login')); $this->view->assign('email', Request::analyze('email')); $this->view->assign('action', Request::analyze('a')); $this->view->assign('hash', Request::analyze('h')); $this->view->assign('time', Request::analyze('t')); $this->view->assign('passReset', $this->view->action === 'passreset' && !empty($this->view->hash) && !empty($this->view->time)); } else { $this->showError(self::ERR_UNAVAILABLE, false); } $this->view->addTemplate('body-footer'); $this->view->addTemplate('body-end'); $this->view(); exit(); } }