From 8907976c869cac9453e8b02a11eee2abdd332ede Mon Sep 17 00:00:00 2001 From: nuxsmin Date: Wed, 15 Jul 2015 15:19:37 +0200 Subject: [PATCH] * [MOD] XML import (work in progress) --- inc/Crypt.class.php | 38 ++++ inc/Import.class.php | 53 +----- inc/KeepassImport.class.php | 102 +++++----- inc/KeepassXImport.class.php | 130 ++++++------- inc/SyspassImport.class.php | 55 ++---- inc/XmlImport.class.php | 39 ++-- inc/XmlImportBase.class.php | 350 +++++++++++++++++++---------------- 7 files changed, 378 insertions(+), 389 deletions(-) diff --git a/inc/Crypt.class.php b/inc/Crypt.class.php index 9f5b3dd4..597fbeb5 100644 --- a/inc/Crypt.class.php +++ b/inc/Crypt.class.php @@ -210,4 +210,42 @@ class Crypt { return mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, ''); } + + /** + * Encriptar datos. Devuelve un array con los datos encriptados y el IV. + * + * @param $data string Los datos a encriptar + * @throws SPException + * @return array + */ + public static function encryptData($data) + { + if (empty($data)) { + return array('pass' => '', 'IV' => ''); + } + + // Comprobar el módulo de encriptación + if (!Crypt::checkCryptModule()) { + throw new SPException( + SPException::SP_CRITICAL, + _('Error interno'), + _('No se puede usar el módulo de encriptación') + ); + } + + // Encriptar clave + $encData['pass'] = Crypt::mkEncrypt($data); + + if (!empty($data) && ($encData['pass'] === false || is_null($encData['pass']))) { + throw new SPException( + SPException::SP_CRITICAL, + _('Error interno'), + _('Error al generar datos cifrados') + ); + } + + $encData['IV'] = Crypt::$strInitialVector; + + return $encData; + } } \ No newline at end of file diff --git a/inc/Import.class.php b/inc/Import.class.php index fb2c2101..891ad29f 100644 --- a/inc/Import.class.php +++ b/inc/Import.class.php @@ -75,18 +75,7 @@ class Import // Analizamos el XML y seleccionamos el formato a importar $xml = new XmlImport($file); - $format = $xml->detectXMLFormat(); - - if ($format == 'syspass') { - $xmlSyspass = new SyspassImport($file); - $xmlSyspass->doImport(); - } elseif ($format == 'keepass') { - $xmlKeepass = new KeepassImport($file); - $xmlKeepass->doImport(); - } elseif ($format == 'keepassx') { - $xmlKeepassx = new KeepassXImport($file); - $xmlKeepassx->doImport(); - } + $xml->doImport(); } else { throw new SPException( SPException::SP_WARNING, @@ -170,7 +159,7 @@ class Import $categoryId = Category::$categoryLastId; } - $pass = self::encryptPass($password); + $pass = Crypt::encryptData($password); $account = new Account; $account->setAccountName($accountName); @@ -187,42 +176,4 @@ class Import // Creamos la cuenta return $account->createAccount(); } - - /** - * Encriptar la clave de una cuenta. - * - * @param string $password con la clave de la cuenta - * @throws SPException - * @return array con la clave y el IV - */ - private static function encryptPass($password) - { - if (empty($password)) { - return array('pass' => '', 'IV' => ''); - } - - // Comprobar el módulo de encriptación - if (!Crypt::checkCryptModule()) { - throw new SPException( - SPException::SP_CRITICAL, - _('Error interno'), - _('No se puede usar el módulo de encriptación') - ); - } - - // Encriptar clave - $data['pass'] = Crypt::mkEncrypt($password); - - if (!empty($password) && ($data['pass'] === false || is_null($data['pass']))) { - throw new SPException( - SPException::SP_CRITICAL, - _('Error interno'), - _('Error al generar datos cifrados') - ); - } - - $data['IV'] = Crypt::$strInitialVector; - - return $data; - } } \ No newline at end of file diff --git a/inc/KeepassImport.class.php b/inc/KeepassImport.class.php index 0f21233a..2c1e0aa2 100644 --- a/inc/KeepassImport.class.php +++ b/inc/KeepassImport.class.php @@ -37,42 +37,10 @@ class KeepassImport extends XmlImportBase */ public function doImport() { - $this->getGroups($this->_xml->Root->Group); - } + $this->setCustomerName('KeePass'); + $this->setCustomerId($this->addCustomer()); - /** - * Obtener los datos de las entradas de KeePass. - * - * @param \SimpleXMLElement $entries El objeto XML con las entradas - * @param string $groupName con nombre del grupo a procesar - */ - protected function getEntryData(\SimpleXMLElement $entries, $groupName) - { - foreach ($entries as $entry) { - foreach ($entry->String as $account) { - $value = (isset($account->Value)) ? (string)$account->Value : ''; - switch ($account->Key) { - case 'Notes': - $notes = $value; - break; - case 'Password': - $password = $value; - break; - case 'Title': - $name = $value; - break; - case 'URL': - $url = $value; - break; - case 'UserName': - $username = $value; - break; - } - } - - $accountData = array($name, 'KeePass', $groupName, $url, $username, $password, $notes); - Import::addAccountData($accountData); - } + $this->processCategories($this->_xml->Root->Group); } /** @@ -80,48 +48,72 @@ class KeepassImport extends XmlImportBase * * @param \SimpleXMLElement $xml El objeto XML del archivo de KeePass */ - protected function getGroups(\SimpleXMLElement $xml) + protected function processCategories(\SimpleXMLElement $xml) { foreach ($xml as $node) { if ($node->Group) { foreach ($node->Group as $group) { - $groupName = $group->Name; // Analizar grupo if ($node->Group->Entry) { - // Obtener entradas - $this->getEntryData($group->Entry, $groupName); + // Crear la categoría + $this->setCategoryName($group->Name); + $this->setCategoryId($this->addCategory()); + + // Crear cuentas + $this->processAccounts($group->Entry); } if ($group->Group) { // Analizar subgrupo - $this->getGroups($group); + $this->processCategories($group); } } } if ($node->Entry) { - $groupName = $node->Name; - // Obtener entradas - $this->getEntryData($node->Entry, $groupName); + // Crear la categoría + $this->setCategoryName($node->Name); + $this->setCategoryId($this->addCategory()); + + // Crear cuentas + $this->processAccounts($node->Entry); } } } /** - * Obtener los datos de las entradas. - */ - protected function getAccountData() - { - // TODO: Implement getAccountData() method. - } - - /** - * Añadir una cuenta en sysPass desde XML + * Obtener los datos de las entradas de KeePass. * - * @return mixed + * @param \SimpleXMLElement $entries El objeto XML con las entradas */ - protected function addAccount() + protected function processAccounts(\SimpleXMLElement $entries) { - // TODO: Implement addAccount() method. + foreach ($entries as $entry) { + foreach ($entry->String as $account) { + $value = (isset($account->Value)) ? (string)$account->Value : ''; + switch ($account->Key) { + case 'Notes': + $this->setAccountNotes($value); + break; + case 'Password': + $passData = Crypt::encryptData($value); + + $this->setAccountPass($passData['pass']); + $this->setAccountPassIV($passData['IV']); + break; + case 'Title': + $this->setAccountName($value); + break; + case 'URL': + $this->setAccountUrl($value); + break; + case 'UserName': + $this->setAccountLogin($value); + break; + } + } + + $this->addAccount(); + } } } \ No newline at end of file diff --git a/inc/KeepassXImport.class.php b/inc/KeepassXImport.class.php index 56ff6484..a0f79569 100644 --- a/inc/KeepassXImport.class.php +++ b/inc/KeepassXImport.class.php @@ -32,13 +32,66 @@ defined('APP_ROOT') || die(_('No es posible acceder directamente a este archivo' */ class KeepassXImport extends XmlImportBase { + /** + * Iniciar la importación desde KeePassX. + * + * @throws SPException + * @return bool + */ + public function doImport() + { + $this->setCustomerName('KeePassX'); + $this->setCustomerId($this->addCustomer()); + + self::processCategories($this->_xml); + } + + + /** + * Obtener los grupos y procesar lan entradas de KeePass. + * + * @param \SimpleXMLElement $xml con objeto XML del archivo de KeePass + */ + protected function processCategories(\SimpleXMLElement $xml) + { + foreach ($xml as $node) { + if ($node->group) { + foreach ($node->group as $group) { + // Analizar grupo + if ($node->group->entry) { + // Crear la categoría + $this->setCategoryName($group->title); + $this->setCategoryId($this->addCategory()); + + // Crear cuentas + $this->processAccounts($group->entry); + } + + if ($group->group) { + // Analizar subgrupo + $this->processCategories($group); + } + } + } + + if ($node->entry) { + // Crear la categoría + $this->setCategoryName($node->title); + $this->setCategoryId($this->addCategory()); + + // Crear cuentas + $this->processAccounts($node->entry); + } + } + } + /** * 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 string $groupName con nombre del grupo a procesar */ - protected function getEntryData(\SimpleXMLElement $entries, $groupName) + protected function processAccounts(\SimpleXMLElement $entries, $groupName) { foreach ($entries as $entry) { $notes = (isset($entry->comment)) ? (string)$entry->comment : ''; @@ -47,69 +100,16 @@ class KeepassXImport extends XmlImportBase $url = (isset($entry->url)) ? (string)$entry->url : ''; $username = (isset($entry->username)) ? (string)$entry->username : ''; - $accountData = array($name, 'KeePassX', $groupName, $url, $username, $password, $notes); - Import::addAccountData($accountData); + $passData = Crypt::encryptData($password); + + $this->setAccountPass($passData['pass']); + $this->setAccountPassIV($passData['IV']); + $this->setAccountNotes($notes); + $this->setAccountName($name); + $this->setAccountUrl($url); + $this->setAccountLogin($username); + + $this->addAccount(); } } - - /** - * Obtener los grupos y procesar lan entradas de KeePass. - * - * @param \SimpleXMLElement $xml con objeto XML del archivo de KeePass - */ - protected function getGroups(\SimpleXMLElement $xml) - { - foreach ($xml as $node) { - if ($node->group) { - foreach ($node->group as $group) { - $groupName = $group->title; - // Analizar grupo - if ($node->group->entry) { - // Obtener entradas - $this->getEntryData($group->entry, $groupName); - } - - if ($group->group) { - // Analizar subgrupo - $this->getGroups($group); - } - } - } - - if ($node->entry) { - $groupName = $node->title; - // Obtener entradas - $this->getEntryData($node->entry, $groupName); - } - } - } - - /** - * Obtener los datos de las entradas. - */ - protected function getAccountData() - { - // TODO: Implement getAccountData() method. - } - - /** - * Añadir una cuenta en sysPass desde XML - * - * @return mixed - */ - protected function addAccount() - { - // TODO: Implement addAccount() method. - } - - /** - * Iniciar la importación desde KeePassX. - * - * @throws SPException - * @return bool - */ - public function doImport() - { - self::getGroups($this->_xml); - } } \ No newline at end of file diff --git a/inc/SyspassImport.class.php b/inc/SyspassImport.class.php index 70e472a9..68fab4a0 100644 --- a/inc/SyspassImport.class.php +++ b/inc/SyspassImport.class.php @@ -53,18 +53,10 @@ class SyspassImport extends XmlImportBase */ public function doImport() { - if ($this->getUserId() === 0){ - $this->setUserId(Session::getUserId()); - } - - if ($this->getUserGroupId() === 0){ - $this->setUserGroupId(Session::getUserGroupId()); - } - try { - $this->addCategories(); - $this->addCustomers(); - $this->getAccountData(); + $this->processCategories(); + $this->processCustomers(); + $this->processAccounts(); } catch (SPException $e){ return false; } @@ -73,11 +65,10 @@ class SyspassImport extends XmlImportBase } /** - * Obtener los datos de las entradas de KeePass. + * Obtener los datos de las entradas de sysPass y crearlas. */ - protected function getAccountData() + protected function processAccounts() { - foreach ($this->_xml->Accounts as $entry) { $account = $entry->Account; @@ -98,42 +89,26 @@ class SyspassImport extends XmlImportBase /** * Obtener las categorías y añadirlas a sysPass. */ - protected function addCategories() + protected function processCategories() { foreach ($this->_xml->Categories as $category) { - $this->_categories[$category['id']] = Category::addCategoryReturnId($category->name, $category->description); + $this->setCustomerName($category->name); + $this->setCategoryDescription($category->description); + + $this->_categories[$category['id']] = $this->addCategory(); } } /** * Obtener los clientes y añadirlos a sysPass. */ - protected function addCustomers() + protected function processCustomers() { foreach ($this->_xml->Customers as $customer) { - $this->_customers[$customer['id']] = Customer::addCustomerReturnId($customer->name, $customer->description); + $this->setCustomerName($customer->name); + $this->setCustomerDescription($customer->description); + + $this->_customers[$customer['id']] = $this->addCustomer(); } } - - /** - * Añadir una cuenta en sysPass desde XML - * - * @return mixed - */ - protected function addAccount() - { - $account = new Account; - $account->setAccountName($this->getAccountName()); - $account->setAccountCustomerId($this->getCustomerId()); - $account->setAccountCategoryId($this->getCategoryId()); - $account->setAccountLogin($this->getAccountLogin()); - $account->setAccountUrl($this->getAccountUrl()); - $account->setAccountPass($this->getAccountPass()); - $account->setAccountIV($this->getAccountPassIV()); - $account->setAccountNotes($this->getAccountNotes()); - $account->setAccountUserId($this->getUserId()); - $account->setAccountUserGroupId($this->getUserGroupId()); - - return $account->createAccount(); - } } \ No newline at end of file diff --git a/inc/XmlImport.class.php b/inc/XmlImport.class.php index 422588f6..f79b4c35 100644 --- a/inc/XmlImport.class.php +++ b/inc/XmlImport.class.php @@ -29,33 +29,30 @@ defined('APP_ROOT') || die(_('No es posible acceder directamente a este archivo' class XmlImport extends XmlImportBase { - - /** - * Obtener los datos de las entradas. - */ - protected function getAccountData() - { - // TODO: Implement getAccountData() method. - } - - /** - * Añadir una cuenta en sysPass desde XML - * - * @return mixed - */ - protected function addAccount() - { - // TODO: Implement addAccount() method. - } - /** * Iniciar la importación desde XML. * * @throws SPException * @return bool */ - protected function doImport() + public function doImport() { - // TODO: Implement doImport() method. + $format = $this->detectXMLFormat(); + + switch ($format) { + case 'syspass': + $import = new SyspassImport($this->_file); + break; + case 'keepass': + $import = new KeepassImport($this->_file); + break; + case 'keepassx': + $import = new KeepassXImport($this->_file); + break; + } + + if (is_object($import)){ + $import->doImport(); + } } } \ No newline at end of file diff --git a/inc/XmlImportBase.class.php b/inc/XmlImportBase.class.php index 694060b3..6ae4b287 100644 --- a/inc/XmlImportBase.class.php +++ b/inc/XmlImportBase.class.php @@ -139,6 +139,123 @@ abstract class XmlImportBase } } + /** + * Leer el archivo a un objeto XML. + * + * @throws SPException + * @return \SimpleXMLElement Con los datos del archivo XML + */ + protected function readXMLFile() + { + $this->_xml = simplexml_load_file($this->_file->getTmpFile()); + + if ($this->_xml === false) { + throw new SPException( + SPException::SP_CRITICAL, + _('Error interno'), + _('No es posible procesar el archivo XML') + ); + } + } + + /** + * Detectar la aplicación que generó el XML. + * + * @throws SPException + */ + public function detectXMLFormat() + { + if ($this->_xml->Meta->Generator == 'KeePass') { + return 'keepass'; + } else if ($this->_xml->Meta->Generator == 'sysPass') { + return 'syspass'; + } else if ($xmlApp = $this->parseFileHeader()) { + switch ($xmlApp) { + case 'keepassx_database': + return 'keepassx'; + case 'revelationdata': + return 'revelation'; + default: + break; + } + } else { + throw new SPException( + SPException::SP_CRITICAL, + _('Archivo XML no soportado'), + _('No es posible detectar la aplicación que exportó los datos') + ); + } + + return ''; + } + + /** + * Leer la cabecera del archivo XML y obtener patrones de aplicaciones conocidas. + * + * @return bool + */ + protected function parseFileHeader() + { + $handle = @fopen($this->_file->getTmpFile(), "r"); + $headersRegex = '/(KEEPASSX_DATABASE|revelationdata)/i'; + + if ($handle) { + // No. de líneas a leer como máximo + $maxLines = 5; + $count = 0; + + while (($buffer = fgets($handle, 4096)) !== false && $count <= $maxLines) { + if (preg_match($headersRegex, $buffer, $app)) { + fclose($handle); + return strtolower($app[0]); + } + $count++; + } + + fclose($handle); + } + + return false; + } + + /** + * Iniciar la importación desde XML. + * + * @throws SPException + * @return bool + */ + public abstract function doImport(); + + /** + * Añadir una cuenta desde XML. + * + * @return bool + */ + protected function addAccount() + { + if ($this->getUserId() === 0) { + $this->setUserId(Session::getUserId()); + } + + if ($this->getUserGroupId() === 0) { + $this->setUserGroupId(Session::getUserGroupId()); + } + + $account = new Account; + $account->setAccountName($this->getAccountName()); + $account->setAccountCustomerId($this->getCustomerId()); + $account->setAccountCategoryId($this->getCategoryId()); + $account->setAccountLogin($this->getAccountLogin()); + $account->setAccountUrl($this->getAccountUrl()); + $account->setAccountPass($this->getAccountPass()); + $account->setAccountIV($this->getAccountPassIV()); + $account->setAccountNotes($this->getAccountNotes()); + $account->setAccountUserId($this->getUserId()); + $account->setAccountUserGroupId($this->getUserGroupId()); + + return $account->createAccount(); + } + /** * @return int */ @@ -171,70 +288,6 @@ abstract class XmlImportBase $this->userGroupId = $userGroupId; } - /** - * @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; - } - - /** - * @return string - */ - public function getCustomerDescription() - { - return $this->_customerDescription; - } - - /** - * @param string $customerDescription - */ - public function setCustomerDescription($customerDescription) - { - $this->_customerDescription = $customerDescription; - } - - /** - * @return string - */ - public function getCustomerName() - { - return $this->_customerName; - } - - /** - * @param string $_customerName - */ - public function setCustomerName($_customerName) - { - $this->_customerName = $_customerName; - } - /** * @return string */ @@ -315,22 +368,6 @@ abstract class XmlImportBase $this->_accountUrl = $_accountUrl; } - /** - * @return string - */ - public function getAccountNotes() - { - return $this->_accountNotes; - } - - /** - * @param string $_accountNotes - */ - public function setAccountNotes($_accountNotes) - { - $this->_accountNotes = $_accountNotes; - } - /** * @return string */ @@ -364,101 +401,100 @@ abstract class XmlImportBase } /** - * Obtener los datos de las entradas. + * @return string */ - protected abstract function getAccountData(); - - /** - * Añadir una cuenta en sysPass desde XML - * - * @return mixed - */ - protected abstract function addAccount(); - - /** - * Detectar la aplicación que generó el XML. - * - * @throws SPException - */ - public function detectXMLFormat() + public function getAccountNotes() { - if ($this->_xml->Meta->Generator == 'KeePass') { - return 'keepass'; - } else if ($this->_xml->Meta->Generator == 'sysPass') { - return 'syspass'; - } else if ($xmlApp = $this->parseFileHeader()) { - switch ($xmlApp) { - case 'keepassx_database': - return 'keepassx'; - case 'revelationdata': - return 'revelation'; - default: - break; - } - } else { - throw new SPException( - SPException::SP_CRITICAL, - _('Archivo XML no soportado'), - _('No es posible detectar la aplicación que exportó los datos') - ); - } - - return ''; + return $this->_accountNotes; } /** - * Leer el archivo a un objeto XML. - * - * @throws SPException - * @return \SimpleXMLElement Con los datos del archivo XML + * @param string $_accountNotes */ - protected function readXMLFile() + public function setAccountNotes($_accountNotes) { - $this->_xml = simplexml_load_file($this->_file->getTmpFile()); - - if ($this->_xml === false) { - throw new SPException( - SPException::SP_CRITICAL, - _('Error interno'), - _('No es posible procesar el archivo XML') - ); - } + $this->_accountNotes = $_accountNotes; } /** - * Iniciar la importación desde XML. - * - * @throws SPException - * @return bool + * Añadir una categoría y devolver el Id + * @return int */ - public abstract function doImport(); + protected function addCategory() + { + return Category::addCategoryReturnId($this->getCategoryName(), $this->getCategoryDescription()); + } /** - * Leer la cabecera del archivo XML y obtener patrones de aplicaciones conocidas. - * - * @return bool + * @return string */ - protected function parseFileHeader() + public function getCategoryName() { - $handle = @fopen($this->_file->getTmpFile(), "r"); - $headersRegex = '/(KEEPASSX_DATABASE|revelationdata)/i'; + return $this->_categoryName; + } - if ($handle) { - // No. de líneas a leer como máximo - $maxLines = 5; - $count = 0; + /** + * @param string $_categoryName + */ + public function setCategoryName($_categoryName) + { + $this->_categoryName = $_categoryName; + } - while (($buffer = fgets($handle, 4096)) !== false && $count <= $maxLines) { - if (preg_match($headersRegex, $buffer, $app)) { - fclose($handle); - return strtolower($app[0]); - } - $count++; - } + /** + * @return string + */ + public function getCategoryDescription() + { + return $this->_categoryDescription; + } - fclose($handle); - } + /** + * @param string $categoryDescription + */ + public function setCategoryDescription($categoryDescription) + { + $this->_categoryDescription = $categoryDescription; + } - return false; + /** + * Añadir un cliente y devolver el Id + * @return int + */ + protected function addCustomer() + { + return Customer::addCustomerReturnId($this->getCustomerName(), $this->getCustomerDescription()); + } + + /** + * @return string + */ + public function getCustomerName() + { + return $this->_customerName; + } + + /** + * @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; } } \ No newline at end of file