diff --git a/ajax/ajax_export.php b/ajax/ajax_export.php deleted file mode 100644 index 0886cc00..00000000 --- a/ajax/ajax_export.php +++ /dev/null @@ -1,92 +0,0 @@ -. - * - */ - -use SP\Core\ActionsInterface; -use SP\Core\Init; -use SP\Core\SessionUtil; -use SP\Core\XmlExport; -use SP\Http\JsonResponse; -use SP\Http\Request; -use SP\Util\Checks; -use SP\Util\Json; - -define('APP_ROOT', '..'); - -require_once APP_ROOT . DIRECTORY_SEPARATOR . 'inc' . DIRECTORY_SEPARATOR . 'Base.php'; - -Request::checkReferer('POST'); - -$Json = new JsonResponse(); - -if (!Init::isLoggedIn()) { - $Json->setStatus(10); - $Json->setDescription(_('La sesión no se ha iniciado o ha caducado')); - Json::returnJson($Json); -} - -$sk = Request::analyze('sk', false); - -if (!$sk || !SessionUtil::checkSessionKey($sk)) { - $Json->setDescription(_('CONSULTA INVÁLIDA')); - Json::returnJson($Json); -} - -$actionId = Request::analyze('actionId', 0); -$onCloseAction = Request::analyze('onCloseAction'); -$activeTab = Request::analyze('activeTab', 0); -$exportPassword = Request::analyzeEncrypted('exportPwd'); -$exportPasswordR = Request::analyzeEncrypted('exportPwdR'); - -if ($actionId === ActionsInterface::ACTION_CFG_BACKUP) { - if (Checks::demoIsEnabled()) { - $Json->setDescription(_('Ey, esto es una DEMO!!')); - Json::returnJson($Json); - } - - if (!\SP\Core\Backup::doBackup()) { - $Json->setDescription(_('Error al realizar el backup')); - $Json->addMessage(_('Revise el registro de eventos para más detalles')); - Json::returnJson($Json); - } - - $Json->setStatus(0); - $Json->setDescription(_('Proceso de backup finalizado')); - Json::returnJson($Json); -} elseif ($actionId === ActionsInterface::ACTION_CFG_EXPORT) { - if (!empty($exportPassword) && $exportPassword !== $exportPasswordR) { - $Json->setDescription(_('Las claves no coinciden')); - Json::returnJson($Json); - } - - if (!XmlExport::doExport($exportPassword)) { - $Json->setDescription(_('Error al realizar la exportación')); - $Json->addMessage(_('Revise el registro de eventos para más detalles')); - Json::returnJson($Json); - } - - $Json->setStatus(0); - $Json->setDescription(_('Proceso de exportación finalizado')); - Json::returnJson($Json); -} \ No newline at end of file diff --git a/ajax/ajax_getEnvironment.php b/ajax/ajax_getEnvironment.php index 103242eb..938674df 100644 --- a/ajax/ajax_getEnvironment.php +++ b/ajax/ajax_getEnvironment.php @@ -46,7 +46,7 @@ $data = array( 'app_root' => Init::$WEBURI, 'pk' => '', 'max_file_size' => $Config->getFilesAllowedSize(), - 'check_updates' => ($Config->isCheckUpdates() || $Config->isChecknotices()) && (Session::getUserData()->isUserIsAdminApp() || Checks::demoIsEnabled()), + 'check_updates' => Session::getAuthCompleted() && ($Config->isCheckUpdates() || $Config->isChecknotices()) && (Session::getUserData()->isUserIsAdminApp() || Checks::demoIsEnabled()), 'timezone' => date_default_timezone_get(), 'debug' => DEBUG || $Config->isDebug() ); diff --git a/ajax/ajax_import.php b/ajax/ajax_import.php deleted file mode 100644 index 8b6e39af..00000000 --- a/ajax/ajax_import.php +++ /dev/null @@ -1,74 +0,0 @@ -. - * - */ - -use SP\Core\Init; -use SP\Core\SessionUtil; -use SP\Http\Request; -use SP\Http\Response; -use SP\Import\Import; -use SP\Util\Checks; - -define('APP_ROOT', '..'); - -require_once APP_ROOT . DIRECTORY_SEPARATOR . 'inc' . DIRECTORY_SEPARATOR . 'Base.php'; - -Request::checkReferer('POST'); - -if (!Init::isLoggedIn()) { - Response::printJson(_('La sesión no se ha iniciado o ha caducado'), 10); -} - -if (Checks::demoIsEnabled()) { - Response::printJson(_('Ey, esto es una DEMO!!')); -} - -$sk = Request::analyze('sk', false); -$defaultUser= Request::analyze('defUser', 0); -$defaultGroup = Request::analyze('defGroup', 0); -$importPwd = Request::analyzeEncrypted('importPwd'); -$csvDelimiter = Request::analyze('csvDelimiter'); - -if (!$sk || !SessionUtil::checkSessionKey($sk)) { - Response::printJson(_('CONSULTA INVÁLIDA')); -} - -Import::setDefUser($defaultUser); -Import::setDefGroup($defaultGroup); -Import::setImportPwd($importPwd); -Import::setCsvDelimiter($csvDelimiter); - -$res = Import::doImport($_FILES["inFile"]); - -if (isset($res['error']) && is_array($res['error'])) { - error_log($res['error']['hint']); - - $out = implode('\n\n', $res['error']); - - Response::printJson($out); -} else if (is_array($res['ok'])) { - $out = implode('\n\n', $res['ok']); - - Response::printJson($out, 0); -} \ No newline at end of file diff --git a/inc/SP/Account/Account.class.php b/inc/SP/Account/Account.class.php index 60019a2d..0675c91e 100644 --- a/inc/SP/Account/Account.class.php +++ b/inc/SP/Account/Account.class.php @@ -325,9 +325,11 @@ class Account extends AccountBase implements AccountInterface * @return $this * @throws \SP\Core\Exceptions\SPException */ - public function createAccount() + public function createAccount($encryptPass = true) { - $this->setPasswordEncrypted(); + if ($encryptPass === true) { + $this->setPasswordEncrypted(); + } $query = /** @lang SQL */ 'INSERT INTO accounts SET ' diff --git a/inc/SP/Account/AccountHistory.class.php b/inc/SP/Account/AccountHistory.class.php index 53bfb421..f21a2cda 100644 --- a/inc/SP/Account/AccountHistory.class.php +++ b/inc/SP/Account/AccountHistory.class.php @@ -489,8 +489,9 @@ class AccountHistory extends AccountBase implements AccountInterface * Crear una cuenta en el historial * * @return bool + * @throws \SP\Core\Exceptions\SPException */ - public function createAccount() + public function createAccount($encryptPass = true) { $query = /** @lang SQL */ 'INSERT INTO accHistory SET ' diff --git a/inc/SP/Account/AccountInterface.class.php b/inc/SP/Account/AccountInterface.class.php index 23bb9a31..d9f4b99f 100644 --- a/inc/SP/Account/AccountInterface.class.php +++ b/inc/SP/Account/AccountInterface.class.php @@ -42,9 +42,10 @@ interface AccountInterface public function getData(); /** + * @param bool $encryptPass Si se encripta la clave de la cuenta * @return mixed */ - public function createAccount(); + public function createAccount($encryptPass = true); /** * @param $id diff --git a/inc/SP/Controller/ConfigActionController.class.php b/inc/SP/Controller/ConfigActionController.class.php index bb956bec..25f4c5ee 100644 --- a/inc/SP/Controller/ConfigActionController.class.php +++ b/inc/SP/Controller/ConfigActionController.class.php @@ -29,12 +29,16 @@ use SP\Account\AccountHistory; use SP\Config\Config; use SP\Config\ConfigDB; use SP\Core\ActionsInterface; +use SP\Core\Backup; use SP\Core\Crypt; use SP\Core\CryptMasterPass; use SP\Core\Exceptions\SPException; use SP\Core\Session; +use SP\Core\XmlExport; use SP\Html\Html; use SP\Http\Request; +use SP\Import\Import; +use SP\Import\ImportParams; use SP\Log\Email; use SP\Log\Log; use SP\Mgmt\CustomFields\CustomFieldsUtil; @@ -87,6 +91,15 @@ class ConfigActionController implements ItemControllerInterface case ActionsInterface::ACTION_CFG_ENCRYPTION_TEMPPASS: $this->tempMasterPassAction(); break; + case ActionsInterface::ACTION_CFG_IMPORT: + $this->importAction(); + break; + case ActionsInterface::ACTION_CFG_EXPORT: + $this->exportAction(); + break; + case ActionsInterface::ACTION_CFG_BACKUP: + $this->backupAction(); + break; default: $this->invalidAction(); } @@ -508,7 +521,7 @@ class ConfigActionController implements ItemControllerInterface } /** - * Acción para generarclave maestra temporal + * Acción para generar clave maestra temporal */ protected function tempMasterPassAction() { @@ -533,4 +546,74 @@ class ConfigActionController implements ItemControllerInterface $Log->writeLog(); Email::sendEmail($Log); } + + /** + * Acción para importar cuentas + * + * @throws \SP\Core\Exceptions\SPException + */ + protected function importAction() + { + if (Checks::demoIsEnabled()) { + $this->jsonResponse->setDescription(_('Ey, esto es una DEMO!!')); + return; + } + + $ImportParams = new ImportParams(); + $ImportParams->setDefaultUser(Request::analyze('defUser', Session::getUserData()->getUserId())); + $ImportParams->setDefaultGroup(Request::analyze('defGroup', Session::getUserData()->getUserGroupId())); + $ImportParams->setImportPwd(Request::analyzeEncrypted('importPwd')); + $ImportParams->setImportMasterPwd(Request::analyzeEncrypted('importMasterPwd')); + $ImportParams->setCsvDelimiter(Request::analyze('csvDelimiter')); + + $Import = new Import($ImportParams); + $Message = $Import->doImport($_FILES['inFile']); + + $this->jsonResponse->setDescription($Message->getDescription()); + $this->jsonResponse->addMessage($Message->getHint()); + $this->jsonResponse->setStatus(0); + } + + /** + * Acción para exportar cuentas + */ + protected function exportAction() + { + $exportPassword = Request::analyzeEncrypted('exportPwd'); + $exportPasswordR = Request::analyzeEncrypted('exportPwdR'); + + if (!empty($exportPassword) && $exportPassword !== $exportPasswordR) { + $this->jsonResponse->setDescription(_('Las claves no coinciden')); + return; + } + + if (!XmlExport::doExport($exportPassword)) { + $this->jsonResponse->setDescription(_('Error al realizar la exportación')); + $this->jsonResponse->addMessage(_('Revise el registro de eventos para más detalles')); + return; + } + + $this->jsonResponse->setStatus(0); + $this->jsonResponse->setDescription(_('Proceso de exportación finalizado')); + } + + /** + * Acción para realizar el backup de sysPass + */ + protected function backupAction() + { + if (Checks::demoIsEnabled()) { + $this->jsonResponse->setDescription(_('Ey, esto es una DEMO!!')); + return; + } + + if (!Backup::doBackup()) { + $this->jsonResponse->setDescription(_('Error al realizar el backup')); + $this->jsonResponse->addMessage(_('Revise el registro de eventos para más detalles')); + return; + } + + $this->jsonResponse->setStatus(0); + $this->jsonResponse->setDescription(_('Proceso de backup finalizado')); + } } \ No newline at end of file diff --git a/inc/SP/Core/Backup.class.php b/inc/SP/Core/Backup.class.php index e6a2c5ef..8f33edde 100644 --- a/inc/SP/Core/Backup.class.php +++ b/inc/SP/Core/Backup.class.php @@ -55,7 +55,7 @@ class Backup $backupDir = Init::$SERVERROOT; // Generar hash unico para evitar descargas no permitidas - $backupUniqueHash = uniqid(); + $backupUniqueHash = sha1(uniqid('sysPassBackup', true)); Config::getConfig()->setBackupHash($backupUniqueHash); Config::saveConfig(); @@ -86,12 +86,43 @@ class Backup return true; } + /** + * Comprobar y crear el directorio de backups. + * + * @param string $backupDir ruta del directorio de backup + * @throws SPException + * @return bool + */ + private static function checkBackupDir($backupDir) + { + if (@mkdir($backupDir, 0750) === false && is_dir($backupDir) === false) { + throw new SPException(SPException::SP_CRITICAL, sprintf(_('No es posible crear el directorio de backups ("%s")'), $backupDir)); + } + + if (!is_writable($backupDir)) { + throw new SPException(SPException::SP_CRITICAL, _('Compruebe los permisos del directorio de backups')); + } + + return true; + } + + /** + * Eliminar las copias de seguridad anteriores + * + * @param string $backupDir El directorio de backups + */ + private static function deleteOldBackups($backupDir) + { + array_map('unlink', glob($backupDir . DIRECTORY_SEPARATOR . '*.tar.gz')); + array_map('unlink', glob($backupDir . DIRECTORY_SEPARATOR . '*.sql')); + } + /** * Backup de las tablas de la BBDD. * Utilizar '*' para toda la BBDD o 'table1 table2 table3...' * - * @param string $tables - * @param string $backupFile + * @param string|array $tables + * @param string $backupFile * @throws SPException * @return bool */ @@ -104,10 +135,8 @@ class Backup $Data = new QueryData(); - if ($tables == '*') { - $Data->setQuery('SHOW TABLES'); - - $resTables = DB::getResults($Data); + if ($tables === '*') { + $resTables = DBUtil::$tables; } else { $resTables = is_array($tables) ? $tables : explode(',', $tables); } @@ -121,52 +150,63 @@ class Backup $sqlOut .= 'USE `' . $dbname . '`;' . PHP_EOL . PHP_EOL; fwrite($handle, $sqlOut); + $sqlOutViews = ''; // Recorrer las tablas y almacenar los datos foreach ($resTables as $table) { - $tableName = $table->{'Tables_in_' . $dbname}; + $tableName = is_object($table) ? $table->{'Tables_in_' . $dbname} : $table; $Data->setQuery('SHOW CREATE TABLE ' . $tableName); - $sqlOut = '-- ' . PHP_EOL; - $sqlOut .= '-- Table ' . strtoupper($tableName) . PHP_EOL; - $sqlOut .= '-- ' . PHP_EOL; - // Consulta para crear la tabla - $sqlOut .= 'DROP TABLE IF EXISTS `' . $tableName . '`;' . PHP_EOL . PHP_EOL; $txtCreate = DB::getResults($Data); - $sqlOut .= $txtCreate->{'Create Table'} . ';' . PHP_EOL . PHP_EOL; - fwrite($handle, $sqlOut); - $Data->setQuery('SELECT * FROM ' . $tableName); + if (isset($txtCreate->{'Create Table'})) { + $sqlOut = '-- ' . PHP_EOL; + $sqlOut .= '-- Table ' . strtoupper($tableName) . PHP_EOL; + $sqlOut .= '-- ' . PHP_EOL; + $sqlOut .= 'DROP TABLE IF EXISTS `' . $tableName . '`;' . PHP_EOL . PHP_EOL; + $sqlOut .= $txtCreate->{'Create Table'} . ';' . PHP_EOL . PHP_EOL; + fwrite($handle, $sqlOut); - // Consulta para obtener los registros de la tabla - $queryRes = DB::getResultsRaw($Data); + $Data->setQuery('SELECT * FROM ' . $tableName); - $numColumns = $queryRes->columnCount(); + // Consulta para obtener los registros de la tabla + $queryRes = DB::getResultsRaw($Data); - while ($row = $queryRes->fetch(\PDO::FETCH_NUM)) { - fwrite($handle, 'INSERT INTO `' . $tableName . '` VALUES('); + $numColumns = $queryRes->columnCount(); - $field = 1; - foreach ($row as $value) { - if (is_numeric($value)) { - fwrite($handle, $value); - } else { - fwrite($handle, DBUtil::escape($value)); + while ($row = $queryRes->fetch(\PDO::FETCH_NUM)) { + fwrite($handle, 'INSERT INTO `' . $tableName . '` VALUES('); + + $field = 1; + foreach ($row as $value) { + if (is_numeric($value)) { + fwrite($handle, $value); + } else { + fwrite($handle, DBUtil::escape($value)); + } + + if ($field < $numColumns) { + fwrite($handle, ','); + } + + $field++; } - - if ($field < $numColumns) { - fwrite($handle, ','); - } - - $field++; + fwrite($handle, ');' . PHP_EOL); } - fwrite($handle, ');' . PHP_EOL); + } elseif ($txtCreate->{'Create View'}) { + $sqlOutViews .= '-- ' . PHP_EOL; + $sqlOutViews .= '-- View ' . strtoupper($tableName) . PHP_EOL; + $sqlOutViews .= '-- ' . PHP_EOL; + $sqlOutViews .= 'DROP TABLE IF EXISTS `' . $tableName . '`;' . PHP_EOL . PHP_EOL; + $sqlOutViews .= $txtCreate->{'Create View'} . ';' . PHP_EOL . PHP_EOL; } fwrite($handle, PHP_EOL . PHP_EOL); } + fwrite($handle, $sqlOutViews); + $sqlOut = '--' . PHP_EOL; $sqlOut .= '-- sysPass DB dump generated on ' . time() . ' (END)' . PHP_EOL; $sqlOut .= '--' . PHP_EOL; @@ -191,7 +231,7 @@ class Backup */ private static function backupApp($backupFile) { - if (!class_exists('PharData')) { + if (!class_exists(\PharData::class)) { if (Checks::checkIsWindows()) { throw new SPException(SPException::SP_CRITICAL, _('Esta operación sólo es posible en entornos Linux')); } elseif (!self::backupAppLegacyLinux($backupFile)) { @@ -237,37 +277,4 @@ class Backup return $resBakApp; } - - /** - * Comprobar y crear el directorio de backups. - * - * @param string $backupDir ruta del directorio de backup - * @throws SPException - * @return bool - */ - private static function checkBackupDir($backupDir) - { - if (!is_dir($backupDir)) { - if (!@mkdir($backupDir, 0550)) { - throw new SPException(SPException::SP_CRITICAL, _('No es posible crear el directorio de backups') . ' (' . $backupDir . ')'); - } - } - - if (!is_writable($backupDir)) { - throw new SPException(SPException::SP_CRITICAL, _('Compruebe los permisos del directorio de backups')); - } - - return true; - } - - /** - * Eliminar las copias de seguridad anteriores - * - * @param string $backupDir El directorio de backups - */ - private static function deleteOldBackups($backupDir) - { - array_map('unlink', glob($backupDir . DIRECTORY_SEPARATOR . '*.tar.gz')); - array_map('unlink', glob($backupDir . DIRECTORY_SEPARATOR . '*.sql')); - } } \ No newline at end of file diff --git a/inc/SP/Core/Crypt.class.php b/inc/SP/Core/Crypt.class.php index 4293c64f..6868dc94 100644 --- a/inc/SP/Core/Crypt.class.php +++ b/inc/SP/Core/Crypt.class.php @@ -254,7 +254,6 @@ class Crypt if (null === $password) { $password = SessionUtil::getSessionMPass(); -// self::getSessionMasterPass(); } $mcryptRes = self::getMcryptResource(); diff --git a/inc/SP/Core/XmlExport.class.php b/inc/SP/Core/XmlExport.class.php index ead07c8b..90396246 100644 --- a/inc/SP/Core/XmlExport.class.php +++ b/inc/SP/Core/XmlExport.class.php @@ -33,6 +33,7 @@ use SP\Log\Email; use SP\Mgmt\Customers\Customer; use SP\Log\Log; use SP\Mgmt\Categories\Category; +use SP\Mgmt\Tags\Tag; use SP\Util\Util; defined('APP_ROOT') || die(_('No es posible acceder directamente a este archivo')); @@ -132,6 +133,7 @@ class XmlExport $this->createMeta(); $this->createCategories(); $this->createCustomers(); + $this->createTags(); $this->createAccounts(); $this->createHash(); $this->writeXML(); @@ -316,16 +318,14 @@ class XmlExport foreach ($customers as $CustomerData) { $customerName = $this->xml->createElement('name', $this->escapeChars($CustomerData->getCustomerName())); $customerDescription = $this->xml->createElement('description', $this->escapeChars($CustomerData->getCustomerDescription())); - $customerHash = $this->xml->createElement('hash', $this->escapeChars($CustomerData->getCustomerHash())); - // Crear el nodo de categoría + // Crear el nodo de clientes $nodeCustomer = $this->xml->createElement('Customer'); $nodeCustomer->setAttribute('id', $CustomerData->getCustomerId()); $nodeCustomer->appendChild($customerName); $nodeCustomer->appendChild($customerDescription); - $nodeCustomer->appendChild($customerHash); - // Añadir categoría al nodo de categorías + // Añadir cliente al nodo de clientes $nodeCustomers->appendChild($nodeCustomer); } @@ -335,6 +335,41 @@ class XmlExport } } + /** + * Crear el nodo con los datos de las etiquetas + * + * #@throws SPException + */ + private function createTags() + { + $Tags = Tag::getItem()->getAll(); + + if (count($Tags) === 0) { + return; + } + + try { + // Crear el nodo de etiquetas + $nodeTags= $this->xml->createElement('Tags'); + + foreach ($Tags as $TagData) { + $tagName = $this->xml->createElement('name', $this->escapeChars($TagData->getTagName())); + + // Crear el nodo de etiquetas + $nodeTag = $this->xml->createElement('Tag'); + $nodeTag->setAttribute('id', $TagData->getTagId()); + $nodeTag->appendChild($tagName); + + // Añadir etiqueta al nodo de etiquetas + $nodeTags->appendChild($nodeTag); + } + + $this->appendNode($nodeTags); + } catch (\DOMException $e) { + throw new SPException(SPException::SP_WARNING, $e->getMessage(), __FUNCTION__); + } + } + /** * Crear el nodo con los datos de las cuentas * @@ -473,8 +508,8 @@ class XmlExport */ private function checkExportDir() { - if (!is_dir($this->exportDir) && !@mkdir($this->exportDir, 0750)) { - throw new SPException(SPException::SP_CRITICAL, _('No es posible crear el directorio de backups') . ' (' . $this->exportDir . ')'); + if (@mkdir($this->exportDir, 0750) === false && is_dir($this->exportDir) === false) { + throw new SPException(SPException::SP_CRITICAL, sprintf(_('No es posible crear el directorio de backups ("%s")'), $this->exportDir)); } clearstatcache(true, $this->exportDir); diff --git a/inc/SP/Http/Message.class.php b/inc/SP/Http/Message.class.php new file mode 100644 index 00000000..20dece46 --- /dev/null +++ b/inc/SP/Http/Message.class.php @@ -0,0 +1,98 @@ +. + */ + +namespace SP\Http; + +/** + * Class Message + * + * @package SP\Http + */ +class Message +{ + const TYPE_OK = 0; + const TYPE_ERROR = 1; + const TYPE_WARNING = 2; + + /** + * @var int + */ + protected $type = 0; + /** + * @var string + */ + protected $description; + /** + * @var string + */ + protected $hint; + + /** + * @return int + */ + public function getType() + { + return $this->type; + } + + /** + * @param int $type + */ + public function setType($type) + { + $this->type = $type; + } + + /** + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * @param string $description + */ + public function setDescription($description) + { + $this->description = $description; + } + + /** + * @return string + */ + public function getHint() + { + return $this->hint; + } + + /** + * @param string $hint + */ + public function setHint($hint) + { + $this->hint = $hint; + } +} \ No newline at end of file diff --git a/inc/SP/Http/XMLRPCResponseParse.class.php b/inc/SP/Http/XMLRPCResponseParse.class.php index 4c44213d..9386e30f 100644 --- a/inc/SP/Http/XMLRPCResponseParse.class.php +++ b/inc/SP/Http/XMLRPCResponseParse.class.php @@ -49,12 +49,13 @@ abstract class XMLRPCResponseParse /** * @var array */ - private $data = array(); + private $data = []; /** * Constructor * * @param string $xml El documento XML + * @throws \InvalidArgumentException */ public function __construct($xml) { @@ -125,7 +126,7 @@ abstract class XMLRPCResponseParse */ private function parseStruct(DOMElement $xmlStruct) { - $dataStruct = array(); + $dataStruct = []; $nStruct = 0; foreach ($xmlStruct->childNodes as $struct) { @@ -179,20 +180,18 @@ abstract class XMLRPCResponseParse */ private function parseArray(DOMElement $xmlArray) { - $arrayData = array(); + $arrayData = []; foreach ($xmlArray->childNodes as $array) { foreach ($array->childNodes as $data) { - if ($data instanceof DOMElement) { - /** - * @var $data DOMElement - */ - if ($data->nodeName === 'value') { - $values = $this->parseValues($data); + /** + * @var $data DOMElement + */ + if ($data instanceof DOMElement && $data->nodeName === 'value') { + $values = $this->parseValues($data); - if (is_array($values)) { - $arrayData[] = $values; - } + if (is_array($values)) { + $arrayData[] = $values; } } } @@ -209,13 +208,13 @@ abstract class XMLRPCResponseParse */ private function parseValues(DOMElement $xmlValues) { - $valuesData = array(); + $valuesData = []; foreach ($xmlValues->childNodes as $xmlValue) { if ($xmlValue instanceof DOMElement) { $val = $this->parseNodeType($xmlValue); - if (is_null($val)) { + if (null === $val) { return $this->parseNodes($xmlValues->childNodes); } else { $valuesData[] = $val; @@ -234,19 +233,17 @@ abstract class XMLRPCResponseParse */ private function parseFault(DOMElement $xmlFault) { - $faultData = array(); + $faultData = []; foreach ($xmlFault->childNodes as $fault) { - if ($fault instanceof DOMElement) { - /** - * @var $fault DOMElement - */ - if ($fault->nodeName === 'value') { - $values = $this->parseValues($fault); + /** + * @var $fault DOMElement + */ + if ($fault instanceof DOMElement && $fault->nodeName === 'value') { + $values = $this->parseValues($fault); - if (is_array($values)) { - return $values; - } + if (is_array($values)) { + return $values; } } } diff --git a/inc/SP/Import/CsvImportBase.class.php b/inc/SP/Import/CsvImportBase.class.php index e0b1cdf2..dbf49122 100644 --- a/inc/SP/Import/CsvImportBase.class.php +++ b/inc/SP/Import/CsvImportBase.class.php @@ -25,8 +25,7 @@ namespace SP\Import; -use SP\DataModel\AccountData; -use SP\Core\Crypt; +use SP\DataModel\AccountExtData; use SP\DataModel\CategoryData; use SP\DataModel\CustomerData; use SP\Mgmt\Customers\Customer; @@ -50,34 +49,7 @@ abstract class CsvImportBase extends ImportBase /** * @var array */ - protected $mapFields = array(); - /** - * @var string - */ - protected $fieldDelimiter = ';'; - - /** - * Constructor - * - * @param $file FileImport Instancia de la clase FileImport - * @throws SPException - */ - public function __construct($file) - { - try { - $this->file = $file; - } catch (SPException $e) { - throw $e; - } - } - - /** - * @param string $fieldDelimiter - */ - public function setFieldDelimiter($fieldDelimiter) - { - $this->fieldDelimiter = $fieldDelimiter; - } + protected $mapFields = []; /** * @param int $numFields @@ -99,6 +71,7 @@ abstract class CsvImportBase extends ImportBase * Obtener los datos de las entradas de sysPass y crearlas * * @throws SPException + * @throws \SP\Core\Exceptions\InvalidClassException */ protected function processAccounts() { @@ -108,7 +81,7 @@ abstract class CsvImportBase extends ImportBase foreach ($this->file->getFileContent() as $data) { $line++; - $fields = explode($this->fieldDelimiter, $data); + $fields = str_getcsv($data, $this->ImportParams->getCsvDelimiter()); $numfields = count($fields); // Comprobar el número de campos de la línea @@ -121,30 +94,31 @@ abstract class CsvImportBase extends ImportBase } // Eliminar las " del principio/fin de los campos - array_walk($fields, - function (&$value, $key) { - $value = trim($value, '"'); - } - ); +// array_walk($fields, +// function (&$value, $key) { +// $value = trim($value, '"'); +// } +// ); // Asignar los valores del array a variables list($accountName, $customerName, $categoryName, $url, $login, $password, $notes) = $fields; - // Obtener los ids de cliente, categoría y la clave encriptada - $customerId = Customer::getItem(new CustomerData(null, $customerName))->add()->getItemData()->getCustomerId(); - $categoryId = Category::getItem(new CategoryData(null, $categoryName))->add()->getItemData()->getCategoryId(); - $pass = Crypt::encryptData($password); + // Obtener los ids de cliente y categoría + $CustomerData = new CustomerData(null, $customerName); + Customer::getItem($CustomerData)->add(); + + $CategoryData = new CategoryData(null, $categoryName); + Category::getItem($CategoryData)->add(); // Crear la nueva cuenta - $AccountData = new AccountData(); + $AccountData = new AccountExtData(); $AccountData->setAccountName($accountName); $AccountData->setAccountLogin($login); - $AccountData->setAccountCategoryId($categoryId); - $AccountData->setAccountCustomerId($customerId); + $AccountData->setAccountCategoryId($CategoryData->getCategoryId()); + $AccountData->setAccountCustomerId($CustomerData->getCustomerId()); $AccountData->setAccountNotes($notes); $AccountData->setAccountUrl($url); - $AccountData->setAccountPass($pass['data']); - $AccountData->setAccountIV($pass['iv']); + $AccountData->setAccountPass($password); try { $this->addAccount($AccountData); diff --git a/inc/SP/Import/FileImport.class.php b/inc/SP/Import/FileImport.class.php index 28eaf60d..b71dd11f 100644 --- a/inc/SP/Import/FileImport.class.php +++ b/inc/SP/Import/FileImport.class.php @@ -85,6 +85,9 @@ class FileImport /** * FileImport constructor. + * + * @param array $fileData Datos del archivo a importar + * @throws SPException */ public function __construct(&$fileData) { @@ -100,7 +103,6 @@ class FileImport * * @param array $fileData con los datos del archivo * @throws SPException - * @return bool */ private function checkFile(&$fileData) { @@ -112,7 +114,7 @@ class FileImport // Comprobamos la extensión del archivo $fileExtension = strtoupper(pathinfo($fileData['name'], PATHINFO_EXTENSION)); - if ($fileExtension != 'CSV' && $fileExtension != 'XML') { + if ($fileExtension !== 'CSV' && $fileExtension !== 'XML') { throw new SPException( SPException::SP_CRITICAL, _('Tipo de archivo no soportado'), @@ -123,7 +125,7 @@ class FileImport // Variables con información del archivo $this->tmpFile = $fileData['tmp_name']; - $this->fileType = $fileData['type']; + $this->fileType = strtolower($fileData['type']); if (!file_exists($this->tmpFile) || !is_readable($this->tmpFile)) { // Registramos el máximo tamaño permitido por PHP @@ -144,6 +146,8 @@ class FileImport */ public function readFileToArray() { + $this->autodetectEOL(); + $this->fileContent = file($this->tmpFile, FILE_SKIP_EMPTY_LINES); if ($this->fileContent === false){ @@ -162,6 +166,8 @@ class FileImport */ public function readFileToString() { + $this->autodetectEOL(); + $this->fileContent = file_get_contents($this->tmpFile); if ($this->fileContent === false){ @@ -172,4 +178,12 @@ class FileImport ); } } + + /** + * Activar la autodetección de fin de línea + */ + protected function autodetectEOL() + { + ini_set('auto_detect_line_endings', true); + } } \ No newline at end of file diff --git a/inc/SP/Import/Import.class.php b/inc/SP/Import/Import.class.php index 62cce847..cb642082 100644 --- a/inc/SP/Import/Import.class.php +++ b/inc/SP/Import/Import.class.php @@ -26,6 +26,7 @@ namespace SP\Import; +use SP\Http\Message; use SP\Log\Email; use SP\Log\Log; use SP\Core\Exceptions\SPException; @@ -38,61 +39,28 @@ defined('APP_ROOT') || die(_('No es posible acceder directamente a este archivo' class Import { /** - * @var string + * @var ImportParams Parámetros de importación */ - public static $importPwd = ''; - /** - * @var int - */ - public static $defUser = 0; - /** - * @var int - */ - public static $defGroup = 0; - /** - * @var string - */ - public static $csvDelimiter = ''; + protected $ImportParams; /** - * @param string $importPwd + * Import constructor. + * + * @param ImportParams $ImportParams */ - public static function setImportPwd($importPwd) + public function __construct(ImportParams $ImportParams) { - self::$importPwd = $importPwd; - } - - /** - * @param int $defUser - */ - public static function setDefUser($defUser) - { - self::$defUser = $defUser; - } - - /** - * @param int $defGroup - */ - public static function setDefGroup($defGroup) - { - self::$defGroup = $defGroup; - } - - /** - * @param string $csvDelimiter - */ - public static function setCsvDelimiter($csvDelimiter) - { - self::$csvDelimiter = $csvDelimiter; + $this->ImportParams = $ImportParams; } /** * Iniciar la importación de cuentas. * - * @param array $fileData Los datos del archivo - * @return array resultado del proceso + * @param array $fileData Los datos del archivo + * @return Message + * @throws \SP\Core\Exceptions\SPException */ - public static function doImport(&$fileData) + public function doImport(&$fileData) { $Log = new Log(_('Importar Cuentas')); @@ -102,32 +70,27 @@ class Import switch ($file->getFileType()) { case 'text/csv': case 'application/vnd.ms-excel': - $import = new CsvImport($file); - $import->setFieldDelimiter(self::$csvDelimiter); + $Import = new CsvImport($file, $this->ImportParams); break; case 'text/xml': - $import = new XmlImport($file); - $import->setImportPass(self::$importPwd); + $Import = new XmlImport($file, $this->ImportParams); break; default: throw new SPException( SPException::SP_WARNING, - _('Tipo mime no soportado'), + sprintf(_('Tipo mime no soportado ("%s")'), $file->getFileType()), _('Compruebe el formato del archivo') ); } - $import->setUserId(self::$defUser); - $import->setUserGroupId(self::$defGroup); - $import->doImport(); + $Import->doImport(); } catch (SPException $e) { $Log->setLogLevel(Log::ERROR); $Log->addDescription($e->getMessage()); $Log->addDetails(_('Ayuda'), $e->getHint()); $Log->writeLog(); - $result['error'] = array('description' => $e->getMessage(), 'hint' => $e->getHint()); - return $result; + throw $e; } $Log->addDescription(_('Importación finalizada')); @@ -135,11 +98,10 @@ class Import Email::sendEmail($Log); - $result['ok'] = array( - _('Importación finalizada'), - _('Revise el registro de eventos para más detalles') - ); + $Message = new Message(); + $Message->setDescription(_('Importación finalizada')); + $Message->setHint(_('Revise el registro de eventos para más detalles')); - return $result; + return $Message; } } \ No newline at end of file diff --git a/inc/SP/Import/ImportBase.class.php b/inc/SP/Import/ImportBase.class.php index 386e3a8d..62e6af4b 100644 --- a/inc/SP/Import/ImportBase.class.php +++ b/inc/SP/Import/ImportBase.class.php @@ -26,15 +26,16 @@ namespace SP\Import; use SP\Account\Account; +use SP\Core\Crypt; use SP\Core\Exceptions\SPException; -use SP\DataModel\AccountData; use SP\DataModel\AccountExtData; use SP\DataModel\CategoryData; use SP\DataModel\CustomerData; +use SP\DataModel\TagData; use SP\Log\Log; use SP\Mgmt\Customers\Customer; use SP\Mgmt\Categories\Category; -use SP\Core\Session; +use SP\Mgmt\Tags\Tag; defined('APP_ROOT') || die(_('No es posible acceder directamente a este archivo')); @@ -46,66 +47,24 @@ defined('APP_ROOT') || die(_('No es posible acceder directamente a este archivo' abstract class ImportBase { /** - * El id de usuario propietario de la cuenta. - * - * @var int + * @var ImportParams */ - public $userId; - /** - * El id del grupo propietario de la cuenta. - * - * @var int - */ - public $userGroupId; - /** - * Nombre de la categoría. - * - * @var string - */ - protected $categoryName = ''; - /** - * Nombre del cliente. - * - * @var string - */ - protected $customerName = ''; - /** - * Descrición de la categoría. - * - * @var string - */ - protected $categoryDescription = ''; - /** - * Descripción del cliente. - * - * @var string - */ - protected $customerDescription = ''; + protected $ImportParams; /** * @var FileImport */ protected $file; + /** - * La clave de importación + * ImportBase constructor. * - * @var string + * @param FileImport $File + * @param ImportParams $ImportParams */ - protected $importPass; - - /** - * @return string - */ - public function getImportPass() + public function __construct(FileImport $File, ImportParams $ImportParams) { - return $this->importPass; - } - - /** - * @param string $importPass - */ - public function setImportPass($importPass) - { - $this->importPass = $importPass; + $this->file = $File; + $this->ImportParams = $ImportParams; } /** @@ -116,38 +75,6 @@ abstract class ImportBase */ public abstract function doImport(); - /** - * @return string - */ - public function getCategoryName() - { - return $this->categoryName; - } - - /** - * @param string $categoryName - */ - public function setCategoryName($categoryName) - { - $this->categoryName = $categoryName; - } - - /** - * @return string - */ - public function getCategoryDescription() - { - return $this->categoryDescription; - } - - /** - * @param string $categoryDescription - */ - public function setCategoryDescription($categoryDescription) - { - $this->categoryDescription = $categoryDescription; - } - /** * Leer la cabecera del archivo XML y obtener patrones de aplicaciones conocidas. * @@ -186,122 +113,85 @@ abstract class ImportBase */ protected function addAccount(AccountExtData $AccountData) { - $userId = $this->getUserId(); - $groupId = $this->getUserGroupId(); - - if (null === $userId || $userId === 0) { - $this->setUserId(Session::getUserData()->getUserId()); + if ($AccountData->getAccountCategoryId() === 0) { + Log::writeNewLog(__FUNCTION__, _('Id de categoría no definido. No es posible importar cuenta.'), Log::INFO); + return false; + } elseif ($AccountData->getAccountCustomerId() === 0) { + Log::writeNewLog(__FUNCTION__, _('Id de cliente no definido. No es posible importar cuenta.'), Log::INFO); + return false; } - if (null === $groupId || $groupId === 0) { - $this->setUserGroupId(Session::getUserData()->getUserGroupId()); + $encryptPass = false; + + if ($this->ImportParams->getImportMasterPwd() !== '') { + $pass = Crypt::getDecrypt($AccountData->getAccountPass(), $AccountData->getAccountIV(), $this->ImportParams->getImportMasterPwd()); + $AccountData->setAccountPass($pass); + + $encryptPass = true; } - $AccountData->setAccountUserId($this->getUserId()); - $AccountData->setAccountUserGroupId($this->getUserGroupId()); + $AccountData->setAccountUserId($this->ImportParams->getDefaultUser()); + $AccountData->setAccountUserGroupId($this->ImportParams->getDefaultGroup()); $Account = new Account($AccountData); - $Account->createAccount(); - } + $Account->createAccount($encryptPass); - /** - * @return int - */ - public function getUserId() - { - return $this->userId; - } - - /** - * @param int $userId - */ - public function setUserId($userId) - { - $this->userId = $userId; - } - - /** - * @return int - */ - public function getUserGroupId() - { - return $this->userGroupId; - } - - /** - * @param int $userGroupId - */ - public function setUserGroupId($userGroupId) - { - $this->userGroupId = $userGroupId; + return true; } /** * Añadir una categoría y devolver el Id * - * @param $name - * @param $description - * @return int + * @param CategoryData $CategoryData + * @return Category|null + * @throws \SP\Core\Exceptions\InvalidClassException * @throws \SP\Core\Exceptions\SPException */ - protected function addCategory($name, $description = null) + protected function addCategory(CategoryData $CategoryData) { - $CategoryData = new CategoryData(null, $name, $description); - try { - return Category::getItem($CategoryData)->add()->getItemData()->getCategoryId(); + return Category::getItem($CategoryData)->add(); } catch (SPException $e) { Log::writeNewLog(__FUNCTION__, $e->getMessage(), Log::ERROR); } + + return null; } /** * Añadir un cliente y devolver el Id * - * @param $name - * @param $description - * @return int + * @param CustomerData $CustomerData + * @return Customer|null + * @throws \SP\Core\Exceptions\InvalidClassException */ - protected function addCustomer($name, $description = null) + protected function addCustomer(CustomerData $CustomerData) { - $CustomerData = new CustomerData(null, $name, $description); - try { - return Customer::getItem($CustomerData)->add()->getItemData()->getCustomerId(); + return Customer::getItem($CustomerData)->add(); } catch (SPException $e) { Log::writeNewLog(__FUNCTION__, $e->getMessage(), Log::ERROR); } + + return null; } /** - * @return string + * Añadir una etiqueta y devolver el Id + * + * @param TagData $TagData + * @return Tag|null + * @throws \SP\Core\Exceptions\InvalidClassException + * @throws \SP\Core\Exceptions\SPException */ - public function getCustomerName() + protected function addTag(TagData $TagData) { - return $this->customerName; - } + try { + return Tag::getItem($TagData)->add(); + } catch (SPException $e) { + Log::writeNewLog(__FUNCTION__, $e->getMessage(), Log::ERROR); + } - /** - * @param string $customerName - */ - public function setCustomerName($customerName) - { - $this->customerName = $customerName; - } - - /** - * @return string - */ - public function getCustomerDescription() - { - return $this->customerDescription; - } - - /** - * @param string $customerDescription - */ - public function setCustomerDescription($customerDescription) - { - $this->customerDescription = $customerDescription; + return null; } } \ No newline at end of file diff --git a/inc/SP/Import/ImportParams.class.php b/inc/SP/Import/ImportParams.class.php new file mode 100644 index 00000000..da92d898 --- /dev/null +++ b/inc/SP/Import/ImportParams.class.php @@ -0,0 +1,135 @@ +. + */ + +namespace SP\Import; + + +/** + * Class ImportParams + * + * @package SP\Import + */ +class ImportParams +{ + /** + * @var string + */ + protected $importPwd; + /** + * @var string + */ + protected $importMasterPwd; + /** + * @var int + */ + protected $defaultUser = 0; + /** + * @var int + */ + protected $defaultGroup = 0; + /** + * @var string + */ + protected $csvDelimiter = ';'; + + /** + * @return string + */ + public function getImportPwd() + { + return $this->importPwd; + } + + /** + * @param string $importPwd + */ + public function setImportPwd($importPwd) + { + $this->importPwd = $importPwd; + } + + /** + * @return int + */ + public function getDefaultGroup() + { + return $this->defaultGroup; + } + + /** + * @param int $defaultGroup + */ + public function setDefaultGroup($defaultGroup) + { + $this->defaultGroup = (int)$defaultGroup; + } + + /** + * @return string + */ + public function getCsvDelimiter() + { + return $this->csvDelimiter; + } + + /** + * @param string $csvDelimiter + */ + public function setCsvDelimiter($csvDelimiter) + { + $this->csvDelimiter = $csvDelimiter; + } + + /** + * @return string + */ + public function getImportMasterPwd() + { + return $this->importMasterPwd; + } + + /** + * @param string $importMasterPwd + */ + public function setImportMasterPwd($importMasterPwd) + { + $this->importMasterPwd = $importMasterPwd; + } + + /** + * @return int + */ + public function getDefaultUser() + { + return $this->defaultUser; + } + + /** + * @param int $defaultUser + */ + public function setDefaultUser($defaultUser) + { + $this->defaultUser = (int)$defaultUser; + } +} \ No newline at end of file diff --git a/inc/SP/Import/KeepassImport.class.php b/inc/SP/Import/KeepassImport.class.php index 11cf029c..b6ac8b78 100644 --- a/inc/SP/Import/KeepassImport.class.php +++ b/inc/SP/Import/KeepassImport.class.php @@ -26,8 +26,9 @@ namespace SP\Import; use SimpleXMLElement; -use SP\DataModel\AccountData; -use SP\Core\Crypt; +use SP\DataModel\AccountExtData; +use SP\DataModel\CategoryData; +use SP\DataModel\CustomerData; defined('APP_ROOT') || die(_('No es posible acceder directamente a este archivo')); @@ -39,20 +40,20 @@ class KeepassImport extends XmlImportBase /** * @var int */ - private $customerId = 0; - /** - * @var int - */ - private $categoryId = 0; + protected $customerId = 0; /** * Iniciar la importación desde KeePass * * @throws \SP\Core\Exceptions\SPException + * @throws \SP\Core\Exceptions\InvalidClassException */ public function doImport() { - $this->customerId = $this->addCustomer('KeePass'); + $customerData = new CustomerData(null, 'KeePass'); + $this->addCustomer($customerData); + + $this->customerId = $customerData->getCustomerId(); $this->processCategories($this->xml->Root->Group); } @@ -62,6 +63,7 @@ class KeepassImport extends XmlImportBase * * @param SimpleXMLElement $xml El objeto XML del archivo de KeePass * @throws \SP\Core\Exceptions\SPException + * @throws \SP\Core\Exceptions\InvalidClassException */ protected function processCategories(SimpleXMLElement $xml) { @@ -71,10 +73,11 @@ class KeepassImport extends XmlImportBase // Analizar grupo if ($node->Group->Entry) { // Crear la categoría - $this->categoryId = $this->addCategory($group->Name, 'KeePass'); + $CategoryData = new CategoryData(null, $group->Name, 'KeePass'); + $this->addCategory($CategoryData); // Crear cuentas - $this->processAccounts($group->Entry); + $this->processAccounts($group->Entry, $CategoryData->getCategoryId()); } if ($group->Group) { @@ -86,10 +89,11 @@ class KeepassImport extends XmlImportBase if ($node->Entry) { // Crear la categoría - $this->categoryId = $this->addCategory($node->Name, 'KeePass'); + $CategoryData = new CategoryData(null, $node->Name, 'KeePass'); + $this->addCategory($CategoryData); // Crear cuentas - $this->processAccounts($node->Entry); + $this->processAccounts($node->Entry, $CategoryData->getCategoryId()); } } } @@ -97,25 +101,24 @@ class KeepassImport extends XmlImportBase /** * Obtener los datos de las entradas de KeePass. * - * @param SimpleXMLElement $entries El objeto XML con las entradas + * @param SimpleXMLElement $entries El objeto XML con las entradas + * @param int $categoryId Id de la categoría * @throws \SP\Core\Exceptions\SPException */ - protected function processAccounts(SimpleXMLElement $entries) + protected function processAccounts(SimpleXMLElement $entries, $categoryId) { foreach ($entries as $entry) { - $AccountData = new AccountData(); + $AccountData = new AccountExtData(); foreach ($entry->String as $account) { $value = isset($account->Value) ? (string)$account->Value : ''; + switch ($account->Key) { case 'Notes': $AccountData->setAccountNotes($value); break; case 'Password': - $passData = Crypt::encryptData($value); - - $AccountData->setAccountPass($passData['data']); - $AccountData->setAccountIV($passData['iv']); + $AccountData->setAccountPass($value); break; case 'Title': $AccountData->setAccountName($value); @@ -129,7 +132,7 @@ class KeepassImport extends XmlImportBase } } - $AccountData->setAccountCategoryId($this->categoryId); + $AccountData->setAccountCategoryId($categoryId); $AccountData->setAccountCustomerId($this->customerId); $this->addAccount($AccountData); diff --git a/inc/SP/Import/KeepassXImport.class.php b/inc/SP/Import/KeepassXImport.class.php index d407e86f..d6cc98c2 100644 --- a/inc/SP/Import/KeepassXImport.class.php +++ b/inc/SP/Import/KeepassXImport.class.php @@ -26,8 +26,9 @@ namespace SP\Import; use SimpleXMLElement; -use SP\DataModel\AccountData; -use SP\Core\Crypt; +use SP\DataModel\AccountExtData; +use SP\DataModel\CategoryData; +use SP\DataModel\CustomerData; defined('APP_ROOT') || die(_('No es posible acceder directamente a este archivo')); @@ -39,21 +40,21 @@ class KeepassXImport extends XmlImportBase /** * @var int */ - private $customerId = 0; - /** - * @var int - */ - private $categoryId = 0; + protected $customerId = 0; /** * Iniciar la importación desde KeePassX. * * @throws \SP\Core\Exceptions\SPException * @return bool + * @throws \SP\Core\Exceptions\InvalidClassException */ public function doImport() { - $this->customerId = $this->addCustomer('KeePassX'); + $customerData = new CustomerData(null, 'KeePassX'); + $this->addCustomer($customerData); + + $this->customerId = $customerData->getCustomerId(); $this->processCategories($this->xml); } @@ -63,6 +64,7 @@ class KeepassXImport extends XmlImportBase * * @param SimpleXMLElement $xml con objeto XML del archivo de KeePass * @throws \SP\Core\Exceptions\SPException + * @throws \SP\Core\Exceptions\InvalidClassException */ protected function processCategories(SimpleXMLElement $xml) { @@ -72,10 +74,11 @@ class KeepassXImport extends XmlImportBase // Analizar grupo if ($node->group->entry) { // Crear la categoría - $this->categoryId = $this->addCategory($group->title, 'KeePassX'); + $CategoryData = new CategoryData(null, $group->title, 'KeePassX'); + $this->addCategory($CategoryData); // Crear cuentas - $this->processAccounts($group->entry); + $this->processAccounts($group->entry, $CategoryData->getCategoryId()); } if ($group->group) { @@ -86,11 +89,11 @@ class KeepassXImport extends XmlImportBase } if ($node->entry) { - // Crear la categoría - $this->categoryId = $this->addCategory($node->title, 'KeePassX'); + $CategoryData = new CategoryData(null, $node->title, 'KeePassX'); + $this->addCategory($CategoryData); // Crear cuentas - $this->processAccounts($node->entry); + $this->processAccounts($node->entry, $CategoryData->getCategoryId()); } } } @@ -98,29 +101,27 @@ class KeepassXImport extends XmlImportBase /** * Obtener los datos de las entradas de KeePass. * - * @param SimpleXMLElement $entries El objeto XML con las entradas + * @param SimpleXMLElement $entries El objeto XML con las entradas + * @param int $categoryId Id de la categoría * @throws \SP\Core\Exceptions\SPException */ - protected function processAccounts(SimpleXMLElement $entries) + protected function processAccounts(SimpleXMLElement $entries, $categoryId) { foreach ($entries as $entry) { - $notes = isset($entry->comment) ? (string)$entry->comment : ''; - $password = isset($entry->password) ? (string)$entry->password : ''; $name = isset($entry->title) ? (string)$entry->title : ''; + $password = isset($entry->password) ? (string)$entry->password : ''; $url = isset($entry->url) ? (string)$entry->url : ''; + $notes = isset($entry->comment) ? (string)$entry->comment : ''; $username = isset($entry->username) ? (string)$entry->username : ''; - $passData = Crypt::encryptData($password); - - $AccountData = new AccountData(); - $AccountData->setAccountPass($passData['data']); - $AccountData->setAccountIV($passData['iv']); + $AccountData = new AccountExtData(); + $AccountData->setAccountPass($password); $AccountData->setAccountNotes($notes); $AccountData->setAccountName($name); $AccountData->setAccountUrl($url); $AccountData->setAccountLogin($username); $AccountData->setAccountCustomerId($this->customerId); - $AccountData->setAccountCategoryId($this->categoryId); + $AccountData->setAccountCategoryId($categoryId); $this->addAccount($AccountData); } diff --git a/inc/SP/Import/SyspassImport.class.php b/inc/SP/Import/SyspassImport.class.php index 59c0f82d..44a1f040 100644 --- a/inc/SP/Import/SyspassImport.class.php +++ b/inc/SP/Import/SyspassImport.class.php @@ -28,6 +28,9 @@ namespace SP\Import; use SP\Core\Crypt; use SP\Core\Exceptions\SPException; use SP\DataModel\AccountExtData; +use SP\DataModel\CategoryData; +use SP\DataModel\CustomerData; +use SP\DataModel\TagData; defined('APP_ROOT') || die(_('No es posible acceder directamente a este archivo')); @@ -36,18 +39,24 @@ defined('APP_ROOT') || die(_('No es posible acceder directamente a este archivo' */ class SyspassImport extends XmlImportBase { + /** + * Mapeo de etiquetas + * + * @var array + */ + protected $tags = []; /** * Mapeo de categorías. * * @var array */ - private $categories = []; + protected $categories = []; /** * Mapeo de clientes. * * @var array */ - private $customers = []; + protected $customers = []; /** * Iniciar la importación desde sysPass. @@ -57,11 +66,16 @@ class SyspassImport extends XmlImportBase public function doImport() { try { - if ($this->detectEncrypted() && null !== $this->getImportPass()) { + if ($this->detectEncrypted()) { + if ($this->ImportParams->getImportPwd() === '') { + throw new SPException(SPException::SP_ERROR, _('Clave de encriptación no indicada')); + } + $this->processEncrypted(); } $this->processCategories(); $this->processCustomers(); + $this->processTags(); $this->processAccounts(); } catch (SPException $e) { throw $e; @@ -90,7 +104,7 @@ class SyspassImport extends XmlImportBase $data = base64_decode($node->nodeValue); $iv = base64_decode($node->getAttribute('iv')); - $xmlDecrypted = Crypt::getDecrypt($data, $iv, $this->getImportPass()); + $xmlDecrypted = Crypt::getDecrypt($data, $iv, $this->ImportParams->getImportPwd()); $newXmlData = new \DOMDocument(); // $newXmlData->preserveWhiteSpace = true; @@ -110,117 +124,148 @@ class SyspassImport extends XmlImportBase /** * Obtener las categorías y añadirlas a sysPass. * - * @throws \SP\Core\Exceptions\SPException + * @param \DOMElement $Category + * @throws SPException + * @throws \SP\Core\Exceptions\InvalidClassException */ - protected function processCategories() + protected function processCategories(\DOMElement $Category = null) { - if ($this->xmlDOM->getElementsByTagName('Categories')->length === 0) { - throw new SPException(SPException::SP_WARNING, _('Formato de XML inválido'), _('No hay categorías para importar')); + if ($Category === null) { + $this->getNodesData('Categories', 'Category', __FUNCTION__); + return; } - /** @var \DOMElement $category */ - foreach ($this->xmlDOM->getElementsByTagName('Category') as $category) { - $name = ''; - $description = ''; + $CategoryData = new CategoryData(); - foreach ($category->childNodes as $node) { - if (isset($node->tagName)) { - switch ($node->tagName) { - case 'name': - $name = $node->nodeValue; - break; - case 'description': - $description = $node->nodeValue; - break; - } + foreach ($Category->childNodes as $categoryNode) { + if (isset($categoryNode->tagName)) { + switch ($categoryNode->tagName) { + case 'name': + $CategoryData->setCategoryName($categoryNode->nodeValue); + break; + case 'description': + $CategoryData->setCategoryDescription($categoryNode->nodeValue); + break; } } - - $this->categories[$category->getAttribute('id')] = $this->addCategory($name, $description); } + + $this->addCategory($CategoryData); + + $this->categories[$Category->getAttribute('id')] = $CategoryData->getCategoryId(); } /** * Obtener los clientes y añadirlos a sysPass. * - * @throws \SP\Core\Exceptions\SPException + * @param \DOMElement $Customer + * @throws SPException + * @throws \SP\Core\Exceptions\InvalidClassException */ - protected function processCustomers() + protected function processCustomers(\DOMElement $Customer = null) { - if ($this->xmlDOM->getElementsByTagName('Customers')->length === 0) { - throw new SPException(SPException::SP_WARNING, _('Formato de XML inválido'), _('No hay clientes para importar')); + if ($Customer === null) { + $this->getNodesData('Customers', 'Customer', __FUNCTION__); + return; } - /** @var \DOMElement $customer */ - foreach ($this->xmlDOM->getElementsByTagName('Customer') as $customer) { - $name = ''; - $description = ''; + $CustomerData = new CustomerData(); - foreach ($customer->childNodes as $node) { - if (isset($node->tagName)) { - switch ($node->tagName) { - case 'name': - $name = $node->nodeValue; - break; - case 'description': - $description = $node->nodeValue; - break; - } + foreach ($Customer->childNodes as $customerNode) { + if (isset($customerNode->tagName)) { + switch ($customerNode->tagName) { + case 'name': + $CustomerData->setCustomerName($customerNode->nodeValue); + break; + case 'description': + $CustomerData->setCustomerDescription($customerNode->nodeValue); + break; } } - - $this->customers[$customer->getAttribute('id')] = $this->addCustomer($name, $description); } + + $this->addCustomer($CustomerData); + + $this->customers[$Customer->getAttribute('id')] = $CustomerData->getCustomerId(); } /** - * Obtener los datos de las entradas de sysPass y crearlas. + * Obtener las etiquetas y añadirlas a sysPass. * - * @throws \SP\Core\Exceptions\SPException + * @param \DOMElement $Tag + * @throws SPException + * @throws \SP\Core\Exceptions\InvalidClassException */ - protected function processAccounts() + protected function processTags(\DOMElement $Tag = null) { - if ($this->xmlDOM->getElementsByTagName('Accounts')->length === 0) { - throw new SPException(SPException::SP_WARNING, _('Formato de XML inválido'), _('No hay cuentas para importar')); + if ($Tag === null) { + $this->getNodesData('Tags', 'Tag', __FUNCTION__); + return; + } + + $TagData = new TagData(); + + foreach ($Tag->childNodes as $tagNode) { + if (isset($tagNode->tagName)) { + switch ($tagNode->tagName) { + case 'name': + $TagData->setTagName($tagNode->nodeValue); + break; + } + } + } + + $this->addTag($TagData); + + $this->tags[$Tag->getAttribute('id')] = $TagData->getTagId(); + } + + /** + * Obtener los datos de las cuentas de sysPass y crearlas. + * + * @param \DOMElement $Account + * @throws SPException + */ + protected function processAccounts(\DOMElement $Account = null) + { + if ($Account === null) { + $this->getNodesData('Accounts', 'Account', __FUNCTION__); + return; } $AccountData = new AccountExtData(); - foreach ($this->xmlDOM->getElementsByTagName('Account') as $account) { - $AccountDataClone = clone $AccountData; - - foreach ($account->childNodes as $node) { - if (isset($node->tagName)) { - switch ($node->tagName) { - case 'name'; - $AccountDataClone->setAccountName($node->nodeValue); - break; - case 'login'; - $AccountDataClone->setAccountLogin($node->nodeValue); - break; - case 'categoryId'; - $AccountDataClone->setAccountCategoryId($this->categories[(int)$node->nodeValue]); - break; - case 'customerId'; - $AccountDataClone->setAccountCustomerId($this->customers[(int)$node->nodeValue]); - break; - case 'url'; - $AccountDataClone->setAccountUrl($node->nodeValue); - break; - case 'pass'; - $AccountDataClone->setAccountPass(base64_decode($node->nodeValue)); - break; - case 'passiv'; - $AccountDataClone->setAccountIV(base64_decode($node->nodeValue)); - break; - case 'notes'; - $AccountDataClone->setAccountNotes($node->nodeValue); - break; - } + foreach ($Account->childNodes as $accountNode) { + if (isset($accountNode->tagName)) { + switch ($accountNode->tagName) { + case 'name'; + $AccountData->setAccountName($accountNode->nodeValue); + break; + case 'login'; + $AccountData->setAccountLogin($accountNode->nodeValue); + break; + case 'categoryId'; + $AccountData->setAccountCategoryId($this->categories[(int)$accountNode->nodeValue]); + break; + case 'customerId'; + $AccountData->setAccountCustomerId($this->customers[(int)$accountNode->nodeValue]); + break; + case 'url'; + $AccountData->setAccountUrl($accountNode->nodeValue); + break; + case 'pass'; + $AccountData->setAccountPass(base64_decode($accountNode->nodeValue)); + break; + case 'passiv'; + $AccountData->setAccountIV(base64_decode($accountNode->nodeValue)); + break; + case 'notes'; + $AccountData->setAccountNotes($accountNode->nodeValue); + break; } } - - $this->addAccount($AccountDataClone); } + + $this->addAccount($AccountData); } } \ No newline at end of file diff --git a/inc/SP/Import/XmlImport.class.php b/inc/SP/Import/XmlImport.class.php index 4c976124..45e8a75b 100644 --- a/inc/SP/Import/XmlImport.class.php +++ b/inc/SP/Import/XmlImport.class.php @@ -46,29 +46,26 @@ class XmlImport extends XmlImportBase */ public function doImport() { - $import = null; + $Import = null; $format = $this->detectXMLFormat(); switch ($format) { case 'syspass': - $import = new SyspassImport($this->file); + $Import = new SyspassImport($this->file, $this->ImportParams); break; case 'keepass': - $import = new KeepassImport($this->file); + $Import = new KeepassImport($this->file, $this->ImportParams); break; case 'keepassx': - $import = new KeepassXImport($this->file); + $Import = new KeepassXImport($this->file, $this->ImportParams); break; } - if (is_object($import)){ + if (is_object($Import)){ Log::writeNewLog(_('Importar Cuentas'), _('Inicio')); - Log::writeNewLog(_('Importar Cuentas'), _('Formato detectado') . ': ' . strtoupper($format)); + Log::writeNewLog(_('Importar Cuentas'), sprintf(_('Formato detectado: %s'), strtoupper($format))); - $import->setUserId($this->getUserId()); - $import->setUserGroupId($this->getUserGroupId()); - $import->setImportPass($this->getImportPass()); - $import->doImport(); + $Import->doImport(); } } } \ No newline at end of file diff --git a/inc/SP/Import/XmlImportBase.class.php b/inc/SP/Import/XmlImportBase.class.php index d062a59d..db4e160e 100644 --- a/inc/SP/Import/XmlImportBase.class.php +++ b/inc/SP/Import/XmlImportBase.class.php @@ -46,26 +46,28 @@ abstract class XmlImportBase extends ImportBase protected $xmlDOM; /** - * Constructor + * ImportBase constructor. * - * @param $file FileImport Instancia de la clase FileImport + * @param FileImport $File + * @param ImportParams $ImportParams * @throws SPException */ - public function __construct($file) + public function __construct(FileImport $File, ImportParams $ImportParams) { + parent::__construct($File, $ImportParams); + try { - $this->file = $file; $this->readXMLFile(); } catch (SPException $e) { throw $e; } } + /** * Leer el archivo a un objeto XML. * * @throws SPException - * @return \SimpleXMLElement Con los datos del archivo XML */ protected function readXMLFile() { @@ -91,9 +93,9 @@ abstract class XmlImportBase extends ImportBase */ public function detectXMLFormat() { - if ($this->xml->Meta->Generator == 'KeePass') { + if ((string)$this->xml->Meta->Generator === 'KeePass') { return 'keepass'; - } else if ($this->xml->Meta->Generator == 'sysPass') { + } else if ((string)$this->xml->Meta->Generator === 'sysPass') { return 'syspass'; } else if ($xmlApp = $this->parseFileHeader()) { switch ($xmlApp) { @@ -114,4 +116,30 @@ abstract class XmlImportBase extends ImportBase return ''; } + + /** + * Obtener los datos de los nodos + * + * @param string $nodeName Nombre del nodo principal + * @param string $childNodeName Nombre de los nodos hijos + * @throws SPException + */ + protected function getNodesData($nodeName, $childNodeName, $callback) + { + $ParentNode = $this->xmlDOM->getElementsByTagName($nodeName); + + if ($ParentNode->length === 0) { + throw new SPException(SPException::SP_WARNING, _('Formato de XML inválido'), sprintf(_('El nodo "%s" no existe'), $nodeName)); + } elseif (!is_callable([$this, $callback])) { + throw new SPException(SPException::SP_WARNING, _('Método inválido')); + } + + /** @var \DOMElement $nodes */ + foreach ($ParentNode as $nodes) { + /** @var \DOMElement $Account */ + foreach ($nodes->getElementsByTagName($childNodeName) as $Node) { + $this->$callback($Node); + } + } + } } \ No newline at end of file diff --git a/inc/SP/Mgmt/Categories/Category.class.php b/inc/SP/Mgmt/Categories/Category.class.php index ecd444df..b5d06d3f 100644 --- a/inc/SP/Mgmt/Categories/Category.class.php +++ b/inc/SP/Mgmt/Categories/Category.class.php @@ -57,7 +57,7 @@ class Category extends CategoryBase implements ItemInterface, ItemSelectInterfac public function add() { if ($this->checkDuplicatedOnAdd()) { - throw new SPException(SPException::SP_WARNING, _('Nombre de categoría duplicado')); + throw new SPException(SPException::SP_WARNING, _('Categoría duplicada')); } $query = /** @lang SQL */ @@ -100,7 +100,17 @@ class Category extends CategoryBase implements ItemInterface, ItemSelectInterfac $Data->addParam($this->makeItemHash($this->itemData->getCategoryName())); $Data->addParam($this->itemData->getCategoryName()); - return (DB::getQuery($Data) === false || $Data->getQueryNumRows() >= 1); + $queryRes = DB::getResults($Data); + + if ($queryRes !== false) { + if ($Data->getQueryNumRows() === 0) { + return false; + } elseif ($Data->getQueryNumRows() === 1) { + $this->itemData->setCategoryId($queryRes->category_id); + } + } + + return true; } /** @@ -252,7 +262,7 @@ class Category extends CategoryBase implements ItemInterface, ItemSelectInterfac public function getAll() { $query = /** @lang SQL */ - 'SELECT category_id, category_name, category_description FROM categories ORDER BY category_name'; + 'SELECT category_id, category_name, category_description, category_hash FROM categories ORDER BY category_name'; $Data = new QueryData(); $Data->setMapClassName($this->getDataModel()); diff --git a/inc/SP/Mgmt/Customers/Customer.class.php b/inc/SP/Mgmt/Customers/Customer.class.php index 4f65d246..7ef273db 100644 --- a/inc/SP/Mgmt/Customers/Customer.class.php +++ b/inc/SP/Mgmt/Customers/Customer.class.php @@ -99,7 +99,17 @@ class Customer extends CustomerBase implements ItemInterface, ItemSelectInterfac $Data->setQuery($query); $Data->addParam($this->makeItemHash($this->itemData->getCustomerName())); - return (DB::getQuery($Data) === false || $Data->getQueryNumRows() >= 1); + $queryRes = DB::getResults($Data); + + if ($queryRes !== false) { + if ($Data->getQueryNumRows() === 0) { + return false; + } elseif ($Data->getQueryNumRows() === 1) { + $this->itemData->setCustomerId($queryRes->customer_id); + } + } + + return true; } /** diff --git a/inc/SP/Mgmt/ItemBase.class.php b/inc/SP/Mgmt/ItemBase.class.php index 93059a59..8cba5357 100644 --- a/inc/SP/Mgmt/ItemBase.class.php +++ b/inc/SP/Mgmt/ItemBase.class.php @@ -65,6 +65,7 @@ abstract class ItemBase * * @param $itemData * @return static + * @throws \SP\Core\Exceptions\InvalidClassException */ public final static function getItem($itemData = null) { diff --git a/inc/SP/Mgmt/Tags/Tag.class.php b/inc/SP/Mgmt/Tags/Tag.class.php index 482608a4..1a9681ed 100644 --- a/inc/SP/Mgmt/Tags/Tag.class.php +++ b/inc/SP/Mgmt/Tags/Tag.class.php @@ -76,12 +76,22 @@ class Tag extends TagBase implements ItemInterface, ItemSelectInterface public function checkDuplicatedOnAdd() { $query = /** @lang SQL */ - 'SELECT tag_hash FROM tags WHERE tag_hash = ?'; + 'SELECT tag_id FROM tags WHERE tag_hash = ?'; $Data = new QueryData(); $Data->setQuery($query); $Data->addParam($this->itemData->getTagHash()); - return (DB::getQuery($Data) === false || $Data->getQueryNumRows() > 0); + $queryRes = DB::getResults($Data); + + if ($queryRes !== false) { + if ($Data->getQueryNumRows() === 0) { + return false; + } elseif ($Data->getQueryNumRows() === 1) { + $this->itemData->setTagId($queryRes->tag_id); + } + } + + return true; } /** @@ -185,7 +195,7 @@ class Tag extends TagBase implements ItemInterface, ItemSelectInterface public function getAll() { $query = /** @lang SQL */ - 'SELECT tag_id, tag_name FROM tags ORDER BY tag_name'; + 'SELECT tag_id, tag_name, tag_hash FROM tags ORDER BY tag_name'; $Data = new QueryData(); $Data->setQuery($query); diff --git a/inc/SP/Storage/DBUtil.class.php b/inc/SP/Storage/DBUtil.class.php index 43401caf..c5924e9a 100644 --- a/inc/SP/Storage/DBUtil.class.php +++ b/inc/SP/Storage/DBUtil.class.php @@ -37,6 +37,37 @@ use SP\Core\Exceptions\SPException; */ class DBUtil { + /** + * @var array Tablas de la BBDD + */ + public static $tables = [ + 'customers', + 'categories', + 'usrGroups', + 'usrProfiles', + 'usrData', + 'accounts', + 'accFavorites', + 'accFiles', + 'accGroups', + 'accHistory', + 'accTags', + 'accUsers', + 'authTokens', + 'config', + 'customFieldsData', + 'customFieldsDef', + 'log', + 'publicLinks', + 'tags', + 'usrPassRecover', + 'usrToGroups', + 'plugins', + 'notices', + 'account_data_v', + 'account_search_v' + ]; + /** * Comprobar que la base de datos existe. * @@ -51,17 +82,12 @@ class DBUtil $query = /** @lang SQL */ 'SELECT COUNT(*) FROM information_schema.tables - WHERE table_schema = \'' . Config::getConfig()->getDbName() . '\' - AND table_name = \'usrData\''; + WHERE table_schema = \'' . Config::getConfig()->getDbName() . '\''; - if ((int)$db->query($query)->fetchColumn() !== 0) { - return true; - } + return (int)$db->query($query)->fetchColumn() === count(self::$tables); } catch (\Exception $e) { throw new SPException(SPException::SP_CRITICAL, $e->getMessage(), $e->getCode()); } - - return false; } /** diff --git a/inc/themes/material-blue/views/config/backup.inc b/inc/themes/material-blue/views/config/backup.inc index 17223a23..d8aa8c56 100644 --- a/inc/themes/material-blue/views/config/backup.inc +++ b/inc/themes/material-blue/views/config/backup.inc @@ -24,12 +24,16 @@ - Backup BBDD + - Backup + @@ -100,7 +104,9 @@ - XML > + diff --git a/inc/themes/material-blue/views/config/import.inc b/inc/themes/material-blue/views/config/import.inc index 70bbb700..8e404be7 100644 --- a/inc/themes/material-blue/views/config/import.inc +++ b/inc/themes/material-blue/views/config/import.inc @@ -9,7 +9,7 @@ use SP\Core\Session; ?>