diff --git a/.travis.yml b/.travis.yml
index 601aec29..8881edd4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,7 +20,7 @@ before_script:
- composer self-update
- composer install --prefer-source --no-interaction --dev
-script: ./vendor/bin/phpunit -c ./phpunit.xml --testsuite Core
+script: ./vendor/bin/phpunit -c ./tests/phpunit.xml --testsuite Core
after_script:
- if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then mv ./tests/_output/coverage-clover.xml clover.xml && ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT -t clover; fi
@@ -33,8 +33,9 @@ env:
- DB_SERVER=127.0.0.1 DB_NAME=syspass DB_USER=root DB_PASS=
before_install:
- - mysql -e 'CREATE DATABASE IF NOT EXISTS `'"$DB_NAME"'` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;'
- - mysql $DB_NAME < $TRAVIS_BUILD_DIR/schemas/dbstructure.sql
+ - mysql -e 'DROP DATABASE IF EXISTS `'"$DB_NAME"'`;'
+ - mysql -e 'CREATE DATABASE `'"$DB_NAME"'` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;'
+ - mysql $DB_NAME < ./schemas/dbstructure.sql
cache:
directories:
diff --git a/app/modules/api/Controllers/AccountController.php b/app/modules/api/Controllers/AccountController.php
index 66011b5c..26f59d96 100644
--- a/app/modules/api/Controllers/AccountController.php
+++ b/app/modules/api/Controllers/AccountController.php
@@ -29,6 +29,7 @@ use SP\Core\Crypt\Crypt;
use SP\Core\Events\Event;
use SP\Core\Events\EventMessage;
use SP\Modules\Api\Controllers\Help\AccountHelp;
+use SP\Mvc\Model\QueryCondition;
use SP\Services\Account\AccountRequest;
use SP\Services\Account\AccountSearchFilter;
use SP\Services\Account\AccountService;
@@ -162,6 +163,26 @@ final class AccountController extends ControllerBase
$accountSearchFilter->setCleanTxtSearch($this->apiService->getParamString('text'));
$accountSearchFilter->setCategoryId($this->apiService->getParamInt('categoryId'));
$accountSearchFilter->setClientId($this->apiService->getParamInt('clientId'));
+
+ $tagsId = $this->apiService->getParamArray('tagsId', false, []);
+
+ if (!empty($tagsId)) {
+ $accountSearchFilter->setTagsId($tagsId);
+ }
+
+ $op = $this->apiService->getParamString('op');
+
+ if ($op !== null) {
+ switch ($op) {
+ case 'and':
+ $accountSearchFilter->setFilterOperator(QueryCondition::CONDITION_AND);
+ break;
+ case 'or':
+ $accountSearchFilter->setFilterOperator(QueryCondition::CONDITION_OR);
+ break;
+ }
+ }
+
$accountSearchFilter->setLimitCount($this->apiService->getParamInt('count', false, 50));
$accountSearchFilter->setSortOrder($this->apiService->getParamInt('order', false, AccountSearchFilter::SORT_DEFAULT));
@@ -194,7 +215,7 @@ final class AccountController extends ControllerBase
->addDetail(__u('Cliente'), $accountDetails->getClientName()))
);
- $this->returnResponse(new ApiResponse(__u('Cuenta eliminada'), ApiResponse::RESULT_SUCCESS, $accountId));
+ $this->returnResponse(new ApiResponse(__('Cuenta eliminada'), ApiResponse::RESULT_SUCCESS, $accountId));
} catch (\Exception $e) {
processException($e);
diff --git a/app/modules/api/Controllers/ConfigController.php b/app/modules/api/Controllers/ConfigController.php
index 53e29748..42fccbe0 100644
--- a/app/modules/api/Controllers/ConfigController.php
+++ b/app/modules/api/Controllers/ConfigController.php
@@ -45,7 +45,7 @@ final class ConfigController extends ControllerBase
public function backupAction()
{
try {
- $this->setupApi(ActionsInterface::CONFIG_BACKUP);
+ $this->setupApi(ActionsInterface::CONFIG_BACKUP_RUN);
$path = $this->apiService->getParamString('path', false, BACKUP_PATH);
@@ -58,7 +58,7 @@ final class ConfigController extends ControllerBase
->addDetail(__u('Ruta'), $path))
);
- $this->returnResponse(new ApiResponse(__u('Proceso de backup finalizado')));
+ $this->returnResponse(new ApiResponse(__('Proceso de backup finalizado')));
} catch (\Exception $e) {
processException($e);
@@ -72,7 +72,7 @@ final class ConfigController extends ControllerBase
public function exportAction()
{
try {
- $this->setupApi(ActionsInterface::CONFIG_EXPORT);
+ $this->setupApi(ActionsInterface::CONFIG_EXPORT_RUN);
$password = $this->apiService->getParamString('password');
$path = $this->apiService->getParamString('path', false, BACKUP_PATH);
@@ -91,7 +91,7 @@ final class ConfigController extends ControllerBase
->addDescription(__u('Proceso de exportación finalizado')))
);
- $this->returnResponse(new ApiResponse(__u('Proceso de exportación finalizado')));
+ $this->returnResponse(new ApiResponse(__('Proceso de exportación finalizado')));
} catch (\Exception $e) {
processException($e);
diff --git a/app/modules/api/Controllers/ControllerBase.php b/app/modules/api/Controllers/ControllerBase.php
index c41f674a..de6d11cc 100644
--- a/app/modules/api/Controllers/ControllerBase.php
+++ b/app/modules/api/Controllers/ControllerBase.php
@@ -30,6 +30,7 @@ use Psr\Container\ContainerInterface;
use SP\Core\Context\StatelessContext;
use SP\Core\Events\EventDispatcher;
use SP\Core\Exceptions\SPException;
+use SP\Http\Json;
use SP\Services\Api\ApiResponse;
use SP\Services\Api\ApiService;
use SP\Services\Api\JsonRpcResponse;
@@ -145,25 +146,30 @@ abstract class ControllerBase
throw new SPException(__u('Acceso no permitido'));
}
- $this->router->response()->headers()->set('Content-type', 'application/json; charset=utf-8');
- $this->router->response()->send(true);
-
- echo JsonRpcResponse::getResponse($apiResponse, $this->apiService->getRequestId());
+ $this->sendJsonResponse(JsonRpcResponse::getResponse($apiResponse, $this->apiService->getRequestId()));
} catch (SPException $e) {
processException($e);
- echo JsonRpcResponse::getResponseException($e, $this->apiService->getRequestId());
+ $this->returnResponseException($e);
}
}
+ /**
+ * Returns a JSON response back to the browser
+ *
+ * @param string $response
+ */
+ final private function sendJsonResponse(string $response)
+ {
+ $json = Json::factory($this->router->response());
+ $json->returnRawJson($response);
+ }
+
/**
* @param \Exception $e
*/
final protected function returnResponseException(\Exception $e)
{
- $this->router->response()->headers()->set('Content-type', 'application/json; charset=utf-8');
- $this->router->response()->send(true);
-
- echo JsonRpcResponse::getResponseException($e, $this->apiService->getRequestId());
+ $this->sendJsonResponse(JsonRpcResponse::getResponseException($e, $this->apiService->getRequestId()));
}
}
\ No newline at end of file
diff --git a/app/modules/api/Controllers/Help/AccountHelp.php b/app/modules/api/Controllers/Help/AccountHelp.php
index 84f47526..2a3d5c4d 100644
--- a/app/modules/api/Controllers/Help/AccountHelp.php
+++ b/app/modules/api/Controllers/Help/AccountHelp.php
@@ -89,7 +89,9 @@ class AccountHelp implements HelpInterface
self::getItem('text', __('Texto a buscar')),
self::getItem('count', __('Número de resultados a mostrar')),
self::getItem('categoryId', __('Id de categoría a filtrar')),
- self::getItem('clientId', __('Id de cliente a filtrar'))
+ self::getItem('clientId', __('Id de cliente a filtrar')),
+ self::getItem('tagsId', __('Array de Ids de etiquetas a filtrar')),
+ self::getItem('op', __('Operador de filtrado'))
];
}
diff --git a/composer.lock b/composer.lock
index 367a08f5..b207e24e 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1027,16 +1027,16 @@
},
{
"name": "php-di/php-di",
- "version": "6.0.2",
+ "version": "6.0.3",
"source": {
"type": "git",
"url": "https://github.com/PHP-DI/PHP-DI.git",
- "reference": "71a7f2ed1e728138060ea159cbb1a92dc8620bd2"
+ "reference": "61ef5c9fcbf04c8504a46d20bd27ea608eab7864"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/71a7f2ed1e728138060ea159cbb1a92dc8620bd2",
- "reference": "71a7f2ed1e728138060ea159cbb1a92dc8620bd2",
+ "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/61ef5c9fcbf04c8504a46d20bd27ea608eab7864",
+ "reference": "61ef5c9fcbf04c8504a46d20bd27ea608eab7864",
"shasum": ""
},
"require": {
@@ -1086,7 +1086,7 @@
"ioc",
"psr11"
],
- "time": "2018-04-23T06:54:40+00:00"
+ "time": "2018-08-25T22:21:38+00:00"
},
{
"name": "php-di/phpdoc-reader",
@@ -1435,12 +1435,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
- "reference": "f834fd02a512672f298ed7c59e44e46621b6423d"
+ "reference": "0abc14e0df387fe74b4b32e0b8647d1639256d38"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/f834fd02a512672f298ed7c59e44e46621b6423d",
- "reference": "f834fd02a512672f298ed7c59e44e46621b6423d",
+ "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/0abc14e0df387fe74b4b32e0b8647d1639256d38",
+ "reference": "0abc14e0df387fe74b4b32e0b8647d1639256d38",
"shasum": ""
},
"conflict": {
@@ -1577,7 +1577,7 @@
"zendframework/zend-validator": ">=2.3,<2.3.6",
"zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1",
"zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6",
- "zendframework/zendframework": ">=2,<2.4.11|>=2.5,<2.5.1",
+ "zendframework/zendframework": "<2.5.1",
"zendframework/zendframework1": "<1.12.20",
"zendframework/zendopenid": ">=2,<2.0.2",
"zendframework/zendxml": ">=1,<1.0.1",
@@ -1599,7 +1599,7 @@
}
],
"description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it",
- "time": "2018-08-10T10:05:21+00:00"
+ "time": "2018-08-14T15:39:17+00:00"
},
{
"name": "symfony/polyfill-php56",
@@ -2535,16 +2535,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "6.5.11",
+ "version": "6.5.12",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "7bab54cb366076023bbf457a2a0d513332cd40f2"
+ "reference": "24da433d7384824d65ea93fbb462e2f31bbb494e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7bab54cb366076023bbf457a2a0d513332cd40f2",
- "reference": "7bab54cb366076023bbf457a2a0d513332cd40f2",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/24da433d7384824d65ea93fbb462e2f31bbb494e",
+ "reference": "24da433d7384824d65ea93fbb462e2f31bbb494e",
"shasum": ""
},
"require": {
@@ -2615,7 +2615,7 @@
"testing",
"xunit"
],
- "time": "2018-08-07T07:05:35+00:00"
+ "time": "2018-08-22T06:32:48+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
diff --git a/lib/SP/Http/Json.php b/lib/SP/Http/Json.php
index e7cfe5f2..76ced573 100644
--- a/lib/SP/Http/Json.php
+++ b/lib/SP/Http/Json.php
@@ -2,8 +2,8 @@
/**
* sysPass
*
- * @author nuxsmin
- * @link https://syspass.org
+ * @author nuxsmin
+ * @link https://syspass.org
* @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
@@ -130,7 +130,7 @@ final class Json
*
* @return bool
*/
- public function returnRawJson($data)
+ public function returnRawJson(string $data)
{
return $this->response
->header('Content-type', 'application/json; charset=utf-8')
diff --git a/lib/SP/Services/Api/ApiService.php b/lib/SP/Services/Api/ApiService.php
index 2bf4cbcc..487491e9 100644
--- a/lib/SP/Services/Api/ApiService.php
+++ b/lib/SP/Services/Api/ApiService.php
@@ -97,7 +97,7 @@ final class ApiService extends Service
__u('Intentos excedidos'),
ServiceException::ERROR,
null,
- -32601
+ JsonRpcResponse::INTERNAL_ERROR
);
}
@@ -132,7 +132,7 @@ final class ApiService extends Service
__u('Error interno'),
ServiceException::ERROR,
null,
- -32601
+ JsonRpcResponse::INTERNAL_ERROR
);
}
}
@@ -144,7 +144,7 @@ final class ApiService extends Service
* @param bool $required Si es requerido
* @param mixed $default Valor por defecto
*
- * @return int|string
+ * @return mixed
* @throws ServiceException
*/
public function getParam($param, $required = false, $default = null)
@@ -155,7 +155,7 @@ final class ApiService extends Service
__u('Parámetros incorrectos'),
ServiceException::ERROR,
$this->getHelp($this->apiRequest->getMethod()),
- -32602
+ JsonRpcResponse::INVALID_PARAMS
);
}
@@ -189,7 +189,7 @@ final class ApiService extends Service
__u('Acceso no permitido'),
ServiceException::ERROR,
null,
- -32601
+ JsonRpcResponse::INTERNAL_ERROR
);
}
@@ -230,7 +230,7 @@ final class ApiService extends Service
__u('Error interno'),
ServiceException::ERROR,
__u('Datos inválidos'),
- -32603
+ JsonRpcResponse::INTERNAL_ERROR
);
}
} catch (CryptoException $e) {
@@ -238,7 +238,7 @@ final class ApiService extends Service
__u('Error interno'),
ServiceException::ERROR,
$e->getMessage(),
- -32603
+ JsonRpcResponse::INTERNAL_ERROR
);
}
}
@@ -246,7 +246,7 @@ final class ApiService extends Service
/**
* @param string $param
* @param bool $required
- * @param null $default
+ * @param mixed $default
*
* @return int
* @throws ServiceException
@@ -259,7 +259,7 @@ final class ApiService extends Service
/**
* @param string $param
* @param bool $required
- * @param null $default
+ * @param mixed $default
*
* @return string
* @throws ServiceException
@@ -272,7 +272,20 @@ final class ApiService extends Service
/**
* @param string $param
* @param bool $required
- * @param null $default
+ * @param mixed $default
+ *
+ * @return array
+ * @throws ServiceException
+ */
+ public function getParamArray($param, $required = false, $default = null)
+ {
+ return Filter::getArray($this->getParam($param, $required, $default));
+ }
+
+ /**
+ * @param string $param
+ * @param bool $required
+ * @param mixed $default
*
* @return int|string
* @throws ServiceException
@@ -285,7 +298,7 @@ final class ApiService extends Service
/**
* @param string $param
* @param bool $required
- * @param null $default
+ * @param mixed $default
*
* @return string
* @throws ServiceException
diff --git a/lib/SP/Services/Export/XmlExportService.php b/lib/SP/Services/Export/XmlExportService.php
index 03f91045..850d9a1a 100644
--- a/lib/SP/Services/Export/XmlExportService.php
+++ b/lib/SP/Services/Export/XmlExportService.php
@@ -88,6 +88,7 @@ final class XmlExportService extends Service
* @param string $pass string La clave de exportación
*
* @throws ServiceException
+ * @throws \SP\Storage\File\FileException
*/
public function doExport(string $exportPath, string $pass = null)
{
@@ -118,6 +119,8 @@ final class XmlExportService extends Service
/**
* Genera el nombre del archivo usado para la exportación.
+ *
+ * @throws \SP\Storage\File\FileException
*/
private function generateExportFilename(): string
{
@@ -497,7 +500,10 @@ final class XmlExportService extends Service
$hashNode = $this->xml->createElement('Hash', $hash);
$hashNode->appendChild($this->xml->createAttribute('sign'));
- $hashNode->setAttribute('sign', Hash::signMessage($hash, $this->configData->getPasswordSalt()));
+
+ $key = $this->exportPass ?: sha1($this->configData->getPasswordSalt());
+
+ $hashNode->setAttribute('sign', Hash::signMessage($hash, $key));
$this->root
->getElementsByTagName('Meta')
diff --git a/lib/SP/Services/Export/XmlVerifyService.php b/lib/SP/Services/Export/XmlVerifyService.php
index 28960dab..a2cd1ee4 100644
--- a/lib/SP/Services/Export/XmlVerifyService.php
+++ b/lib/SP/Services/Export/XmlVerifyService.php
@@ -116,11 +116,9 @@ final class XmlVerifyService extends Service
* Obtener la versión del XML
*
* @param DOMDocument $document
- *
* @param string $key
*
- * @return void
- * @throws ServiceException
+ * @return bool
*/
public static function checkXmlHash(DOMDocument $document, string $key)
{
@@ -128,11 +126,7 @@ final class XmlVerifyService extends Service
$hash = $DOMXPath->query('/Root/Meta/Hash')->item(0)->nodeValue;
$hmac = $DOMXPath->query('/Root/Meta/Hash/@sign')->item(0)->nodeValue;
- if (!Hash::checkMessage($hash, $key, $hmac)
- || $hash !== XmlExportService::generateHashFromNodes($document)
- ) {
- throw new ServiceException(__u('Fallo en la verificación del hash de integridad'));
- }
+ return Hash::checkMessage($hash, $key, $hmac) || $hash === XmlExportService::generateHashFromNodes($document);
}
/**
@@ -177,7 +171,11 @@ final class XmlVerifyService extends Service
$this->checkPassword();
- self::checkXmlHash($this->xml, $this->config->getConfigData()->getPasswordSalt());
+ $key = $password !== '' ? $password : $this->config->getConfigData()->getPasswordSalt();
+
+ if (!self::checkXmlHash($this->xml, $key)) {
+ throw new ServiceException(__u('Fallo en la verificación del hash de integridad'));
+ }
return new VerifyResult($this->getXmlVersion(), $this->detectEncrypted(), $this->countItemNodes($this->processEncrypted()));
}
diff --git a/lib/SP/Services/Import/CsvImportBase.php b/lib/SP/Services/Import/CsvImportBase.php
index 05d88134..5fa3cdd1 100644
--- a/lib/SP/Services/Import/CsvImportBase.php
+++ b/lib/SP/Services/Import/CsvImportBase.php
@@ -24,7 +24,7 @@
namespace SP\Services\Import;
-use DI\Container;
+use Psr\Container\ContainerInterface;
use SP\Core\Events\Event;
use SP\Core\Events\EventDispatcher;
use SP\Core\Events\EventMessage;
@@ -75,14 +75,12 @@ abstract class CsvImportBase
/**
* ImportBase constructor.
*
- * @param Container $dic
- * @param FileImport $fileImport
- * @param ImportParams $importParams
+ * @param ContainerInterface $dic
+ * @param FileImport $fileImport
+ * @param ImportParams $importParams
*
- * @throws \DI\DependencyException
- * @throws \DI\NotFoundException
*/
- public function __construct(Container $dic, FileImport $fileImport, ImportParams $importParams)
+ public function __construct(ContainerInterface $dic, FileImport $fileImport, ImportParams $importParams)
{
$this->fileImport = $fileImport;
$this->importParams = $importParams;
diff --git a/lib/SP/Services/Import/ImportService.php b/lib/SP/Services/Import/ImportService.php
index de0f4070..1407d750 100644
--- a/lib/SP/Services/Import/ImportService.php
+++ b/lib/SP/Services/Import/ImportService.php
@@ -35,7 +35,7 @@ defined('APP_ROOT') || die();
final class ImportService extends Service
{
const ALLOWED_EXTS = ['CSV', 'XML'];
-
+
/**
* @var ImportParams
*/
@@ -71,8 +71,6 @@ final class ImportService extends Service
/**
* @return ImportInterface
* @throws ImportException
- * @throws \DI\DependencyException
- * @throws \DI\NotFoundException
* @throws \SP\Storage\File\FileException
*/
protected function selectImportType()
diff --git a/lib/SP/Services/Import/SyspassImport.php b/lib/SP/Services/Import/SyspassImport.php
index befd1cd4..e505e903 100644
--- a/lib/SP/Services/Import/SyspassImport.php
+++ b/lib/SP/Services/Import/SyspassImport.php
@@ -72,7 +72,7 @@ final class SyspassImport extends XmlImportBase implements ImportInterface
$this->processEncrypted();
}
- XmlVerifyService::checkXmlHash($this->xmlDOM, $this->configData->getPasswordSalt());
+ $this->checkIntegrity();
$this->processCategories();
@@ -163,6 +163,22 @@ final class SyspassImport extends XmlImportBase implements ImportInterface
);
}
+ /**
+ * Checks XML file's data integrity using the signed hash
+ */
+ protected function checkIntegrity()
+ {
+ $key = $this->importParams->getImportPwd() ?: sha1($this->configData->getPasswordSalt());
+
+ if (!XmlVerifyService::checkXmlHash($this->xmlDOM, $key)) {
+ $this->eventDispatcher->notifyEvent('run.import.syspass.process.verify',
+ new Event($this, EventMessage::factory()
+ ->addDescription(__u('Fallo en la verificación del hash de integridad'))
+ ->addDescription(__u('Si está importando un archivo exportado desde el mismo origen, los datos pueden estar comprometidos.')))
+ );
+ }
+ }
+
/**
* Obtener las categorías y añadirlas a sysPass.
*
diff --git a/lib/SP/Services/Import/XmlImport.php b/lib/SP/Services/Import/XmlImport.php
index ca726322..8fbb6282 100644
--- a/lib/SP/Services/Import/XmlImport.php
+++ b/lib/SP/Services/Import/XmlImport.php
@@ -25,6 +25,7 @@
namespace SP\Services\Import;
use DI\Container;
+use Psr\Container\ContainerInterface;
defined('APP_ROOT') || die();
@@ -52,11 +53,11 @@ final class XmlImport implements ImportInterface
/**
* XmlImport constructor.
*
- * @param Container $dic
- * @param XmlFileImport $xmlFileImport
- * @param ImportParams $importParams
+ * @param ContainerInterface $dic
+ * @param XmlFileImport $xmlFileImport
+ * @param ImportParams $importParams
*/
- public function __construct(Container $dic, XmlFileImport $xmlFileImport, ImportParams $importParams)
+ public function __construct(ContainerInterface $dic, XmlFileImport $xmlFileImport, ImportParams $importParams)
{
$this->xmlFileImport = $xmlFileImport;
$this->importParams = $importParams;
diff --git a/lib/SP/Services/Install/Installer.php b/lib/SP/Services/Install/Installer.php
index 7d7bfeaa..39751105 100644
--- a/lib/SP/Services/Install/Installer.php
+++ b/lib/SP/Services/Install/Installer.php
@@ -57,7 +57,7 @@ final class Installer extends Service
*/
const VERSION = [3, 0, 0];
const VERSION_TEXT = '3.0-beta';
- const BUILD = 18082201;
+ const BUILD = 18082701;
/**
* @var DatabaseSetupInterface
diff --git a/phpunit.xml b/phpunit.xml
deleted file mode 100644
index ab271201..00000000
--- a/phpunit.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
- ./tests/SP
- ./tests/SP/Modules
-
-
- ./tests/SP/Modules
-
-
-
-
- ./lib/SP
-
- ./lib/SP/DataModel
- ./lib/SP/Html/Assets
- ./lib/SP/Html/DataGrid
- ./lib/SP/Config/ConfigData.php
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/tests/SP/Modules/Api/ApiTest.php b/tests/SP/Modules/Api/ApiTest.php
index d9c514ac..3664f4c6 100644
--- a/tests/SP/Modules/Api/ApiTest.php
+++ b/tests/SP/Modules/Api/ApiTest.php
@@ -26,7 +26,6 @@ namespace SP\Tests\Modules\Api;
use SP\Services\Api\JsonRpcResponse;
use SP\Tests\WebTestCase;
-use Symfony\Component\BrowserKit\Response;
/**
* Class ApiTest
@@ -36,21 +35,13 @@ use Symfony\Component\BrowserKit\Response;
class ApiTest extends WebTestCase
{
- const API_TOKEN = 'ca28f2ad2af09064ce0bfc2aff144cacb4a48df09b0978c4b6dc3970db7b2c48';
- const API_PASS = '123456';
+ const API_TOKEN = '4eb7a989fab4c8fd9ade0ea80df7032d5ee78d4496c1c10f9c4388a872bfff28';
+ const API_PASS = ')%Iykm*4A]wg';
const API_URL = 'http://syspass-app-test/api.php';
public function testInvalidRequest()
{
- $client = self::postJson(self::API_URL);
-
- /** @var Response $response */
- $response = $client->getResponse();
-
- $this->assertEquals(200, $response->getStatus());
- $this->assertEquals('application/json; charset=utf-8', $response->getHeader('Content-Type'));
-
- $result = json_decode($response->getContent());
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL));
$this->assertInstanceOf(\stdClass::class, $result);
$this->assertEquals('2.0', $result->jsonrpc);
@@ -70,15 +61,7 @@ class ApiTest extends WebTestCase
'id' => 1
];
- $client = self::postJson(self::API_URL, $data);
-
- /** @var Response $response */
- $response = $client->getResponse();
-
- $this->assertEquals(200, $response->getStatus());
- $this->assertEquals('application/json; charset=utf-8', $response->getHeader('Content-Type'));
-
- $result = json_decode($response->getContent());
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
$this->assertInstanceOf(\stdClass::class, $result);
$this->assertEquals('2.0', $result->jsonrpc);
@@ -99,15 +82,7 @@ class ApiTest extends WebTestCase
'id' => 1
];
- $client = self::postJson(self::API_URL, $data);
-
- /** @var Response $response */
- $response = $client->getResponse();
-
- $this->assertEquals(200, $response->getStatus());
- $this->assertEquals('application/json; charset=utf-8', $response->getHeader('Content-Type'));
-
- $result = json_decode($response->getContent());
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
$this->assertInstanceOf(\stdClass::class, $result);
$this->assertEquals('2.0', $result->jsonrpc);
diff --git a/tests/SP/Modules/Api/Controllers/AccountControllerTest.php b/tests/SP/Modules/Api/Controllers/AccountControllerTest.php
index 24c239d5..78926e6c 100644
--- a/tests/SP/Modules/Api/Controllers/AccountControllerTest.php
+++ b/tests/SP/Modules/Api/Controllers/AccountControllerTest.php
@@ -26,7 +26,6 @@ namespace SP\Tests\Modules\Api\Controllers;
use SP\Tests\Modules\Api\ApiTest;
use SP\Tests\WebTestCase;
-use Symfony\Component\BrowserKit\Response;
/**
* Class AccountControllerTest
@@ -35,6 +34,9 @@ use Symfony\Component\BrowserKit\Response;
*/
class AccountControllerTest extends WebTestCase
{
+ /**
+ * @return int
+ */
public function testCreateAction()
{
$data = [
@@ -44,38 +46,35 @@ class AccountControllerTest extends WebTestCase
'authToken' => ApiTest::API_TOKEN,
'tokenPass' => ApiTest::API_PASS,
'name' => 'API test',
- 'categoryId' => 1,
- 'clientId' => 1,
+ 'categoryId' => 2,
+ 'clientId' => 2,
'login' => 'root',
'pass' => 'password_test',
- 'passDateChange' => time() + 86400,
+ 'expireDate' => time() + 86400,
'url' => 'http://syspass.org',
- 'notes' => "test\ntest",
+ 'notes' => "test\n\ntest",
'isPrivate' => 1,
'isPrivateGroup' => 1,
],
'id' => 1
];
- $client = self::postJson(ApiTest::API_URL, $data);
-
- /** @var Response $response */
- $response = $client->getResponse();
-
- $this->assertEquals(200, $response->getStatus());
- $this->assertEquals('application/json; charset=utf-8', $response->getHeader('Content-Type'));
-
- $result = json_decode($response->getContent());
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
$this->assertInstanceOf(\stdClass::class, $result);
$this->assertEquals(0, $result->result->resultCode);
- $this->assertEquals(1, $result->result->count);
- $this->assertCount(2, $result->result->result);
- $this->assertEquals(1, $result->result->result->itemId);
- $this->assertNotEmpty($result->result->result->password);
+ $this->assertNull($result->result->count);
+ $this->assertEquals(3, $result->result->itemId);
+
+ return $result->result->itemId;
}
- public function testViewPassAction()
+ /**
+ * @depends testCreateAction
+ *
+ * @param int $id
+ */
+ public function testViewPassAction($id)
{
$data = [
'jsonrpc' => '2.0',
@@ -83,27 +82,64 @@ class AccountControllerTest extends WebTestCase
'params' => [
'authToken' => ApiTest::API_TOKEN,
'tokenPass' => ApiTest::API_PASS,
- 'id' => 1,
+ 'id' => $id,
],
'id' => 1
];
- $client = self::postJson(ApiTest::API_URL, $data);
-
- /** @var Response $response */
- $response = $client->getResponse();
-
- $this->assertEquals(200, $response->getStatus());
- $this->assertEquals('application/json; charset=utf-8', $response->getHeader('Content-Type'));
-
- $result = json_decode($response->getContent());
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
$this->assertInstanceOf(\stdClass::class, $result);
$this->assertEquals(0, $result->result->resultCode);
- $this->assertEquals(1, $result->result->count);
- $this->assertCount(2, $result->result->result);
- $this->assertEquals(1, $result->result->result->itemId);
- $this->assertNotEmpty($result->result->result->password);
+ $this->assertEquals(2, $result->result->count);
+ $this->assertInstanceOf(\stdClass::class, $result->result->result);
+ $this->assertEquals($id, $result->result->result->itemId);
+ $this->assertEquals('password_test', $result->result->result->password);
+ }
+
+ /**
+ * @depends testCreateAction
+ *
+ * @param int $id
+ */
+ public function testViewAction($id)
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/view',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'id' => $id,
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertInstanceOf(\stdClass::class, $result->result->result);
+ $this->assertEquals($id, $result->result->result->id);
+ $this->assertEquals(1, $result->result->result->userId);
+ $this->assertEquals(1, $result->result->result->userGroupId);
+ $this->assertEquals(1, $result->result->result->userEditId);
+ $this->assertEquals('API test', $result->result->result->name);
+ $this->assertEquals(2, $result->result->result->clientId);
+ $this->assertEquals(2, $result->result->result->categoryId);
+ $this->assertEquals('root', $result->result->result->login);
+ $this->assertEquals('http://syspass.org', $result->result->result->url);
+ $this->assertEmpty($result->result->result->pass);
+ $this->assertEmpty($result->result->result->key);
+ $this->assertEquals("test\n\ntest", $result->result->result->notes);
+ $this->assertNull($result->result->result->dateEdit);
+ $this->assertEquals(0, $result->result->result->countView);
+ $this->assertEquals(1, $result->result->result->countDecrypt);
+ $this->assertEquals(0, $result->result->result->isPrivate);
+ $this->assertEquals(0, $result->result->result->isPrivateGroup);
+ $this->assertGreaterThan(0, $result->result->result->passDate);
+ $this->assertGreaterThan(0, $result->result->result->passDateChange);
+ $this->assertEquals(0, $result->result->result->parentId);
}
public function testSearchAction()
@@ -117,29 +153,363 @@ class AccountControllerTest extends WebTestCase
'id' => 1
];
- $client = self::postJson(ApiTest::API_URL, $data);
-
- /** @var Response $response */
- $response = $client->getResponse();
-
- $this->assertEquals(200, $response->getStatus());
- $this->assertEquals('application/json; charset=utf-8', $response->getHeader('Content-Type'));
-
- $result = json_decode($response->getContent());
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
$this->assertInstanceOf(\stdClass::class, $result);
$this->assertEquals(0, $result->result->resultCode);
$this->assertEquals(3, $result->result->count);
$this->assertCount(3, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'count' => 1
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
}
- public function testDeleteAction()
+ public function testSearchByTextAction()
{
- $this->markTestSkipped();
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'text' => 'Simple'
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'text' => 'admin'
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(2, $result->result->count);
+ $this->assertCount(2, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'text' => 'cloud'
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
}
- public function testViewAction()
+ public function testSearchByClientAction()
{
- $this->markTestSkipped();
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'clientId' => 2
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'clientId' => 3
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'clientId' => 10
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(0, $result->result->count);
+ $this->assertCount(0, $result->result->result);
+ }
+
+ public function testSearchByCategoryAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'categoryId' => 1
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'categoryId' => 3
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(0, $result->result->count);
+ $this->assertCount(0, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'categoryId' => 10
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(0, $result->result->count);
+ $this->assertCount(0, $result->result->result);
+ }
+
+ public function testSearchByTagsAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'tagsId' => [3]
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'tagsId' => [3, 6]
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(0, $result->result->count);
+ $this->assertCount(0, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'tagsId' => [3, 6],
+ 'op' => 'or'
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(2, $result->result->count);
+ $this->assertCount(2, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'tagsId' => [10]
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(0, $result->result->count);
+ $this->assertCount(0, $result->result->result);
+ }
+
+ public function testSearchByCategoryAndClientAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'categoryId' => 1,
+ 'clientId' => 1
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'categoryId' => 2,
+ 'clientId' => 3
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'categoryId' => 2,
+ 'clientId' => 1
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(0, $result->result->count);
+ $this->assertCount(0, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'categoryId' => 1,
+ 'clientId' => 3,
+ 'op' => 'or'
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(2, $result->result->count);
+ $this->assertCount(2, $result->result->result);
+ }
+
+ /**
+ * @depends testCreateAction
+ *
+ * @param int $id
+ */
+ public function testDeleteAction($id)
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'account/delete',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'id' => $id,
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals($id, $result->result->itemId);
}
}
diff --git a/tests/SP/Modules/Api/Controllers/CategoryControllerTest.php b/tests/SP/Modules/Api/Controllers/CategoryControllerTest.php
new file mode 100644
index 00000000..1945eeb1
--- /dev/null
+++ b/tests/SP/Modules/Api/Controllers/CategoryControllerTest.php
@@ -0,0 +1,199 @@
+.
+ */
+
+namespace SP\Tests\Modules\Api\Controllers;
+
+use SP\Tests\Modules\Api\ApiTest;
+use SP\Tests\WebTestCase;
+
+/**
+ * Class CategoryControllerTest
+ *
+ * @package SP\Tests\Modules\Api\Controllers
+ */
+class CategoryControllerTest extends WebTestCase
+{
+ /**
+ * @return int
+ */
+ public function testCreateAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'category/create',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'name' => 'API Category',
+ 'description' => "API test\ndescription"
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals(5, $result->result->itemId);
+
+ return $result->result->itemId;
+ }
+
+ /**
+ * @depends testCreateAction
+ *
+ * @param $id
+ */
+ public function testViewAction($id)
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'category/view',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'id' => $id
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals($id, $result->result->result->id);
+ $this->assertEquals('API Category', $result->result->result->name);
+ $this->assertEquals("API test\ndescription", $result->result->result->description);
+ }
+
+ /**
+ * @depends testCreateAction
+ *
+ * @param int $id
+ */
+ public function testEditAction($id)
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'category/edit',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'id' => $id,
+ 'name' => 'API category edit',
+ 'description' => "API test\ndescription\nedit"
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals($id, $result->result->itemId);
+ }
+
+ public function testSearchAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'category/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(5, $result->result->count);
+ $this->assertCount(5, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'category/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'count' => 1
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
+ }
+
+ public function testSearchByTextAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'category/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'text' => 'API category edit'
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
+ $this->assertEquals('API category edit', $result->result->result[0]->name);
+ $this->assertEquals("API test\ndescription\nedit", $result->result->result[0]->description);
+ }
+
+ /**
+ * @depends testCreateAction
+ *
+ * @param int $id
+ */
+ public function testDeleteAction($id)
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'category/delete',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'id' => $id,
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals($id, $result->result->itemId);
+ }
+}
diff --git a/tests/SP/Modules/Api/Controllers/ClientControllerTest.php b/tests/SP/Modules/Api/Controllers/ClientControllerTest.php
new file mode 100644
index 00000000..e253af2c
--- /dev/null
+++ b/tests/SP/Modules/Api/Controllers/ClientControllerTest.php
@@ -0,0 +1,203 @@
+.
+ */
+
+namespace SP\Tests\Modules\Api\Controllers;
+
+use SP\Tests\Modules\Api\ApiTest;
+use SP\Tests\WebTestCase;
+
+/**
+ * Class ClientControllerTest
+ *
+ * @package SP\Tests\Modules\Api\Controllers
+ */
+class ClientControllerTest extends WebTestCase
+{
+ /**
+ * @return int
+ */
+ public function testCreateAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'client/create',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'name' => 'API Client',
+ 'description' => "API test\ndescription",
+ 'global' => 1
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals(4, $result->result->itemId);
+
+ return $result->result->itemId;
+ }
+
+ /**
+ * @depends testCreateAction
+ *
+ * @param $id
+ */
+ public function testViewAction($id)
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'client/view',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'id' => $id
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals($id, $result->result->result->id);
+ $this->assertEquals('API Client', $result->result->result->name);
+ $this->assertEquals("API test\ndescription", $result->result->result->description);
+ $this->assertEquals(1, $result->result->result->isGlobal);
+ }
+
+ /**
+ * @depends testCreateAction
+ *
+ * @param int $id
+ */
+ public function testEditAction($id)
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'client/edit',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'id' => $id,
+ 'name' => 'API Client edit',
+ 'description' => "API test\ndescription\nedit",
+ 'global' => 0
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals($id, $result->result->itemId);
+ }
+
+ public function testSearchAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'client/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(4, $result->result->count);
+ $this->assertCount(4, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'client/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'count' => 1
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
+ }
+
+ public function testSearchByTextAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'client/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'text' => 'API Client edit'
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
+ $this->assertEquals('API Client edit', $result->result->result[0]->name);
+ $this->assertEquals("API test\ndescription\nedit", $result->result->result[0]->description);
+ $this->assertEquals(0, $result->result->result[0]->isGlobal);
+ }
+
+ /**
+ * @depends testCreateAction
+ *
+ * @param int $id
+ */
+ public function testDeleteAction($id)
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'client/delete',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'id' => $id,
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals($id, $result->result->itemId);
+ }
+}
diff --git a/tests/SP/Modules/Api/Controllers/ConfigControllerTest.php b/tests/SP/Modules/Api/Controllers/ConfigControllerTest.php
new file mode 100644
index 00000000..802032b6
--- /dev/null
+++ b/tests/SP/Modules/Api/Controllers/ConfigControllerTest.php
@@ -0,0 +1,77 @@
+.
+ */
+
+namespace SP\Tests\Modules\Api\Controllers;
+
+use SP\Tests\Modules\Api\ApiTest;
+use SP\Tests\WebTestCase;
+
+/**
+ * Class ConfigControllerTest
+ *
+ * @package SP\Tests\Modules\Api\Controllers
+ */
+class ConfigControllerTest extends WebTestCase
+{
+ public function testExportAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'config/export',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals('Export process finished', $result->result->result);
+ $this->assertEquals(0, $result->result->resultCode);
+ }
+
+ public function testBackupAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'config/backup',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals(0, $result->result->itemId);
+ $this->assertEquals('Backup process finished', $result->result->result);
+ $this->assertEquals(0, $result->result->resultCode);
+ }
+}
diff --git a/tests/SP/Modules/Api/Controllers/TagControllerTest.php b/tests/SP/Modules/Api/Controllers/TagControllerTest.php
new file mode 100644
index 00000000..bef85181
--- /dev/null
+++ b/tests/SP/Modules/Api/Controllers/TagControllerTest.php
@@ -0,0 +1,196 @@
+.
+ */
+
+namespace SP\Tests\Modules\Api\Controllers;
+
+use SP\Tests\Modules\Api\ApiTest;
+use SP\Tests\WebTestCase;
+
+/**
+ * Class TagControllerTest
+ *
+ * @package SP\Tests\Modules\Api\Controllers
+ */
+class TagControllerTest extends WebTestCase
+{
+ /**
+ * @return int
+ */
+ public function testCreateAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'tag/create',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'name' => 'API Tag',
+ 'description' => "API test\ndescription"
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals(7, $result->result->itemId);
+
+ return $result->result->itemId;
+ }
+
+ /**
+ * @depends testCreateAction
+ *
+ * @param $id
+ */
+ public function testViewAction($id)
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'tag/view',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'id' => $id
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals($id, $result->result->result->id);
+ $this->assertEquals('API Tag', $result->result->result->name);
+ }
+
+ /**
+ * @depends testCreateAction
+ *
+ * @param int $id
+ */
+ public function testEditAction($id)
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'tag/edit',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'id' => $id,
+ 'name' => 'API Tag edit'
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals($id, $result->result->itemId);
+ }
+
+ public function testSearchAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'tag/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(7, $result->result->count);
+ $this->assertCount(7, $result->result->result);
+
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'tag/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'count' => 1
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
+ }
+
+ public function testSearchByTextAction()
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'tag/search',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'text' => 'API Tag edit'
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertEquals(1, $result->result->count);
+ $this->assertCount(1, $result->result->result);
+ $this->assertEquals('API Tag edit', $result->result->result[0]->name);
+ }
+
+ /**
+ * @depends testCreateAction
+ *
+ * @param int $id
+ */
+ public function testDeleteAction($id)
+ {
+ $data = [
+ 'jsonrpc' => '2.0',
+ 'method' => 'tag/delete',
+ 'params' => [
+ 'authToken' => ApiTest::API_TOKEN,
+ 'id' => $id,
+ ],
+ 'id' => 1
+ ];
+
+ $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data));
+
+ $this->assertInstanceOf(\stdClass::class, $result);
+ $this->assertEquals(0, $result->result->resultCode);
+ $this->assertNull($result->result->count);
+ $this->assertEquals($id, $result->result->itemId);
+ }
+}
diff --git a/tests/SP/Services/Api/ApiServiceTest.php b/tests/SP/Services/Api/ApiServiceTest.php
index 1c84cd60..6f8a5d42 100644
--- a/tests/SP/Services/Api/ApiServiceTest.php
+++ b/tests/SP/Services/Api/ApiServiceTest.php
@@ -192,6 +192,9 @@ class ApiServiceTest extends DatabaseTestCase
$this->assertEquals(10, self::$service->getRequestId());
}
+ /**
+ * @throws ServiceException
+ */
public function testGetMasterPass()
{
$this->assertEquals('12345678900', self::$service->getMasterPass());
diff --git a/tests/SP/Services/Export/XmlVerifyServiceTest.php b/tests/SP/Services/Export/XmlVerifyServiceTest.php
new file mode 100644
index 00000000..5b048ef1
--- /dev/null
+++ b/tests/SP/Services/Export/XmlVerifyServiceTest.php
@@ -0,0 +1,114 @@
+.
+ */
+
+namespace SP\Tests\Services\Export;
+
+use PHPUnit\Framework\TestCase;
+use SP\Services\Export\VerifyResult;
+use SP\Services\Export\XmlVerifyService;
+use function SP\Tests\setupContext;
+
+/**
+ * Class XmlVerifyServiceTest
+ *
+ * @package SP\Tests\Services\Export
+ */
+class XmlVerifyServiceTest extends TestCase
+{
+
+ /**
+ * @throws \DI\DependencyException
+ * @throws \DI\NotFoundException
+ * @throws \Defuse\Crypto\Exception\CryptoException
+ * @throws \SP\Core\Context\ContextException
+ * @throws \SP\Services\ServiceException
+ * @throws \SP\Storage\File\FileException
+ */
+ public function testVerifyEncrypted()
+ {
+ $dic = setupContext();
+ $service = $dic->get(XmlVerifyService::class);
+
+ $file = RESOURCE_DIR . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'data_syspass_encrypted.xml';
+
+ $result = $service->verifyEncrypted($file, 'test_encrypt');
+
+ $this->assertInstanceOf(VerifyResult::class, $result);
+ $this->assertEquals(300.18082201, $result->getVersion());
+
+ $nodes = $result->getNodes();
+
+ $this->assertCount(4, $nodes);
+ $this->assertEquals(4, $nodes['Category']);
+ $this->assertEquals(3, $nodes['Client']);
+ $this->assertEquals(6, $nodes['Tag']);
+ $this->assertEquals(2, $nodes['Account']);
+ }
+
+ /**
+ * @throws \DI\DependencyException
+ * @throws \DI\NotFoundException
+ * @throws \SP\Core\Context\ContextException
+ * @throws \SP\Services\ServiceException
+ * @throws \SP\Storage\File\FileException
+ */
+ public function testVerify()
+ {
+ $dic = setupContext();
+ $service = $dic->get(XmlVerifyService::class);
+
+ $file = RESOURCE_DIR . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'data_syspass.xml';
+
+ $result = $service->verify($file);
+
+ $this->assertInstanceOf(VerifyResult::class, $result);
+ $this->assertEquals(300.18071701, $result->getVersion());
+
+ $nodes = $result->getNodes();
+
+ $this->assertCount(4, $nodes);
+ $this->assertEquals(5, $nodes['Category']);
+ $this->assertEquals(4, $nodes['Client']);
+ $this->assertEquals(7, $nodes['Tag']);
+ $this->assertEquals(5, $nodes['Account']);
+ }
+
+ public function testCheckXmlHash()
+ {
+ $dom = new \DOMDocument();
+ $dom->load(RESOURCE_DIR . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'data_syspass_encrypted.xml');
+
+ $this->assertTrue(XmlVerifyService::checkXmlHash($dom, 'test_encrypt'));
+
+ $dom->load(RESOURCE_DIR . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'data_syspass_invalid.xml');
+
+ $this->assertFalse(XmlVerifyService::checkXmlHash($dom, 'test_encrypt'));
+
+ $key = sha1('d5851082a3914a647a336d8910e24eb64b8f8adef24d27329040ebd0d4c1');
+
+ $dom->load(RESOURCE_DIR . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'data_syspass_valid_hash.xml');
+
+ $this->assertTrue(XmlVerifyService::checkXmlHash($dom, $key));
+ }
+}
diff --git a/tests/SP/Services/Import/SyspassImportTest.php b/tests/SP/Services/Import/SyspassImportTest.php
index d52ba198..ee85dead 100644
--- a/tests/SP/Services/Import/SyspassImportTest.php
+++ b/tests/SP/Services/Import/SyspassImportTest.php
@@ -30,7 +30,6 @@ use SP\Services\Account\AccountService;
use SP\Services\Category\CategoryService;
use SP\Services\Client\ClientService;
use SP\Services\Import\FileImport;
-use SP\Services\Import\ImportException;
use SP\Services\Import\ImportParams;
use SP\Services\Import\SyspassImport;
use SP\Services\Import\XmlFileImport;
@@ -224,23 +223,4 @@ class SyspassImportTest extends DatabaseTestCase
$this->assertEquals(7, $this->conn->getRowCount('Account'));
}
-
- /**
- * @throws ImportException
- * @throws \SP\Storage\File\FileException
- */
- public function testDoImportInvalidData()
- {
- $file = RESOURCE_DIR . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'data_syspass_invalid.xml';
-
- $params = new ImportParams();
- $params->setDefaultUser(1);
- $params->setDefaultGroup(1);
-
- $import = new SyspassImport(self::$dic, new XmlFileImport(FileImport::fromFilesystem($file)), $params);
-
- $this->expectException(ImportException::class);
-
- $import->doImport();
- }
}
diff --git a/tests/SP/Services/Install/InstallerTest.php b/tests/SP/Services/Install/InstallerTest.php
index 2b0682da..1d4933ec 100644
--- a/tests/SP/Services/Install/InstallerTest.php
+++ b/tests/SP/Services/Install/InstallerTest.php
@@ -55,8 +55,6 @@ class InstallerTest extends TestCase
private static $dic;
/**
- * @throws \DI\DependencyException
- * @throws \DI\NotFoundException
* @throws \SP\Core\Context\ContextException
*/
public static function setUpBeforeClass()
diff --git a/tests/SP/WebTestCase.php b/tests/SP/WebTestCase.php
index 98c7e9fd..921c43f2 100644
--- a/tests/SP/WebTestCase.php
+++ b/tests/SP/WebTestCase.php
@@ -26,6 +26,7 @@ namespace SP\Tests;
use Goutte\Client;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\BrowserKit\Response;
/**
* Class WebTestCase
@@ -57,4 +58,20 @@ abstract class WebTestCase extends TestCase
{
return new Client($server);
}
+
+ /**
+ * @param Client $client
+ *
+ * @return \stdClass
+ */
+ protected static function checkAndProcessJsonResponse(Client $client)
+ {
+ /** @var Response $response */
+ $response = $client->getResponse();
+
+ self::assertEquals(200, $response->getStatus());
+ self::assertEquals('application/json; charset=utf-8', $response->getHeader('Content-Type'));
+
+ return json_decode($response->getContent());
+ }
}
\ No newline at end of file
diff --git a/tests/SP/bootstrap.php b/tests/SP/bootstrap.php
index 2e5db2a7..00cc5307 100644
--- a/tests/SP/bootstrap.php
+++ b/tests/SP/bootstrap.php
@@ -96,8 +96,6 @@ function getRealIpAddress()
/**
* Configura el contexto de la aplicación para los tests
*
- * @throws \DI\DependencyException
- * @throws \DI\NotFoundException
* @throws \SP\Core\Context\ContextException
* @return \DI\Container
* @throws \Exception
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
new file mode 100644
index 00000000..9d5a1069
--- /dev/null
+++ b/tests/phpunit.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+ ./SP
+ ./SP/Modules
+
+
+ ./SP/Modules
+
+
+
+
+ ../lib/SP
+
+ ../lib/SP/DataModel
+ ../lib/SP/Html/Assets
+ ../lib/SP/Html/DataGrid
+ ../lib/SP/Config/ConfigData.php
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/res/config/config.xml b/tests/res/config/config.xml
index 9a6df428..f34b246b 100644
--- a/tests/res/config/config.xml
+++ b/tests/res/config/config.xml
@@ -9,11 +9,11 @@
1
1
- 9a55c224e6cf0886bf4ddcf657f826bc7ea85c87
+ 3a965d46f6da8fdf1fe749f137455863bdbe7409
0
0
- 1533969857
- 579424e820c8c2f327d6eb42a8f19e37464fc260
+ 1535360745
+ e623682d0999b924c0c70e03e8f42f2ff3806fe0
@@ -32,7 +32,7 @@
0
- 3489b3a48cc900d02d5b6eefbdf29fc22296c288
+ f247312233ce03616c1de4d4512e53963cb620ac
- PDF
- JPG
diff --git a/tests/res/import/data_syspass_encrypted.xml b/tests/res/import/data_syspass_encrypted.xml
new file mode 100644
index 00000000..11c4fcfa
--- /dev/null
+++ b/tests/res/import/data_syspass_encrypted.xml
@@ -0,0 +1,17 @@
+
+
+
+ sysPass
+ 300.18082201
+
+ admin
+
+ 77eac126d14f717b60bee5de7d78fb53ee512433
+
+
+ ZGVmNTAyMDAwYjMyYWIxMjRjNmMzZjU0ZWE1Njg5YzM1MjcwN2I0YmEzZDYwMzE1NjFjYTk5NzA2NDI0NTc3OGIwMWMwNzQ3MjllOGVmOGFiOGEzYWE4YTE2OGE3NjhmY2RhODFhNTE0NTJjM2FjYWVmZGMyNjc4YjI3NWExMWI4MjI3MTliNjIzNmM3ZGZjYzg4YTNkYmY0MTQ0ODRkMmJkZGJjMDIzMzY5NDhkOTA2ODg1MjUyMmIzNjViY2Y2ZDAzM2VjZDdkMmFmZjljZDA4NmFiMzNjNTM3YTNlMmZkZmU4OGE1YmEwZGI0NjI4NDFjZDg0ODVjYzI0ODc1YTc1MTY5ZWZlZjY4MDk2NDI3NDRhYjhmMWVjZTQ3ZDg3ODQ1N2U4YjdiN2E2MGU1ZjM4N2M3OTg5ZGZkZTRmZDg2ZjJmMmRjODBhZTU4M2U4OWQwNWFjZGZiMzc0YzQxZjhkMTE4MzBkZDgwOWExNmE2MDJjMzgzZWUxOTM5ODdiN2M0NDA3YWM4MjcxZWM4M2NkMjViOWJhMjhlYzg2MzVkNjA5NTkwYzNiODA3ZDU3YWU5NzE2NjE2ZWEyNjQxYjFhMjY1MWVmNzhhNDc2M2EwYzljNDUyNzRiYTk2ZTZkMGRiMDUyZjM4MzM5ZDc2YzRjZjFhNjBjZWIyY2U2ZmUzOGQ3NjkxMTJmM2VlZmRkZjE0YWU3OTZmYmU0ODIxM2FiN2FhMDg1OTRmNzRlZTU4N2MxYTExODhjMGQ0MThhNzkwMDc5NDk1MGIwMGRlZmE5NDg0MmU4YTg3ZDA2MzViOTVhYjgwNzc3MjJiYjAxMjM2NDM4MjcyYzRhZmVmMTFkMDliNjA4Mjk5ZDY5NzJjOTFhMGI2MjUwM2I3OWJjZDRhN2NmMzZmMw==
+ ZGVmNTAyMDAwMmYwZmEzM2U5Mzc2ZDViNDRhNTFhNWM1ZTg5ZjVhYTUyMjkzYzczZDQ0MTM1YTY4ODI4OTAzMWJhZDQ1NTVjMmY2M2EzYmNlYzAwMzI5NzUwZDhmNzczMjA0MzQ1MDQ0MTVhZDQ1MDU3ODEzODM3ODhmYjM5YzhhMGVmYzVhNDJhY2JlMTQ1OWQzMmRmMDM1OWE2YmFiOWI2ZmZmYmNkYThiNzE2Y2QxMDQ0Y2IyNmFmNmY5Y2M3OWM2YTVkZGFhZTY0ZDFjMjYwYzkxYTIxMGI0ZDEwNjI4NTc3MTkwYTMwNjM4M2U2ZGQxYjNmNjRlMjE0MWFhNDhhOGVjMTkwNzg1NTllNDgwYWQ0ZGM2YzBmZjBmNGQzMThkMWM2MmI2MmFlYTBhM2FlZTJlNWVkN2NiMzhiNjI1MzE5MzllNTRlNmE3ODA3YjRhZTc4ZWI2ZTJhZWQ1NTkyNWMyY2MwMThhNTVjNzQ1MGIzMjFmYmQzNmY3NDNjYzdkNDZmMjA3ODU1ZjFjNzI4MjE1NWU0NGE4MDRhZTdiYTE1MTU0ZDIyMWVmMTQ5MjBiNmRmMjI2MzBiMWY5ODhjZjg4MDEyZDU4MzBkZjAyZWMwNDEyMzA0MmM2MmFjYTYwYTE1MTljNTFlMjJlY2E0ZDNiODI0YzkxM2RjMDMxOWNlMmI4NzcwMjdhMDgxMDhlYzVmMWYwMTVjZWZiMWYy
+ ZGVmNTAyMDA0NzY4NTRiYjAyMzZhNThmZWEzMjMzODQ3MmQ1NGQyOWJjYjQ5YWFkNTdkMGIzNzlmYmYyYmI2OWMxZmJlOTFhMzhlNGQ2MTE1YzNmNGExMTNlY2NlY2Y0ODE5MWU2YjZiNDFiODk0MzcxZmNhMTU1ZmU5MTAwNDUwYTc4N2UxM2NmODQzMTM5MjIyYTA4ZWUwNThlMDkwODMxYmJmZWMwNmUyZjFmNzY1NGNkMGIxYTc2OTllM2E4NmFmYzc2NmMwYjhkYTI1Y2FlNzMxNGFiNTE1ZGMwMzNhOTI3ODNmNjNjZGRhNDQwNmViNGRmMDgxYjYyYjRhNmZjN2NkZmYwZWNmZGI2ZWFhN2FjNDdkNDk5YjY4ZWNlNTVmNjhmYzdjNTVlOTUyNmIwNmI4MzY5NDlmOTYyN2QwMWE5ZjM4Y2ExNmYyZTY4NDA3NzE4YzdjYjhjNGRjMmE3NDRkNzMxMmI2NWQ4MDU2OWIzYWRmZjU1MTFmOTE4ZDZiMjNmZjAyYTg3MjFhMGNiYTk0OWFiOGYwNGM3N2MwNWEyNTVlNDY2YTNkNTE2ZGI5OGI0OGQzOGUwZWY3OThlNzc4NmIxYjdiNjlmZDA1NGRiM2VkMzgzOWEyOWE3YmU0ZjY3NzAwNWE1ZTY5MzAwMDhiOTVkYWYxNDFmZmVjN2I3ZDFiODliZjZiMTkxYmEzMDJhODIyODY3NWRmZjBjMDlkZGI1ZTk4OWE5ZjBmNjU5ZjU1NTI3NmJjZWE2MDg3YzJjZjcwMTIzZDEwODAwMzNlMmQwMDk0NWNkZGI4MmM4ZWQ4YTJlZDIwYjkz
+ ZGVmNTAyMDBjMjgzOGQ2M2Q3ZTU0N2FkODMyM2RmMWM5YTVkZjRiMmM0NDAzNTgwZDU0ODY5MDliYTMzZDY4NDY0ZmNiOTJlM2Q3Y2ZmNDk4YWMzMTg3ODY3Y2EwYzBhMTgzZWVlNDE2MmJlZDAxZTI3MzYwZDI3NDE4MjkwMTQ5ZTc1ZmY1ZDM2ZjM5MDIzZjAwZDI3NzA5NWJkODhhYzQ3NzU2YTlhYWRlNzRlYzhhYTY0YzBjOTBmNzYyMDgxYWY3NTY0MzYwOGM0NjMwNjg1NDU2NDZkMzM5YmY0NmQzODViOTk4Y2NhY2MxYzcyNzE1YjQ4ODc1NDZlYTQwMmRjZmM0ZDJlZjM2YjQ3NDAzMzkwNmUzNDBhMTY3N2I2MzNmOTYzYTg5Y2VlMWYyMzAyNWZhNGY0NDM0OGM4MTI0OWJiZTkxNWI2ZGJlNzdkYzNjYTE1MWU3NjcyYmVmY2M0Y2M1NmFkOWJjOTc0MWM3MDE1OGU4ZjhiZDQ0ODFjZTA5ZjRhZTdjMGFmMzBiOTU3ZjgwMzI3ZjEzZWU0YWY1OWVlNDUxODhmM2MwZDNkYzI2ZWNlMTdiYThiNzI4NGFjOWNiNmFmOTg2ZWQ3MDg5YzZlMjAxNTNhZGI4ZTFkMjkxMTgwNDA3OTYzNGFmNmZmZTJjN2I3MmVlNzU1MDlmMDg1YWVjODY5YjZmNzZkNDFkZmRhZGRkMGZlYTBjMzEyZTAxYWI0ZTc3MDUyNGFlZTY0YmVhYzI3N2JkNzRkMzAzMWNhNzhjMTdjMjNkNThhOTVhNTYxNDIzMjBlYTFmOTkwNDUzNTlmODI3NDczMmZiMmEwODc0YjFlNWIzZjViZWM0ZGI4OTA5OGFiMWU5Y2IxMmI2Yjg2NmNhZWJhZjNlYTI4ZTc4NDE2NmI5N2ViOGFjMTUxM2IzYTc1MDExMzc2ZmNmMzQyODhhZTQzNzBjYjNlNGYwOWVjYzM1OGVjYWQ4YTU2MmJiZTkwNDNiZTcyOTNlYzE0MzE3ZmM0MzEwN2JhOWEzYzM0NmY3NzI3ZWYzMjc1MTY5YTg4ZmQ0OWIxYTkxZWYwNTI0N2ExYmZkNjI3ZDQxYjczMmYwNzA2NDY4ZGMyMDJiYTEyYTI4ZmJlOGI5MDUxYTljMmQxMjUwNWZkMGU0YTIzM2Y5ZDc3MGU1YTc0NGQ1ZDQ4ODQwMDgxMjFhNWQ4OTJlMjJjNmQxYjY5MmM5ZTIwY2I5ZmY1ZDVlMTVkZjVkMjYyYzcxYmYzYmIyMDUzNTI1YjJlODFhNDkyZTk5ZjQzZWZjNDM0M2ViNTE4N2RlZWEwYzY0ZmU2MjM4ZThlNGM2ZDc5YWY5NzZjMmZhZTRmMmNjNzhmN2I0YjRjMTA4MzNiODNjYzNhODM4MTY2ZmQ1OTlhMjljYjdmYTI1NDU1ZTUzMGU2NjNjMDhhMTk0MjI3YmIxNDNkMzJiNDRlNjIzZTQ2MjdhYzIxNjA3ZGQyZGMxMjJiMjM0ZmM1NzAwYzcwYzMzNzIyMTk2M2YwYmI0YWI3ZTJmYjgwYmU2MTJkMDVjM2M4NjZhNmZmMzY4ZmM1MmVkZTUyMDUzOTIwOGE2MzUxMTYwNGViOWVmMmQ1NTIwMDczY2Q3OGM1NGEwOTE5MzE0NDMwYzRmYzkxZjUzZDdkNmI5MTA4OWNmMDA0ZjIyZGJlNmYwOTJhNmRlNzI1ZTAzNzIyNGM4N2Q2OGY0M2M3MmI5MTNjMzlmNGJhNzkzMjNmYTI5YjNkZWIyMjA2YzU0ZmYwYmJmNmQyN2Q3MGIxZTRhZTA0NGE3MmI1ZWRjMTU5ODFkOTRkODBhYmE3MDJiYmYxMWE2ZWQyOGNiMTNiODhhNGQzNmZlY2MyYzU1OGUxYjk4NzNhMjA1YmFjYjg0ZDY2ODQxODU3M2ViODJmMDI5YzQwYTgyOTBhNzI4N2MxMDBhNWIxMTBjOGNiNzZmOTg5NDM2YzdlYmMyOWU1MTU5NzFhMzJiMGJiNDY4MzU1MjFiYjAxZjUzYzA3MDU1NWE1M2QyMTljNjZiYzI4YTI5ZWM4Mzg0ZWRiMTQ5MDVkOTZjNjY5YjFhODRiYjRhNGI0MDVkYTIzMmMyMDk1ODQwYzBhZjc2MzFmZmM0YzIxNmJmNjdlYjVhN2ViOGNkNmY2OGU3MmQ3ZDVjZGNmZTc1MGE1ZDk2YzhkZjIzNGM2MjQwNTJhODM0NWM2Mzk1MGQ1MGYzNzFmNGMyM2YyNDJjZjZjODhjNDUzZWFlNGNmOGY4NzVjNGFkZjEzMGYwZjFhNGRhZDU2Nzc2NGMxM2Y2ZjEyNDc4ZTlkNzhkNTgxMDliODZlYjE4ZDA2ZjI3OWQxMjFlOWMwMjgxOGI3YmZlOTE5M2ViMjU3Zjg0Njg5YWJlMDcwNjIwZWU4ZjYyZmI0MjA4MTZhM2NkNjFkNDI0OGI3YjJmYzhmMGFlMjJhNjc4MmYxZTA1MTJiZGVmN2Y4ZTA5OTFhOTZlNTAxYTk2OTBiNmQ0ZjBjMTUwNmFlNDZkZGYyMWU1MDA2NjRjNzkwMTIzYjZiMzJkZWY4OTE3ZGU1YWY3OGIxMmNiNTUyNWNkM2E3YmY2MDc3MjA3ZjQxMGU3YTdiYzJjNTY0MjMzNmJjYThkNDliYjIwYzk1ODgyM2Q3MDU2N2I5Y2M5ZjA0MzdmOTljNzY0NzVhMDZiMDE1YWRmMGY0MWYyMWQ2ZWM5OTMxNzc1NDVmNWFkNDk1ZDU2OTY2YjBiYzA1NjAyZmYzMjM3MGU5OTFmODIwM2YzNjM2NWE0NTczMTIwNDdmN2ZlYzI4NzgwNTBlYjlhZTYwNjIyOWNkZjhjYTMwMmRiNDU2ODVkYWM2YWYyMDgzYTA5NjIzYmIwYmJiZmQ4ZTM5YzI3MmI2MmNkZTQ5MTVhZGRmZmI3NzRlMTEzNmNhNTcxNjAyM2ZmYjE2YTA1ZWI0MjYzNmI4NjU0NmIwZmExYjY2NzY0NzlkNDM2ZmRiMWQ3NDQ2ZWY1ZDZkNDY4OTA0MTFjY2ZkNzk2MzcxODkwNDhmOTA1NjFiZTVlZTA0MDMxMzJlMjZkYzFlODk0YmQwMTE0ODY2N2VhNDk3MGQwN2Q5NGNlYmNlYWFiYjM4MTgzMDAxMmZjOTMzYTBkZjIyN2Y2ZjFkZThhZjkyYWJjZTM2YjQzN2QzMmM1NTUzNDkwYzE4NzJhZmQzMjlkZjUyMDk2NDIyMWFhZmJhZjQ0YzA0YTdhM2Q5NDg3ZDI5NjZiZTVkMTYzYWQ3Y2Q1NjFjYTRjZDJiMjg1M2I0YjE0MzRlOTMzYjA5YTc2ODE3ZmM2NTZkZDY5MzhjN2I5NDI4ZDEwZjUzYWVkNDJlYmZjN2EyNWQyNmZlZGQzNzMwNjE4NDVjMGQ0NmJiOWUyNzA0YzFmM2I4MTUwMzA3Y2ZkNmNmYjRkZjdlMDdkZDNiNWMxMjJjMzYwOTM5ODg0YTMxZTgzNmUyMjRmZjc4MDFiMzA2OWM1MWE2NjdkNGE3MThkODA4Mzc4NmM5YmY1MzAyMWU3MmUzZmE5YjljZmY2M2MyYWE4MTE4MzlkMTdmZWIxNTE0OTU1ZGE5ZTkxMmExNWJiMTk3Nzc5YTUyMGY2MzllYjVlYzE2MzA1OWZmN2FjMWZmOTM2OWJjOTQxZDc5OTljYWIxNjhlMDM5ZDRiZDg5MjJlOGM2ZjQ0MzRjNDdlNTg3OGNkNTAzOWUyOTUyMDEzNzU3YWFhNTI4MWZkM2NkY2RjMzhlYTg4ZjZkZTdhMjkxNDJmYTc1YTkzNjJjNGYyMTNiZDc1ZjA0NTQ2ZGNlNDUzMzZiZTAwZmY2YTRhYzUxOGE4N2U5ZTk1ODFhYzA0YjhkZDhjMDRlNjEyZWQxNjY4MmI2NGQ0N2VlMTBjNGI0M2Y5MzU4ZmI0YmNmMDVjOGI2YzM0N2QwNTRmNDYxOWRjOTVmZTliZjI5MjhkMDE1MmJlZjk5MWU4NDc1NDU4ZjYyMzI4YzQ2ZDViNDhlNjJlODFjMDgxMTcxNmE4ODMxYTQ1NTVkNDljMmI4MjQ1NWYzOTE4YjQyNGFiZDYwYzc4N2U3YzJkOGQ2ZjVjZDIwY2IzYzYzOTUxZWMxY2RkODllZmJhZTA4YWM1NTBmMmY1ZmFjMGYyMzE0YWMwY2ZhMmI2ZThjMTYwMTA2OGM3YTI1ZDg2YzU2NjJlNDA5Y2Y4YjFmNzRlMGQ4OGRkNDAwMTExZGU0ZTdhMGU3N2VhY2EzZDEwZWI1NzRlNzYxYmE0NThjMGFlY2RiZGY0ZGRjYTMzOTk0ODQyMDNhNGE0MjI3YTA0YjBlZGM5YzBmZDcwZTZiOGY3YTQ2MWNkMTkyZWJjYWI2MTgwNTYzNjI3ODU2MWQxMWRhYWUyZmY1ZDdhZjg2MjBiNzllMmUxNjJmMzAyMDQxN2MzYzFmNjA0OWQ4OTZkMTk1ZDBkNTNmOTdkMTVlMjIwZWM5MDk5YzBiYmRhMjM0NTYwMjI4OGVhYWYxZWRkMzU4ZDcxN2VjZWIxOWRjOTcwYjUwNWQwOWI4MjkwMjk4NTYwMzc0NmE0YmVjZjQ4ODhjYTU1ZTAxMWMyZmNjZTExMWM3NWZlYmFmZjE2NjQyYTA5YTAzNDExZmJhZjQwOWIwOWM0YTMxZGJhMmY0ZGI4MmEzZWYyY2QyYzUzNTM0M2M1MGU0MzJkZjU0ZDI0OWFmYzQzNjRjMTAwMTMyMzhmMWRjYTNkMGEzNzI4YzA1NTQxMWEyM2VlZjQ3MWI5NTNkYTMyNzdkZWQwZDUyOWUwZmYzNzRlOTdiZjZkNzNjZGRlOTZmYWIwMjIyZDczOTA0NTQ3MGMxMjI0ZTA3NmJiNGE1YmQzMDlkNWU5OTlmMjNmYmYzZTExZmI2NDY2ZDdiNjZlZmNiM2I1ZWU5YWY5ODQ1ODQ0NzNmYzQ5MDIwZTUxMjJkNzBlMDAzNGQzZTZhYjQ5YTU3NTczNGY0OGE0ZTYyYmI0MWFhNzljMDhiMTgyYWQxYzBiYjk1MDBmNGJiM2JlNjBlYzkzZmM3YzE4N2RkODBmYzhjMzI2OWQwNWU2ZDAyYjdkMGRmOWFkMGZjOGQxMTM3YjYwYTUzNTdlMTI2Nzg2NTY4OTEyOTYyM2E0ZWIyYjY4MmZkMTQ1MDc1MDQyZGIwYWY0OGRiMDUwN2I3MDNiMmFjMzAwMTljYjVmNTJkNjViMTBiYjFhMTQ4NTMwNWE0YmI5NDliMzQ2YzQyNzAyYmMyOTUyNTAyZDgxYTJhMzlkN2JlOWRiYmNlMDRmM2RmYzhiZjA3
+
+
diff --git a/tests/res/import/data_syspass_invalid.xml b/tests/res/import/data_syspass_invalid.xml
index cbf4e586..c522072d 100644
--- a/tests/res/import/data_syspass_invalid.xml
+++ b/tests/res/import/data_syspass_invalid.xml
@@ -1,27 +1,4 @@
-
-
sysPass
diff --git a/tests/res/import/data_syspass_valid_hash.xml b/tests/res/import/data_syspass_valid_hash.xml
new file mode 100644
index 00000000..8b326db9
--- /dev/null
+++ b/tests/res/import/data_syspass_valid_hash.xml
@@ -0,0 +1,91 @@
+
+
+
+ sysPass
+ 300.18082201
+
+ admin
+
+ aa38d7292f6a00f3c16a19f99dd2940f04a2026a
+
+
+
+ AWS
+
+
+
+ GCP
+
+
+
+ SSH
+
+
+
+ Web
+
+
+
+
+
+ Amazon
+
+
+
+ Apple
+
+
+
+ Google
+
+
+
+
+
+ Apache
+
+
+ Email
+
+
+ JBoss
+
+
+ SaaS
+
+
+ SSH
+
+
+ Tomcat
+
+
+
+
+ Amazon SES
+ 3
+ 2
+ admin
+ https://aws.amazon.com/
+ Simple Email Service
+ def502008fde3a27bb1a6023f1b4ec524ba147c80ae78f90f9b3d0637af6809ad9263afaa36d34237ded5e8803b53f17121bd5e2a66919c0ce21d27c9360dfbc39d8151df9cbb4aadd1a0daa83ff59181ce1f1535c4b93d2701fa72c662115e919
+ def10000def502002937bbb81177ff4f769aeae126108783a141170588f3482fcbab44ae3390e80f85ab38521bafa8ec19648d267c9d46062294f5068a16706dcdb9042ddbb5b23ef54b3a12e7c5723b15d32ab9d8f5559e1fbc658c0b547a2a88a34b2498ef934d55395f857f28c9e3314493f319ebd4140d4a1b968e678cc12b58b5d2d057530be80f5f58c592cedcd85a90529d0895b11d6c04768bfbf4f1527babf5ee563798d35886f1c1017d1a3221cfd317cb84b13302d7d71c33e6753bcdb913dacd8909e195387f47b136a2789f85c48c3600f4ac6433a26e2bd0cae74fa2c461e4495108d340d7120febc65cfe79a4e2acc046f9bac29a0ce35cda
+
+
+
+
+
+ Google GCP
+ 1
+ 1
+ admin
+ https://cloud.google.com/
+ Google Cloud
+ def50200894e22b540a4975a3efe7dae669760aa6337195c690ac79ebff28f03393ae1070c5c7a635fa5a9aee5059df912c1948c41d8198f24f9f892a728d400d12d200c184a793d03a3f13eebfa45614efd0004ad7ea83338d1a04e20e6846078
+ def10000def50200f3d572936f4496e90369f6cc1feadce57c803b01a78c64b5987efd6369f7ecf526494a148d0e056feafe60d903bbb65ea9f79ad9b4f7ad42c7fe3c9c586f1807d252444c42667129da3727cf6f702a5aadf5db2391ba4a581f950df65262ae04b314b88d69d3174e6f226cb1f939f04d102799e58e0b6ed839fe2282056c58069e6298865c386e3d2114635621ed14eb199b1dad6dfcabc9b364ea2ae147c38352cd72bc0c79761a9df0f58690d5da1d1e3cc5e17261d740ca6863383a869b0253790d46ba2df032e741e8bb788033d8eb7b97d124d58b3c4310d15df7a4fd4d373dcc0ae111d46f6d623bac7ccf330520439736b223ae81
+
+
+
+
+
+