diff --git a/app/modules/web/Controllers/ConfigBackupController.php b/app/modules/web/Controllers/ConfigBackupController.php index d3c730ba..4023ca07 100644 --- a/app/modules/web/Controllers/ConfigBackupController.php +++ b/app/modules/web/Controllers/ConfigBackupController.php @@ -160,7 +160,7 @@ final class ConfigBackupController extends SimpleControllerBase $this->eventDispatcher->notifyEvent('download.exportFile', new Event($this, EventMessage::factory() ->addDescription(__u('Archivo descargado')) - ->addDetail(__u('Archivo'), $file->getFile())) + ->addDetail(__u('Archivo'), str_replace(APP_ROOT, '', $file->getFile()))) ); $response = $this->router->response(); @@ -202,7 +202,7 @@ final class ConfigBackupController extends SimpleControllerBase $this->eventDispatcher->notifyEvent('download.backupAppFile', new Event($this, EventMessage::factory() ->addDescription(__u('Archivo descargado')) - ->addDetail(__u('Archivo'), $file->getFile())) + ->addDetail(__u('Archivo'), str_replace(APP_ROOT, '', $file->getFile()))) ); $response = $this->router->response(); @@ -244,7 +244,7 @@ final class ConfigBackupController extends SimpleControllerBase $this->eventDispatcher->notifyEvent('download.backupDbFile', new Event($this, EventMessage::factory() ->addDescription(__u('Archivo descargado')) - ->addDetail(__u('Archivo'), $file->getFile())) + ->addDetail(__u('Archivo'), str_replace(APP_ROOT, '', $file->getFile()))) ); $response = $this->router->response(); diff --git a/app/modules/web/Controllers/ConfigGeneralController.php b/app/modules/web/Controllers/ConfigGeneralController.php index df293f6d..b4919763 100644 --- a/app/modules/web/Controllers/ConfigGeneralController.php +++ b/app/modules/web/Controllers/ConfigGeneralController.php @@ -27,10 +27,13 @@ namespace SP\Modules\Web\Controllers; use SP\Config\ConfigUtil; use SP\Core\Acl\Acl; use SP\Core\Acl\UnauthorizedPageException; +use SP\Core\Context\SessionContext; use SP\Core\Events\Event; use SP\Core\Events\EventMessage; use SP\Http\JsonResponse; use SP\Modules\Web\Controllers\Traits\ConfigTrait; +use SP\Services\Config\ConfigBackupService; +use SP\Storage\File\FileHandler; /** * Class ConfigGeneral @@ -168,6 +171,95 @@ final class ConfigGeneralController extends SimpleControllerBase }); } + /** + * @return bool + * @throws \SP\Core\Exceptions\SPException + */ + public function downloadLogAction() + { + $this->checkSecurityToken($this->previousSk, $this->request); + + try { + SessionContext::close(); + + $file = new FileHandler(LOG_FILE); + $file->checkFileExists(); + + $this->eventDispatcher->notifyEvent('download.logFile', + new Event($this, EventMessage::factory() + ->addDescription(__u('Archivo descargado')) + ->addDetail(__u('Archivo'), str_replace(APP_ROOT, '', $file->getFile()))) + ); + + $response = $this->router->response(); + $response->header('Cache-Control', 'max-age=60, must-revalidate'); + $response->header('Content-length', $file->getFileSize()); + $response->header('Content-type', $file->getFileType()); + $response->header('Content-Description', ' sysPass file'); + $response->header('Content-transfer-encoding', 'chunked'); + $response->header('Content-Disposition', 'attachment; filename="' . basename($file->getFile()) . '"'); + $response->header('Set-Cookie', 'fileDownload=true; path=/'); + $response->send(); + + $file->readChunked(); + } catch (\Exception $e) { + processException($e); + + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + } + + return ''; + } + + /** + * @param string $type + * + * @return bool + * @throws \SP\Core\Exceptions\SPException + */ + public function downloadConfigBackupAction($type) + { + $this->checkSecurityToken($this->previousSk, $this->request); + + try { + $this->eventDispatcher->notifyEvent('download.configBackupFile', + new Event($this, EventMessage::factory() + ->addDescription(__u('Archivo descargado')) + ->addDetail(__u('Archivo'), 'config.json')) + ); + + $configBackupService = $this->dic->get(ConfigBackupService::class); + + switch ($type) { + case 'json': + $data = ConfigBackupService::configToJson($configBackupService->getBackup()); + break; + default: + throw new \RuntimeException('Not implemented'); + } + + $response = $this->router->response(); + $response->header('Cache-Control', 'max-age=60, must-revalidate'); + $response->header('Content-length', strlen($data)); + $response->header('Content-type', 'application/json'); + $response->header('Content-Description', ' sysPass file'); + $response->header('Content-transfer-encoding', 'chunked'); + $response->header('Content-Disposition', 'attachment; filename="config.json"'); + $response->header('Set-Cookie', 'fileDownload=true; path=/'); + $response->header('Content-transfer-encoding', 'binary'); + $response->header('Set-Cookie', 'fileDownload=true; path=/'); + + $response->body($data); + $response->send(true); + } catch (\Exception $e) { + processException($e); + + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + } + + return ''; + } + /** * @return bool */ diff --git a/app/modules/web/Controllers/ConfigManagerController.php b/app/modules/web/Controllers/ConfigManagerController.php index 3675a145..550fafdf 100644 --- a/app/modules/web/Controllers/ConfigManagerController.php +++ b/app/modules/web/Controllers/ConfigManagerController.php @@ -354,6 +354,11 @@ final class ConfigManagerController extends ControllerBase $template->assign('missingExtensions', $this->extensionChecker->getMissing()); $template->assign('downloadRate', round(Util::getMaxDownloadChunk() / 1024 / 1024)); + $isDemo = $this->configData->isDemoEnabled(); + + $template->assign('downloadConfigBackup', !$isDemo && $this->userData->getIsAdminApp()); + $template->assign('downloadLog', !$isDemo && is_readable(LOG_FILE) && $this->userData->getIsAdminApp()); + return new DataTab(__('Información'), $template); } diff --git a/app/modules/web/themes/material-blue/views/config/info.inc b/app/modules/web/themes/material-blue/views/config/info.inc index eff15099..25b9f0ca 100644 --- a/app/modules/web/themes/material-blue/views/config/info.inc +++ b/app/modules/web/themes/material-blue/views/config/info.inc @@ -88,6 +88,18 @@
+ + +
+ + + + +
+ @@ -152,6 +164,25 @@ + + + + + + + +
+ + + + + + + +
diff --git a/composer.json b/composer.json index 88cedacf..cd441995 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,8 @@ "ext-gd": "*", "ext-json": "*", "ext-gettext": "*", - "ext-fileinfo": "*" + "ext-fileinfo": "*", + "ext-zlib": "*" }, "require-dev": { "phpunit/phpunit": "^6", diff --git a/lib/SP/Bootstrap.php b/lib/SP/Bootstrap.php index af6e5a4e..c3f54386 100644 --- a/lib/SP/Bootstrap.php +++ b/lib/SP/Bootstrap.php @@ -316,18 +316,20 @@ final class Bootstrap */ public function initPHPVars() { - // Set debug mode if an Xdebug session is active - if ($this->router->request()->cookies()->get('XDEBUG_SESSION') - && !defined('DEBUG') - ) { - define('DEBUG', true); - } - if (defined('DEBUG') && DEBUG) { Debug::enable(); } else { - error_reporting(E_ALL & ~(E_DEPRECATED | E_STRICT | E_NOTICE)); - ini_set('display_errors', 'Off'); + // Set debug mode if an Xdebug session is active + if (($this->router->request()->cookies()->get('XDEBUG_SESSION') + || $this->configData->isDebug()) + && !defined('DEBUG') + ) { + define('DEBUG', true); + Debug::enable(); + } else { + error_reporting(E_ALL & ~(E_DEPRECATED | E_STRICT | E_NOTICE)); + ini_set('display_errors', 0); + } } if (!file_exists(LOG_FILE) diff --git a/lib/SP/Config/Config.php b/lib/SP/Config/Config.php index 8a013d02..9102dde1 100644 --- a/lib/SP/Config/Config.php +++ b/lib/SP/Config/Config.php @@ -144,6 +144,8 @@ final class Config * * @return Config * @throws FileException + * @throws \DI\DependencyException + * @throws \DI\NotFoundException */ public function saveConfig(ConfigData $configData, $backup = true) { diff --git a/lib/SP/Http/Json.php b/lib/SP/Http/Json.php index 76ced573..537071c4 100644 --- a/lib/SP/Http/Json.php +++ b/lib/SP/Http/Json.php @@ -165,14 +165,15 @@ final class Json /** * Devuelve una cadena en formato JSON * - * @param $data + * @param mixed $data + * @param int $flags JSON_* flags * * @return string La cadena en formato JSON - * @throws \SP\Core\Exceptions\SPException + * @throws SPException */ - public static function getJson($data) + public static function getJson($data, $flags = 0) { - $json = json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR); + $json = json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR | $flags); if ($json === false || json_last_error() !== JSON_ERROR_NONE) { throw new SPException( diff --git a/lib/SP/Services/Config/ConfigBackupService.php b/lib/SP/Services/Config/ConfigBackupService.php index a6185be0..bbbbe726 100644 --- a/lib/SP/Services/Config/ConfigBackupService.php +++ b/lib/SP/Services/Config/ConfigBackupService.php @@ -25,6 +25,7 @@ namespace SP\Services\Config; use SP\Config\ConfigData; +use SP\Http\Json; use SP\Repositories\NoSuchItemException; use SP\Services\Service; use SP\Services\ServiceException; @@ -42,6 +43,25 @@ final class ConfigBackupService extends Service */ protected $configService; + /** + * @param string $configData + * + * @return string + * @throws \SP\Core\Exceptions\SPException + */ + public static function configToJson(string $configData): string + { + return Json::getJson(Util::unserialize(ConfigData::class, $configData), JSON_PRETTY_PRINT); + } + + /** + * @param string $configData + */ + public static function configToXml(string $configData) + { + throw new \RuntimeException('Not implemented'); + } + /** * Backs up the config data into the database * @@ -70,8 +90,23 @@ final class ConfigBackupService extends Service /** * @return ConfigData * @throws ServiceException + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \SP\Storage\File\FileException */ public function restore() + { + return $this->config->saveConfig(Util::unserialize( + ConfigData::class, + $this->getBackup()) + )->getConfigData(); + } + + /** + * @return ConfigData + * @throws ServiceException + */ + public function getBackup(): string { try { $data = $this->configService->getByParam('config_backup'); @@ -80,7 +115,7 @@ final class ConfigBackupService extends Service throw new ServiceException(__u('No es posible restaurar la configuración')); } - return $this->config->saveConfig($this->unpackConfigData($data))->getConfigData(); + return gzuncompress(hex2bin($data)); } catch (NoSuchItemException $e) { processException($e); @@ -88,16 +123,6 @@ final class ConfigBackupService extends Service } } - /** - * @param string $configData - * - * @return ConfigData - */ - private function unpackConfigData(string $configData) - { - return Util::unserialize(ConfigData::class, gzuncompress(hex2bin($configData))); - } - protected function initialize() { $this->configService = $this->dic->get(ConfigService::class);