1
0
mirror of https://github.com/devcode-it/openstamanager.git synced 2025-06-05 22:09:38 +02:00

feat: Cifratura backup tramite password

This commit is contained in:
valentina
2025-04-23 17:29:38 +02:00
parent 4bd6dbf3e8
commit ffd41bf29f
6 changed files with 205 additions and 30 deletions

View File

@@ -1 +0,0 @@
Deny from all

View File

@@ -28,9 +28,6 @@ $db_options = [
// 'sort_buffer_size' => '2M',
];
// Percorso della cartella di backup
$backup_dir = __DIR__.'/backup/';
// Tema selezionato per il front-end
$theme = 'default';

View File

@@ -70,6 +70,7 @@ switch (filter('op')) {
if ($result) {
flash()->info(tr('Nuovo backup creato correttamente!'));
} else {
$backup_dir = Backup::getDirectory();
flash()->error(tr('Errore durante la creazione del backup!').' '.str_replace('_DIR_', '"'.$backup_dir.'"', tr('Verifica che la cartella _DIR_ abbia i permessi di scrittura!')));
}
} catch (Exception $e) {
@@ -83,7 +84,7 @@ switch (filter('op')) {
$number = intval($number);
$backups = Backup::getList();
$backup = $backups[$number] ?: $backup_dir;
$backup = $backups[$number] ?: Backup::getDirectory();
echo Util\FileSystem::size($backup);
@@ -111,7 +112,10 @@ if (filter('op') == 'restore') {
}
try {
$result = Backup::restore($path, is_file($path));
// Ottieni la password per i backup esterni se impostata
$password = setting('Password backup esterni');
$result = Backup::restore($path, is_file($path), $password);
$database->beginTransaction();
if ($result) {

View File

@@ -45,9 +45,46 @@ class Backup
*/
public static function getDirectory()
{
$result = App::getConfig()['backup_dir'];
// Ottieni l'adattatore di archiviazione selezionato
$adapter = self::getStorageAdapter();
$backup_dir = base_dir().'/backups';
$result = rtrim((string) $result, '/');
// Estrai la directory dalle opzioni dell'adattatore
if (!empty($adapter)) {
$options = $adapter->options;
// Se options è una stringa JSON, decodificala
if (is_string($options)) {
// Prova a decodificare il JSON normalmente
$decoded = json_decode($options, true);
// Se la decodifica fallisce, prova a gestire il caso specifico con $ nella password
if ($decoded === null && strpos($options, 'password') !== false) {
// Estrai manualmente il valore di root usando espressioni regolari
if (preg_match('/"root":"([^"]+)"/', $options, $matches)) {
$backup_dir = $matches[1];
}
} else {
$options = $decoded ?: [];
// Verifica se esiste la chiave 'directory' o 'root'
if (!empty($options)) {
if (isset($options['directory'])) {
$backup_dir = base_dir().$options['directory'];
} elseif (isset($options['root'])) {
$backup_dir = $options['root'];
}
}
}
}
}
// Fallback al percorso di configurazione se disponibile
if (empty($backup_dir)) {
$config = App::getConfig();
$backup_dir = isset($config['backup_dir']) ? $config['backup_dir'] : base_dir().'/backups';
}
$result = rtrim((string) $backup_dir, '/');
if (!directory($result) || !is_writable($result)) {
// throw new UnexpectedValueException();
}
@@ -177,6 +214,7 @@ class Backup
'tmp',
'.git',
'.github',
'.config', // Aggiungi la directory .config per evitare errori di permesso
],
];
@@ -187,20 +225,79 @@ class Backup
$ignores['dirs'][] = basename($backup_dir);
}
// Nome del file di backup
$backup_filename = $backup_name.'.zip';
$backup_path = $backup_dir.'/'.$backup_filename;
// Creazione backup in formato ZIP
if (extension_loaded('zip')) {
$result = Zip::create([
base_dir(),
self::getDatabaseDirectory(),
], $backup_dir.'/'.$backup_name.'.zip', $ignores);
}
// Verifica se è impostata una password per il backup
$password = setting('Password di protezione backup');
// Se è impostata una password e ZipArchive è disponibile, crea un backup protetto da password
if (!empty($password) && class_exists('ZipArchive')) {
// Crea un percorso temporaneo per il backup
$temp_path = $backup_path . '.tmp';
// Crea prima un backup normale
$result = Zip::create([
base_dir(),
self::getDatabaseDirectory(),
], $temp_path, $ignores);
if ($result) {
// Crea un nuovo ZIP protetto da password
$zip = new \ZipArchive();
if ($zip->open($backup_path, \ZipArchive::CREATE) === true) {
// Apri il file ZIP originale in lettura
$original_zip = new \ZipArchive();
if ($original_zip->open($temp_path) === true) {
// Imposta la password per il nuovo ZIP
$zip->setPassword($password);
// Copia tutti i file dal backup originale al nuovo backup protetto da password
for ($i = 0; $i < $original_zip->numFiles; $i++) {
$stat = $original_zip->statIndex($i);
$file_content = $original_zip->getFromIndex($i);
// Aggiungi il file al nuovo ZIP
$zip->addFromString($stat['name'], $file_content);
// Cripta il file con AES-256
$zip->setEncryptionIndex($i, \ZipArchive::EM_AES_256, $password);
}
// Chiudi i file ZIP
$original_zip->close();
$zip->close();
// Rimuovi il file temporaneo
unlink($temp_path);
} else {
// Fallback al metodo normale
$zip->close();
unlink($backup_path);
rename($temp_path, $backup_path);
}
} else {
// Fallback al metodo normale
rename($temp_path, $backup_path);
}
}
} else {
// Crea un backup normale senza password
$result = Zip::create([
base_dir(),
self::getDatabaseDirectory(),
], $backup_path, $ignores);
}
}
// Creazione backup attraverso la copia dei file
else {
$result = copyr([
base_dir(),
self::getDatabaseDirectory(),
], $backup_dir.'/'.$backup_name.'.zip', $ignores);
], $backup_path, $ignores);
}
// Rimozione cartella temporanea
@@ -248,11 +345,37 @@ class Backup
* Ripristina un backup esistente.
*
* @param string $path
* @param bool $cleanup
* @param string|null $password Password per decriptare il backup (se criptato)
*/
public static function restore($path, $cleanup = true)
public static function restore($path, $cleanup = true, $password = null)
{
$database = database();
$extraction_dir = is_dir($path) ? $path : Zip::extract($path);
// Se il backup non è una directory e è stata fornita una password, prova a estrarlo con la password
if (!is_dir($path) && !empty($password) && class_exists('ZipArchive')) {
$zip = new \ZipArchive();
if ($zip->open($path) === true) {
// Imposta la password per l'estrazione
$zip->setPassword($password);
// Estrai il backup in una directory temporanea
$extraction_dir = sys_get_temp_dir().'/'.basename($path, '.zip');
if (!directory($extraction_dir)) {
mkdir($extraction_dir, 0777, true);
}
// Estrai tutti i file
$zip->extractTo($extraction_dir);
$zip->close();
} else {
// Se non è possibile aprire il file ZIP con la password, prova a estrarlo normalmente
$extraction_dir = Zip::extract($path);
}
} else {
// Estrai il backup normalmente
$extraction_dir = is_dir($path) ? $path : Zip::extract($path);
}
// TODO: Forzo il log out di tutti gli utenti e ne impedisco il login
// fino a ripristino ultimato
@@ -340,6 +463,8 @@ class Backup
return slashes($result);
}
/**
* Restituisce l'elenco delle variabili da sostituire normalizzato per l'utilizzo.
*/
@@ -348,6 +473,23 @@ class Backup
return Generator::getReplaces();
}
/**
* Restituisce l'adattatore di archiviazione da utilizzare per i backup.
*
* @return \Modules\FileAdapters\FileAdapter
*/
public static function getStorageAdapter()
{
$adapter_id = setting('Adattatore archiviazione backup');
// Se non è stato selezionato un adattatore, utilizzo quello predefinito
if (empty($adapter_id)) {
return \Modules\FileAdapters\FileAdapter::getDefaultConnector();
}
return \Modules\FileAdapters\FileAdapter::find($adapter_id);
}
/**
* Restituisce il nome previsto per il backup successivo.
*

View File

@@ -1,5 +1,16 @@
<?php
// Spostamento backup
$directory = 'backup/';
$files = glob($directory.'*.{zip}', GLOB_BRACE);
$new_folder = 'files/backups/';
directory($new_folder);
foreach ($files as $file) {
$filename = basename($file);
rename($file, $new_folder.$filename);
}
// File e cartelle deprecate
$files = [
'templates/bilancio/settings.php',
@@ -12,10 +23,11 @@ $files = [
'templates/preventivi/settings.php',
'templates/prima_nota/settings.php',
'templates/scadenzario/settings.php',
'backup/',
];
foreach ($files as $key => $value) {
$files[$key] = realpath(base_dir().'/'.$value);
}
delete($files);
delete($files);

View File

@@ -6,15 +6,15 @@ INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`) VALUES (1, (SEL
INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`) VALUES (1, (SELECT id FROM zz_settings WHERE `nome`='Api key ibanapi.com'), 'Api key ibanapi.com');
-- Aggiunta impostazione per OpenRouter API Key
INSERT INTO `zz_settings` (`id`, `nome`, `valore`, `tipo`, `editable`, `sezione`, `order`) VALUES
INSERT INTO `zz_settings` (`id`, `nome`, `valore`, `tipo`, `editable`, `sezione`, `order`) VALUES
(NULL, 'OpenRouter API Key', '', 'string', 1, 'API', NULL);
INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`, `help`) VALUES
INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`, `help`) VALUES
(1, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'OpenRouter API Key'),
'OpenRouter API Key',
'API Key per l''integrazione con OpenRouter AI. Ottieni la tua chiave da https://openrouter.ai/keys');
INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`, `help`) VALUES
INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`, `help`) VALUES
(2, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'OpenRouter API Key'),
'OpenRouter API Key',
'API Key for OpenRouter AI integration. Get your key from https://openrouter.ai/keys');
@@ -54,23 +54,23 @@ INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`, `help`) VALUES
INSERT INTO `zz_modules` (`name`, `directory`, `options`, `options2`, `icon`, `version`, `compatibility`, `order`, `parent`, `default`, `enabled`, `use_notes`, `use_checklists`) VALUES ('Descrizioni predefinite', 'descrizioni_predefinite', 'SELECT |select| FROM `zz_default_description` WHERE 1=1 HAVING 2=2', '', 'fa fa-circle-o', '2.8', '2.8', '8', (SELECT `id` FROM `zz_modules` AS `t` WHERE `name` = 'Tabelle'), '1', '1', '1', '1');
SELECT @id_module := `id` FROM `zz_modules` WHERE `name` = 'Descrizioni predefinite';
INSERT INTO `zz_modules_lang` (`id_lang`, `id_record`, `title`, `meta_title`) VALUES
INSERT INTO `zz_modules_lang` (`id_lang`, `id_record`, `title`, `meta_title`) VALUES
('1', @id_module, 'Descrizioni predefinite', 'Descrizioni predefinite'),
('2', @id_module, 'Descrizioni predefinite', 'Descrizioni predefinite');
SELECT @id_module := `id` FROM `zz_modules` WHERE `name` = 'Descrizioni predefinite';
INSERT INTO `zz_views` (`id_module`, `name`, `query`, `order`, `search`, `slow`, `format`, `html_format`, `search_inside`, `order_by`, `visible`, `summable`, `avg`, `default`) VALUES
INSERT INTO `zz_views` (`id_module`, `name`, `query`, `order`, `search`, `slow`, `format`, `html_format`, `search_inside`, `order_by`, `visible`, `summable`, `avg`, `default`) VALUES
(@id_module, 'Descrizione', '`zz_default_description`.`descrizione`', '3', '1', '0', '0', '0', NULL, NULL, '1', '0', '0', '1'),
(@id_module, 'Nome', 'zz_default_description.name', '2', '1', '0', '0', '0', NULL, NULL, '1', '0', '0', '1'),
(@id_module, 'id', '`zz_default_description`.`id`', '1', '0', '0', '0', '0', NULL, NULL, '0', '0', '0', '1');
(@id_module, 'id', '`zz_default_description`.`id`', '1', '0', '0', '0', '0', NULL, NULL, '0', '0', '0', '1');
SELECT @id_module := `id` FROM `zz_modules` WHERE `name` = 'Descrizioni predefinite';
INSERT INTO `zz_views_lang` (`id_lang`, `id_record`, `title`) VALUES
('1', (SELECT `id` FROM `zz_views` WHERE `name` = 'Descrizione' AND `id_module` = @id_module), 'Descrizione'),
('2', (SELECT `id` FROM `zz_views` WHERE `name` = 'Descrizione' AND `id_module` = @id_module), 'Description'),
INSERT INTO `zz_views_lang` (`id_lang`, `id_record`, `title`) VALUES
('1', (SELECT `id` FROM `zz_views` WHERE `name` = 'Descrizione' AND `id_module` = @id_module), 'Descrizione'),
('2', (SELECT `id` FROM `zz_views` WHERE `name` = 'Descrizione' AND `id_module` = @id_module), 'Description'),
('1', (SELECT `id` FROM `zz_views` WHERE `name` = 'Nome' AND `id_module` = @id_module), 'Nome'),
('2', (SELECT `id` FROM `zz_views` WHERE `name` = 'Nome' AND `id_module` = @id_module), 'Name'),
('1', (SELECT `id` FROM `zz_views` WHERE `name` = 'id' AND `id_module` = @id_module), 'id'),
('2', (SELECT `id` FROM `zz_views` WHERE `name` = 'Nome' AND `id_module` = @id_module), 'Name'),
('1', (SELECT `id` FROM `zz_views` WHERE `name` = 'id' AND `id_module` = @id_module), 'id'),
('2', (SELECT `id` FROM `zz_views` WHERE `name` = 'id' AND `id_module` = @id_module), 'id');
CREATE TABLE `zz_default_description` (`id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(255) NOT NULL , `descrizione` TEXT NOT NULL , `note` TEXT NOT NULL , PRIMARY KEY (`id`));
@@ -92,7 +92,7 @@ INSERT INTO `zz_views_lang` (`id_lang`, `id_record`, `title`) VALUES
-- Miglioria plugin Assicurazione crediti
UPDATE `zz_plugins` SET `options` = '{ \"main_query\": [ { \"type\": \"table\", \"fields\": \"Fido assicurato, Data inizio, Data fine, Totale, Residuo\", \"query\": \"SELECT id, DATE_FORMAT(data_inizio,\'%d/%m/%Y\') AS \'Data inizio\', DATE_FORMAT(data_fine,\'%d/%m/%Y\') AS \'Data fine\', ROUND(fido_assicurato, 2) AS \'Fido assicurato\', ROUND(totale, 2) AS Totale, ROUND(fido_assicurato - totale, 2) AS Residuo, IF((fido_assicurato - totale) < 0, \'#f4af1b\', \'#4dc347\') AS _bg_ FROM an_assicurazione_crediti WHERE 1=1 AND id_anagrafica = |id_parent| HAVING 2=2 ORDER BY an_assicurazione_crediti.id DESC\"} ]}' WHERE `zz_plugins`.`name` = 'Assicurazione crediti';
ALTER TABLE `my_impianti` ADD `note` VARCHAR(255) NULL AFTER `descrizione`;
ALTER TABLE `my_impianti` ADD `note` VARCHAR(255) NULL AFTER `descrizione`;
-- Aggiunta colonne Marchio e Modello nella vista Articoli (nascoste di default)
SELECT @id_module := `id` FROM `zz_modules` WHERE `name` = 'Articoli';
@@ -105,4 +105,25 @@ INSERT INTO `zz_views_lang` (`id_lang`, `id_record`, `title`) VALUES
(1, @id-1, 'Marchio'),
(2, @id-1, 'Brand'),
(1, @id, 'Modello'),
(2, @id, 'Model');
(2, @id, 'Model');
INSERT INTO `zz_storage_adapters` (`name`, `class`, `options`, `can_delete`, `is_default`, `is_local`) VALUES
('Backup', '\\Modules\\FileAdapters\\Adapters\\LocalAdapter', '{ \"directory\":\"/files/backups\" }', 1, 0, 1);
ALTER TABLE `zz_settings` CHANGE `is_user_setting` `is_user_setting` TINYINT(1) NOT NULL DEFAULT '0';
-- Aggiunta impostazione per l'adattatore di archiviazione per i backup
INSERT INTO `zz_settings` (`id`, `nome`, `valore`, `tipo`, `editable`, `sezione`, `order`) VALUES
(NULL, 'Adattatore archiviazione backup', (SELECT `id` FROM `zz_storage_adapters` WHERE name = 'Backup'), 'query=SELECT `id`, `name` AS descrizione FROM `zz_storage_adapters` WHERE `deleted_at` IS NULL ORDER BY `name`', 1, 'Backup', NULL);
INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`, `help`) VALUES
(1, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Adattatore archiviazione backup'), 'Adattatore archiviazione backup', 'Adattatore di archiviazione da utilizzare per i backup'),
(2, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Adattatore archiviazione backup'), 'Backup storage adapter', 'Storage adapter to use for backups');
-- Aggiunta impostazione per la password dei backup esterni
INSERT INTO `zz_settings` (`id`, `nome`, `valore`, `tipo`, `editable`, `sezione`, `order`) VALUES
(NULL, 'Password di protezione backup', '', 'password', 1, 'Backup', NULL);
INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`, `help`) VALUES
(1, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Password di protezione backup'), 'Password di protezione backup', 'Password da utilizzare per proteggere i backup in formato zip'),
(2, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Password di protezione backup'), 'Backup protection password', 'Password to use for protecting zip backups');