* [MOD] Config module. Work in progress

* [MOD] Improved import module. Work in progress
This commit is contained in:
nuxsmin
2018-02-20 01:31:32 +01:00
parent af02a59218
commit 62a4b388cb
30 changed files with 1258 additions and 1044 deletions

View File

@@ -63,7 +63,9 @@ class BootstrapController extends SimpleControllerBase
'plugins' => [],
'loggedin' => $this->session->isLoggedIn(),
'authbasic_autologin' => Browser::getServerAuthUser() && $configData->isAuthBasicAutoLoginEnabled(),
'pk' => $this->session->getPublicKey() ?: (new CryptPKI())->getPublicKey()
'pk' => $this->session->getPublicKey() ?: (new CryptPKI())->getPublicKey(),
'import_allowed_exts' => ['CSV', 'XML'],
'files_allowed_exts' => $configData->getFilesAllowedExts()
];
Response::printJson($data, 0);

View File

@@ -0,0 +1,74 @@
<?php
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Modules\Web\Controllers;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use SP\Http\JsonResponse;
use SP\Http\Request;
use SP\Modules\Web\Controllers\Traits\JsonTrait;
use SP\Services\Import\FileImport;
use SP\Services\Import\ImportParams;
use SP\Services\Import\ImportService;
/**
* Class ConfigImportController
*
* @package SP\Modules\Web\Controllers
*/
class ConfigImportController extends SimpleControllerBase
{
use JsonTrait;
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function importAction()
{
if ($this->config->getConfigData()->isDemoEnabled()) {
$this->returnJsonResponse(JsonResponse::JSON_WARNING, __u('Ey, esto es una DEMO!!'));
}
$importParams = new ImportParams();
$importParams->setDefaultUser(Request::analyze('import_defaultuser', $this->session->getUserData()->getId()));
$importParams->setDefaultGroup(Request::analyze('import_defaultgroup', $this->session->getUserData()->getUserGroupId()));
$importParams->setImportPwd(Request::analyzeEncrypted('importPwd'));
$importParams->setImportMasterPwd(Request::analyzeEncrypted('importMasterPwd'));
$importParams->setCsvDelimiter(Request::analyze('csvDelimiter'));
try {
$importService = $this->dic->get(ImportService::class);
$importService->doImport($importParams, new FileImport($this->router->request()->files()->get('inFile')));
$this->returnJsonResponse(JsonResponse::JSON_SUCCESS, __u('Importación finalizada'), [__u('Revise el registro de eventos para más detalles')]);
} catch (\Exception $e) {
processException($e);
$this->returnJsonResponseException($e);
}
}
}

View File

@@ -72,8 +72,8 @@
<div class="lowres-title"><?php echo __('Archivo'); ?></div>
<div id="drop-import-files" class="round active-tooltip dropzone"
data-files-ext="csv,xml"
title="<?php echo __('Soltar archivo aquí o click para seleccionar'); ?>">
title="<?php echo __('Soltar archivo aquí o click para seleccionar'); ?>"
data-action-route="configImport/import">
<i class="material-icons md-60 mdl-color-text--teal-500">cloud_upload</i>
</div>
<form method="post" enctype="multipart/form-data" name="upload_form" id="fileUploadForm">

View File

@@ -26,8 +26,6 @@ namespace SP\Controller;
use SP\Account\AccountCrypt;
use SP\Account\AccountHistoryCrypt;
use SP\Config\Config;
use SP\Config\ConfigData;
use SP\Config\ConfigDB;
use SP\Core\ActionsInterface;
use SP\Core\Backup;
@@ -43,15 +41,14 @@ use SP\Core\TaskFactory;
use SP\Core\Traits\InjectableTrait;
use SP\Core\XmlExport;
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;
use SP\Mgmt\Users\UserPass;
use SP\Mgmt\Users\UserUtil;
use SP\Services\Import\ImportParams;
use SP\Services\Import\ImportService;
use SP\Storage\DbWrapper;
use SP\Util\Checks;
use SP\Util\Json;
use SP\Util\Util;
@@ -710,7 +707,7 @@ class ConfigActionController implements ItemControllerInterface
$ImportParams->setImportMasterPwd(Request::analyzeEncrypted('importMasterPwd'));
$ImportParams->setCsvDelimiter(Request::analyze('csvDelimiter'));
$Import = new Import($ImportParams);
$Import = new ImportService($ImportParams);
$LogMessage = $Import->doImport($_FILES['inFile']);
$this->JsonResponse->setDescription($LogMessage->getHtmlDescription(true));

View File

@@ -1,115 +0,0 @@
<?php
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
use SP\Core\Exceptions\SPException;
use SP\DataModel\AccountExtData;
use SP\DataModel\CategoryData;
use SP\DataModel\ClientData;
defined('APP_ROOT') || die();
/**
* Clase CsvImportBase para base de clases de importación desde archivos CSV
*
* @package SP
*/
abstract class CsvImportBase extends ImportBase
{
/**
* @var int
*/
protected $numFields = 7;
/**
* @var array
*/
protected $mapFields = [];
/**
* @param int $numFields
*/
public function setNumFields($numFields)
{
$this->numFields = $numFields;
}
/**
* @param array $mapFields
*/
public function setMapFields($mapFields)
{
$this->mapFields = $mapFields;
}
/**
* Obtener los datos de las entradas de sysPass y crearlas
*
* @throws SPException
*/
protected function processAccounts()
{
$line = 0;
foreach ($this->file->getFileContent() as $data) {
$line++;
$fields = str_getcsv($data, $this->ImportParams->getCsvDelimiter(), '"');
$numfields = count($fields);
// Comprobar el número de campos de la línea
if ($numfields !== $this->numFields) {
throw new SPException(
sprintf(__('El número de campos es incorrecto (%d)', false), $numfields), SPException::CRITICAL, sprintf(__('Compruebe el formato del archivo CSV en línea %s', false), $line)
);
}
// Asignar los valores del array a variables
list($accountName, $customerName, $categoryName, $url, $login, $password, $notes) = $fields;
// Obtener los ids de cliente y categoría
$CustomerData = new ClientData(null, $customerName);
$this->addCustomer($CustomerData);
$CategoryData = new CategoryData(null, $categoryName);
$this->addCategory($CategoryData);
// Crear la nueva cuenta
$AccountData = new AccountExtData();
$AccountData->setName($accountName);
$AccountData->setLogin($login);
$AccountData->setClientId($CustomerData->getId());
$AccountData->setCategoryId($CategoryData->getId());
$AccountData->setNotes($notes);
$AccountData->setUrl($url);
$AccountData->setPass($password);
try {
$this->addAccount($AccountData);
} catch (SPException $e) {
$this->LogMessage->addDetails(__('Error importando cuenta', false), $accountName);
$this->LogMessage->addDetails(__('Error procesando línea', false), $line);
$this->LogMessage->addDetails(__('Error', false), $e->getMessage());
}
}
}
}

View File

@@ -1,119 +0,0 @@
<?php
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
use SP\Core\Exceptions\SPException;
use SP\Core\Messages\LogMessage;
use SP\Log\Email;
use SP\Log\Log;
use SP\Storage\DbWrapper;
defined('APP_ROOT') || die();
/**
* Esta clase es la encargada de importar cuentas.
*/
class Import
{
/**
* @var ImportParams Parámetros de importación
*/
protected $ImportParams;
/**
* Import constructor.
*
* @param ImportParams $ImportParams
*/
public function __construct(ImportParams $ImportParams)
{
$this->ImportParams = $ImportParams;
}
/**
* Iniciar la importación de cuentas.
*
* @param array $fileData Los datos del archivo
* @return LogMessage
* @throws SPException
*/
public function doImport(&$fileData)
{
set_time_limit(0);
$LogMessage = new LogMessage();
$LogMessage->setAction(__('Importar Cuentas', false));
$Log = new Log($LogMessage);
try {
$file = new FileImport($fileData);
switch ($file->getFileType()) {
case 'text/csv':
case 'application/vnd.ms-excel':
$Import = new CsvImport($file, $this->ImportParams, $LogMessage);
break;
case 'text/xml':
$Import = new XmlImport($file, $this->ImportParams, $LogMessage);
break;
default:
throw new SPException(
sprintf(__('Tipo mime no soportado ("%s")'), $file->getFileType()), SPException::WARNING, __('Compruebe el formato del archivo', false)
);
}
if (!DbWrapper::beginTransaction()) {
throw new SPException(__('No es posible iniciar una transacción', false), SPException::ERROR);
}
$Import->doImport();
if (!DbWrapper::endTransaction()) {
throw new SPException(__('No es posible finalizar una transacción', false), SPException::ERROR);
}
$LogMessage->addDetails(__('Cuentas importadas'), $Import->getCounter());
} catch (SPException $e) {
DbWrapper::rollbackTransaction();
$LogMessage->addDescription($e->getMessage());
$LogMessage->addDetails(__('Ayuda', false), $e->getHint());
$Log->setLogLevel(Log::ERROR);
$Log->writeLog();
throw $e;
}
$Log->writeLog(true);
Email::sendEmail($LogMessage);
$LogMessage->addDescription(__('Importación finalizada', false));
$LogMessage->addDescription(__('Revise el registro de eventos para más detalles', false));
return $LogMessage;
}
}

View File

@@ -1,258 +0,0 @@
<?php
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
use SP\Account\Account;
use SP\Account\AccountTags;
use SP\Core\Crypt\Crypt;
use SP\Core\Exceptions\SPException;
use SP\Core\Messages\LogMessage;
use SP\Core\OldCrypt;
use SP\DataModel\AccountExtData;
use SP\DataModel\CategoryData;
use SP\DataModel\ClientData;
use SP\DataModel\TagData;
use SP\Log\Log;
use SP\Mgmt\Categories\Category;
use SP\Mgmt\Customers\Customer;
use SP\Mgmt\Tags\Tag;
defined('APP_ROOT') || die();
/**
* Class ImportBase abstracta para manejo de archivos de importación
*
* @package SP
*/
abstract class ImportBase implements ImportInterface
{
/**
* @var ImportParams
*/
protected $ImportParams;
/**
* @var FileImport
*/
protected $file;
/**
* @var LogMessage
*/
protected $LogMessage;
/**
* @var int
*/
protected $counter = 0;
/**
* @var int
*/
protected $version = 0;
/**
* @var bool Indica si el hash de la clave suministrada es igual a la actual
*/
protected $mPassValidHash = false;
/**
* ImportBase constructor.
*
* @param FileImport $File
* @param ImportParams $ImportParams
* @param LogMessage $LogMessage
*/
public function __construct(FileImport $File = null, ImportParams $ImportParams = null, LogMessage $LogMessage = null)
{
$this->file = $File;
$this->ImportParams = $ImportParams;
$this->LogMessage = null !== $LogMessage ? $LogMessage : new LogMessage(__('Importar Cuentas', false));
}
/**
* @return LogMessage
*/
public function getLogMessage()
{
return $this->LogMessage;
}
/**
* @param LogMessage $LogMessage
*/
public function setLogMessage($LogMessage)
{
$this->LogMessage = $LogMessage;
}
/**
* @return int
*/
public function getCounter()
{
return $this->counter;
}
/**
* @param ImportParams $ImportParams
*/
public function setImportParams($ImportParams)
{
$this->ImportParams = $ImportParams;
}
/**
* Añadir una cuenta desde un archivo importado.
*
* @param \SP\DataModel\AccountExtData $AccountData
* @return bool
*/
protected function addAccount(AccountExtData $AccountData)
{
if ($AccountData->getCategoryId() === 0) {
Log::writeNewLog(__FUNCTION__, __('Id de categoría no definido. No es posible importar cuenta.', false), Log::INFO);
return false;
}
if ($AccountData->getClientId() === 0) {
Log::writeNewLog(__FUNCTION__, __('Id de cliente no definido. No es posible importar cuenta.', false), Log::INFO);
return false;
}
try {
$AccountData->setUserId($this->ImportParams->getDefaultUser());
$AccountData->setUserGroupId($this->ImportParams->getDefaultGroup());
if ($this->mPassValidHash === false && $this->ImportParams->getImportMasterPwd() !== '') {
if ($this->version >= 210) {
$securedKey = Crypt::unlockSecuredKey($AccountData->getKey(), $this->ImportParams->getImportMasterPwd());
$pass = Crypt::decrypt($AccountData->getPass(), $securedKey, $this->ImportParams->getImportMasterPwd());
} else {
$pass = OldCrypt::getDecrypt($AccountData->getPass(), $AccountData->getKey(), $this->ImportParams->getImportMasterPwd());
}
$AccountData->setPass($pass);
$AccountData->setKey('');
}
$encrypt = $AccountData->getKey() === '';
$Account = new Account($AccountData);
$Account->createAccount($encrypt);
$this->LogMessage->addDetails(__('Cuenta creada', false), $AccountData->getName());
$this->counter++;
} catch (SPException $e) {
$this->LogMessage->addDetails($e->getMessage(), $AccountData->getName());
$this->LogMessage->addDetails(__('Error', false), $e->getHint());
} catch (\Exception $e) {
$this->LogMessage->addDetails(__('Error', false), $e->getMessage());
$this->LogMessage->addDetails(__('Cuenta', false), $AccountData->getName());
}
return true;
}
/**
* Añadir una categoría y devolver el Id
*
* @param CategoryData $CategoryData
* @return Category|null
*/
protected function addCategory(CategoryData $CategoryData)
{
try {
$Category = Category::getItem($CategoryData)->add();
$this->LogMessage->addDetails(__('Categoría creada', false), $CategoryData->getName());
return $Category;
} catch (SPException $e) {
$this->LogMessage->addDetails($e->getMessage(), $CategoryData->name);
$this->LogMessage->addDetails(__('Error', false), $e->getHint());
}
return null;
}
/**
* Añadir un cliente y devolver el Id
*
* @param ClientData $CustomerData
* @return Customer|null
*/
protected function addCustomer(ClientData $CustomerData)
{
try {
$Customer = Customer::getItem($CustomerData)->add();
$this->LogMessage->addDetails(__('Cliente creado', false), $CustomerData->getName());
return $Customer;
} catch (SPException $e) {
$this->LogMessage->addDetails($e->getMessage(), $CustomerData->getName());
$this->LogMessage->addDetails(__('Error', false), $e->getHint());
}
return null;
}
/**
* Añadir una etiqueta y devolver el Id
*
* @param TagData $TagData
* @return Tag|null
*/
protected function addTag(TagData $TagData)
{
try {
$Tag = Tag::getItem($TagData)->add();
$this->LogMessage->addDetails(__('Etiqueta creada', false), $TagData->getName());
return $Tag;
} catch (SPException $e) {
$this->LogMessage->addDetails($e->getMessage(), $TagData->getName());
$this->LogMessage->addDetails(__('Error', false), $e->getHint());
}
return null;
}
/**
* Añadir las etiquetas de la cuenta
*
* @param AccountExtData $accountExtData
* @param array $tags
*/
protected function addAccountTags(AccountExtData $accountExtData, array $tags)
{
try {
$accountExtData->setTags($tags);
$AccountTags = new AccountTags();
$AccountTags->addTags($accountExtData);
} catch (SPException $e) {
$this->LogMessage->addDetails($e->getMessage(), $accountExtData->getName());
$this->LogMessage->addDetails(__('Error', false), $e->getHint());
}
}
}

View File

@@ -1,337 +0,0 @@
<?php
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
use DOMXPath;
use SP\Config\ConfigDB;
use SP\Core\Crypt\Crypt;
use SP\Core\Crypt\Hash;
use SP\Core\Exceptions\SPException;
use SP\Core\OldCrypt;
use SP\DataModel\AccountExtData;
use SP\DataModel\CategoryData;
use SP\DataModel\ClientData;
use SP\DataModel\TagData;
defined('APP_ROOT') || die();
/**
* Esta clase es la encargada de importar cuentas desde sysPass
*/
class SyspassImport extends ImportBase
{
use XmlImportTrait;
/**
* Mapeo de etiquetas
*
* @var array
*/
protected $tags = [];
/**
* Mapeo de categorías.
*
* @var array
*/
protected $categories = [];
/**
* Mapeo de clientes.
*
* @var array
*/
protected $customers = [];
/**
* Iniciar la importación desde sysPass.
*
* @throws SPException
*/
public function doImport()
{
try {
if ($this->ImportParams->getImportMasterPwd() !== '') {
$this->mPassValidHash = Hash::checkHashKey($this->ImportParams->getImportMasterPwd(), ConfigDB::getValue('masterPwd'));
}
$this->getXmlVersion();
if ($this->detectEncrypted()) {
if ($this->ImportParams->getImportPwd() === '') {
throw new SPException(__('Clave de encriptación no indicada', false), SPException::ERROR);
}
$this->processEncrypted();
}
$this->processCategories();
$this->processCustomers();
$this->processTags();
$this->processAccounts();
} catch (SPException $e) {
throw $e;
} catch (\Exception $e) {
throw new SPException($e->getMessage(), SPException::CRITICAL);
}
}
/**
* Obtener la versión del XML
*/
protected function getXmlVersion()
{
$DomXpath = new DOMXPath($this->xmlDOM);
$this->version = (int)str_replace('.', '', $DomXpath->query('/Root/Meta/Version')->item(0)->nodeValue);
}
/**
* Verificar si existen datos encriptados
*
* @return bool
*/
protected function detectEncrypted()
{
return ($this->xmlDOM->getElementsByTagName('Encrypted')->length > 0);
}
/**
* Procesar los datos encriptados y añadirlos al árbol DOM desencriptados
*
* @throws \SP\Core\Exceptions\SPException
* @throws \Defuse\Crypto\Exception\CryptoException
*/
protected function processEncrypted()
{
$hash = $this->xmlDOM->getElementsByTagName('Encrypted')->item(0)->getAttribute('hash');
if ($hash !== '' && !Hash::checkHashKey($this->ImportParams->getImportPwd(), $hash)) {
throw new SPException(__('Clave de encriptación incorrecta', false), SPException::ERROR);
}
foreach ($this->xmlDOM->getElementsByTagName('Data') as $node) {
/** @var $node \DOMElement */
$data = base64_decode($node->nodeValue);
if ($this->version >= 210) {
$securedKey = Crypt::unlockSecuredKey($node->getAttribute('key'), $this->ImportParams->getImportPwd());
$xmlDecrypted = Crypt::decrypt($data, $securedKey, $this->ImportParams->getImportPwd());
} else {
$xmlDecrypted = OldCrypt::getDecrypt($data, base64_decode($node->getAttribute('iv'), $this->ImportParams->getImportPwd()));
}
$newXmlData = new \DOMDocument();
// $newXmlData->preserveWhiteSpace = true;
if (!$newXmlData->loadXML($xmlDecrypted)) {
throw new SPException(__('Clave de encriptación incorrecta', false), SPException::ERROR);
}
$newNode = $this->xmlDOM->importNode($newXmlData->documentElement, TRUE);
$this->xmlDOM->documentElement->appendChild($newNode);
}
// Eliminar los datos encriptados tras desencriptar los mismos
if ($this->xmlDOM->getElementsByTagName('Data')->length > 0) {
$nodeData = $this->xmlDOM->getElementsByTagName('Encrypted')->item(0);
$nodeData->parentNode->removeChild($nodeData);
}
}
/**
* Obtener las categorías y añadirlas a sysPass.
*
* @param \DOMElement $Category
* @throws SPException
* @throws \SP\Core\Exceptions\InvalidClassException
*/
protected function processCategories(\DOMElement $Category = null)
{
if ($Category === null) {
$this->getNodesData('Categories', 'Category', __FUNCTION__);
return;
}
$CategoryData = new CategoryData();
foreach ($Category->childNodes as $categoryNode) {
if (isset($categoryNode->tagName)) {
switch ($categoryNode->tagName) {
case 'name':
$CategoryData->setName($categoryNode->nodeValue);
break;
case 'description':
$CategoryData->setDescription($categoryNode->nodeValue);
break;
}
}
}
$this->addCategory($CategoryData);
$this->categories[$Category->getAttribute('id')] = $CategoryData->getId();
}
/**
* Obtener los clientes y añadirlos a sysPass.
*
* @param \DOMElement $Customer
* @throws SPException
* @throws \SP\Core\Exceptions\InvalidClassException
*/
protected function processCustomers(\DOMElement $Customer = null)
{
if ($Customer === null) {
$this->getNodesData('Customers', 'Customer', __FUNCTION__);
return;
}
$CustomerData = new ClientData();
foreach ($Customer->childNodes as $customerNode) {
if (isset($customerNode->tagName)) {
switch ($customerNode->tagName) {
case 'name':
$CustomerData->setName($customerNode->nodeValue);
break;
case 'description':
$CustomerData->setDescription($customerNode->nodeValue);
break;
}
}
}
$this->addCustomer($CustomerData);
$this->customers[$Customer->getAttribute('id')] = $CustomerData->getId();
}
/**
* Obtener las etiquetas y añadirlas a sysPass.
*
* @param \DOMElement $Tag
* @throws SPException
* @throws \SP\Core\Exceptions\InvalidClassException
*/
protected function processTags(\DOMElement $Tag = null)
{
if ($Tag === null) {
$this->getNodesData('Tags', 'Tag', __FUNCTION__, false);
return;
}
$TagData = new TagData();
foreach ($Tag->childNodes as $tagNode) {
if (isset($tagNode->tagName)) {
switch ($tagNode->tagName) {
case 'name':
$TagData->setName($tagNode->nodeValue);
break;
}
}
}
$this->addTag($TagData);
$this->tags[$Tag->getAttribute('id')] = $TagData->getId();
}
/**
* 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();
/** @var \DOMElement $accountNode */
foreach ($Account->childNodes as $accountNode) {
if (isset($accountNode->tagName)) {
switch ($accountNode->tagName) {
case 'name';
$AccountData->setName($accountNode->nodeValue);
break;
case 'login';
$AccountData->setLogin($accountNode->nodeValue);
break;
case 'categoryId';
$AccountData->setCategoryId($this->categories[(int)$accountNode->nodeValue]);
break;
case 'customerId';
$AccountData->setClientId($this->customers[(int)$accountNode->nodeValue]);
break;
case 'url';
$AccountData->setUrl($accountNode->nodeValue);
break;
case 'pass';
$AccountData->setPass($accountNode->nodeValue);
break;
case 'key';
$AccountData->setKey($accountNode->nodeValue);
break;
case 'notes';
$AccountData->setNotes($accountNode->nodeValue);
break;
case 'tags':
$tags = $this->processAccountTags($accountNode->childNodes);
}
}
}
$this->addAccount($AccountData);
if (isset($tags) && count($tags)) {
$this->addAccountTags($AccountData, $tags);
}
}
/**
* Procesar las etiquetas de la cuenta
*
* @param \DOMNodeList $nodes
* @return array
*/
protected function processAccountTags(\DOMNodeList $nodes)
{
$tags = [];
if ($nodes->length > 0) {
/** @var \DOMElement $accountTagNode */
foreach ($nodes as $accountTagNode) {
if (isset($accountTagNode->tagName)) {
$tags[] = $accountTagNode->getAttribute('id');
}
}
}
return $tags;
}
}

View File

@@ -48,8 +48,6 @@ class CategoryRepository extends Repository implements RepositoryItemInterface
* @param CategoryData $itemData
* @return mixed
* @throws SPException
* @throws \SP\Core\Exceptions\ConstraintException
* @throws \SP\Core\Exceptions\QueryException
*/
public function create($itemData)
{

View File

@@ -93,8 +93,6 @@ class CategoryService extends Service
* @param $itemData
* @return mixed
* @throws SPException
* @throws \SP\Core\Exceptions\ConstraintException
* @throws \SP\Core\Exceptions\QueryException
*/
public function create($itemData)
{

View File

@@ -2,8 +2,8 @@
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
@@ -22,9 +22,7 @@
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
use SP\Core\Exceptions\SPException;
namespace SP\Services\Import;
defined('APP_ROOT') || die();
@@ -33,7 +31,7 @@ defined('APP_ROOT') || die();
*
* @package SP
*/
class CsvImport extends CsvImportBase
class CsvImport extends CsvImportBase implements ImportInterface
{
/**
* Iniciar la importación desde XML.
@@ -42,13 +40,10 @@ class CsvImport extends CsvImportBase
*/
public function doImport()
{
try {
$this->LogMessage->addDescription(sprintf(__('Formato detectado: %s'), 'CSV'));
// $this->LogMessage->addDescription(sprintf(__('Formato detectado: %s'), 'CSV'));
$this->fileImport->readFileToArray();
$this->processAccounts();
$this->file->readFileToArray();
$this->processAccounts();
} catch (SPException $e) {
throw $e;
}
return $this;
}
}

View File

@@ -0,0 +1,149 @@
<?php
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Services\Import;
use SP\Account\AccountRequest;
use SP\Bootstrap;
use SP\DataModel\CategoryData;
use SP\DataModel\ClientData;
use SP\Services\Account\AccountService;
use SP\Services\Category\CategoryService;
use SP\Services\Client\ClientService;
use SP\Services\Tag\TagService;
defined('APP_ROOT') || die();
/**
* Clase CsvImportBase para base de clases de importación desde archivos CSV
*
* @package SP
*/
abstract class CsvImportBase
{
use ImportTrait;
/**
* @var int
*/
protected $numFields = 7;
/**
* @var array
*/
protected $mapFields = [];
/**
* @var FileImport
*/
protected $fileImport;
/**
* ImportBase constructor.
*
* @param FileImport $fileImport
* @param ImportParams $importParams
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*/
public function __construct(FileImport $fileImport, ImportParams $importParams)
{
$this->fileImport = $fileImport;
$this->importParams = $importParams;
$dic = Bootstrap::getContainer();
$this->accountService = $dic->get(AccountService::class);
$this->categoryService = $dic->get(CategoryService::class);
$this->clientService = $dic->get(ClientService::class);
$this->tagService = $dic->get(TagService::class);
}
/**
* @param int $numFields
*/
public function setNumFields($numFields)
{
$this->numFields = $numFields;
}
/**
* @param array $mapFields
*/
public function setMapFields($mapFields)
{
$this->mapFields = $mapFields;
}
/**
* Obtener los datos de las entradas de sysPass y crearlas
*
* @throws ImportException
*/
protected function processAccounts()
{
$line = 0;
foreach ($this->fileImport->getFileContent() as $data) {
$line++;
$fields = str_getcsv($data, $this->importParams->getCsvDelimiter(), '"');
$numfields = count($fields);
// Comprobar el número de campos de la línea
if ($numfields !== $this->numFields) {
throw new ImportException(
sprintf(__('El número de campos es incorrecto (%d)'), $numfields),
ImportException::ERROR,
sprintf(__('Compruebe el formato del archivo CSV en línea %s'), $line)
);
}
// Asignar los valores del array a variables
list($accountName, $clientName, $categoryName, $url, $login, $password, $notes) = $fields;
try {
// Obtener los ids de cliente y categoría
$clientData = new ClientData(null, $clientName);
$this->addClient($clientData);
$categoryData = new CategoryData(null, $categoryName);
$this->addCategory($categoryData);
// Crear la nueva cuenta
$accountRequest = new AccountRequest();
$accountRequest->name = $accountName;
$accountRequest->login = $login;
$accountRequest->clientId = $clientData->getId();
$accountRequest->categoryId = $categoryData->getId();
$accountRequest->notes = $notes;
$accountRequest->url = $url;
$accountRequest->pass = $password;
$this->addAccount($accountRequest);
} catch (\Exception $e) {
processException($e);
// $this->LogMessage->addDetails(__('Error importando cuenta', false), $accountName);
// $this->LogMessage->addDetails(__('Error procesando línea', false), $line);
// $this->LogMessage->addDetails(__('Error', false), $e->getMessage());
}
}
}
}

View File

@@ -22,7 +22,7 @@
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
namespace SP\Services\Import;
use SP\Core\Exceptions\SPException;
use SP\Util\Util;
@@ -63,7 +63,7 @@ class FileImport
* @param array $fileData Datos del archivo a importar
* @throws SPException
*/
public function __construct(&$fileData)
public function __construct($fileData)
{
try {
$this->checkFile($fileData);
@@ -78,11 +78,14 @@ class FileImport
* @param array $fileData con los datos del archivo
* @throws SPException
*/
private function checkFile(&$fileData)
private function checkFile($fileData)
{
if (!is_array($fileData)) {
throw new SPException(
__('Archivo no subido correctamente', false), SPException::CRITICAL, __('Verifique los permisos del usuario del servidor web', false));
__u('Archivo no subido correctamente'),
SPException::ERROR,
__u('Verifique los permisos del usuario del servidor web')
);
}
if ($fileData['name']) {
@@ -91,7 +94,9 @@ class FileImport
if ($fileExtension !== 'CSV' && $fileExtension !== 'XML') {
throw new SPException(
__('Tipo de archivo no soportado', false), SPException::CRITICAL, __('Compruebe la extensión del archivo', false)
__u('Tipo de archivo no soportado'),
SPException::ERROR,
__u('Compruebe la extensión del archivo')
);
}
}
@@ -105,7 +110,9 @@ class FileImport
Util::getMaxUpload();
throw new SPException(
__('Error interno al leer el archivo', false), SPException::CRITICAL, __('Compruebe la configuración de PHP para subir archivos', false)
__u('Error interno al leer el archivo'),
SPException::ERROR,
__u('Compruebe la configuración de PHP para subir archivos')
);
}
}
@@ -147,7 +154,9 @@ class FileImport
if ($this->fileContent === false) {
throw new SPException(
__('Error interno al leer el archivo', false), SPException::CRITICAL, __('Compruebe los permisos del directorio temporal', false)
__u('Error interno al leer el archivo'),
SPException::ERROR,
__u('Compruebe los permisos del directorio temporal')
);
}
}
@@ -173,7 +182,9 @@ class FileImport
if ($this->fileContent === false) {
throw new SPException(
__('Error interno al leer el archivo', false), SPException::CRITICAL, __('Compruebe los permisos del directorio temporal', false)
__u('Error interno al leer el archivo'),
SPException::ERROR,
__u('Compruebe los permisos del directorio temporal')
);
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Services\Import;
use SP\Core\Exceptions\SPException;
/**
* Class ImportException
*
* @package SP\Services\Import
*/
class ImportException extends SPException
{
}

View File

@@ -22,7 +22,7 @@
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
namespace SP\Services\Import;
/**
* Interface ImportInterface
@@ -33,6 +33,8 @@ interface ImportInterface
{
/**
* Iniciar la importación
*
* @return ImportInterface
*/
public function doImport();

View File

@@ -22,13 +22,13 @@
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
namespace SP\Services\Import;
/**
* Class ImportParams
*
* @package SP\Import
* @package SP\Services\Import
*/
class ImportParams
{

View File

@@ -0,0 +1,131 @@
<?php
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Services\Import;
use SP\Services\Service;
use SP\Storage\Database;
use SP\Storage\DbWrapper;
defined('APP_ROOT') || die();
/**
* Esta clase es la encargada de importar cuentas.
*/
class ImportService extends Service
{
/**
* @var ImportParams
*/
protected $importParams;
/**
* @var FileImport
*/
protected $fileImport;
/**
* Iniciar la importación de cuentas.
*
* @param ImportParams $importParams
* @param FileImport $fileImport
* @throws \Exception
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*/
public function doImport(ImportParams $importParams, FileImport $fileImport)
{
$this->importParams = $importParams;
$this->fileImport = $fileImport;
// $LogMessage->setAction(__('Importar Cuentas', false));
$import = $this->selectImportType();
$db = $this->dic->get(Database::class);
try {
if (!DbWrapper::beginTransaction($db)) {
throw new ImportException(__u('No es posible iniciar una transacción'));
}
$import->doImport();
if (!DbWrapper::endTransaction($db)) {
throw new ImportException(__u('No es posible finalizar una transacción'));
}
// $LogMessage->addDetails(__('Cuentas importadas'), $Import->getCounter());
} catch (\Exception $e) {
if (DbWrapper::rollbackTransaction($db)) {
debugLog('Rollback');
}
// $LogMessage->addDescription($e->getMessage());
// $LogMessage->addDetails(__('Ayuda', false), $e->getHint());
// $Log->setLogLevel(Log::ERROR);
// $Log->writeLog();
throw $e;
}
// $LogMessage->addDescription(__('Importación finalizada', false));
// $LogMessage->addDescription(__('Revise el registro de eventos para más detalles', false));
}
/**
* @return ImportInterface
* @throws ImportException
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*/
protected function selectImportType()
{
switch ($this->fileImport->getFileType()) {
case 'text/csv':
case 'application/vnd.ms-excel':
return new CsvImport($this->fileImport, $this->importParams);
break;
case 'text/xml':
return new XmlImport(new XmlFileImport($this->fileImport), $this->importParams);
break;
}
throw new ImportException(
sprintf(__('Tipo mime no soportado ("%s")'), $this->fileImport->getFileType()),
ImportException::ERROR,
__u('Compruebe el formato del archivo')
);
}
/**
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*/
protected function initialize()
{
set_time_limit(0);
}
}

View File

@@ -0,0 +1,164 @@
<?php
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Services\Import;
use SP\Account\AccountRequest;
use SP\Core\Crypt\Crypt;
use SP\Core\Exceptions\SPException;
use SP\Core\OldCrypt;
use SP\DataModel\CategoryData;
use SP\DataModel\ClientData;
use SP\DataModel\TagData;
use SP\Services\Account\AccountService;
use SP\Services\Category\CategoryService;
use SP\Services\Client\ClientService;
use SP\Services\Tag\TagService;
/**
* Trait ImportTrait
*
* @package SP\Services\Import
*/
trait ImportTrait
{
/**
* @var ImportParams
*/
protected $importParams;
/**
* @var int
*/
protected $version = 0;
/**
* @var bool Indica si el hash de la clave suministrada es igual a la actual
*/
protected $mPassValidHash = false;
/**
* @var int
*/
protected $counter = 0;
/**
* @var AccountService
*/
private $accountService;
/**
* @var CategoryService
*/
private $categoryService;
/**
* @var ClientService
*/
private $clientService;
/**
* @var TagService
*/
private $tagService;
/**
* @return int
*/
public function getCounter()
{
return $this->counter;
}
/**
* Añadir una cuenta desde un archivo importado.
*
* @param AccountRequest $accountRequest
* @throws ImportException
* @throws SPException
* @throws \Defuse\Crypto\Exception\CryptoException
* @throws \SP\Core\Dic\ContainerException
* @throws \SP\Core\Exceptions\ConstraintException
* @throws \SP\Core\Exceptions\QueryException
*/
protected function addAccount(AccountRequest $accountRequest)
{
if ($accountRequest->categoryId === 0) {
throw new ImportException(__u('Id de categoría no definido. No es posible importar cuenta.'));
}
if ($accountRequest->clientId === 0) {
throw new ImportException(__u('Id de cliente no definido. No es posible importar cuenta.'));
}
$accountRequest->userId = $this->importParams->getDefaultUser();
$accountRequest->userGroupId = $this->importParams->getDefaultGroup();
if ($this->mPassValidHash === false && $this->importParams->getImportMasterPwd() !== '') {
if ($this->version >= 210) {
$securedKey = Crypt::unlockSecuredKey($accountRequest->key, $this->importParams->getImportMasterPwd());
$pass = Crypt::decrypt($accountRequest->pass, $securedKey, $this->importParams->getImportMasterPwd());
} else {
$pass = OldCrypt::getDecrypt($accountRequest->pass, $accountRequest->key, $this->importParams->getImportMasterPwd());
}
$accountRequest->pass = $pass;
$accountRequest->key = '';
}
$this->accountService->create($accountRequest);
// $this->LogMessage->addDetails(__('Cuenta creada', false), $accountRequest->name);
$this->counter++;
}
/**
* Añadir una categoría y devolver el Id
*
* @param CategoryData $categoryData
* @return int
* @throws SPException
*/
protected function addCategory(CategoryData $categoryData)
{
return $this->categoryService->create($categoryData);
}
/**
* Añadir un cliente y devolver el Id
*
* @param ClientData $clientData
* @return int
* @throws SPException
*/
protected function addClient(ClientData $clientData)
{
return $this->clientService->create($clientData);
}
/**
* Añadir una etiqueta y devolver el Id
*
* @param TagData $tagData
* @return int
* @throws SPException
*/
protected function addTag(TagData $tagData)
{
return $this->tagService->create($tagData);
}
}

View File

@@ -22,11 +22,11 @@
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
namespace SP\Services\Import;
use DOMElement;
use DOMXPath;
use SP\DataModel\AccountExtData;
use SP\Account\AccountRequest;
use SP\DataModel\CategoryData;
use SP\DataModel\ClientData;
@@ -35,53 +35,51 @@ defined('APP_ROOT') || die();
/**
* Esta clase es la encargada de importar cuentas desde KeePass
*/
class KeepassImport extends ImportBase
class KeepassImport extends XmlImportBase implements ImportInterface
{
use XmlImportTrait;
/**
* @var int
*/
protected $customerId = 0;
/**
* Iniciar la importación desde KeePass
*
* @throws \SP\Core\Exceptions\SPException
* @throws \SP\Core\Exceptions\InvalidClassException
* @return ImportInterface
*/
public function doImport()
{
$customerData = new ClientData(null, 'KeePass');
$this->addCustomer($customerData);
$this->customerId = $customerData->getId();
$this->process();
return $this;
}
/**
* Obtener los grupos y procesar lan entradas de KeePass.
*
* @throws \SP\Core\Exceptions\SPException
*/
protected function process()
{
$clientId = $this->addClient(new ClientData(null, 'KeePass'));
foreach ($this->getItems() as $group => $entry) {
$CategoryData = new CategoryData(null, $group);
$this->addCategory($CategoryData);
try {
$categoryData = new CategoryData(null, $group);
$this->addCategory($categoryData);
if (count($entry) > 0) {
foreach ($entry as $account) {
$AccountData = new AccountExtData();
$AccountData->setNotes($account['Notes']);
$AccountData->setPass($account['Password']);
$AccountData->setName($account['Title']);
$AccountData->setUrl($account['URL']);
$AccountData->setLogin($account['UserName']);
$AccountData->setCategoryId($CategoryData->getId());
$AccountData->setClientId($this->customerId);
if (count($entry) > 0) {
foreach ($entry as $account) {
$accountRequest = new AccountRequest();
$accountRequest->notes = $account['Notes'];
$accountRequest->pass = $account['Password'];
$accountRequest->name = $account['Title'];
$accountRequest->url = $account['URL'];
$accountRequest->login = $account['UserName'];
$accountRequest->categoryId = $categoryData->getId();
$accountRequest->clientId = $clientId;
$this->addAccount($AccountData);
$this->addAccount($accountRequest);
}
}
} catch (\Exception $e) {
processException($e);
}
}
}
@@ -94,11 +92,11 @@ class KeepassImport extends ImportBase
protected function getItems()
{
$DomXpath = new DOMXPath($this->xmlDOM);
$Tags = $DomXpath->query('/KeePassFile/Root/Group//Group|/KeePassFile/Root/Group//Entry');
$tags = $DomXpath->query('/KeePassFile/Root/Group//Group|/KeePassFile/Root/Group//Entry');
$items = [];
/** @var DOMElement[] $Tags */
foreach ($Tags as $tag) {
/** @var DOMElement[] $tags */
foreach ($tags as $tag) {
if ($tag->nodeType === 1) {
if ($tag->nodeName === 'Entry') {
$path = $tag->getNodePath();

View File

@@ -2,8 +2,8 @@
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
@@ -22,10 +22,10 @@
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
namespace SP\Services\Import;
use SimpleXMLElement;
use SP\DataModel\AccountExtData;
use SP\Account\AccountRequest;
use SP\DataModel\CategoryData;
use SP\DataModel\ClientData;
@@ -33,31 +33,30 @@ defined('APP_ROOT') || die();
/**
* Esta clase es la encargada de importar cuentas desde KeePassX
*
* @todo Use xmlDOM
*/
class KeepassXImport extends ImportBase
class KeepassXImport extends XmlImportBase implements ImportInterface
{
use XmlImportTrait;
/**
* @var int
*/
protected $customerId = 0;
protected $clientId;
/**
* Iniciar la importación desde KeePassX.
*
* @throws \SP\Core\Exceptions\SPException
* @return bool
* @throws \SP\Core\Exceptions\InvalidClassException
* @return ImportInterface
*/
public function doImport()
{
$customerData = new ClientData(null, 'KeePassX');
$this->addCustomer($customerData);
$this->clientId = $this->addClient(new ClientData(null, 'KeePassX'));
$this->customerId = $customerData->getId();
$this->processCategories($this->xmlDOM);
$this->processCategories($this->xml);
return $this;
}
/**
@@ -75,11 +74,8 @@ class KeepassXImport extends ImportBase
// Analizar grupo
if ($node->group->entry) {
// Crear la categoría
$CategoryData = new CategoryData(null, $group->title, 'KeePassX');
$this->addCategory($CategoryData);
// Crear cuentas
$this->processAccounts($group->entry, $CategoryData->getId());
$this->processAccounts($group->entry, $this->addCategory(new CategoryData(null, $group->title, 'KeePassX')));
}
if ($group->group) {
@@ -90,11 +86,8 @@ class KeepassXImport extends ImportBase
}
if ($node->entry) {
$CategoryData = new CategoryData(null, $node->title, 'KeePassX');
$this->addCategory($CategoryData);
// Crear cuentas
$this->processAccounts($node->entry, $CategoryData->getId());
$this->processAccounts($node->entry, $this->addCategory(new CategoryData(null, $node->title, 'KeePassX')));
}
}
}
@@ -102,9 +95,8 @@ class KeepassXImport extends ImportBase
/**
* Obtener los datos de las entradas de KeePass.
*
* @param SimpleXMLElement $entries El objeto XML con las entradas
* @param int $categoryId Id de la categoría
* @throws \SP\Core\Exceptions\SPException
* @param SimpleXMLElement $entries El objeto XML con las entradas
* @param int $categoryId Id de la categoría
*/
protected function processAccounts(SimpleXMLElement $entries, $categoryId)
{
@@ -115,16 +107,20 @@ class KeepassXImport extends ImportBase
$notes = isset($entry->comment) ? (string)$entry->comment : '';
$username = isset($entry->username) ? (string)$entry->username : '';
$AccountData = new AccountExtData();
$AccountData->setPass($password);
$AccountData->setNotes($notes);
$AccountData->setName($name);
$AccountData->setUrl($url);
$AccountData->setLogin($username);
$AccountData->setClientId($this->customerId);
$AccountData->setCategoryId($categoryId);
$accountRequest = new AccountRequest();
$accountRequest->pass = $password;
$accountRequest->notes = $notes;
$accountRequest->name = $name;
$accountRequest->url = $url;
$accountRequest->login = $username;
$accountRequest->clientId = $this->clientId;
$accountRequest->categoryId = $categoryId;
$this->addAccount($AccountData);
try {
$this->addAccount($accountRequest);
} catch (\Exception $e) {
processException($e);
}
}
}
}

View File

@@ -0,0 +1,375 @@
<?php
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Services\Import;
use Defuse\Crypto\Exception\CryptoException;
use DOMXPath;
use SP\Account\AccountRequest;
use SP\Config\ConfigDB;
use SP\Core\Crypt\Crypt;
use SP\Core\Crypt\Hash;
use SP\Core\OldCrypt;
use SP\DataModel\CategoryData;
use SP\DataModel\ClientData;
use SP\DataModel\TagData;
defined('APP_ROOT') || die();
/**
* Esta clase es la encargada de importar cuentas desde sysPass
*/
class SyspassImport extends XmlImportBase implements ImportInterface
{
/**
* Mapeo de etiquetas
*
* @var array
*/
protected $tags = [];
/**
* Mapeo de categorías.
*
* @var array
*/
protected $categories = [];
/**
* Mapeo de clientes.
*
* @var array
*/
protected $clients = [];
/**
* Iniciar la importación desde sysPass.
*
* @throws ImportException
* @return ImportInterface
*/
public function doImport()
{
try {
if ($this->importParams->getImportMasterPwd() !== '') {
$this->mPassValidHash = Hash::checkHashKey($this->importParams->getImportMasterPwd(), ConfigDB::getValue('masterPwd'));
}
$this->version = $this->getXmlVersion();
if ($this->detectEncrypted()) {
if ($this->importParams->getImportPwd() === '') {
throw new ImportException(__u('Clave de encriptación no indicada'), ImportException::INFO);
}
$this->processEncrypted();
}
$this->processCategories();
if ($this->version >= 300) {
$this->processClients();
} else {
$this->processCustomers();
}
$this->processTags();
$this->processAccounts();
return $this;
} catch (ImportException $e) {
throw $e;
} catch (\Exception $e) {
throw new ImportException($e->getMessage(), ImportException::CRITICAL);
}
}
/**
* Obtener la versión del XML
*/
protected function getXmlVersion()
{
return (int)str_replace('.', '', (new DOMXPath($this->xmlDOM))->query('/Root/Meta/Version')->item(0)->nodeValue);
}
/**
* Verificar si existen datos encriptados
*
* @return bool
*/
protected function detectEncrypted()
{
return ($this->xmlDOM->getElementsByTagName('Encrypted')->length > 0);
}
/**
* Procesar los datos encriptados y añadirlos al árbol DOM desencriptados
*
* @throws ImportException
*/
protected function processEncrypted()
{
$hash = $this->xmlDOM->getElementsByTagName('Encrypted')->item(0)->getAttribute('hash');
if ($hash !== '' && !Hash::checkHashKey($this->importParams->getImportPwd(), $hash)) {
throw new ImportException(__u('Clave de encriptación incorrecta'));
}
foreach ($this->xmlDOM->getElementsByTagName('Data') as $node) {
/** @var $node \DOMElement */
$data = base64_decode($node->nodeValue);
try {
if ($this->version >= 210) {
$securedKey = Crypt::unlockSecuredKey($node->getAttribute('key'), $this->importParams->getImportPwd());
$xmlDecrypted = Crypt::decrypt($data, $securedKey, $this->importParams->getImportPwd());
} else {
$xmlDecrypted = OldCrypt::getDecrypt($data, base64_decode($node->getAttribute('iv'), $this->importParams->getImportPwd()));
}
} catch (CryptoException $e) {
processException($e);
continue;
}
$newXmlData = new \DOMDocument();
// $newXmlData->preserveWhiteSpace = true;
if (!$newXmlData->loadXML($xmlDecrypted)) {
throw new ImportException(__u('Clave de encriptación incorrecta'));
}
$newNode = $this->xmlDOM->importNode($newXmlData->documentElement, TRUE);
$this->xmlDOM->documentElement->appendChild($newNode);
}
// Eliminar los datos encriptados tras desencriptar los mismos
if ($this->xmlDOM->getElementsByTagName('Data')->length > 0) {
$nodeData = $this->xmlDOM->getElementsByTagName('Encrypted')->item(0);
$nodeData->parentNode->removeChild($nodeData);
}
}
/**
* Obtener las categorías y añadirlas a sysPass.
*
* @throws ImportException
*/
protected function processCategories()
{
$this->getNodesData('Categories', 'Category',
function (\DOMElement $category) {
$categoryData = new CategoryData();
foreach ($category->childNodes as $node) {
if (isset($node->tagName)) {
switch ($node->tagName) {
case 'name':
$categoryData->setName($node->nodeValue);
break;
case 'description':
$categoryData->setDescription($node->nodeValue);
break;
}
}
}
try {
$this->categories[$category->getAttribute('id')] = $this->addCategory($categoryData);
} catch (\Exception $e) {
processException($e);
}
});
}
/**
* Obtener los clientes y añadirlos a sysPass.
*
* @throws ImportException
*/
protected function processClients()
{
$this->getNodesData('Clients', 'Client',
function (\DOMElement $client) {
$clientData = new ClientData();
foreach ($client->childNodes as $node) {
if (isset($node->tagName)) {
switch ($node->tagName) {
case 'name':
$clientData->setName($node->nodeValue);
break;
case 'description':
$clientData->setDescription($node->nodeValue);
break;
}
}
}
try {
$this->clients[$client->getAttribute('id')] = $this->addClient($clientData);
} catch (\Exception $e) {
processException($e);
}
});
}
/**
* Obtener los clientes y añadirlos a sysPass.
*
* @throws ImportException
* @deprecated
*/
protected function processCustomers()
{
$this->getNodesData('Customers', 'Customer',
function (\DOMElement $client) {
$clientData = new ClientData();
foreach ($client->childNodes as $node) {
if (isset($node->tagName)) {
switch ($node->tagName) {
case 'name':
$clientData->setName($node->nodeValue);
break;
case 'description':
$clientData->setDescription($node->nodeValue);
break;
}
}
}
try {
$this->clients[$client->getAttribute('id')] = $this->addClient($clientData);
} catch (\Exception $e) {
processException($e);
}
});
}
/**
* Obtener las etiquetas y añadirlas a sysPass.
*
* @throws ImportException
*/
protected function processTags()
{
$this->getNodesData('Tags', 'Tag',
function (\DOMElement $tag) {
$tagData = new TagData();
foreach ($tag->childNodes as $node) {
if (isset($node->tagName)) {
switch ($node->tagName) {
case 'name':
$tagData->setName($node->nodeValue);
break;
}
}
}
try {
$this->tags[$tag->getAttribute('id')] = $this->addTag($tagData);
} catch (\Exception $e) {
processException($e);
}
}, false);
}
/**
* Obtener los datos de las cuentas de sysPass y crearlas.
*
* @throws ImportException
*/
protected function processAccounts()
{
$this->getNodesData('Accounts', 'Account',
function (\DOMElement $account) {
$accountRequest = new AccountRequest();
/** @var \DOMElement $node */
foreach ($account->childNodes as $node) {
if (isset($node->tagName)) {
switch ($node->tagName) {
case 'name';
$accountRequest->name = $node->nodeValue;
break;
case 'login';
$accountRequest->login = $node->nodeValue;
break;
case 'categoryId';
$accountRequest->categoryId = $this->categories[(int)$node->nodeValue];
break;
case 'clientId';
case 'customerId';
$accountRequest->clientId = $this->clients[(int)$node->nodeValue];
break;
case 'url';
$accountRequest->url = $node->nodeValue;
break;
case 'pass';
$accountRequest->pass = $node->nodeValue;
break;
case 'key';
$accountRequest->key = $node->nodeValue;
break;
case 'notes';
$accountRequest->notes = $node->nodeValue;
break;
case 'tags':
$accountRequest->tags = $this->processAccountTags($node->childNodes);
}
}
}
try {
$this->addAccount($accountRequest);
} catch (\Exception $e) {
processException($e);
}
});
}
/**
* Procesar las etiquetas de la cuenta
*
* @param \DOMNodeList $nodes
* @return array
*/
protected function processAccountTags(\DOMNodeList $nodes)
{
$tags = [];
if ($nodes->length > 0) {
/** @var \DOMElement $node */
foreach ($nodes as $node) {
if (isset($node->tagName)) {
$tags[] = $node->getAttribute('id');
}
}
}
return $tags;
}
}

View File

@@ -22,9 +22,7 @@
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
use SP\Core\Exceptions\SPException;
namespace SP\Services\Import;
/**
* Class XmlFileImport
@@ -36,7 +34,7 @@ class XmlFileImport
/**
* @var FileImport
*/
protected $FileImport;
protected $fileImport;
/**
* @var \DOMDocument
*/
@@ -45,17 +43,17 @@ class XmlFileImport
/**
* XmlFileImport constructor.
*
* @param FileImport $FileImport
* @param FileImport $fileImport
*/
public function __construct(FileImport $FileImport)
public function __construct(FileImport $fileImport)
{
$this->FileImport = $FileImport;
$this->fileImport = $fileImport;
}
/**
* Detectar la aplicación que generó el XML.
*
* @throws SPException
* @throws ImportException
*/
public function detectXMLFormat()
{
@@ -80,8 +78,10 @@ class XmlFileImport
break;
}
} else {
throw new SPException(
__('Archivo XML no soportado', false), SPException::CRITICAL, __('No es posible detectar la aplicación que exportó los datos', false)
throw new ImportException(
__u('Archivo XML no soportado'),
ImportException::ERROR,
__u('No es posible detectar la aplicación que exportó los datos')
);
}
@@ -91,16 +91,18 @@ class XmlFileImport
/**
* Leer el archivo a un objeto XML.
*
* @throws SPException
* @throws ImportException
*/
protected function readXMLFile()
{
// Cargar el XML con DOM
$this->xmlDOM = new \DOMDocument();
if ($this->xmlDOM->load($this->FileImport->getTmpFile()) === false) {
throw new SPException(
__('Error interno', false), SPException::CRITICAL, __('No es posible procesar el archivo XML', false)
if ($this->xmlDOM->load($this->fileImport->getTmpFile()) === false) {
throw new ImportException(
__u('Error interno'),
ImportException::ERROR,
__u('No es posible procesar el archivo XML')
);
}
}
@@ -112,7 +114,7 @@ class XmlFileImport
*/
protected function parseFileHeader()
{
$handle = @fopen($this->FileImport->getTmpFile(), 'r');
$handle = @fopen($this->fileImport->getTmpFile(), 'r');
$headersRegex = '/(KEEPASSX_DATABASE|revelationdata)/i';
if ($handle) {

View File

@@ -22,9 +22,7 @@
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
use SP\Core\Messages\LogMessage;
namespace SP\Services\Import;
defined('APP_ROOT') || die();
@@ -39,75 +37,66 @@ class XmlImport implements ImportInterface
/**
* @var FileImport
*/
protected $File;
protected $xmlFileImport;
/**
* @var ImportParams
*/
protected $ImportParams;
/**
* @var LogMessage
*/
protected $LogMessage;
/**
* @var ImportBase
*/
protected $Import;
protected $importParams;
/**
* XmlImport constructor.
*
* @param FileImport $File
* @param ImportParams $ImportParams
* @param LogMessage $LogMessage
* @param XmlFileImport $xmlFileImport
* @param ImportParams $importParams
*/
public function __construct(FileImport $File, ImportParams $ImportParams, LogMessage $LogMessage)
public function __construct(XmlFileImport $xmlFileImport, ImportParams $importParams)
{
$this->File = $File;
$this->ImportParams = $ImportParams;
$this->LogMessage = $LogMessage;
$this->xmlFileImport = $xmlFileImport;
$this->importParams = $importParams;
}
/**
* Iniciar la importación desde XML.
*
* @throws \SP\Core\Exceptions\SPException
* @throws ImportException
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
* @return ImportInterface
*/
public function doImport()
{
$XmlFileImport = new XmlFileImport($this->File);
$format = $this->xmlFileImport->detectXMLFormat();
$format = $XmlFileImport->detectXMLFormat();
switch ($format) {
case 'syspass':
$this->Import = new SyspassImport();
break;
case 'keepass':
$this->Import = new KeepassImport();
break;
case 'keepassx':
$this->Import = new KeepassXImport();
break;
default:
return;
}
$this->Import->setImportParams($this->ImportParams);
$this->Import->setXmlDOM($XmlFileImport->getXmlDOM());
$this->Import->setLogMessage($this->LogMessage);
$this->LogMessage->addDescription(sprintf(__('Formato detectado: %s'), mb_strtoupper($format)));
$this->Import->doImport();
// $this->LogMessage->addDescription(sprintf(__('Formato detectado: %s'), mb_strtoupper($format)));
return $this->selectImportType($format)->doImport();
}
/**
* Devolver el contador de objetos importados
*
* @return int
* @param $format
* @return KeepassImport|KeepassXImport|SyspassImport
* @throws ImportException
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*/
protected function selectImportType($format)
{
switch ($format) {
case 'syspass':
return new SyspassImport($this->xmlFileImport, $this->importParams);
case 'keepass':
return new KeepassImport($this->xmlFileImport, $this->importParams);
case 'keepassx':
return new KeepassXImport($this->xmlFileImport, $this->importParams);
}
throw new ImportException(__u('Formato no detectado'));
}
/**
* @throws ImportException
*/
public function getCounter()
{
return $this->Import->getCounter();
throw new ImportException(__u('Not implemented'));
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Services\Import;
use SP\Bootstrap;
use SP\Services\Account\AccountService;
use SP\Services\Category\CategoryService;
use SP\Services\Client\ClientService;
use SP\Services\Tag\TagService;
/**
* Class XmlImportBase
*
* @package SP\Services\Import
*/
abstract class XmlImportBase
{
use ImportTrait;
/**
* @var XmlFileImport
*/
protected $xmlFileImport;
/**
* @var \DOMDocument
*/
protected $xmlDOM;
/**
* ImportBase constructor.
*
* @param XmlFileImport $xmlFileImport
* @param ImportParams $importParams
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*/
public function __construct(XmlFileImport $xmlFileImport, ImportParams $importParams)
{
$this->xmlFileImport = $xmlFileImport;
$this->importParams = $importParams;
$this->xmlDOM = $xmlFileImport->getXmlDOM();
$dic = Bootstrap::getContainer();
$this->accountService = $dic->get(AccountService::class);
$this->categoryService = $dic->get(CategoryService::class);
$this->clientService = $dic->get(ClientService::class);
$this->tagService = $dic->get(TagService::class);
}
/**
* Obtener los datos de los nodos
*
* @param string $nodeName Nombre del nodo principal
* @param string $childNodeName Nombre de los nodos hijos
* @param callable $callback Método a ejecutar
* @param bool $required Indica si el nodo es requerido
* @throws ImportException
*/
protected function getNodesData($nodeName, $childNodeName, $callback, $required = true)
{
$nodeList = $this->xmlDOM->getElementsByTagName($nodeName);
if ($nodeList->length === 0) {
if ($required === true) {
throw new ImportException(
__u('Formato de XML inválido'),
ImportException::WARNING,
sprintf(__('El nodo "%s" no existe'), $nodeName)
);
}
return;
}
if (!is_callable($callback)) {
throw new ImportException(__u('Método inválido'), ImportException::WARNING);
}
/** @var \DOMElement $nodes */
foreach ($nodeList as $nodes) {
/** @var \DOMElement $Account */
foreach ($nodes->getElementsByTagName($childNodeName) as $node) {
$callback($node);
}
}
}
}

View File

@@ -22,7 +22,7 @@
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
namespace SP\Services\Import;
use SP\Core\Exceptions\SPException;
@@ -55,7 +55,7 @@ trait XmlImportTrait
* @param string $childNodeName Nombre de los nodos hijos
* @param string $callback Método a ejecutar
* @param bool $required Indica si el nodo es requerido
* @throws SPException
* @throws ImportException
*/
protected function getNodesData($nodeName, $childNodeName, $callback, $required = true)
{
@@ -63,22 +63,25 @@ trait XmlImportTrait
if ($ParentNode->length === 0) {
if ($required === true) {
throw new SPException(
__('Formato de XML inválido', false), SPException::WARNING, sprintf(__('El nodo "%s" no existe'), $nodeName));
throw new ImportException(
__u('Formato de XML inválido'),
SPException::WARNING,
sprintf(__('El nodo "%s" no existe'), $nodeName)
);
}
return;
}
if (!is_callable([$this, $callback])) {
throw new SPException(__('Método inválido', false), SPException::WARNING);
throw new ImportException(__u('Método inválido'), SPException::WARNING);
}
/** @var \DOMElement $nodes */
foreach ($ParentNode as $nodes) {
/** @var \DOMElement $Account */
foreach ($nodes->getElementsByTagName($childNodeName) as $Node) {
$this->$callback($Node);
foreach ($nodes->getElementsByTagName($childNodeName) as $node) {
$this->$callback($node);
}
}
}

View File

@@ -188,8 +188,8 @@ class DbWrapper
* @param QueryData $queryData Los datos para realizar la consulta
* @param DatabaseInterface $db
* @return bool
* @throws ConstraintException
* @throws QueryException
* @throws ConstraintException
*/
public static function getQuery(QueryData $queryData, DatabaseInterface $db = null)
{
@@ -220,7 +220,13 @@ class DbWrapper
switch ($e->getCode()) {
case 23000:
throw new ConstraintException(__u('Restricción de integridad'), SPException::ERROR, $e->getMessage(), $e->getCode());
throw new ConstraintException(
__u('Restricción de integridad'),
SPException::ERROR,
$e->getMessage(),
$e->getCode(),
$e
);
}
throw new QueryException($errorMessage, SPException::ERROR, $e->getMessage(), $e->getCode());
@@ -240,6 +246,7 @@ class DbWrapper
*
* @param DatabaseInterface $db
* @return bool
* @throws SPException
*/
public static function beginTransaction(DatabaseInterface $db)
{
@@ -253,6 +260,7 @@ class DbWrapper
*
* @param DatabaseInterface $db
* @return bool
* @throws SPException
*/
public static function endTransaction(DatabaseInterface $db)
{
@@ -266,6 +274,7 @@ class DbWrapper
*
* @param DatabaseInterface $db
* @return bool
* @throws SPException
*/
public static function rollbackTransaction(DatabaseInterface $db)
{

View File

@@ -368,7 +368,8 @@ sysPass.Main = function () {
return requestData;
},
beforeSendAction: "",
url: ""
url: "",
allowedExts: []
};
// Subir un archivo
@@ -418,10 +419,8 @@ sysPass.Main = function () {
};
const checkFileExtension = function (name) {
const file_exts_ok = $obj.data("files-ext").toLowerCase().split(",");
for (let i = 0; i <= file_exts_ok.length; i++) {
if (name.indexOf(file_exts_ok[i]) !== -1) {
for (let ext in options.allowedExts) {
if (name.indexOf(options.allowedExts[ext]) !== -1){
return true;
}
}
@@ -440,7 +439,7 @@ sysPass.Main = function () {
const file = filesArray[i];
if (checkFileSize(file.size)) {
msg.error(config.LANG[18] + "<br>" + file.name + " (Max: " + config.MAX_FILE_SIZE + ")");
} else if (!checkFileExtension(file.name)) {
} else if (!checkFileExtension(file.name.toUpperCase())) {
msg.error(config.LANG[19] + "<br>" + file.name);
} else {
sendFile(filesArray[i]);
@@ -557,7 +556,9 @@ sysPass.Main = function () {
COOKIES_ENABLED: false,
PLUGINS: [],
LOGGEDIN: false,
AUTHBASIC_AUTOLOGIN: false
AUTHBASIC_AUTOLOGIN: false,
FILES_ALLOWED_EXTS: "",
IMPORT_ALLOWED_EXTS: []
};
// Atributos del generador de claves
@@ -791,6 +792,8 @@ sysPass.Main = function () {
config.PLUGINS = json.plugins;
config.LOGGEDIN = json.loggedin;
config.AUTHBASIC_AUTOLOGIN = json.authbasic_autologin;
config.IMPORT_ALLOWED_EXTS = json.import_allowed_exts;
config.FILES_ALLOWED_EXTS = json.files_allowed_exts;
Object.freeze(config);
});

View File

@@ -1,25 +1,25 @@
var $jscomp={scope:{},findInternal:function(b,k,l){b instanceof String&&(b=String(b));for(var m=b.length,n=0;n<m;n++){var w=b[n];if(k.call(l,w,n,b))return{i:n,v:w}}return{i:-1,v:void 0}}};$jscomp.defineProperty="function"==typeof Object.defineProperties?Object.defineProperty:function(b,k,l){if(l.get||l.set)throw new TypeError("ES3 does not support getters and setters.");b!=Array.prototype&&b!=Object.prototype&&(b[k]=l.value)};
$jscomp.getGlobal=function(b){return"undefined"!=typeof window&&window===b?b:"undefined"!=typeof global&&null!=global?global:b};$jscomp.global=$jscomp.getGlobal(this);$jscomp.polyfill=function(b,k,l,m){if(k){l=$jscomp.global;b=b.split(".");for(m=0;m<b.length-1;m++){var n=b[m];n in l||(l[n]={});l=l[n]}b=b[b.length-1];m=l[b];k=k(m);k!=m&&null!=k&&$jscomp.defineProperty(l,b,{configurable:!0,writable:!0,value:k})}};
$jscomp.polyfill("Array.prototype.find",function(b){return b?b:function(b,l){return $jscomp.findInternal(this,b,l).v}},"es6-impl","es3");$jscomp.SYMBOL_PREFIX="jscomp_symbol_";$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.symbolCounter_=0;$jscomp.Symbol=function(b){return $jscomp.SYMBOL_PREFIX+(b||"")+$jscomp.symbolCounter_++};
$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var b=$jscomp.global.Symbol.iterator;b||(b=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[b]&&$jscomp.defineProperty(Array.prototype,b,{configurable:!0,writable:!0,value:function(){return $jscomp.arrayIterator(this)}});$jscomp.initSymbolIterator=function(){}};$jscomp.arrayIterator=function(b){var k=0;return $jscomp.iteratorPrototype(function(){return k<b.length?{done:!1,value:b[k++]}:{done:!0}})};
$jscomp.iteratorPrototype=function(b){$jscomp.initSymbolIterator();b={next:b};b[$jscomp.global.Symbol.iterator]=function(){return this};return b};$jscomp.array=$jscomp.array||{};$jscomp.iteratorFromArray=function(b,k){$jscomp.initSymbolIterator();b instanceof String&&(b+="");var l=0,m={next:function(){if(l<b.length){var n=l++;return{value:k(n,b[n]),done:!1}}m.next=function(){return{done:!0,value:void 0}};return m.next()}};m[Symbol.iterator]=function(){return m};return m};
var $jscomp={scope:{},findInternal:function(b,l,h){b instanceof String&&(b=String(b));for(var m=b.length,n=0;n<m;n++){var w=b[n];if(l.call(h,w,n,b))return{i:n,v:w}}return{i:-1,v:void 0}}};$jscomp.defineProperty="function"==typeof Object.defineProperties?Object.defineProperty:function(b,l,h){if(h.get||h.set)throw new TypeError("ES3 does not support getters and setters.");b!=Array.prototype&&b!=Object.prototype&&(b[l]=h.value)};
$jscomp.getGlobal=function(b){return"undefined"!=typeof window&&window===b?b:"undefined"!=typeof global?global:b};$jscomp.global=$jscomp.getGlobal(this);$jscomp.polyfill=function(b,l,h,m){if(l){h=$jscomp.global;b=b.split(".");for(m=0;m<b.length-1;m++){var n=b[m];n in h||(h[n]={});h=h[n]}b=b[b.length-1];m=h[b];l=l(m);l!=m&&null!=l&&$jscomp.defineProperty(h,b,{configurable:!0,writable:!0,value:l})}};
$jscomp.polyfill("Array.prototype.find",function(b){return b?b:function(b,h){return $jscomp.findInternal(this,b,h).v}},"es6-impl","es3");$jscomp.SYMBOL_PREFIX="jscomp_symbol_";$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.symbolCounter_=0;$jscomp.Symbol=function(b){return $jscomp.SYMBOL_PREFIX+(b||"")+$jscomp.symbolCounter_++};
$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var b=$jscomp.global.Symbol.iterator;b||(b=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[b]&&$jscomp.defineProperty(Array.prototype,b,{configurable:!0,writable:!0,value:function(){return $jscomp.arrayIterator(this)}});$jscomp.initSymbolIterator=function(){}};$jscomp.arrayIterator=function(b){var l=0;return $jscomp.iteratorPrototype(function(){return l<b.length?{done:!1,value:b[l++]}:{done:!0}})};
$jscomp.iteratorPrototype=function(b){$jscomp.initSymbolIterator();b={next:b};b[$jscomp.global.Symbol.iterator]=function(){return this};return b};$jscomp.array=$jscomp.array||{};$jscomp.iteratorFromArray=function(b,l){$jscomp.initSymbolIterator();b instanceof String&&(b+="");var h=0,m={next:function(){if(h<b.length){var n=h++;return{value:l(n,b[n]),done:!1}}m.next=function(){return{done:!0,value:void 0}};return m.next()}};m[Symbol.iterator]=function(){return m};return m};
$jscomp.polyfill("Array.prototype.keys",function(b){return b?b:function(){return $jscomp.iteratorFromArray(this,function(b){return b})}},"es6-impl","es3");
sysPass.Main=function(){var b=function(){f.info("checkPluginUpdates");for(var a in t)"function"===typeof t[a].checkVersion&&t[a].checkVersion().then(function(a){0===a.status&&void 0!==a.data.plugin&&e.info(String.format(c.LANG[67],a.data.plugin,a.data.remoteVersion))})},k=function(){var a=document.createElement("div");return function(g){g&&"string"===typeof g&&(g=g.replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi,""),g=g.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi,""),a.innerHTML=g,g=a.textContent,
a.textContent="");return g}}(),l=function(a){f.info("resizeImage");var g=.9*$(window).width(),d=.9*$(window).height(),b={width:a.width(),height:a.height()},c={calc:0,main:0,secondary:0,factor:.9,rel:b.width/b.height},e=function(a){a.main>a.secondary?a.calc=a.main/a.rel:a.main<a.secondary&&(a.calc=a.main*a.rel);a.calc>a.secondary&&(a.main*=a.factor,e(a));return a},D=function(){c.main=g;c.secondary=d;var h=e(c);a.css({width:h.main,height:h.calc});b.width=h.main;b.height=h.calc},E=function(){c.main=
d;c.secondary=g;var h=e(c);a.css({width:h.calc,height:h.main});b.width=h.calc;b.height=h.main};b.width>g?D():b.height>d&&(f.info("height"),E());return b},m=function(a,b){f.info("Eval: "+a);if("function"===typeof a)a(b);else throw Error("Function not found: "+a);},n=function(){f.info("bindPassEncrypt");$("body").on("blur",":input[type=password]",function(a){a=$(this);a.hasClass("passwordfield__no-pki")||z(a)}).on("keypress",":input[type=password]",function(a){13===a.keyCode&&(a.preventDefault(),a=
sysPass.Main=function(){var b=function(){f.info("checkPluginUpdates");for(var a in t)"function"===typeof t[a].checkVersion&&t[a].checkVersion().then(function(a){0===a.status&&void 0!==a.data.plugin&&e.info(String.format(c.LANG[67],a.data.plugin,a.data.remoteVersion))})},l=function(){var a=document.createElement("div");return function(g){g&&"string"===typeof g&&(g=g.replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi,""),g=g.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi,""),a.innerHTML=g,g=a.textContent,
a.textContent="");return g}}(),h=function(a){f.info("resizeImage");var g=.9*$(window).width(),d=.9*$(window).height(),b={width:a.width(),height:a.height()},c={calc:0,main:0,secondary:0,factor:.9,rel:b.width/b.height},e=function(a){a.main>a.secondary?a.calc=a.main/a.rel:a.main<a.secondary&&(a.calc=a.main*a.rel);a.calc>a.secondary&&(a.main*=a.factor,e(a));return a},D=function(){c.main=g;c.secondary=d;var k=e(c);a.css({width:k.main,height:k.calc});b.width=k.main;b.height=k.calc},E=function(){c.main=
d;c.secondary=g;var k=e(c);a.css({width:k.calc,height:k.main});b.width=k.calc;b.height=k.main};b.width>g?D():b.height>d&&(f.info("height"),E());return b},m=function(a,b){f.info("Eval: "+a);if("function"===typeof a)a(b);else throw Error("Function not found: "+a);},n=function(){f.info("bindPassEncrypt");$("body").on("blur",":input[type=password]",function(a){a=$(this);a.hasClass("passwordfield__no-pki")||z(a)}).on("keypress",":input[type=password]",function(a){13===a.keyCode&&(a.preventDefault(),a=
$(this),z(a),a.closest("form").submit())})},w=function(){f.info("initializeClipboard");if(clipboard.isSupported())$("body").on("click",".clip-pass-button",function(){var a=p.account.copyPass($(this)).done(function(a){if(0!==a.status)return e.out(a),!1;x.set(a.csrf)});!1!==a&&clipboard.copy(a.responseJSON.data.accpass).then(function(){e.ok(c.LANG[45])},function(a){e.error(c.LANG[46])})}).on("click",".dialog-clip-button",function(){var a=$(this.dataset.clipboardTarget);clipboard.copy(a.text()).then(function(){$(".dialog-text").removeClass("dialog-clip-copy");
a.addClass("dialog-clip-copy")},function(a){e.error(c.LANG[46])})}).on("click",".clip-pass-icon",function(){var a=$(this.dataset.clipboardTarget);clipboard.copy(k(a.val())).then(function(){e.ok(c.LANG[45])},function(a){e.error(c.LANG[46])})});else f.warn(c.LANG[65])},z=function(a){f.info("encryptFormValue");var b=a.val();""!==b&&parseInt(a.attr("data-length"))!==b.length&&(b=c.CRYPT.encrypt(b),a.val(b),a.attr("data-length",b.length))},C=function(a,b){f.info("outputResult");var d=$(".passLevel-"+b.attr("id")),
a.addClass("dialog-clip-copy")},function(a){e.error(c.LANG[46])})}).on("click",".clip-pass-icon",function(){var a=$(this.dataset.clipboardTarget);clipboard.copy(l(a.val())).then(function(){e.ok(c.LANG[45])},function(a){e.error(c.LANG[46])})});else f.warn(c.LANG[65])},z=function(a){f.info("encryptFormValue");var b=a.val();""!==b&&parseInt(a.attr("data-length"))!==b.length&&(b=c.CRYPT.encrypt(b),a.val(b),a.attr("data-length",b.length))},C=function(a,b){f.info("outputResult");var d=$(".passLevel-"+b.attr("id")),
g=a.score;d.show();d.removeClass("weak good strong strongest");0===u.passLength?d.attr("title","").empty():u.passLength<u.minPasswordLength?d.attr("title",c.LANG[11]).addClass("weak"):0===g?d.attr("title",c.LANG[9]+" - "+a.feedback.warning).addClass("weak"):1===g||2===g?d.attr("title",c.LANG[8]+" - "+a.feedback.warning).addClass("good"):3===g?d.attr("title",c.LANG[7]).addClass("strong"):4===g&&d.attr("title",c.LANG[10]).addClass("strongest")},F=function(a){f.info("checkPassLevel");u.passLength=a.val().length;
C(zxcvbn(a.val()),a)},G=function(a){var b=function(a){var b=$("#fileUploadForm");!1===a&&b.hide();a=b.find("input[type='file']");a.on("change",function(){"function"===typeof h.beforeSendAction&&h.beforeSendAction();k(this.files)});return a},d={actionId:a.data("action-id"),itemId:a.data("item-id"),sk:x.get()},h={requestDoneAction:"",setRequestData:function(a){$.extend(d,a)},getRequestData:function(){return d},beforeSendAction:"",url:""},l=function(a){if(void 0===h.url||""===h.url)return!1;var b=new FormData;
b.append("inFile",a);b.append("isAjax",1);d.sk=x.get();Object.keys(d).forEach(function(a){b.append(a,d[a])});a=v.getRequestOpts();a.url=h.url;a.processData=!1;a.contentType=!1;a.data=b;v.getActionCall(a,function(a){var b=a.status;a=a.description;0===b?("function"===typeof h.requestDoneAction&&h.requestDoneAction(),e.ok(a)):10===b?p.main.logout():e.error(a)})},k=function(b){if(5<b.length)e.error(c.LANG[17]+" (Max: 5)");else for(var d=0;d<b.length;d++){var g=b[d];if(g.size/1E3>c.MAX_FILE_SIZE)e.error(c.LANG[18]+
"<br>"+g.name+" (Max: "+c.MAX_FILE_SIZE+")");else{var h;a:{h=g.name;for(var f=a.data("files-ext").toLowerCase().split(","),k=0;k<=f.length;k++)if(-1!==h.indexOf(f[k])){h=!0;break a}h=!1}h?l(b[d]):e.error(c.LANG[19]+"<br>"+g.name)}}};window.File&&window.FileList&&window.FileReader?function(){f.info("fileUpload:init");var d=b(!1);a.on("dragover dragenter",function(a){f.info("fileUpload:drag");a.stopPropagation();a.preventDefault()});a.on("drop",function(a){f.info("fileUpload:drop");a.stopPropagation();
a.preventDefault();"function"===typeof h.beforeSendAction&&h.beforeSendAction();k(a.originalEvent.dataTransfer.files)});a.on("click",function(){d.click()})}():b(!0);return h},A=function(a){window.location.replace(a)},I=function(){f.info("checkLogout");return"login/logout"===parseInt(H("r"))?(e.sticky(c.LANG[61],function(){A("index.php?r=login")}),!0):!1},J=function(){$("html, body").animate({scrollTop:0},"slow")},K=function(){var a=$("#container");a.hasClass("content-no-auto-resize")||a.css("height",
$("#content").height()+200)},x={get:function(){f.info("sk:get");return $("#container").attr("data-sk")},set:function(a){f.info("sk:set");f.debug(a);$("#container").attr("data-sk",a)}},c={APP_ROOT:"",LANG:[],PK:"",MAX_FILE_SIZE:1024,CRYPT:new JSEncrypt,CHECK_UPDATES:!1,TIMEZONE:"",LOCALE:"",DEBUG:"",COOKIES_ENABLED:!1,PLUGINS:[],LOGGEDIN:!1,AUTHBASIC_AUTOLOGIN:!1},u={passLength:0,minPasswordLength:8,complexity:{chars:!0,numbers:!0,symbols:!0,uppercase:!0,numlength:12}};Object.seal(u);var B={},q={},
p={},v={},t={},y={},r={},f={log:function(a){!0===c.DEBUG&&console.log(a)},info:function(a){!0===c.DEBUG&&console.info(a)},error:function(a){console.error(a)},warn:function(a){console.warn(a)},debug:function(a){!0===c.DEBUG&&console.debug(a)}};Object.freeze(f);toastr.options={closeButton:!0,debug:!1,newestOnTop:!1,progressBar:!1,positionClass:"toast-top-center",preventDuplicates:!1,onclick:null,showDuration:"300",hideDuration:"1000",timeOut:"5000",extendedTimeOut:"1000",showEasing:"swing",hideEasing:"linear",
showMethod:"fadeIn",hideMethod:"fadeOut"};var L=function(){f.info("setupCallbacks");var a=$("#container").data("page");if(""!==a&&"function"===typeof q.views[a])q.views[a]();0<$("footer").length&&q.views.footer();$("#btnBack").click(function(){A("index.php")});q.bodyHooks()},e={ok:function(a){toastr.success(a)},error:function(a){toastr.error(a)},warn:function(a){toastr.warning(a)},info:function(a){toastr.info(a)},sticky:function(a,b){var d={timeOut:0};"function"===typeof b&&(d.onHidden=b);toastr.warning(a,
c.LANG[60],d)},out:function(a){if("object"===typeof a){var b=a.status,d=a.description;void 0!==a.messages&&0<a.messages.length&&(d=d+"<br>"+a.messages.join("<br>"));switch(b){case 0:e.ok(d);break;case 1:e.error(d);break;case 2:e.warn(d);break;case 10:p.main.logout();break;case 100:e.ok(d);e.sticky(d);break;case 101:e.error(d);e.sticky(d);break;case 102:e.warn(d);e.sticky(d);break;default:e.error(d)}}},html:{error:function(a){return'<p class="error round">Oops...<br>'+c.LANG[1]+"<br>"+a+"</p>"}}};
Object.freeze(e);String.format||(String.format=function(a){var b=Array.prototype.slice.call(arguments,1);return a.replace(/{(\d+)}/g,function(a,c){return"undefined"!==typeof b[c]?b[c]:a})});var M=function(){f.info("getEnvironment");var a=window.location.pathname.split("/");c.APP_ROOT=window.location.protocol+"//"+window.location.host+function(){for(var b="",c=1;c<=a.length-2;c++)b+="/"+a[c];return b}();var b=v.getRequestOpts();b.url="/index.php?r=bootstrap/getEnvironment";b.method="get";b.useLoading=
!1;b.data={isAjax:1};return v.getActionCall(b,function(a){c.LANG=a.lang;c.PK=a.pk;c.CHECK_UPDATES=a.check_updates;c.CRYPT.setPublicKey(a.pk);c.TIMEZONE=a.timezone;c.LOCALE=a.locale;c.DEBUG=a.debug;c.MAX_FILE_SIZE=parseInt(a.max_file_size);c.COOKIES_ENABLED=a.cookies_enabled;c.PLUGINS=a.plugins;c.LOGGEDIN=a.loggedin;c.AUTHBASIC_AUTOLOGIN=a.authbasic_autologin;Object.freeze(c)})},H=function(a){for(var b=[],c,e=window.location.href.slice(window.location.href.indexOf("?")+1).split("&"),f=0;f<e.length;f++)c=
e[f].split("="),b.push(c[0]),b[c[0]]=c[1];return void 0!==a&&void 0!==b[a]?b[a]:b};return function(){f.info("init");y={actions:function(){return p},triggers:function(){return q},theme:function(){return B},sk:x,msg:e,log:f,passwordData:u,outputResult:C,checkPassLevel:F,encryptFormValue:z,fileUpload:G,redirect:A,scrollUp:J,setContentSize:K};r=$.extend({log:f,config:function(){return c},appTheme:function(){return B},appActions:function(){return p},appTriggers:function(){return q},appRequests:function(){return v},
appPlugins:function(){return t},evalAction:m,resizeImage:l},y);Object.freeze(y);Object.freeze(r);q=sysPass.Triggers(r);p=sysPass.Actions(r);v=sysPass.Requests(r);"function"===typeof sysPass.Theme&&(B=sysPass.Theme(r));M().then(function(){if(!I()&&(""!==c.PK&&n(),!0===c.CHECK_UPDATES&&p.main.getUpdates(),!1===c.COOKIES_ENABLED&&e.sticky(c.LANG[64]),w(),L(),0<c.PLUGINS.length)){f.info(c.PLUGINS);f.info("initPlugins");for(var a=0;a<c.PLUGINS.length;a++){var g=c.PLUGINS[a];"function"===typeof sysPass.Plugin[g]&&
(t[g]=sysPass.Plugin[g](r))}Object.freeze(t);!0===c.LOGGEDIN&&!0===c.CHECK_UPDATES&&b()}});return y}()};
C(zxcvbn(a.val()),a)},G=function(a){var b=function(a){var b=$("#fileUploadForm");!1===a&&b.hide();a=b.find("input[type='file']");a.on("change",function(){"function"===typeof k.beforeSendAction&&k.beforeSendAction();l(this.files)});return a},d={actionId:a.data("action-id"),itemId:a.data("item-id"),sk:x.get()},k={requestDoneAction:"",setRequestData:function(a){$.extend(d,a)},getRequestData:function(){return d},beforeSendAction:"",url:"",allowedExts:[]},h=function(a){if(void 0===k.url||""===k.url)return!1;
var b=new FormData;b.append("inFile",a);b.append("isAjax",1);d.sk=x.get();Object.keys(d).forEach(function(a){b.append(a,d[a])});a=v.getRequestOpts();a.url=k.url;a.processData=!1;a.contentType=!1;a.data=b;v.getActionCall(a,function(a){var b=a.status;a=a.description;0===b?("function"===typeof k.requestDoneAction&&k.requestDoneAction(),e.ok(a)):10===b?p.main.logout():e.error(a)})},l=function(a){if(5<a.length)e.error(c.LANG[17]+" (Max: 5)");else for(var b=0;b<a.length;b++){var d=a[b];if(d.size/1E3>c.MAX_FILE_SIZE)e.error(c.LANG[18]+
"<br>"+d.name+" (Max: "+c.MAX_FILE_SIZE+")");else{var g;a:{g=d.name.toUpperCase();var f=void 0;for(f in k.allowedExts)if(-1!==g.indexOf(k.allowedExts[f])){g=!0;break a}g=!1}g?h(a[b]):e.error(c.LANG[19]+"<br>"+d.name)}}};window.File&&window.FileList&&window.FileReader?function(){f.info("fileUpload:init");var d=b(!1);a.on("dragover dragenter",function(a){f.info("fileUpload:drag");a.stopPropagation();a.preventDefault()});a.on("drop",function(a){f.info("fileUpload:drop");a.stopPropagation();a.preventDefault();
"function"===typeof k.beforeSendAction&&k.beforeSendAction();l(a.originalEvent.dataTransfer.files)});a.on("click",function(){d.click()})}():b(!0);return k},A=function(a){window.location.replace(a)},I=function(){f.info("checkLogout");return"login/logout"===parseInt(H("r"))?(e.sticky(c.LANG[61],function(){A("index.php?r=login")}),!0):!1},J=function(){$("html, body").animate({scrollTop:0},"slow")},K=function(){var a=$("#container");a.hasClass("content-no-auto-resize")||a.css("height",$("#content").height()+
200)},x={get:function(){f.info("sk:get");return $("#container").attr("data-sk")},set:function(a){f.info("sk:set");f.debug(a);$("#container").attr("data-sk",a)}},c={APP_ROOT:"",LANG:[],PK:"",MAX_FILE_SIZE:1024,CRYPT:new JSEncrypt,CHECK_UPDATES:!1,TIMEZONE:"",LOCALE:"",DEBUG:"",COOKIES_ENABLED:!1,PLUGINS:[],LOGGEDIN:!1,AUTHBASIC_AUTOLOGIN:!1,FILES_ALLOWED_EXTS:"",IMPORT_ALLOWED_EXTS:[]},u={passLength:0,minPasswordLength:8,complexity:{chars:!0,numbers:!0,symbols:!0,uppercase:!0,numlength:12}};Object.seal(u);
var B={},q={},p={},v={},t={},y={},r={},f={log:function(a){!0===c.DEBUG&&console.log(a)},info:function(a){!0===c.DEBUG&&console.info(a)},error:function(a){console.error(a)},warn:function(a){console.warn(a)},debug:function(a){!0===c.DEBUG&&console.debug(a)}};Object.freeze(f);toastr.options={closeButton:!0,debug:!1,newestOnTop:!1,progressBar:!1,positionClass:"toast-top-center",preventDuplicates:!1,onclick:null,showDuration:"300",hideDuration:"1000",timeOut:"5000",extendedTimeOut:"1000",showEasing:"swing",
hideEasing:"linear",showMethod:"fadeIn",hideMethod:"fadeOut"};var L=function(){f.info("setupCallbacks");var a=$("#container").data("page");if(""!==a&&"function"===typeof q.views[a])q.views[a]();0<$("footer").length&&q.views.footer();$("#btnBack").click(function(){A("index.php")});q.bodyHooks()},e={ok:function(a){toastr.success(a)},error:function(a){toastr.error(a)},warn:function(a){toastr.warning(a)},info:function(a){toastr.info(a)},sticky:function(a,b){var d={timeOut:0};"function"===typeof b&&(d.onHidden=
b);toastr.warning(a,c.LANG[60],d)},out:function(a){if("object"===typeof a){var b=a.status,d=a.description;void 0!==a.messages&&0<a.messages.length&&(d=d+"<br>"+a.messages.join("<br>"));switch(b){case 0:e.ok(d);break;case 1:e.error(d);break;case 2:e.warn(d);break;case 10:p.main.logout();break;case 100:e.ok(d);e.sticky(d);break;case 101:e.error(d);e.sticky(d);break;case 102:e.warn(d);e.sticky(d);break;default:e.error(d)}}},html:{error:function(a){return'<p class="error round">Oops...<br>'+c.LANG[1]+
"<br>"+a+"</p>"}}};Object.freeze(e);String.format||(String.format=function(a){var b=Array.prototype.slice.call(arguments,1);return a.replace(/{(\d+)}/g,function(a,c){return"undefined"!==typeof b[c]?b[c]:a})});var M=function(){f.info("getEnvironment");var a=window.location.pathname.split("/");c.APP_ROOT=window.location.protocol+"//"+window.location.host+function(){for(var b="",c=1;c<=a.length-2;c++)b+="/"+a[c];return b}();var b=v.getRequestOpts();b.url="/index.php?r=bootstrap/getEnvironment";b.method=
"get";b.useLoading=!1;b.data={isAjax:1};return v.getActionCall(b,function(a){c.LANG=a.lang;c.PK=a.pk;c.CHECK_UPDATES=a.check_updates;c.CRYPT.setPublicKey(a.pk);c.TIMEZONE=a.timezone;c.LOCALE=a.locale;c.DEBUG=a.debug;c.MAX_FILE_SIZE=parseInt(a.max_file_size);c.COOKIES_ENABLED=a.cookies_enabled;c.PLUGINS=a.plugins;c.LOGGEDIN=a.loggedin;c.AUTHBASIC_AUTOLOGIN=a.authbasic_autologin;c.IMPORT_ALLOWED_EXTS=a.import_allowed_exts;c.FILES_ALLOWED_EXTS=a.files_allowed_exts;Object.freeze(c)})},H=function(a){for(var b=
[],c,e=window.location.href.slice(window.location.href.indexOf("?")+1).split("&"),f=0;f<e.length;f++)c=e[f].split("="),b.push(c[0]),b[c[0]]=c[1];return void 0!==a&&void 0!==b[a]?b[a]:b};return function(){f.info("init");y={actions:function(){return p},triggers:function(){return q},theme:function(){return B},sk:x,msg:e,log:f,passwordData:u,outputResult:C,checkPassLevel:F,encryptFormValue:z,fileUpload:G,redirect:A,scrollUp:J,setContentSize:K};r=$.extend({log:f,config:function(){return c},appTheme:function(){return B},
appActions:function(){return p},appTriggers:function(){return q},appRequests:function(){return v},appPlugins:function(){return t},evalAction:m,resizeImage:h},y);Object.freeze(y);Object.freeze(r);q=sysPass.Triggers(r);p=sysPass.Actions(r);v=sysPass.Requests(r);"function"===typeof sysPass.Theme&&(B=sysPass.Theme(r));M().then(function(){if(!I()&&(""!==c.PK&&n(),!0===c.CHECK_UPDATES&&p.main.getUpdates(),!1===c.COOKIES_ENABLED&&e.sticky(c.LANG[64]),w(),L(),0<c.PLUGINS.length)){f.info(c.PLUGINS);f.info("initPlugins");
for(var a=0;a<c.PLUGINS.length;a++){var g=c.PLUGINS[a];"function"===typeof sysPass.Plugin[g]&&(t[g]=sysPass.Plugin[g](r))}Object.freeze(t);!0===c.LOGGEDIN&&!0===c.CHECK_UPDATES&&b()}});return y}()};

View File

@@ -332,7 +332,8 @@ sysPass.Triggers = function (Common) {
if ($dropFiles.length > 0) {
const upload = Common.fileUpload($dropFiles);
upload.url = Common.appActions().ajaxUrl.config.import;
upload.url = Common.appActions().ajaxUrl.entrypoint + "?r=" + $dropFiles.data("action-route");
upload.allowedExts = Common.config().IMPORT_ALLOWED_EXTS;
upload.beforeSendAction = function () {
upload.setRequestData({
sk: Common.sk.get(),

View File

@@ -1,17 +1,17 @@
var $jscomp={scope:{},findInternal:function(b,d,f){b instanceof String&&(b=String(b));for(var a=b.length,c=0;c<a;c++){var e=b[c];if(d.call(f,e,c,b))return{i:c,v:e}}return{i:-1,v:void 0}}};$jscomp.defineProperty="function"==typeof Object.defineProperties?Object.defineProperty:function(b,d,f){if(f.get||f.set)throw new TypeError("ES3 does not support getters and setters.");b!=Array.prototype&&b!=Object.prototype&&(b[d]=f.value)};
$jscomp.getGlobal=function(b){return"undefined"!=typeof window&&window===b?b:"undefined"!=typeof global&&null!=global?global:b};$jscomp.global=$jscomp.getGlobal(this);$jscomp.polyfill=function(b,d,f,a){if(d){f=$jscomp.global;b=b.split(".");for(a=0;a<b.length-1;a++){var c=b[a];c in f||(f[c]={});f=f[c]}b=b[b.length-1];a=f[b];d=d(a);d!=a&&null!=d&&$jscomp.defineProperty(f,b,{configurable:!0,writable:!0,value:d})}};
$jscomp.getGlobal=function(b){return"undefined"!=typeof window&&window===b?b:"undefined"!=typeof global?global:b};$jscomp.global=$jscomp.getGlobal(this);$jscomp.polyfill=function(b,d,f,a){if(d){f=$jscomp.global;b=b.split(".");for(a=0;a<b.length-1;a++){var c=b[a];c in f||(f[c]={});f=f[c]}b=b[b.length-1];a=f[b];d=d(a);d!=a&&null!=d&&$jscomp.defineProperty(f,b,{configurable:!0,writable:!0,value:d})}};
$jscomp.polyfill("Array.prototype.find",function(b){return b?b:function(b,f){return $jscomp.findInternal(this,b,f).v}},"es6-impl","es3");
sysPass.Triggers=function(b){var d=b.log,f=function(a){var c={valueField:"id",labelField:"name",searchField:["name"]};a.find(".select-box").each(function(a){var d=$(this);c.plugins=d.hasClass("select-box-deselect")?{clear_selection:{title:b.config().LANG[51]}}:{};if(d.data("onchange")){var e=d.data("onchange").split("/");c.onChange=function(a){if(0<a)if(2===e.length)sysPassApp.actions()[e[0]][e[1]](d);else sysPassApp.actions()[e[0]](d)}}d.selectize(c)});a.find("#allowed_exts").selectize({create:function(a){return{value:a.toUpperCase(),
text:a.toUpperCase()}},createFilter:/^[a-z0-9]{1,4}$/i,plugins:["remove_button"]});a.find("#wikifilter").selectize({create:!0,createFilter:/^[a-z0-9:._-]+$/i,plugins:["remove_button"]})};return{views:{main:function(){d.info("views:main");clipboard.isSupported()||b.msg.info(b.config().LANG[65]);$(".btn-menu").click(function(){var a=$(this);"1"===a.attr("data-history-reset")&&b.appRequests().history.reset();b.appActions().doAction({r:a.data("route")},a.data("view"))});$("#btnLogout").click(function(a){b.appActions().main.logout()});
$("#btnPrefs").click(function(a){b.appActions().doAction({actionId:$(this).data("route")})});b.appActions().doAction({r:"account/index"},"search");"function"===typeof b.appTheme().viewsTriggers.main&&b.appTheme().viewsTriggers.main()},search:function(){d.info("views:search");var a=$("#frmSearch");0!==a.length&&(a.find("input[name='search']").on("keyup",function(b){b.preventDefault();13!==b.which&&13!==b.keyCode||a.submit()}),a.find("select, #rpp").on("change",function(){a.submit()}),a.find("button.btn-clear").on("click",
function(b){b.preventDefault();a.find('input[name="searchfav"]').val(0);a[0].reset()}),a.find("input:text:visible:first").focus(),$("#globalSearch").click(function(){var b=1==$(this).prop("checked")?1:0;a.find("input[name='gsearch']").val(b);a.submit()}),"function"===typeof b.appTheme().viewsTriggers.search&&b.appTheme().viewsTriggers.search())},login:function(){d.info("views:login");var a=$("#frmLogin");b.config().AUTHBASIC_AUTOLOGIN&&"0"===a.find("input[name='loggedOut']").val()&&(d.info("views:login:autologin"),
b.msg.info(b.config().LANG[66]),b.appActions().main.login(a))},passreset:function(){d.info("views:passreset");var a=$("#frmPassReset");b.appTheme().passwordDetect(a)},footer:function(){d.info("views:footer")},common:function(a){d.info("views:common");f(a);var c=a.find(":input [name='sk']");0<c.length&&b.sk.set(c.val());"function"===typeof b.appTheme().viewsTriggers.common&&b.appTheme().viewsTriggers.common(a);b.appTriggers().updateFormHash(a)},datatabs:function(){d.info("views:datatabs");$(".datagrid-action-search>form").each(function(){var a=
$(this);a.find("button.btn-clear").on("click",function(b){b.preventDefault();a.trigger("reset")})})},config:function(){d.info("views:config");var a=$("#drop-import-files");if(0<a.length){var c=b.fileUpload(a);c.url=b.appActions().ajaxUrl.config["import"];c.beforeSendAction=function(){c.setRequestData({sk:b.sk.get(),csvDelimiter:$("#csvDelimiter").val(),importPwd:$("#importPwd").val(),importMasterPwd:$("#importMasterPwd").val(),import_defaultuser:$("#import_defaultuser").val(),import_defaultgroup:$("#import_defaultgroup").val()})}}},
account:function(){d.info("views:account");var a=$("#list-account-files");0<a.length&&b.appActions().account.listFiles(a);var c=$("#drop-account-files");if(0<c.length){var e=b.fileUpload(c);e.url=b.appActions().ajaxUrl.entrypoint+"?r="+c.data("action-route")+"/"+c.data("item-id");e.requestDoneAction=function(){b.appActions().account.listFiles(a)}}c=$(".show-extra-info");if(0<c.length)c.on("click",function(){var a=$(this),b=$(a.data("target"));b.is(":hidden")?(b.slideDown("slow"),a.html(a.data("icon-up"))):
(b.slideUp("slow"),a.html(a.data("icon-down")))});c=$("#selParentAccount");0<c.length&&(c.on("change",function(){var a=$(this),b=$("#accountpass,#accountpassR");0<a[0].value?b.each(function(){$(this).prop("disabled","true");$(this).prop("required","false")}):b.each(function(){$(this).prop("disabled","");$(this).prop("required","true")})}),b.appActions().items.get(c));c=$("#selTags");0<c.length&&c.selectize({persist:!1,maxItems:null,valueField:"id",labelField:"name",searchField:["name"],plugins:["remove_button"]});
var f=$("#otherUsers");0<f.length&&f.selectize({persist:!1,valueField:"id",labelField:"name",searchField:["name"],plugins:["remove_button"],onInitialize:function(){var a=f.data("userId");0<a&&this.removeOption(a)}});var g=$("#otherUserGroups");0<g.length&&g.selectize({persist:!1,valueField:"id",labelField:"name",searchField:["name"],plugins:["remove_button"],onInitialize:function(){var a=g.data("userGroupId");0<a&&this.removeOption(a)}});c=$("#data-accesses");0<c.length&&1===c[0].childNodes.length&&
c.parent(".data").hide();$("input:text:visible:first").focus()},install:function(){d.info("views:install");var a=$("#frmInstall");b.appTheme().passwordDetect(a);f(a)}},selectDetect:f,updateSk:function(){$("#content").find("[data-sk]").each(function(){d.info("updateSk");$(this).data("sk",b.sk.get())})},updateFormHash:function(a){d.info("updateFormHash");a=void 0!==a?a.find(".form-action[data-hash]"):$(".form-action[data-hash]");0<a.length&&a.each(function(){var a=$(this);a.attr("data-hash",SparkMD5.hash(a.serialize(),
!1))})},bodyHooks:function(){d.info("bodyHooks");$("body").on("click","button.btn-action[data-onclick][type='button'],li.btn-action[data-onclick],span.btn-action[data-onclick],i.btn-action[data-onclick],.btn-action-pager[data-onclick]",function(){var a=$(this);d.info("handleActionButton: "+a.attr("id"));var c=a.data("onclick").split("/"),e;e=a.data("plugin");e=void 0!==e&&void 0!==b.appPlugins()[e]?b.appPlugins()[e]:b.appActions();if(2===c.length)e[c[0]][c[1]](a);else e[c[0]](a)}).on("click",".btn-back",
function(){var a=b.appRequests();if(0<a.history.length()){d.info("back");var c=a.history.del();a.getActionCall(c,c.callback)}}).on("submit",".form-action",function(a){a.preventDefault();a=$(this);d.info("formAction");var c=a.attr("data-hash"),e=SparkMD5.hash(a.serialize(),!1);if(c===e)b.msg.ok(b.config().LANG[55]);else if(c=a.data("plugin"),c=void 0!==c&&void 0!==b.appPlugins()[c]?b.appPlugins()[c]:b.appActions(),e=a.data("onsubmit").split("/"),a.find("input[name='sk']").val(b.sk.get()),2===e.length)c[e[0]][e[1]](a);
else c[e[0]](a)}).on("click",".btn-help",function(){var a=$(this),a=$("#"+a.data("help")).html();mdlDialog().show({title:b.config().LANG[54],text:a,positive:{title:b.config().LANG[43]}})}).on("reset",".form-action",function(a){a.preventDefault();d.info("reset");a=$(this);a.find("input:text, input:password, input:file, textarea").val("").parent("div").removeClass("is-dirty");a.find("input:radio, input:checkbox").prop("checked",!1).prop("selected",!1);a.find("input[name='start'], input[name='skey'], input[name='sorder']").val(0);
a.find("select").each(function(){$(this)[0].selectize.clear(!0)});a.submit()}).on("click",".btn-popup-close",function(a){$.magnificPopup.close()})}}};
$(this);a.find("button.btn-clear").on("click",function(b){b.preventDefault();a.trigger("reset")})})},config:function(){d.info("views:config");var a=$("#drop-import-files");if(0<a.length){var c=b.fileUpload(a);c.url=b.appActions().ajaxUrl.entrypoint+"?r="+a.data("action-route");c.allowedExts=b.config().IMPORT_ALLOWED_EXTS;c.beforeSendAction=function(){c.setRequestData({sk:b.sk.get(),csvDelimiter:$("#csvDelimiter").val(),importPwd:$("#importPwd").val(),importMasterPwd:$("#importMasterPwd").val(),import_defaultuser:$("#import_defaultuser").val(),
import_defaultgroup:$("#import_defaultgroup").val()})}}},account:function(){d.info("views:account");var a=$("#list-account-files");0<a.length&&b.appActions().account.listFiles(a);var c=$("#drop-account-files");if(0<c.length){var e=b.fileUpload(c);e.url=b.appActions().ajaxUrl.entrypoint+"?r="+c.data("action-route")+"/"+c.data("item-id");e.requestDoneAction=function(){b.appActions().account.listFiles(a)}}c=$(".show-extra-info");if(0<c.length)c.on("click",function(){var a=$(this),b=$(a.data("target"));
b.is(":hidden")?(b.slideDown("slow"),a.html(a.data("icon-up"))):(b.slideUp("slow"),a.html(a.data("icon-down")))});c=$("#selParentAccount");0<c.length&&(c.on("change",function(){var a=$(this),b=$("#accountpass,#accountpassR");0<a[0].value?b.each(function(){$(this).prop("disabled","true");$(this).prop("required","false")}):b.each(function(){$(this).prop("disabled","");$(this).prop("required","true")})}),b.appActions().items.get(c));c=$("#selTags");0<c.length&&c.selectize({persist:!1,maxItems:null,valueField:"id",
labelField:"name",searchField:["name"],plugins:["remove_button"]});var f=$("#otherUsers");0<f.length&&f.selectize({persist:!1,valueField:"id",labelField:"name",searchField:["name"],plugins:["remove_button"],onInitialize:function(){var a=f.data("userId");0<a&&this.removeOption(a)}});var g=$("#otherUserGroups");0<g.length&&g.selectize({persist:!1,valueField:"id",labelField:"name",searchField:["name"],plugins:["remove_button"],onInitialize:function(){var a=g.data("userGroupId");0<a&&this.removeOption(a)}});
c=$("#data-accesses");0<c.length&&1===c[0].childNodes.length&&c.parent(".data").hide();$("input:text:visible:first").focus()},install:function(){d.info("views:install");var a=$("#frmInstall");b.appTheme().passwordDetect(a);f(a)}},selectDetect:f,updateSk:function(){$("#content").find("[data-sk]").each(function(){d.info("updateSk");$(this).data("sk",b.sk.get())})},updateFormHash:function(a){d.info("updateFormHash");a=void 0!==a?a.find(".form-action[data-hash]"):$(".form-action[data-hash]");0<a.length&&
a.each(function(){var a=$(this);a.attr("data-hash",SparkMD5.hash(a.serialize(),!1))})},bodyHooks:function(){d.info("bodyHooks");$("body").on("click","button.btn-action[data-onclick][type='button'],li.btn-action[data-onclick],span.btn-action[data-onclick],i.btn-action[data-onclick],.btn-action-pager[data-onclick]",function(){var a=$(this);d.info("handleActionButton: "+a.attr("id"));var c=a.data("onclick").split("/"),e;e=a.data("plugin");e=void 0!==e&&void 0!==b.appPlugins()[e]?b.appPlugins()[e]:b.appActions();
if(2===c.length)e[c[0]][c[1]](a);else e[c[0]](a)}).on("click",".btn-back",function(){var a=b.appRequests();if(0<a.history.length()){d.info("back");var c=a.history.del();a.getActionCall(c,c.callback)}}).on("submit",".form-action",function(a){a.preventDefault();a=$(this);d.info("formAction");var c=a.attr("data-hash"),e=SparkMD5.hash(a.serialize(),!1);if(c===e)b.msg.ok(b.config().LANG[55]);else if(c=a.data("plugin"),c=void 0!==c&&void 0!==b.appPlugins()[c]?b.appPlugins()[c]:b.appActions(),e=a.data("onsubmit").split("/"),
a.find("input[name='sk']").val(b.sk.get()),2===e.length)c[e[0]][e[1]](a);else c[e[0]](a)}).on("click",".btn-help",function(){var a=$(this),a=$("#"+a.data("help")).html();mdlDialog().show({title:b.config().LANG[54],text:a,positive:{title:b.config().LANG[43]}})}).on("reset",".form-action",function(a){a.preventDefault();d.info("reset");a=$(this);a.find("input:text, input:password, input:file, textarea").val("").parent("div").removeClass("is-dirty");a.find("input:radio, input:checkbox").prop("checked",
!1).prop("selected",!1);a.find("input[name='start'], input[name='skey'], input[name='sorder']").val(0);a.find("select").each(function(){$(this)[0].selectize.clear(!0)});a.submit()}).on("click",".btn-popup-close",function(a){$.magnificPopup.close()})}}};