. */ namespace SP\Tests\Domain\Export\Services; use DOMDocument; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\MockObject\MockObject; use SP\Core\Crypt\Hash; use SP\Domain\Common\Services\ServiceException; use SP\Domain\Config\Adapters\ConfigData; use SP\Domain\Config\Ports\ConfigDataInterface; use SP\Domain\Config\Ports\ConfigFileService; use SP\Domain\Core\Crypt\CryptInterface; use SP\Domain\Core\Exceptions\CryptException; use SP\Domain\Export\Services\XmlVerify; use SP\Tests\UnitaryTestCase; /** * Class XmlVerifyTest * */ #[Group('unitary')] class XmlVerifyTest extends UnitaryTestCase { private const VALID_ENCRYPTED_FILE = RESOURCE_PATH . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'data_syspass_encrypted.xml'; private const VALID_FILE = RESOURCE_PATH . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'data_syspass.xml'; private const NO_VALID_FILE = RESOURCE_PATH . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'data_syspass_invalid.xml'; private const NO_VALID_VERSION_FILE = RESOURCE_PATH . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'data_syspass_invalid_version.xml'; private const NO_VALID_HASH_FILE = RESOURCE_PATH . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'data_syspass_invalid_hash.xml'; private CryptInterface|MockObject $crypt; private XmlVerify $xmlVerify; public function testCheckXmlHashWithNoHash() { $xml = ''; $document = new DOMDocument(); $document->loadXML($xml); $out = XmlVerify::checkXmlHash($document, 'test'); $this->assertFalse($out); } public function testCheckXmlHashWithNoSign() { $xml = 'a_hash'; $document = new DOMDocument(); $document->loadXML($xml); $out = XmlVerify::checkXmlHash($document, 'test'); $this->assertFalse($out); } public function testCheckXmlHashWithWrongHash() { $xml = 'a_hash'; $document = new DOMDocument(); $document->loadXML($xml); $out = XmlVerify::checkXmlHash($document, 'test'); $this->assertFalse($out); } public function testCheckXmlHash() { $sign = Hash::signMessage('test_data', 'test_key'); $xml = sprintf('test_data', $sign); $document = new DOMDocument(); $document->loadXML($xml); $out = XmlVerify::checkXmlHash($document, 'test_key'); $this->assertTrue($out); } public function testCheckXmlHashWithNodesHash() { $xml = sprintf( '%stest_data', sha1('test_data') ); $document = new DOMDocument(); $document->loadXML($xml); $out = XmlVerify::checkXmlHash($document, 'test_key'); $this->assertTrue($out); } /** * @throws ServiceException */ public function testVerify() { $out = $this->xmlVerify->verify(self::VALID_FILE); $this->assertEquals('300.18071701', $out->getVersion()); $nodes = $out->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']); $this->assertFalse($out->isEncrypted()); } /** * @throws ServiceException */ public function testVerifyWithInvalidFile() { $this->expectException(ServiceException::class); $this->expectExceptionMessage('Unable to load XML file'); $this->xmlVerify->verify('a_file'); } /** * @throws ServiceException */ public function testVerifyWithInvalidFileSchema() { $this->expectException(ServiceException::class); $this->expectExceptionMessage('Invalid XML schema'); $this->xmlVerify->verify(self::NO_VALID_FILE); } /** * @throws ServiceException */ public function testVerifyWithInvalidVersion() { $this->expectException(ServiceException::class); $this->expectExceptionMessage('Sorry, this XML version is not compatible. Please use >= 2.1'); $this->xmlVerify->verify(self::NO_VALID_VERSION_FILE); } /** * @throws ServiceException */ public function testVerifyWithInvalidHash() { $this->expectException(ServiceException::class); $this->expectExceptionMessage('Error while checking integrity hash'); $this->xmlVerify->verify(self::NO_VALID_HASH_FILE); } /** * @throws ServiceException */ public function testVerifyEncrypted() { $categories = 'CSV Category 1'; $clients = 'Apple'; $tags = 'Apache'; $accounts = 'Google11adminhttps://google.comatest'; $this->crypt ->expects(self::exactly(4)) ->method('decrypt') ->with( self::stringStartsWith('def50200'), self::stringStartsWith('def10000def5020'), 'test_encrypt' ) ->willReturn($categories, $clients, $tags, $accounts); $out = $this->xmlVerify->verify(self::VALID_ENCRYPTED_FILE, 'test_encrypt'); $this->assertEquals('300.18082201', $out->getVersion()); $nodes = $out->getNodes(); $this->assertCount(4, $nodes); $this->assertEquals(1, $nodes['Category']); $this->assertEquals(1, $nodes['Client']); $this->assertEquals(1, $nodes['Tag']); $this->assertEquals(1, $nodes['Account']); $this->assertFalse($out->isEncrypted()); } /** * @throws ServiceException */ public function testVerifyEncryptedWithCryptException() { $this->crypt ->expects(self::once()) ->method('decrypt') ->willThrowException(CryptException::error('test')); $this->expectException(ServiceException::class); $this->expectExceptionMessage('Wrong encryption password'); $this->xmlVerify->verify(self::VALID_ENCRYPTED_FILE, 'test_encrypt'); } protected function buildConfig(): ConfigFileService { $configData = new ConfigData([ConfigDataInterface::PASSWORD_SALT => 'a_salt']); $config = $this->createStub(ConfigFileService::class); $config->method('getConfigData')->willReturn($configData); return $config; } protected function setUp(): void { parent::setUp(); $this->crypt = $this->createMock(CryptInterface::class); $this->xmlVerify = new XmlVerify($this->application, $this->crypt); } }