From 5a549532f6debfb527d9dc9227cf3b53edef1b73 Mon Sep 17 00:00:00 2001 From: nuxsmin Date: Mon, 12 Nov 2018 00:12:01 +0100 Subject: [PATCH] * [MOD] Use config cache file instead of storing it in PHP's session * [MOD] Unit testing tweaks in order to renew config cache Signed-off-by: nuxsmin --- app/locales/es_ES/LC_MESSAGES/messages.mo | Bin 104573 -> 104606 bytes app/locales/es_ES/LC_MESSAGES/messages.po | 231 +++++++++--------- app/modules/api/Init.php | 3 +- .../web/Controllers/ControllerBase.php | 3 +- .../web/Controllers/Helpers/LayoutHelper.php | 2 +- .../web/Controllers/SimpleControllerBase.php | 1 + app/modules/web/Init.php | 7 +- .../material-blue/views/config/accounts.inc | 1 - .../material-blue/views/config/general.inc | 1 - lib/Definitions.php | 5 +- lib/SP/Config/Config.php | 109 +++++---- lib/SP/Core/Context/ContextInterface.php | 19 +- lib/SP/Core/Context/SessionContext.php | 108 ++++---- lib/SP/Core/Context/StatelessContext.php | 27 +- lib/SP/Mvc/Controller/ControllerTrait.php | 5 +- lib/SP/Services/Account/AccountFilterUser.php | 5 +- lib/SP/Services/Export/XmlExportService.php | 9 +- lib/SP/Services/Install/Installer.php | 6 +- lib/SP/Storage/File/FileCache.php | 7 +- lib/SP/Storage/File/FileCacheBase.php | 18 +- lib/SP/Storage/File/FileCacheInterface.php | 7 +- lib/SP/Storage/File/FileHandler.php | 50 +++- tests/SP/Config/ConfigTest.php | 20 +- .../Repositories/PublicLinkRepositoryTest.php | 2 +- .../Config/ConfigBackupServiceTest.php | 2 + .../Services/Export/XmlExportServiceTest.php | 63 ++--- .../Services/Export/XmlVerifyServiceTest.php | 28 ++- tests/SP/Services/Install/InstallerTest.php | 2 + tests/SP/bootstrap.php | 8 - tests/res/config/config.xml | 8 +- tests/res/datasets/syspass_import.xml | 140 +++++++++++ 31 files changed, 529 insertions(+), 368 deletions(-) diff --git a/app/locales/es_ES/LC_MESSAGES/messages.mo b/app/locales/es_ES/LC_MESSAGES/messages.mo index bd1c8923043b41f239390626fc04e56339fc41c6..5a1e94bddb594e9c44a330fe175d77bb0ba46e76 100644 GIT binary patch delta 27533 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

rpSW)tgMm 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\n" "Language-Team: nuxsmin@syspass.org\n" "Language: es_ES\n" @@ -537,7 +537,7 @@ msgstr "Grupo actualizado" msgid "Group deleted" msgstr "Grupo eliminado" -#: app/modules/api/Init.php:149 +#: app/modules/api/Init.php:150 msgid "Updating needed" msgstr "Es necesario actualizar" @@ -727,7 +727,7 @@ msgstr "Archivo visualizado" #: app/modules/web/Controllers/ConfigBackupController.php:255 #: app/modules/web/Controllers/ConfigGeneralController.php:195 #: app/modules/web/Controllers/ConfigGeneralController.php:236 -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:123 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:124 #: app/modules/web/themes/material-blue/views/config/import.inc:72 msgid "File" msgstr "Archivo" @@ -1118,10 +1118,10 @@ msgid "Verification of exported data finished" msgstr "Verificación de datos exportados finalizada" #: app/modules/web/Controllers/ConfigBackupController.php:122 -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:114 -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:255 -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:306 -#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:163 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:115 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:256 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:307 +#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:164 #: app/modules/web/themes/material-blue/views/config/info.inc:49 #: app/modules/web/themes/material-blue/views/plugin/plugin.inc:34 msgid "Version" @@ -1294,7 +1294,7 @@ msgstr "Importación finalizada" #: app/modules/web/Controllers/ConfigImportController.php:82 #: app/modules/web/Controllers/ConfigImportController.php:85 #: lib/SP/Services/Backup/FileBackupService.php:120 -#: lib/SP/Services/Export/XmlExportService.php:205 +#: lib/SP/Services/Export/XmlExportService.php:201 msgid "Please check out the event log for more details" msgstr "Revise el registro de eventos para más detalles" @@ -1570,10 +1570,10 @@ msgstr "Ver Actual" #: app/modules/web/themes/material-blue/views/_partials/error.inc:15 #: app/modules/web/themes/material-blue/views/account/account-editpass.inc:123 #: app/modules/web/themes/material-blue/views/account/account-request.inc:67 -#: app/modules/web/themes/material-blue/views/config/accounts.inc:367 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:366 #: app/modules/web/themes/material-blue/views/config/backup.inc:173 #: app/modules/web/themes/material-blue/views/config/encryption.inc:310 -#: app/modules/web/themes/material-blue/views/config/general.inc:28 +#: app/modules/web/themes/material-blue/views/config/general.inc:27 #: app/modules/web/themes/material-blue/views/config/import.inc:91 #: app/modules/web/themes/material-blue/views/config/info.inc:193 #: app/modules/web/themes/material-blue/views/config/ldap.inc:295 @@ -1994,7 +1994,7 @@ msgstr "Vaciar registro de eventos" #. (itstool) path: action/text #: app/modules/web/Controllers/Helpers/Grid/FileGrid.php:94 #: app/modules/web/themes/material-blue/views/account/account.inc:41 -#: app/modules/web/themes/material-blue/views/config/accounts.inc:280 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:279 #: app/modules/web/themes/material-blue/views/itemshow/user_profile.inc:150 #: app/modules/web/themes/material-blue/views/itemshow/user_profile.inc:410 #: app/config/actions.xml:19 @@ -3099,12 +3099,12 @@ msgstr "Primera página" msgid "Last page" msgstr "Última página" -#: lib/SP/Bootstrap.php:278 +#: lib/SP/Bootstrap.php:279 #, php-format msgid "Required PHP version >= %s <= %s" msgstr "Versión de PHP requerida >= %s <= %s" -#: lib/SP/Bootstrap.php:280 +#: lib/SP/Bootstrap.php:281 msgid "Please update the PHP version to run sysPass" msgstr "" "Actualice la versión de PHP para que la aplicación funcione correctamente" @@ -3161,7 +3161,7 @@ msgstr "Contexto inválido" msgid "Context not initialized" msgstr "Contexto no inicializado" -#: lib/SP/Core/Context/SessionContext.php:547 +#: lib/SP/Core/Context/SessionContext.php:490 msgid "Session cannot be initialized" msgstr "La sesión no puede ser inicializada" @@ -3204,12 +3204,12 @@ msgstr "Error de codificación" msgid "Invalid XML-RPC response" msgstr "Respuesta XML-RPC inválida" -#: lib/SP/Mvc/Controller/ControllerTrait.php:70 +#: lib/SP/Mvc/Controller/ControllerTrait.php:73 msgid "Session not started or timed out" msgstr "La sesión no se ha iniciado o ha caducado" -#: lib/SP/Mvc/Controller/ControllerTrait.php:115 -#: lib/SP/Mvc/Controller/ControllerTrait.php:124 +#: lib/SP/Mvc/Controller/ControllerTrait.php:118 +#: lib/SP/Mvc/Controller/ControllerTrait.php:127 msgid "Invalid Action" msgstr "Acción Inválida" @@ -4080,34 +4080,33 @@ msgstr "Error al eliminar los campos" msgid "Field type not found" msgstr "Tipo de campo no encontrado" -#: lib/SP/Services/Export/XmlExportService.php:127 -#: lib/SP/Storage/File/FileCache.php:74 -#: lib/SP/Storage/File/FileCachePacked.php:116 +#: lib/SP/Services/Export/XmlExportService.php:125 +#: lib/SP/Storage/File/FileCacheBase.php:98 #, php-format msgid "Unable to create the directory (%s)" msgstr "No es posible crear el directorio (%s)" -#: lib/SP/Services/Export/XmlExportService.php:203 +#: lib/SP/Services/Export/XmlExportService.php:199 msgid "Error while exporting" msgstr "Error al realizar la exportación" -#: lib/SP/Services/Export/XmlExportService.php:271 +#: lib/SP/Services/Export/XmlExportService.php:267 msgid "Exporting categories" msgstr "Exportando categorías" -#: lib/SP/Services/Export/XmlExportService.php:383 +#: lib/SP/Services/Export/XmlExportService.php:379 msgid "Exporting clients" msgstr "Exportando clientes" -#: lib/SP/Services/Export/XmlExportService.php:429 +#: lib/SP/Services/Export/XmlExportService.php:425 msgid "Exporting tags" msgstr "Exportando etiquetas" -#: lib/SP/Services/Export/XmlExportService.php:473 +#: lib/SP/Services/Export/XmlExportService.php:469 msgid "Exporting accounts" msgstr "Exportando cuentas" -#: lib/SP/Services/Export/XmlExportService.php:583 +#: lib/SP/Services/Export/XmlExportService.php:579 msgid "Error while creating the XML file" msgstr "Error al crear el archivo XML" @@ -4519,18 +4518,18 @@ msgstr "Segundos" msgid "IP address not set" msgstr "Dirección IP no establecida" -#: lib/SP/Services/Upgrade/UpgradeAppService.php:65 -#: lib/SP/Services/Upgrade/UpgradeAppService.php:88 +#: lib/SP/Services/Upgrade/UpgradeAppService.php:66 +#: lib/SP/Services/Upgrade/UpgradeAppService.php:89 msgid "Update Application" msgstr "Actualizar Aplicación" -#: lib/SP/Services/Upgrade/UpgradeAppService.php:72 +#: lib/SP/Services/Upgrade/UpgradeAppService.php:73 msgid "Error while applying the application update" msgstr "Error al aplicar la actualización de la aplicación" -#: lib/SP/Services/Upgrade/UpgradeAppService.php:74 -#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:100 -#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:108 +#: lib/SP/Services/Upgrade/UpgradeAppService.php:75 +#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:101 +#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:109 msgid "Please, check the event log for more details" msgstr "Compruebe el registro de eventos para más detalles" @@ -4539,36 +4538,36 @@ msgstr "Compruebe el registro de eventos para más detalles" msgid "API authorizations update" msgstr "Actualización de autorizaciones API" -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:71 -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:207 -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:254 -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:305 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:72 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:208 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:255 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:306 msgid "Update Configuration" msgstr "Actualizar Configuración" -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:83 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:84 msgid "Parameter" msgstr "Parámetro" -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:122 -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:126 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:123 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:127 msgid "Error while updating the configuration" msgstr "Error al actualizar la configuración" -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:282 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:283 msgid "MIME type set for this extension" msgstr "Tipo MIME establecido para esta extensión" -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:283 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:284 msgid "MIME type" msgstr "Tipo MIME" -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:284 -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:293 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:285 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:294 msgid "Extension" msgstr "Extensión" -#: lib/SP/Services/Upgrade/UpgradeConfigService.php:292 +#: lib/SP/Services/Upgrade/UpgradeConfigService.php:293 msgid "MIME type not found for this extension" msgstr "Tipo MIME no encontrado para esta extensión" @@ -4581,27 +4580,27 @@ msgstr "Tipo MIME no encontrado para esta extensión" msgid "Custom fields update" msgstr "Actualización de campos personalizados" -#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:91 -#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:122 +#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:92 +#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:123 msgid "Update DB" msgstr "Actualizar BBDD" -#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:98 +#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:99 msgid "Error while applying an auxiliary update" msgstr "Error al aplicar la actualización auxiliar" -#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:106 -#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:177 -#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:181 +#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:107 +#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:178 +#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:182 msgid "Error while updating the database" msgstr "Error al aplicar la actualización de la Base de Datos" -#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:154 -#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:156 +#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:155 +#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:157 msgid "Update file does not contain data" msgstr "El archivo de actualización no contiene datos" -#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:187 +#: lib/SP/Services/Upgrade/UpgradeDatabaseService.php:188 msgid "Database updating was completed successfully." msgstr "Actualización de la Base de Datos realizada correctamente." @@ -4683,63 +4682,64 @@ msgstr "Compruebe los datos de conexión" msgid "Error while querying" msgstr "Error en la consulta" -#: lib/SP/Storage/File/FileCachePacked.php:57 +#: lib/SP/Storage/File/FileCachePacked.php:45 #, php-format msgid "Error while decompressing the file data (%s)" msgstr "Error al descomprimir datos del archivo (%s)" -#: lib/SP/Storage/File/FileCachePacked.php:61 +#: lib/SP/Storage/File/FileCachePacked.php:51 msgid "Error while retrieving the data" msgstr "Error al obtener los datos" -#: lib/SP/Storage/File/FileCachePacked.php:99 +#: lib/SP/Storage/File/FileCachePacked.php:70 #, php-format msgid "Error while compressing the file data (%s)" msgstr "Error al comprimir datos del archivo (%s)" -#: lib/SP/Storage/File/FileCachePacked.php:144 -msgid "Data not loaded" -msgstr "Datos no cargados" - -#: lib/SP/Storage/File/FileHandler.php:72 -#: lib/SP/Storage/File/FileHandler.php:135 +#: lib/SP/Storage/File/FileHandler.php:76 +#: lib/SP/Storage/File/FileHandler.php:165 #, php-format msgid "Unable to read/write the file (%s)" msgstr "No es posible leer/escribir el archivo (%s)" -#: lib/SP/Storage/File/FileHandler.php:89 +#: lib/SP/Storage/File/FileHandler.php:101 #, php-format msgid "Unable to open the file (%s)" msgstr "No es posible abrir el archivo (%s)" -#: lib/SP/Storage/File/FileHandler.php:104 -#: lib/SP/Storage/File/FileHandler.php:118 +#: lib/SP/Storage/File/FileHandler.php:119 +#, php-format +msgid "Unable to obtain a lock (%s)" +msgstr "No es posible obtener un bloqueo (%s)" + +#: lib/SP/Storage/File/FileHandler.php:134 +#: lib/SP/Storage/File/FileHandler.php:148 #, php-format msgid "Unable to read from file (%s)" msgstr "No es posible leer desde el archivo (%s)" -#: lib/SP/Storage/File/FileHandler.php:173 +#: lib/SP/Storage/File/FileHandler.php:207 #, php-format msgid "Unable to close the file (%s)" msgstr "No es posible cerrar el archivo (%s)" -#: lib/SP/Storage/File/FileHandler.php:219 +#: lib/SP/Storage/File/FileHandler.php:261 #, php-format msgid "Unable to write in file (%s)" msgstr "No es posible escribir el archivo (%s)" -#: lib/SP/Storage/File/FileHandler.php:234 +#: lib/SP/Storage/File/FileHandler.php:276 #, php-format msgid "File not found (%s)" msgstr "Archivo no encontrado (%s)" -#: lib/SP/Storage/File/FileHandler.php:259 -#: lib/SP/Storage/File/FileHandler.php:314 +#: lib/SP/Storage/File/FileHandler.php:301 +#: lib/SP/Storage/File/FileHandler.php:356 #, php-format msgid "Unable to read/write file (%s)" msgstr "No es posible leer el archivo (%s)" -#: lib/SP/Storage/File/FileHandler.php:286 +#: lib/SP/Storage/File/FileHandler.php:328 #, php-format msgid "Unable to delete file (%s)" msgstr "No es posible eliminar el archivo (%s)" @@ -5412,61 +5412,61 @@ msgstr "La extensión '%s' no está disponible" msgid "This extension is needed to display passwords as images" msgstr "Esta extensión es necesaria para mostrar las claves como imágenes" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:30 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:29 msgid "Searching" msgstr "Búsqueda" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:36 -#: app/modules/web/themes/material-blue/views/config/accounts.inc:52 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:35 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:51 #: app/modules/web/themes/material-blue/views/usersettings/general.inc:56 #: app/modules/web/themes/material-blue/views/usersettings/general.inc:72 msgid "Results per page" msgstr "Resultados por página" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:41 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:40 #: app/modules/web/themes/material-blue/views/usersettings/general.inc:61 msgid "Number of results per page to display when performing a search." msgstr "Número de resultados por página a mostrar, al realizar una búsqueda." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:70 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:69 #: app/modules/web/themes/material-blue/views/itemshow/item_preset-password.inc:30 msgid "Accounts password expiry" msgstr "Caducidad clave cuentas" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:71 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:70 msgid "Enables the accounts password expiry date." msgstr "Habilita la caducidad de la clave de cuentas." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:79 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:78 msgid "Password expiry time" msgstr "Tiempo caducidad clave" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:84 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:83 msgid "Number of days for account's password expiry date." msgstr "Número de días para la caducidad de la clave de cuenta." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:94 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:93 #: app/modules/web/themes/material-blue/views/itemshow/item_preset-password.inc:38 msgid "Expire time (days)" msgstr "Tiempo caducidad (días)" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:112 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:111 #: app/modules/web/themes/material-blue/views/usersettings/general.inc:90 msgid "Account name as link" msgstr "Nombre de cuenta como enlace" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:113 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:112 #: app/modules/web/themes/material-blue/views/usersettings/general.inc:92 msgid "Enables to use the account name as a link to account details." msgstr "" "Habilita el nombre de la cuenta de la búsqueda, como enlace a los detalles " "de la cuenta." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:128 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:127 msgid "Global searches" msgstr "Búsquedas globales" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:129 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:128 msgid "" "Allows the users to do searches that includes all accounts, they won't be " "able to display the account details if they don't have permission." @@ -5474,17 +5474,17 @@ msgstr "" "Permite que todos los usuarios puedan realizar búsquedas en todas las " "cuentas, pero no pueden ver el contenido de las que no tienen permisos." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:144 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:143 #: app/modules/web/themes/material-blue/views/itemshow/item_preset-password.inc:154 msgid "Image to show password" msgstr "Imagen para mostrar clave" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:146 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:145 #: app/modules/web/themes/material-blue/views/itemshow/item_preset-password.inc:156 msgid "Generate an image with a text of the account password." msgstr "Generar una imagen con el texto de la clave de la cuenta." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:148 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:147 #: app/modules/web/themes/material-blue/views/itemshow/item_preset-password.inc:158 msgid "" "Useful for environments where copying a password to clipboard is a security " @@ -5492,27 +5492,27 @@ msgid "" msgstr "" "Util para entornos donde copiar la clave supone un riesgo de seguridad." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:164 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:163 #: app/modules/web/themes/material-blue/views/usersettings/general.inc:158 msgid "Results like Cards" msgstr "Resultados en Tarjetas" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:166 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:165 #: app/modules/web/themes/material-blue/views/usersettings/general.inc:160 msgid "Displays account's search results on a card like format." msgstr "Muestra los resultados de búsqueda de cuentas en formato tarjeta." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:182 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:181 msgid "Secondary Groups Access" msgstr "Acceso Grupos Secundarios" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:184 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:183 msgid "Grants access to users that are included in secondary groups." msgstr "" "Habilita el acceso a los usuarios que estén incluidos en los grupos " "secundarios." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:186 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:185 msgid "" "By default, user in a secondary group is granted if the secondary group is " "set as user's main group." @@ -5521,28 +5521,28 @@ msgstr "" "secundario está establecido como el primario del usuario." #. (itstool) path: action/text -#: app/modules/web/themes/material-blue/views/config/accounts.inc:197 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:196 #: app/modules/web/themes/material-blue/views/itemshow/user_profile.inc:378 #: app/config/actions.xml:73 msgid "Public Links" msgstr "Enlaces Públicos" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:215 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:214 msgid "Enable Public Links" msgstr "Habilitar Enlaces Públicos" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:217 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:216 msgid "Enables the ability to create public links to view an account's details" msgstr "" "Habilita la posibilidad de generar enlaces públicos para ver los detalles de " "una cuenta." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:219 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:218 msgid "Linked accounts will be visible by anyone that have the link." msgstr "" "Las cuentas enlazadas serán visibles por cualquiera que disponga del enlace." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:221 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:220 msgid "" "In order to create links, users must have activated the option on their " "profiles." @@ -5550,43 +5550,43 @@ msgstr "" "Para crear enlaces, los usuarios tienen que tener activada la opción en su " "perfl." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:237 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:236 msgid "Use an image for password" msgstr "Usar imagen para clave" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:239 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:238 msgid "The account password is shown as image." msgstr "La clave de la cuenta es visualizada como una imagen." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:248 -#: app/modules/web/themes/material-blue/views/config/accounts.inc:257 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:247 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:256 msgid "Expire time" msgstr "Tiempo de caducidad" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:263 -#: app/modules/web/themes/material-blue/views/config/accounts.inc:272 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:262 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:271 msgid "Maximum visits" msgstr "Número máximo de visitas" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:298 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:297 #: app/modules/web/themes/material-blue/views/itemshow/user_profile.inc:411 msgid "Files management" msgstr "Gestión de archivos" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:299 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:298 msgid "Enables the uploading and downloading of accounts files." msgstr "Habilita la subida/descarga de archivos para las cuentas." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:307 -#: app/modules/web/themes/material-blue/views/config/accounts.inc:320 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:306 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:319 msgid "Allowed MIME types" msgstr "Tipos MIME permitidos" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:312 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:311 msgid "Allowed file MIME types for uploading." msgstr "Tipos MIME permitidos para subida de archivos" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:315 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:314 msgid "" "In order to add more MIME types, you need to add them into mime.xml file " "within the config directory." @@ -5594,19 +5594,19 @@ msgstr "" "Para añadir más tipos MIME, es necesario modificar el archivo mime.xml en el " "directorio de configuración." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:335 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:334 msgid "Maximum file size" msgstr "Tamaño máximo de archivo" -#: app/modules/web/themes/material-blue/views/config/accounts.inc:340 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:339 msgid "Sets the maximum file size for uploading." msgstr "Establece el tamaño máximo para subir archivos." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:344 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:343 msgid "Absolute maximum is 16MB." msgstr "El máximo absuluto es de 16MB." -#: app/modules/web/themes/material-blue/views/config/accounts.inc:355 +#: app/modules/web/themes/material-blue/views/config/accounts.inc:354 msgid "Maximum file size in kilobytes" msgstr "Tamaño máximo de archivo en kilobytes" @@ -7512,3 +7512,6 @@ msgstr "Área de Texto" #: app/config/strings.xml:8 msgid "Text" msgstr "Texto" + +#~ msgid "Data not loaded" +#~ msgstr "Datos no cargados" diff --git a/app/modules/api/Init.php b/app/modules/api/Init.php index 80de32cd..f74de45f 100644 --- a/app/modules/api/Init.php +++ b/app/modules/api/Init.php @@ -73,6 +73,7 @@ final class Init extends ModuleBase * @throws \DI\DependencyException * @throws \DI\NotFoundException * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException + * @throws \SP\Storage\File\FileException */ public function initialize($controller) { @@ -82,7 +83,7 @@ final class Init extends ModuleBase $this->context->initialize(); // Load config - $this->config->loadConfig($this->context); + $this->config->loadConfig(); // Load language $this->language->setLanguage(); diff --git a/app/modules/web/Controllers/ControllerBase.php b/app/modules/web/Controllers/ControllerBase.php index ec44329d..4743a0df 100644 --- a/app/modules/web/Controllers/ControllerBase.php +++ b/app/modules/web/Controllers/ControllerBase.php @@ -137,7 +137,7 @@ abstract class ControllerBase $this->view->assign('isDemo', $this->configData->isDemoEnabled()); $this->view->assign('themeUri', $this->view->getTheme()->getThemeUri()); $this->view->assign('configData', $this->configData); - $this->view->assign('sk', $loggedIn ? $this->session->generateSecurityKey() : ''); + $this->view->assign('sk', $loggedIn ? $this->session->generateSecurityKey($this->configData->getPasswordSalt()) : ''); // Pass the action name to the template as a variable $this->view->assign($this->actionName, true); @@ -238,6 +238,7 @@ abstract class ControllerBase $this->checkLoggedInSession( $this->session, $this->request, + $this->configData, function ($redirect) { $this->router->response() ->redirect($redirect) diff --git a/app/modules/web/Controllers/Helpers/LayoutHelper.php b/app/modules/web/Controllers/Helpers/LayoutHelper.php index 93b9de62..1deccf45 100644 --- a/app/modules/web/Controllers/Helpers/LayoutHelper.php +++ b/app/modules/web/Controllers/Helpers/LayoutHelper.php @@ -111,7 +111,7 @@ final class LayoutHelper extends HelperBase $this->loggedIn = $this->context->isLoggedIn(); - $this->view->assign('sk', $this->view->get('sk') ?: $this->context->generateSecurityKey()); + $this->view->assign('sk', $this->view->get('sk') ?: $this->context->generateSecurityKey($this->configData->getPasswordSalt())); $this->view->assign('loggedIn', $this->loggedIn); $this->view->assign('lang', $this->loggedIn ? Language::$userLang : substr(Language::$globalLang, 0, 2)); $this->view->assign('loadApp', $this->context->getAuthCompleted()); diff --git a/app/modules/web/Controllers/SimpleControllerBase.php b/app/modules/web/Controllers/SimpleControllerBase.php index 7d6c57cb..51ee862c 100644 --- a/app/modules/web/Controllers/SimpleControllerBase.php +++ b/app/modules/web/Controllers/SimpleControllerBase.php @@ -78,6 +78,7 @@ abstract class SimpleControllerBase $this->checkLoggedInSession( $this->session, $this->request, + $this->configData, function ($redirect) { $this->router->response() ->redirect($redirect) diff --git a/app/modules/web/Init.php b/app/modules/web/Init.php index ae86bd58..953d707a 100644 --- a/app/modules/web/Init.php +++ b/app/modules/web/Init.php @@ -138,7 +138,7 @@ final class Init extends ModuleBase // Volver a cargar la configuración si se recarga la página if ($this->request->checkReload() === false) { // Cargar la configuración - $this->config->loadConfig($this->context); + $this->config->loadConfig(); // Cargar el lenguaje $this->language->setLanguage(); @@ -151,7 +151,7 @@ final class Init extends ModuleBase $this->context->setAppStatus(SessionContext::APP_STATUS_RELOADED); // Cargar la configuración - $this->config->loadConfig($this->context, true); + $this->config->loadConfig(true); // Restablecer el idioma $this->language->setLanguage(true); @@ -313,9 +313,6 @@ final class Init extends ModuleBase if ($sidStartTime === 0) { // Try to set PHP's session lifetime @ini_set('session.gc_maxlifetime', $this->getSessionLifeTime()); - - $this->context->setSidStartTime(time()); - $this->context->setStartActivity(time()); } else if (!$inMaintenance && time() > ($sidStartTime + SessionContext::MAX_SID_TIME) && $this->context->isLoggedIn() diff --git a/app/modules/web/themes/material-blue/views/config/accounts.inc b/app/modules/web/themes/material-blue/views/config/accounts.inc index 726adcf0..be466f87 100644 --- a/app/modules/web/themes/material-blue/views/config/accounts.inc +++ b/app/modules/web/themes/material-blue/views/config/accounts.inc @@ -22,7 +22,6 @@

diff --git a/app/modules/web/themes/material-blue/views/config/general.inc b/app/modules/web/themes/material-blue/views/config/general.inc index 1030a51f..e5f6a801 100644 --- a/app/modules/web/themes/material-blue/views/config/general.inc +++ b/app/modules/web/themes/material-blue/views/config/general.inc @@ -9,7 +9,6 @@ includeTemplate('general-site'); ?> diff --git a/lib/Definitions.php b/lib/Definitions.php index ee17285d..65a07178 100644 --- a/lib/Definitions.php +++ b/lib/Definitions.php @@ -55,7 +55,10 @@ return [ } }, Config::class => function (ContainerInterface $c) { - return new Config(new XmlHandler(new FileHandler(CONFIG_FILE)), $c->get(ContextInterface::class), $c); + return new Config( + new XmlHandler(new FileHandler(CONFIG_FILE)), + new FileCache(Config::CONFIG_CACHE_FILE), + $c); }, ConfigData::class => function (Config $config) { return $config->getConfigData(); diff --git a/lib/SP/Config/Config.php b/lib/SP/Config/Config.php index f65d979a..e0a1e794 100644 --- a/lib/SP/Config/Config.php +++ b/lib/SP/Config/Config.php @@ -24,12 +24,12 @@ namespace SP\Config; -use DI\Container; use Psr\Container\ContainerInterface; use ReflectionObject; use SP\Core\Context\ContextInterface; use SP\Core\Exceptions\ConfigException; use SP\Services\Config\ConfigBackupService; +use SP\Storage\File\FileCacheInterface; use SP\Storage\File\FileException; use SP\Storage\File\XmlFileStorageInterface; use SP\Util\PasswordUtil; @@ -41,10 +41,18 @@ defined('APP_ROOT') || die(); */ final class Config { + /** + * Cache file name + */ + const CONFIG_CACHE_FILE = CACHE_PATH . DIRECTORY_SEPARATOR . 'config.cache'; /** * @var int */ private static $timeUpdated; + /** + * @var ContextInterface + */ + private $context; /** * @var bool */ @@ -54,13 +62,13 @@ final class Config */ private $configData; /** - * @var \SP\Storage\File\XmlFileStorageInterface + * @var XmlFileStorageInterface */ private $fileStorage; /** - * @var ContextInterface + * @var FileCacheInterface */ - private $context; + private $fileCache; /** * @var ContainerInterface */ @@ -69,16 +77,17 @@ final class Config /** * Config constructor. * - * @param \SP\Storage\File\XmlFileStorageInterface $fileStorage - * @param ContextInterface $session - * @param Container $dic + * @param XmlFileStorageInterface $fileStorage + * @param FileCacheInterface $fileCache + * @param ContainerInterface $dic * * @throws ConfigException */ - public function __construct(XmlFileStorageInterface $fileStorage, ContextInterface $session, Container $dic) + public function __construct(XmlFileStorageInterface $fileStorage, FileCacheInterface $fileCache, ContainerInterface $dic) { - $this->context = $session; + $this->fileCache = $fileCache; $this->fileStorage = $fileStorage; + $this->context = $dic->get(ContextInterface::class); $this->dic = $dic; $this->initialize(); @@ -91,24 +100,48 @@ final class Config { if (!$this->configLoaded) { try { - if (file_exists($this->fileStorage->getFileHandler()->getFile())) { - $this->configData = $this->loadConfigFromFile(); + if ($this->fileCache->exists() + && !$this->checkCacheDate() + ) { + logger('Config cache loaded'); + $this->configData = $this->fileCache->load(); } else { - $this->saveConfig(new ConfigData(), false); + if (file_exists($this->fileStorage->getFileHandler()->getFile())) { + $this->configData = $this->loadConfigFromFile(); + $this->fileCache->save($this->configData); + } else { + $this->saveConfig(new ConfigData(), false); + } + + logger('Config loaded'); } self::$timeUpdated = $this->configData->getConfigDate(); $this->configLoaded = true; - - logger('Config loaded'); } catch (\Exception $e) { processException($e); - throw new ConfigException($e->getMessage(), ConfigException::CRITICAL, null, $e->getCode(), $e); + throw new ConfigException($e->getMessage(), + ConfigException::CRITICAL, + null, + $e->getCode(), + $e); } } } + /** + * @return bool + */ + private function checkCacheDate() + { + try { + return $this->fileCache->isExpiredDate($this->fileStorage->getFileHandler()->getFileTime()); + } catch (FileException $e) { + return true; + } + } + /** * Cargar el archivo de configuración * @@ -144,8 +177,6 @@ final class Config * * @return Config * @throws FileException - * @throws \DI\DependencyException - * @throws \DI\NotFoundException */ public function saveConfig(ConfigData $configData, $backup = true) { @@ -159,6 +190,7 @@ final class Config $configData->setConfigHash(); $this->fileStorage->save($configData, 'config'); + $this->fileCache->save($configData); $this->configData = $configData; @@ -196,37 +228,30 @@ final class Config /** * Cargar la configuración desde el contexto * - * @param ContextInterface $context - * @param bool $reload + * @param bool $reload * * @return ConfigData */ - public function loadConfig(ContextInterface $context, $reload = false) + public function loadConfig($reload = false) { - $configData = $context->getConfig(); + try { + $configData = $this->fileCache->load(); - if ($reload === true - || $configData === null - || time() >= ($context->getConfigTime() + $configData->getSessionTimeout() / 2) - ) { - return $this->saveConfigInSession($context); + if ($reload === true + || $configData === null + || !$this->checkCacheDate() + ) { + $this->configData = $this->loadConfigFromFile(); + $this->fileCache->save($this->configData); + + return $this->configData; + } + + return $configData; + } catch (FileException $e) { + processException($e); } - return $configData; - } - - /** - * Guardar la configuración en la sesión - * - * @param ContextInterface $context - * - * @return ConfigData - */ - private function saveConfigInSession(ContextInterface $context) - { - $context->setConfig($this->configData); - $context->setConfigTime(time()); - return $this->configData; } @@ -241,8 +266,6 @@ final class Config /** * @return Config * @throws FileException - * @throws \DI\DependencyException - * @throws \DI\NotFoundException * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException */ public function generateUpgradeKey() diff --git a/lib/SP/Core/Context/ContextInterface.php b/lib/SP/Core/Context/ContextInterface.php index 4b4dc439..d4ebc35f 100644 --- a/lib/SP/Core/Context/ContextInterface.php +++ b/lib/SP/Core/Context/ContextInterface.php @@ -24,7 +24,6 @@ namespace SP\Core\Context; -use SP\Config\ConfigData; use SP\DataModel\Dto\AccountCache; use SP\DataModel\ProfileData; use SP\Services\User\UserLoginResponse; @@ -41,13 +40,6 @@ interface ContextInterface */ public function initialize(); - /** - * Establecer la configuración - * - * @param ConfigData $config - */ - public function setConfig(ConfigData $config); - /** * Establecer la hora de carga de la configuración * @@ -103,22 +95,17 @@ interface ContextInterface public function getSecurityKey(); /** + * @param string $salt + * * @return string */ - public function generateSecurityKey(); + public function generateSecurityKey(string $salt); /** * @param $sk */ public function setSecurityKey($sk); - /** - * Devolver la configuración - * - * @return ConfigData - */ - public function getConfig(); - /** * Establecer el lenguaje de la sesión * diff --git a/lib/SP/Core/Context/SessionContext.php b/lib/SP/Core/Context/SessionContext.php index e7c0e2ff..50842702 100644 --- a/lib/SP/Core/Context/SessionContext.php +++ b/lib/SP/Core/Context/SessionContext.php @@ -24,7 +24,6 @@ namespace SP\Core\Context; -use SP\Config\ConfigData; use SP\Core\Crypt\Vault; use SP\DataModel\Dto\AccountCache; use SP\DataModel\ProfileData; @@ -139,16 +138,6 @@ final class SessionContext extends ContextBase return null; } - /** - * Establecer la configuración - * - * @param ConfigData $config - */ - public function setConfig(ConfigData $config) - { - $this->setContextKey('config', $config); - } - /** * Establecer la hora de carga de la configuración * @@ -288,11 +277,13 @@ final class SessionContext extends ContextBase } /** + * @param string $salt + * * @return string */ - public function generateSecurityKey() + public function generateSecurityKey(string $salt) { - return $this->setSecurityKey(sha1(time() . $this->getConfig()->getPasswordSalt())); + return $this->setSecurityKey(sha1(time() . $salt)); } /** @@ -305,16 +296,6 @@ final class SessionContext extends ContextBase return $this->setContextKey('sk', $sk); } - /** - * Devolver la configuración - * - * @return ConfigData - */ - public function getConfig() - { - return $this->getContextKey('config'); - } - /** * Devolver la clave pública * @@ -379,30 +360,6 @@ final class SessionContext extends ContextBase $this->setContextKey('lastActivity', $time); } - /** - * Devuelve la hora en la que el SID de sesión fue creado - * - * @return int - */ - public function getSidStartTime() - { - return $this->getContextKey('sidStartTime', 0); - } - - /** - * Establece la hora de creación del SID - * - * @param $time int La marca de hora - * - * @return int - */ - public function setSidStartTime($time) - { - $this->setContextKey('sidStartTime', $time); - - return $time; - } - /** * Devuelve la hora de inicio de actividad. * @@ -413,20 +370,6 @@ final class SessionContext extends ContextBase return $this->getContextKey('startActivity', 0); } - /** - * Establece la hora de inicio de actividad - * - * @param $time int La marca de hora - * - * @return int - */ - public function setStartActivity($time) - { - $this->setContextKey('startActivity', $time); - - return $time; - } - /** * Establecer el lenguaje de la sesión * @@ -548,6 +491,49 @@ final class SessionContext extends ContextBase } $this->setContextReference($_SESSION); + + if ($this->getSidStartTime() === 0) { + $this->setSidStartTime(time()); + $this->setStartActivity(time()); + } + } + + /** + * Devuelve la hora en la que el SID de sesión fue creado + * + * @return int + */ + public function getSidStartTime() + { + return $this->getContextKey('sidStartTime', 0); + } + + /** + * Establece la hora de creación del SID + * + * @param $time int La marca de hora + * + * @return int + */ + public function setSidStartTime($time) + { + $this->setContextKey('sidStartTime', $time); + + return $time; + } + + /** + * Establece la hora de inicio de actividad + * + * @param $time int La marca de hora + * + * @return int + */ + public function setStartActivity($time) + { + $this->setContextKey('startActivity', $time); + + return $time; } /** diff --git a/lib/SP/Core/Context/StatelessContext.php b/lib/SP/Core/Context/StatelessContext.php index 5652e8e2..127dff30 100644 --- a/lib/SP/Core/Context/StatelessContext.php +++ b/lib/SP/Core/Context/StatelessContext.php @@ -24,7 +24,6 @@ namespace SP\Core\Context; -use SP\Config\ConfigData; use SP\DataModel\ProfileData; use SP\Services\User\UserLoginResponse; @@ -35,16 +34,6 @@ use SP\Services\User\UserLoginResponse; */ final class StatelessContext extends ContextBase { - /** - * Establecer la configuración - * - * @param ConfigData $config - */ - public function setConfig(ConfigData $config) - { - $this->setContextKey('config', $config); - } - /** * Establecer una variable de sesión * @@ -144,11 +133,13 @@ final class StatelessContext extends ContextBase } /** + * @param string $salt + * * @return string */ - public function generateSecurityKey() + public function generateSecurityKey(string $salt) { - return $this->setSecurityKey(sha1(time() . $this->getConfig()->getPasswordSalt())); + return $this->setSecurityKey(sha1(time() . $salt)); } /** @@ -161,16 +152,6 @@ final class StatelessContext extends ContextBase return $this->setContextKey('sk', $sk); } - /** - * Devolver la configuración - * - * @return ConfigData - */ - public function getConfig() - { - return $this->getContextKey('config'); - } - /** * Establecer el lenguaje de la sesión * diff --git a/lib/SP/Mvc/Controller/ControllerTrait.php b/lib/SP/Mvc/Controller/ControllerTrait.php index a42be6d3..9b17082b 100644 --- a/lib/SP/Mvc/Controller/ControllerTrait.php +++ b/lib/SP/Mvc/Controller/ControllerTrait.php @@ -24,6 +24,7 @@ namespace SP\Mvc\Controller; +use SP\Config\ConfigData; use SP\Core\Context\SessionContext; use SP\Core\Exceptions\SPException; use SP\Http\Json; @@ -55,11 +56,13 @@ trait ControllerTrait * * @param SessionContext $context * @param Request $request + * @param ConfigData $configData * @param \Closure $onRedirect * @param bool $requireAuthCompleted */ protected function checkLoggedInSession(SessionContext $context, Request $request, + ConfigData $configData, \Closure $onRedirect, $requireAuthCompleted = true) { @@ -85,7 +88,7 @@ trait ControllerTrait $uri->addParam('_r', 'login'); if ($route && $hash) { - $key = $context->getConfig()->getPasswordSalt(); + $key = $configData->getPasswordSalt(); $request->verifySignature($key); $uri->addParam('from', $route); diff --git a/lib/SP/Services/Account/AccountFilterUser.php b/lib/SP/Services/Account/AccountFilterUser.php index 1f1f0c37..9bce44ec 100644 --- a/lib/SP/Services/Account/AccountFilterUser.php +++ b/lib/SP/Services/Account/AccountFilterUser.php @@ -60,10 +60,12 @@ final class AccountFilterUser * AccountFilterUser constructor. * * @param ContextInterface $context + * @param ConfigData $configData */ - public function __construct(ContextInterface $context) + public function __construct(ContextInterface $context, ConfigData $configData) { $this->context = $context; + $this->configData = $configData; } /** @@ -117,7 +119,6 @@ final class AccountFilterUser */ private function setUp() { - $this->configData = $this->context->getConfig(); $this->userData = $this->context->getUserData(); $this->userProfile = $this->context->getUserProfile(); } diff --git a/lib/SP/Services/Export/XmlExportService.php b/lib/SP/Services/Export/XmlExportService.php index ecb00958..29ee0bd4 100644 --- a/lib/SP/Services/Export/XmlExportService.php +++ b/lib/SP/Services/Export/XmlExportService.php @@ -95,8 +95,6 @@ final class XmlExportService extends Service * @param string $pass string La clave de exportación * * @throws ServiceException - * @throws \DI\DependencyException - * @throws \DI\NotFoundException * @throws \SP\Storage\File\FileException */ public function doExport(string $exportPath, string $pass = null) @@ -134,8 +132,6 @@ final class XmlExportService extends Service * Genera el nombre del archivo usado para la exportación. * * @return string - * @throws \DI\DependencyException - * @throws \DI\NotFoundException * @throws \SP\Storage\File\FileException */ private function generateExportFilename(): string @@ -217,8 +213,8 @@ final class XmlExportService extends Service private function createRoot() { try { - $root = $this->xml->createElement('Root'); - $this->root = $this->xml->appendChild($root); + $this->xml = new \DOMDocument('1.0', 'UTF-8'); + $this->root = $this->xml->appendChild($this->xml->createElement('Root')); } catch (\Exception $e) { throw new ServiceException($e->getMessage(), ServiceException::ERROR, __FUNCTION__); } @@ -624,7 +620,6 @@ final class XmlExportService extends Service { $this->extensionChecker = $this->dic->get(PhpExtensionChecker::class); $this->configData = $this->config->getConfigData(); - $this->xml = new \DOMDocument('1.0', 'UTF-8'); } /** diff --git a/lib/SP/Services/Install/Installer.php b/lib/SP/Services/Install/Installer.php index d57573d7..f4a8776b 100644 --- a/lib/SP/Services/Install/Installer.php +++ b/lib/SP/Services/Install/Installer.php @@ -53,11 +53,11 @@ defined('APP_ROOT') || die(); final class Installer extends Service { /** - * Versión y número de compilación de sysPass + * sysPass' version and build number */ const VERSION = [3, 0, 0]; - const VERSION_TEXT = '3.0-beta'; - const BUILD = 18111001; + const VERSION_TEXT = '3.0-rc1'; + const BUILD = 18111201; /** * @var DatabaseSetupInterface diff --git a/lib/SP/Storage/File/FileCache.php b/lib/SP/Storage/File/FileCache.php index bcf47c42..68e72040 100644 --- a/lib/SP/Storage/File/FileCache.php +++ b/lib/SP/Storage/File/FileCache.php @@ -50,9 +50,10 @@ final class FileCache extends FileCacheBase { $this->createPath(); - $this->path->checkIsWritable() - ->write(serialize($data)) - ->close(); + $this->path->checkIsWritable(); + $this->path->open('wb', true); + $this->path->write(serialize($data)); + $this->path->close(); return $this; } diff --git a/lib/SP/Storage/File/FileCacheBase.php b/lib/SP/Storage/File/FileCacheBase.php index c7bbbb25..394c8202 100644 --- a/lib/SP/Storage/File/FileCacheBase.php +++ b/lib/SP/Storage/File/FileCacheBase.php @@ -47,6 +47,16 @@ abstract class FileCacheBase implements FileCacheInterface $this->path = new FileHandler($path); } + /** + * @param $path + * + * @return FileCacheBase + */ + public static function factory($path) + { + return new static($path); + } + /** * Returns if the file is expired adding time to modification date * @@ -101,12 +111,10 @@ abstract class FileCacheBase implements FileCacheInterface } /** - * @param $path - * - * @return FileCacheBase + * @return bool */ - public static function factory($path) + public function exists(): bool { - return new static($path); + return file_exists($this->path->getFile()); } } \ No newline at end of file diff --git a/lib/SP/Storage/File/FileCacheInterface.php b/lib/SP/Storage/File/FileCacheInterface.php index 944221c8..f10e00de 100644 --- a/lib/SP/Storage/File/FileCacheInterface.php +++ b/lib/SP/Storage/File/FileCacheInterface.php @@ -60,7 +60,7 @@ interface FileCacheInterface public function isExpired($time = 86400): bool; /** - * Returns if the file is expired adding time to modification date + * Returns if the file is expired comparing against a reference date * * @param int $date * @@ -68,4 +68,9 @@ interface FileCacheInterface * @throws FileException */ public function isExpiredDate($date): bool; + + /** + * @return bool + */ + public function exists(): bool; } \ No newline at end of file diff --git a/lib/SP/Storage/File/FileHandler.php b/lib/SP/Storage/File/FileHandler.php index b8845886..f6e27fcb 100644 --- a/lib/SP/Storage/File/FileHandler.php +++ b/lib/SP/Storage/File/FileHandler.php @@ -43,6 +43,10 @@ final class FileHandler * @var resource */ protected $handle; + /** + * @var bool + */ + private $locked = false; /** * FileHandler constructor. @@ -57,7 +61,7 @@ final class FileHandler /** * Writes data into file * - * @param $data + * @param mixed $data * * @return FileHandler * @throws FileException @@ -78,20 +82,46 @@ final class FileHandler /** * Opens the file * - * @param $mode + * @param string $mode + * + * @param bool $lock * * @return resource * @throws FileException */ - public function open($mode = 'r') + public function open($mode = 'r', $lock = false) { - if (($this->handle = @fopen($this->file, $mode)) === false) { + $this->handle = @fopen($this->file, $mode); + + if ($lock && $this->locked === false) { + $this->lock(); + } + + if ($this->handle === false) { throw new FileException(sprintf(__('Unable to open the file (%s)'), $this->file)); } return $this->handle; } + /** + * Lock the file + * + * @param int $mode + * + * @throws FileException + */ + private function lock($mode = LOCK_EX) + { + $this->locked = flock($this->handle, $mode); + + if (!$this->locked) { + throw new FileException(sprintf(__('Unable to obtain a lock (%s)'), $this->file)); + } + + logger(sprintf('File locked: %s', $this->file)); + } + /** * Reads data from file into a string * @@ -169,6 +199,10 @@ final class FileHandler */ public function close() { + if ($this->locked) { + $this->unlock(); + } + if (!is_resource($this->handle) || @fclose($this->handle) === false) { throw new FileException(sprintf(__('Unable to close the file (%s)'), $this->file)); } @@ -176,6 +210,14 @@ final class FileHandler return $this; } + /** + * Unlock the file + */ + private function unlock() + { + $this->locked = !flock($this->handle, LOCK_UN); + } + /** * @param callable $chunker * @param float $rate diff --git a/tests/SP/Config/ConfigTest.php b/tests/SP/Config/ConfigTest.php index d485ae15..6f132f67 100644 --- a/tests/SP/Config/ConfigTest.php +++ b/tests/SP/Config/ConfigTest.php @@ -30,8 +30,8 @@ use DI\NotFoundException; use PHPUnit\Framework\TestCase; use SP\Config\Config; use SP\Config\ConfigData; -use SP\Core\Context\ContextInterface; use function SP\Tests\getResource; +use function SP\Tests\recreateDir; use function SP\Tests\saveResource; use function SP\Tests\setupContext; @@ -60,6 +60,7 @@ class ConfigTest extends TestCase { self::$dic = setupContext(); + // Save current config self::$currentConfig = getResource('config', 'config.xml'); } @@ -68,7 +69,9 @@ class ConfigTest extends TestCase */ public static function tearDownAfterClass() { + // Restore to the initial state saveResource('config', 'config.xml', self::$currentConfig); + recreateDir(CACHE_PATH); } /** @@ -94,13 +97,11 @@ class ConfigTest extends TestCase * * @param Config $config * - * @throws DependencyException - * @throws NotFoundException * @throws \SP\Storage\File\FileException */ public function testSaveConfig($config) { - $config->saveConfig(new ConfigData(), false); + $config->saveConfig($config->getConfigData(), false); $this->assertFileExists(CONFIG_FILE); } @@ -113,16 +114,10 @@ class ConfigTest extends TestCase * * @param Config $config * - * @throws DependencyException - * @throws NotFoundException */ public function testLoadConfig($config) { - $context = self::$dic->get(ContextInterface::class); - - $config->loadConfig($context); - - $this->assertInstanceOf(ConfigData::class, $context->getConfig()); + $this->assertInstanceOf(ConfigData::class, $config->loadConfig()); } /** @@ -134,7 +129,7 @@ class ConfigTest extends TestCase */ public function testUpdateConfig($config) { - $config->updateConfig(new ConfigData()); + $config->updateConfig($config->getConfigData()); $this->assertEquals(Config::getTimeUpdated(), $config->getConfigData()->getConfigDate()); } @@ -147,6 +142,7 @@ class ConfigTest extends TestCase * @param Config $config * * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException + * @throws \SP\Storage\File\FileException */ public function testGenerateUpgradeKey($config) { diff --git a/tests/SP/Repositories/PublicLinkRepositoryTest.php b/tests/SP/Repositories/PublicLinkRepositoryTest.php index bb3874b3..46a21752 100644 --- a/tests/SP/Repositories/PublicLinkRepositoryTest.php +++ b/tests/SP/Repositories/PublicLinkRepositoryTest.php @@ -184,7 +184,7 @@ class PublicLinkRepositoryTest extends DatabaseTestCase $this->assertEquals($data->getTypeId(), $resultData->getTypeId()); $this->assertEquals($data->isNotify(), $resultData->isNotify()); $this->assertEquals($data->getDateExpire(), $resultData->getDateExpire()); - $this->assertEquals($data->getDateAdd(), $resultData->getDateAdd()); + $this->assertTrue($data->getDateAdd() >= $resultData->getDateAdd()); $this->assertEquals($data->getMaxCountViews(), $resultData->getMaxCountViews()); $this->expectException(DuplicatedItemException::class); diff --git a/tests/SP/Services/Config/ConfigBackupServiceTest.php b/tests/SP/Services/Config/ConfigBackupServiceTest.php index 723460aa..d260d3ce 100644 --- a/tests/SP/Services/Config/ConfigBackupServiceTest.php +++ b/tests/SP/Services/Config/ConfigBackupServiceTest.php @@ -29,6 +29,7 @@ use SP\Config\Config; use SP\Config\ConfigData; use SP\Services\Config\ConfigBackupService; use function SP\Tests\getResource; +use function SP\Tests\recreateDir; use function SP\Tests\saveResource; use function SP\Tests\setupContext; @@ -52,6 +53,7 @@ class ConfigBackupServiceTest extends TestCase public static function tearDownAfterClass() { saveResource('config', 'config.xml', self::$currentConfig); + recreateDir(CACHE_PATH); } /** diff --git a/tests/SP/Services/Export/XmlExportServiceTest.php b/tests/SP/Services/Export/XmlExportServiceTest.php index faf12a4b..1bc97872 100644 --- a/tests/SP/Services/Export/XmlExportServiceTest.php +++ b/tests/SP/Services/Export/XmlExportServiceTest.php @@ -24,6 +24,7 @@ namespace SP\Tests\Services\Export; +use Defuse\Crypto\Exception\CryptoException; use SP\Services\Export\VerifyResult; use SP\Services\Export\XmlExportService; use SP\Services\Export\XmlVerifyService; @@ -40,6 +41,15 @@ use function SP\Tests\setupContext; */ class XmlExportServiceTest extends DatabaseTestCase { + /** + * @var XmlExportService + */ + private static $xmlExportService; + /** + * @var XmlVerifyService + */ + private static $xmlVerifyService; + /** * @throws \DI\DependencyException * @throws \DI\NotFoundException @@ -55,24 +65,21 @@ class XmlExportServiceTest extends DatabaseTestCase // Datos de conexión a la BBDD self::$databaseConnectionData = $dic->get(DatabaseConnectionData::class); + self::$xmlExportService = $dic->get(XmlExportService::class); + self::$xmlVerifyService = $dic->get(XmlVerifyService::class); } /** - * @throws \DI\DependencyException - * @throws \DI\NotFoundException - * @throws \SP\Core\Context\ContextException - * @throws \SP\Services\ServiceException + * @throws ServiceException * @throws \SP\Storage\File\FileException */ public function testDoExportWithoutPassword() { - $dic = setupContext(); - $service = $dic->get(XmlExportService::class); - $service->doExport(TMP_DIR); + self::$xmlExportService->doExport(TMP_DIR); - $this->assertFileExists($service->getExportFile()); + $this->assertFileExists(self::$xmlExportService->getExportFile()); - $this->verifyExportWithoutPassword($service->getExportFile()); + $this->verifyExportWithoutPassword(self::$xmlExportService->getExportFile()); } /** @@ -80,18 +87,12 @@ class XmlExportServiceTest extends DatabaseTestCase * * @param $file * - * @throws \DI\DependencyException - * @throws \DI\NotFoundException - * @throws \SP\Core\Context\ContextException * @throws \SP\Services\ServiceException * @throws \SP\Storage\File\FileException */ private function verifyExportWithoutPassword($file) { - $dic = setupContext(); - $service = $dic->get(XmlVerifyService::class); - - $result = $service->verify($file); + $result = self::$xmlVerifyService->verify($file); $this->assertInstanceOf(VerifyResult::class, $result); @@ -117,44 +118,32 @@ class XmlExportServiceTest extends DatabaseTestCase } /** - * @throws \DI\DependencyException - * @throws \DI\NotFoundException - * @throws \Defuse\Crypto\Exception\CryptoException - * @throws \SP\Core\Context\ContextException - * @throws \SP\Services\ServiceException + * @throws CryptoException + * @throws ServiceException * @throws \SP\Storage\File\FileException */ public function testDoExportWithPassword() { - $dic = setupContext(); - $service = $dic->get(XmlExportService::class); - $password = PasswordUtil::randomPassword(); - $service->doExport(TMP_DIR, $password); + self::$xmlExportService->doExport(TMP_DIR, $password); - $this->assertFileExists($service->getExportFile()); + $this->assertFileExists(self::$xmlExportService->getExportFile()); - $this->verifyExportWithPassword($service->getExportFile(), $password); + $this->verifyExportWithPassword(self::$xmlExportService->getExportFile(), $password); } /** * @param $file * @param $password * - * @throws \DI\DependencyException - * @throws \DI\NotFoundException - * @throws \Defuse\Crypto\Exception\CryptoException - * @throws \SP\Core\Context\ContextException - * @throws \SP\Services\ServiceException + * @throws CryptoException + * @throws ServiceException * @throws \SP\Storage\File\FileException */ private function verifyExportWithPassword($file, $password) { - $dic = setupContext(); - $service = $dic->get(XmlVerifyService::class); - - $result = $service->verifyEncrypted($file, $password); + $result = self::$xmlVerifyService->verifyEncrypted($file, $password); $this->assertInstanceOf(VerifyResult::class, $result); $this->assertTrue($result->isEncrypted()); @@ -163,6 +152,6 @@ class XmlExportServiceTest extends DatabaseTestCase $this->expectException(ServiceException::class); - $service->verifyEncrypted($file, 'test123'); + self::$xmlVerifyService->verifyEncrypted($file, 'test123'); } } diff --git a/tests/SP/Services/Export/XmlVerifyServiceTest.php b/tests/SP/Services/Export/XmlVerifyServiceTest.php index 5b048ef1..0030244d 100644 --- a/tests/SP/Services/Export/XmlVerifyServiceTest.php +++ b/tests/SP/Services/Export/XmlVerifyServiceTest.php @@ -36,23 +36,33 @@ use function SP\Tests\setupContext; */ class XmlVerifyServiceTest extends TestCase { + /** + * @var XmlVerifyService + */ + private static $xmlVerifyService; /** * @throws \DI\DependencyException * @throws \DI\NotFoundException - * @throws \Defuse\Crypto\Exception\CryptoException * @throws \SP\Core\Context\ContextException + */ + public static function setUpBeforeClass() + { + $dic = setupContext(); + + self::$xmlVerifyService = $dic->get(XmlVerifyService::class); + } + + /** + * @throws \Defuse\Crypto\Exception\CryptoException * @throws \SP\Services\ServiceException * @throws \SP\Storage\File\FileException */ public function testVerifyEncrypted() { - $dic = setupContext(); - $service = $dic->get(XmlVerifyService::class); - $file = RESOURCE_DIR . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'data_syspass_encrypted.xml'; - $result = $service->verifyEncrypted($file, 'test_encrypt'); + $result = self::$xmlVerifyService->verifyEncrypted($file, 'test_encrypt'); $this->assertInstanceOf(VerifyResult::class, $result); $this->assertEquals(300.18082201, $result->getVersion()); @@ -67,20 +77,14 @@ class XmlVerifyServiceTest extends TestCase } /** - * @throws \DI\DependencyException - * @throws \DI\NotFoundException - * @throws \SP\Core\Context\ContextException * @throws \SP\Services\ServiceException * @throws \SP\Storage\File\FileException */ public function testVerify() { - $dic = setupContext(); - $service = $dic->get(XmlVerifyService::class); - $file = RESOURCE_DIR . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'data_syspass.xml'; - $result = $service->verify($file); + $result = self::$xmlVerifyService->verify($file); $this->assertInstanceOf(VerifyResult::class, $result); $this->assertEquals(300.18071701, $result->getVersion()); diff --git a/tests/SP/Services/Install/InstallerTest.php b/tests/SP/Services/Install/InstallerTest.php index d14d2944..987f021e 100644 --- a/tests/SP/Services/Install/InstallerTest.php +++ b/tests/SP/Services/Install/InstallerTest.php @@ -35,6 +35,7 @@ use SP\Storage\Database\DBStorageInterface; use SP\Tests\DatabaseUtil; use SP\Util\PasswordUtil; use function SP\Tests\getResource; +use function SP\Tests\recreateDir; use function SP\Tests\saveResource; use function SP\Tests\setupContext; @@ -70,6 +71,7 @@ class InstallerTest extends TestCase public static function tearDownAfterClass() { saveResource('config', 'config.xml', self::$currentConfig); + recreateDir(CACHE_PATH); } /** diff --git a/tests/SP/bootstrap.php b/tests/SP/bootstrap.php index 00cc5307..dba1636b 100644 --- a/tests/SP/bootstrap.php +++ b/tests/SP/bootstrap.php @@ -25,7 +25,6 @@ namespace SP\Tests; use DI\ContainerBuilder; -use SP\Config\ConfigData; use SP\Core\Context\ContextInterface; use SP\DataModel\ProfileData; use SP\Services\User\UserLoginResponse; @@ -113,10 +112,6 @@ function setupContext() $context = $dic->get(ContextInterface::class); $context->initialize(); -// $configData = new ConfigData(); -// $configData->setPasswordSalt('fd1058ca0bbaf967d08184ed22ee2c8d5675ca0c9d569c1f237f23fefadf'); - - $context->setConfig($dic->get(ConfigData::class)); $context->setTrasientKey('_masterpass', '12345678900'); $userData = new UserLoginResponse(); @@ -129,9 +124,6 @@ function setupContext() $context->setUserProfile(new ProfileData()); - // Inicializar la configuración -// $dic->set(ConfigData::class, $configData); - // Inicializar los datos de conexión a la BBDD $dic->set(DBStorageInterface::class, getDbHandler()); diff --git a/tests/res/config/config.xml b/tests/res/config/config.xml index f48f7344..b6a94ba3 100644 --- a/tests/res/config/config.xml +++ b/tests/res/config/config.xml @@ -9,11 +9,11 @@ 1 1 - 335463b295db194139a4483b30bed5dc4a8e8fab + d076af62e68e3974a8c80d75bfd6fef31fcb9073 0 0 - 1541937033 - c80d905efab57187b4a1f32374fafbcff3c4d57b + 1541977640 + 71556fad3a4bf8a87356c496fb4afca8c980c424 @@ -32,7 +32,7 @@ 0 - d101e3b4cfc15c62090eb1c5f737a454e9a7c35e + e64dc162b9c052e097f7abd149bffcaccdd72d63 PDF JPG diff --git a/tests/res/datasets/syspass_import.xml b/tests/res/datasets/syspass_import.xml index e634ba18..9a5616e6 100644 --- a/tests/res/datasets/syspass_import.xml +++ b/tests/res/datasets/syspass_import.xml @@ -1,6 +1,146 @@ + + + 1 + Admins + sysPass Admins + + + 2 + Demo + + + + 3 + Usuarios + Grupo Usuarios + + + + + 1 + Admin + 4F3A32343A2253505C446174614D6F64656C5C50726F66696C6544617461223A32393A7B733A31303A22002A0061636356696577223B623A303B733A31343A22002A006163635669657750617373223B623A303B733A31373A22002A0061636356696577486973746F7279223B623A303B733A31303A22002A0061636345646974223B623A303B733A31343A22002A006163634564697450617373223B623A303B733A393A22002A00616363416464223B623A303B733A31323A22002A0061636344656C657465223B623A303B733A31313A22002A0061636346696C6573223B623A303B733A31333A22002A0061636350726976617465223B623A313B733A31383A22002A006163635072697661746547726F7570223B623A313B733A31363A22002A006163635065726D697373696F6E223B623A303B733A31373A22002A006163635075626C69634C696E6B73223B623A303B733A31383A22002A00616363476C6F62616C536561726368223B623A303B733A31363A22002A00636F6E66696747656E6572616C223B623A303B733A31393A22002A00636F6E666967456E6372797074696F6E223B623A303B733A31353A22002A00636F6E6669674261636B7570223B623A303B733A31353A22002A00636F6E666967496D706F7274223B623A303B733A31313A22002A006D676D5573657273223B623A303B733A31323A22002A006D676D47726F757073223B623A303B733A31343A22002A006D676D50726F66696C6573223B623A303B733A31363A22002A006D676D43617465676F72696573223B623A303B733A31353A22002A006D676D437573746F6D657273223B623A303B733A31353A22002A006D676D417069546F6B656E73223B623A303B733A31373A22002A006D676D5075626C69634C696E6B73223B623A303B733A31343A22002A006D676D4163636F756E7473223B623A303B733A31303A22002A006D676D54616773223B623A303B733A31313A22002A006D676D46696C6573223B623A303B733A363A22002A0065766C223B623A303B733A31383A22002A006D676D437573746F6D4669656C6473223B623A303B7D + + + 2 + Demo + 4F3A32343A2253505C446174614D6F64656C5C50726F66696C6544617461223A32393A7B733A31303A22002A0061636356696577223B623A313B733A31343A22002A006163635669657750617373223B623A313B733A31373A22002A0061636356696577486973746F7279223B623A313B733A31303A22002A0061636345646974223B623A313B733A31343A22002A006163634564697450617373223B623A313B733A393A22002A00616363416464223B623A313B733A31323A22002A0061636344656C657465223B623A313B733A31313A22002A0061636346696C6573223B623A303B733A31333A22002A0061636350726976617465223B623A303B733A31383A22002A006163635072697661746547726F7570223B623A303B733A31363A22002A006163635065726D697373696F6E223B623A303B733A31373A22002A006163635075626C69634C696E6B73223B623A303B733A31383A22002A00616363476C6F62616C536561726368223B623A303B733A31363A22002A00636F6E66696747656E6572616C223B623A303B733A31393A22002A00636F6E666967456E6372797074696F6E223B623A303B733A31353A22002A00636F6E6669674261636B7570223B623A303B733A31353A22002A00636F6E666967496D706F7274223B623A303B733A31313A22002A006D676D5573657273223B623A303B733A31323A22002A006D676D47726F757073223B623A303B733A31343A22002A006D676D50726F66696C6573223B623A303B733A31363A22002A006D676D43617465676F72696573223B623A303B733A31353A22002A006D676D437573746F6D657273223B623A303B733A31353A22002A006D676D417069546F6B656E73223B623A303B733A31373A22002A006D676D5075626C69634C696E6B73223B623A303B733A31343A22002A006D676D4163636F756E7473223B623A303B733A31303A22002A006D676D54616773223B623A303B733A31313A22002A006D676D46696C6573223B623A303B733A363A22002A0065766C223B623A303B733A31383A22002A006D676D437573746F6D4669656C6473223B623A303B7D + + + 3 + Usuarios + 4F3A32343A2253505C446174614D6F64656C5C50726F66696C6544617461223A32393A7B733A31303A22002A0061636356696577223B623A313B733A31343A22002A006163635669657750617373223B623A313B733A31373A22002A0061636356696577486973746F7279223B623A313B733A31303A22002A0061636345646974223B623A313B733A31343A22002A006163634564697450617373223B623A313B733A393A22002A00616363416464223B623A313B733A31323A22002A0061636344656C657465223B623A313B733A31313A22002A0061636346696C6573223B623A303B733A31333A22002A0061636350726976617465223B623A303B733A31383A22002A006163635072697661746547726F7570223B623A303B733A31363A22002A006163635065726D697373696F6E223B623A303B733A31373A22002A006163635075626C69634C696E6B73223B623A303B733A31383A22002A00616363476C6F62616C536561726368223B623A303B733A31363A22002A00636F6E66696747656E6572616C223B623A303B733A31393A22002A00636F6E666967456E6372797074696F6E223B623A303B733A31353A22002A00636F6E6669674261636B7570223B623A303B733A31353A22002A00636F6E666967496D706F7274223B623A303B733A31313A22002A006D676D5573657273223B623A303B733A31323A22002A006D676D47726F757073223B623A303B733A31343A22002A006D676D50726F66696C6573223B623A303B733A31363A22002A006D676D43617465676F72696573223B623A303B733A31353A22002A006D676D437573746F6D657273223B623A303B733A31353A22002A006D676D417069546F6B656E73223B623A303B733A31373A22002A006D676D5075626C69634C696E6B73223B623A303B733A31343A22002A006D676D4163636F756E7473223B623A303B733A31303A22002A006D676D54616773223B623A303B733A31313A22002A006D676D46696C6573223B623A303B733A363A22002A0065766C223B623A303B733A31383A22002A006D676D437573746F6D4669656C6473223B623A303B7D + + + + + 1 + sysPass Admin + 1 + admin + + 243279243130247635695230547A4933744E3036416A304C4A656B39755371496834356C70575539366E644A71444A704E6969713139306A444A486D + 64656635303230306330383635633335373637316233366261353266333137346134356466333633626135656431613962323261356461353965376134373930356664656239373436356462373934613831373635316133316432626363636266663836626233353235643930333932393734323535663937316261616239656436346637383266363066646465386539336637363164356663633436393031356433363164333234643436633533633138313335613334663739633039 + 6465663130303030646566353032303062316163626161346361643036643237386562323533616462643433613966376463636439343063656265313962343061383436643464633035303636306466653630613561653139363433643636353936643733333764646236386536363930336562383031373764356463386430663963623661643361663565643766303936376262393964663530373936316330656132373462663830346333663966373563336538643539396336326231623738333730303963616263373836383637366433636337376162383365386338323335336335626164396534656535333532656132346632653434653663316336656131643162313264393332386335303539623437656235343534666138356435626437343637353333383132636662313230316634633635383733316465323934613664643035396332613362373333613765343462633539306338363337393032306662303263363262303565613030623234646235323566653863303263323138666561356661353139306563396266333461316637613937633733396637343534323333316466323932343965323138656338343233306161623038373336346463353236363935383630656638623232313439626262656636656266373030366638376434343661333535353863323062353462376336623330 + + + 81 + 1 + 2018-04-19 23:46:48 + + 1521887152 + 1 + 0 + 0 + 0 + + 0 + 0 + 0 + 4F3A33323A2253505C446174614D6F64656C5C55736572507265666572656E63657344617461223A31303A7B733A373A22757365725F6964223B693A313B733A363A22757365324661223B623A303B733A343A226C616E67223B733A353A22656E5F5553223B733A353A227468656D65223B733A31333A226D6174657269616C2D626C7565223B733A31343A22726573756C747350657250616765223B693A34383B733A31313A226163636F756E744C696E6B223B623A313B733A393A22736F72745669657773223B623A303B733A393A22746F704E6176626172223B623A303B733A31353A226F7074696F6E616C416374696F6E73223B623A303B733A31343A22726573756C747341734361726473223B623A303B7D + + + 2 + sysPass demo + 2 + demo + demo + 2432792431302454726E69756C5763754361433635346F76566F35392E766B4C5433414E31624A6D726A79553462696335325069436A6B5572396669 + 64656635303230303231616533353730373263373165626239393534353966366236636164373235336534316336633534353036336339326136653730616366333930393165373934613865376662386662326664333931383932363562396466303133333631623063323732323339653465373165343839313030646534326265633737623966343238396635633936613837646531343864313963653663643338613131343932623163313765653630326430623532343564346566 + 6465663130303030646566353032303035643534316262633462653032333563313338626561366561333536626436663037353365313035653030333563653166316235336534663364343565366262353335626163396639646538653131316262356334383865336535633637323333666632626365313837626335386135353839373535373034386564353634366361646638623736396132323164363032353435653034306264613135663138323638383665373536313236353361313037306530333261323365636364336339616438323162306363383962643130333035303931653965626332653935313465656631373462663339343664656132393661346262366264343463646333363361643335623032373561356633323430313936346531633131663937313764313139633130633561373161666332356365346534366661623234646663626362326237303964336335316532623834326464303933653230353965373265356638376363366236626239306231346265376264373637663163303937366231313362393630613265636565336633313131663538656131346139353736623332653163303962636435313366383733656664653062373333366238643464646637616237323333373038613264393965633738356139393036306135643262316366306262663739346262663765 + demo@syspass.org + aaaa + 12 + 2 + 2018-04-01 21:29:47 + 2018-04-14 08:47:43 + 1522582852 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + + + + 3 + User A + 2 + user_a + user_a + 2432792431302469444B442E2F4F624D79742E6F43594F5249514D5065624454783966744D636A703034365A435976662E765479597A594F6A4C472E + + + user_a@syspass.org + + 0 + 1 + + 2018-04-14 08:48:08 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + + + + 4 + User B + 2 + user_b + + 243279243130244C37643658736A663955794F6E583662472E6F384E4F713961674B6F64536B4B5674485350462F6861414E657971517065372E6532 + + + user_b@syspass.org + + 0 + 1 + + 2018-03-30 18:38:32 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + + + 1