mirror of
https://github.com/LibreSign/libresign.git
synced 2025-12-18 05:20:45 +01:00
- Add reset() method to AppConfigOverwrite that clears overWrite and deleted arrays and returns self - Integrate reset() directly into getMockAppConfig() to ensure clean state on every call - All tests now automatically get clean AppConfig state without explicit reset calls - Prevents state pollution across test suites by resetting at the source - Simplifies test code by removing need for separate reset wrapper method Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1321 lines
42 KiB
PHP
1321 lines
42 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
/**
|
|
* SPDX-FileCopyrightText: 2020-2024 LibreCode coop and contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
use bovigo\vfs\vfsStream;
|
|
use OC\User\NoUserException;
|
|
use OCA\Libresign\Db\File;
|
|
use OCA\Libresign\Db\FileElement;
|
|
use OCA\Libresign\Db\FileElementMapper;
|
|
use OCA\Libresign\Db\FileMapper;
|
|
use OCA\Libresign\Db\IdDocsMapper;
|
|
use OCA\Libresign\Db\IdentifyMethod;
|
|
use OCA\Libresign\Db\IdentifyMethodMapper;
|
|
use OCA\Libresign\Db\SignRequest;
|
|
use OCA\Libresign\Db\SignRequestMapper;
|
|
use OCA\Libresign\Db\UserElement;
|
|
use OCA\Libresign\Db\UserElementMapper;
|
|
use OCA\Libresign\Events\SignedEvent;
|
|
use OCA\Libresign\Events\SignedEventFactory;
|
|
use OCA\Libresign\Exception\LibresignException;
|
|
use OCA\Libresign\Handler\DocMdpHandler;
|
|
use OCA\Libresign\Handler\FooterHandler;
|
|
use OCA\Libresign\Handler\PdfTk\Pdf;
|
|
use OCA\Libresign\Handler\SignEngine\Pkcs12Handler;
|
|
use OCA\Libresign\Handler\SignEngine\Pkcs7Handler;
|
|
use OCA\Libresign\Handler\SignEngine\SignEngineFactory;
|
|
use OCA\Libresign\Helper\JavaHelper;
|
|
use OCA\Libresign\Helper\ValidateHelper;
|
|
use OCA\Libresign\Service\FolderService;
|
|
use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod;
|
|
use OCA\Libresign\Service\IdentifyMethodService;
|
|
use OCA\Libresign\Service\PdfSignatureDetectionService;
|
|
use OCA\Libresign\Service\SignerElementsService;
|
|
use OCA\Libresign\Service\SignFileService;
|
|
use OCP\AppFramework\Db\DoesNotExistException;
|
|
use OCP\AppFramework\Utility\ITimeFactory;
|
|
use OCP\EventDispatcher\IEventDispatcher;
|
|
use OCP\Files\IRootFolder;
|
|
use OCP\Files\NotFoundException;
|
|
use OCP\Files\NotPermittedException;
|
|
use OCP\Http\Client\IClientService;
|
|
use OCP\IAppConfig;
|
|
use OCP\IDateTimeZone;
|
|
use OCP\IL10N;
|
|
use OCP\ITempManager;
|
|
use OCP\IURLGenerator;
|
|
use OCP\IUserManager;
|
|
use OCP\IUserSession;
|
|
use OCP\Security\ISecureRandom;
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
/**
|
|
* @group DB
|
|
*/
|
|
final class SignFileServiceTest extends \OCA\Libresign\Tests\Unit\TestCase {
|
|
private IL10N&MockObject $l10n;
|
|
private FooterHandler&MockObject $footerHandler;
|
|
private FileMapper&MockObject $fileMapper;
|
|
private SignRequestMapper&MockObject $signRequestMapper;
|
|
private IdDocsMapper&MockObject $idDocsMapper;
|
|
private IClientService&MockObject $clientService;
|
|
private IUserManager&MockObject $userManager;
|
|
private FolderService&MockObject $folderService;
|
|
private LoggerInterface&MockObject $logger;
|
|
private IAppConfig $appConfig;
|
|
private ValidateHelper&MockObject $validateHelper;
|
|
private SignerElementsService&MockObject $signerElementsService;
|
|
private IRootFolder&MockObject $root;
|
|
private IUserSession&MockObject $userSession;
|
|
private IDateTimeZone $dateTimeZone;
|
|
private FileElementMapper&MockObject $fileElementMapper;
|
|
private UserElementMapper&MockObject $userElementMapper;
|
|
private IEventDispatcher&MockObject $eventDispatcher;
|
|
private ISecureRandom $secureRandom;
|
|
private IURLGenerator&MockObject $urlGenerator;
|
|
private IdentifyMethodMapper&MockObject $identifyMethodMapper;
|
|
private ITempManager|MockObject $tempManager;
|
|
private IdentifyMethodService&MockObject $identifyMethodService;
|
|
private ITimeFactory&MockObject $timeFactory;
|
|
private JavaHelper&MockObject $javaHelper;
|
|
private SignEngineFactory $signEngineFactory;
|
|
private SignedEventFactory&MockObject $signedEventFactory;
|
|
private Pdf&MockObject $pdf;
|
|
private DocMdpHandler $docMdpHandler;
|
|
private PdfSignatureDetectionService&MockObject $pdfSignatureDetectionService;
|
|
private \OCA\Libresign\Service\SequentialSigningService&MockObject $sequentialSigningService;
|
|
|
|
public function setUp(): void {
|
|
parent::setUp();
|
|
$this->l10n = $this->createMock(IL10N::class);
|
|
$this->l10n
|
|
->method('t')
|
|
->willReturnArgument(0);
|
|
$this->fileMapper = $this->createMock(FileMapper::class);
|
|
$this->signRequestMapper = $this->createMock(SignRequestMapper::class);
|
|
$this->idDocsMapper = $this->createMock(IdDocsMapper::class);
|
|
$this->footerHandler = $this->createMock(FooterHandler::class);
|
|
$this->clientService = $this->createMock(IClientService::class);
|
|
$this->userManager = $this->createMock(IUserManager::class);
|
|
$this->folderService = $this->createMock(FolderService::class);
|
|
$this->logger = $this->createMock(LoggerInterface::class);
|
|
$this->appConfig = $this->getMockAppConfigWithReset();
|
|
$this->validateHelper = $this->createMock(\OCA\Libresign\Helper\ValidateHelper::class);
|
|
$this->signerElementsService = $this->createMock(SignerElementsService::class);
|
|
$this->root = $this->createMock(\OCP\Files\IRootFolder::class);
|
|
$this->userSession = $this->createMock(IUserSession::class);
|
|
$this->dateTimeZone = \OCP\Server::get(IDateTimeZone::class);
|
|
$this->fileElementMapper = $this->createMock(FileElementMapper::class);
|
|
$this->userElementMapper = $this->createMock(UserElementMapper::class);
|
|
$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
|
|
$this->secureRandom = \OCP\Server::get(\OCP\Security\ISecureRandom::class);
|
|
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
|
$this->identifyMethodMapper = $this->createMock(IdentifyMethodMapper::class);
|
|
$this->tempManager = $this->createMock(ITempManager::class);
|
|
$this->identifyMethodService = $this->createMock(IdentifyMethodService::class);
|
|
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
|
$this->javaHelper = $this->createMock(JavaHelper::class);
|
|
$this->signEngineFactory = \OCP\Server::get(SignEngineFactory::class);
|
|
$this->signedEventFactory = $this->createMock(SignedEventFactory::class);
|
|
$this->pdf = $this->createMock(Pdf::class);
|
|
$this->docMdpHandler = new DocMdpHandler($this->l10n);
|
|
$this->pdfSignatureDetectionService = $this->createMock(PdfSignatureDetectionService::class);
|
|
$this->sequentialSigningService = $this->createMock(\OCA\Libresign\Service\SequentialSigningService::class);
|
|
}
|
|
|
|
private function getService(array $methods = []): SignFileService|MockObject {
|
|
if ($methods) {
|
|
return $this->getMockBuilder(SignFileService::class)
|
|
->setConstructorArgs([
|
|
$this->l10n,
|
|
$this->fileMapper,
|
|
$this->signRequestMapper,
|
|
$this->idDocsMapper,
|
|
$this->footerHandler,
|
|
$this->folderService,
|
|
$this->clientService,
|
|
$this->userManager,
|
|
$this->logger,
|
|
$this->appConfig,
|
|
$this->validateHelper,
|
|
$this->signerElementsService,
|
|
$this->root,
|
|
$this->userSession,
|
|
$this->dateTimeZone,
|
|
$this->fileElementMapper,
|
|
$this->userElementMapper,
|
|
$this->eventDispatcher,
|
|
$this->secureRandom,
|
|
$this->urlGenerator,
|
|
$this->identifyMethodMapper,
|
|
$this->tempManager,
|
|
$this->identifyMethodService,
|
|
$this->timeFactory,
|
|
$this->signEngineFactory,
|
|
$this->signedEventFactory,
|
|
$this->pdf,
|
|
$this->docMdpHandler,
|
|
$this->pdfSignatureDetectionService,
|
|
$this->sequentialSigningService,
|
|
])
|
|
->onlyMethods($methods)
|
|
->getMock();
|
|
}
|
|
return new SignFileService(
|
|
$this->l10n,
|
|
$this->fileMapper,
|
|
$this->signRequestMapper,
|
|
$this->idDocsMapper,
|
|
$this->footerHandler,
|
|
$this->folderService,
|
|
$this->clientService,
|
|
$this->userManager,
|
|
$this->logger,
|
|
$this->appConfig,
|
|
$this->validateHelper,
|
|
$this->signerElementsService,
|
|
$this->root,
|
|
$this->userSession,
|
|
$this->dateTimeZone,
|
|
$this->fileElementMapper,
|
|
$this->userElementMapper,
|
|
$this->eventDispatcher,
|
|
$this->secureRandom,
|
|
$this->urlGenerator,
|
|
$this->identifyMethodMapper,
|
|
$this->tempManager,
|
|
$this->identifyMethodService,
|
|
$this->timeFactory,
|
|
$this->signEngineFactory,
|
|
$this->signedEventFactory,
|
|
$this->pdf,
|
|
$this->docMdpHandler,
|
|
$this->pdfSignatureDetectionService,
|
|
$this->sequentialSigningService,
|
|
);
|
|
}
|
|
|
|
public function testCanDeleteRequestSignatureWhenDocumentAlreadySigned():void {
|
|
$file = $this->createMock(\OCA\Libresign\Db\File::class);
|
|
$file->method('__call')->with($this->equalTo('getId'))->willReturn(1);
|
|
$this->fileMapper->method('getByUuid')->willReturn($file);
|
|
$signRequest = $this->createMock(\OCA\Libresign\Db\SignRequest::class);
|
|
$signRequest
|
|
->method('__call')
|
|
->willReturnCallback(fn (string $method)
|
|
=> match ($method) {
|
|
'getSigned' => '2021-01-01 01:01:01',
|
|
}
|
|
);
|
|
$this->signRequestMapper->method('getByFileUuid')->willReturn([$signRequest]);
|
|
$this->expectExceptionMessage('Document already signed');
|
|
$this->getService()->canDeleteRequestSignature(['uuid' => 'valid']);
|
|
}
|
|
|
|
public function testCanDeleteRequestSignatureWhenNoSignatureWasRequested():void {
|
|
$file = $this->createMock(\OCA\Libresign\Db\File::class);
|
|
$file->method('__call')->with($this->equalTo('getId'))->willReturn(1);
|
|
$this->fileMapper->method('getByUuid')->willReturn($file);
|
|
$signRequest = $this->createMock(\OCA\Libresign\Db\SignRequest::class);
|
|
$signRequest
|
|
->method('__call')
|
|
->willReturnCallback(fn (string $method)
|
|
=> match ($method) {
|
|
'getSigned' => null,
|
|
'getId' => 171,
|
|
}
|
|
);
|
|
$this->signRequestMapper->method('getByFileUuid')->willReturn([$signRequest]);
|
|
$this->expectExceptionMessage('No signature was requested to %');
|
|
$this->getService()->canDeleteRequestSignature([
|
|
'uuid' => 'valid',
|
|
'users' => [
|
|
[
|
|
'email' => 'test@test.coop'
|
|
]
|
|
]
|
|
]);
|
|
}
|
|
|
|
public function testNotifyCallback():void {
|
|
$libreSignFile = new \OCA\Libresign\Db\File();
|
|
$libreSignFile->setCallback('https://test.coop');
|
|
$service = $this->getService();
|
|
$service->setLibreSignFile($libreSignFile);
|
|
$file = $this->createMock(\OCP\Files\File::class);
|
|
$actual = $service->notifyCallback($file);
|
|
$this->assertNull($actual);
|
|
}
|
|
|
|
public function testSignWithFileNotFound():void {
|
|
$this->expectExceptionMessage('File not found');
|
|
|
|
$file = new \OCA\Libresign\Db\File();
|
|
$file->setUserId('username');
|
|
|
|
$this->root->method('getUserFolder')
|
|
->willReturn($this->root);
|
|
|
|
$signRequest = new \OCA\Libresign\Db\SignRequest();
|
|
$this->getService()
|
|
->setLibreSignFile($file)
|
|
->setSignRequest($signRequest)
|
|
->setPassword('password')
|
|
->sign();
|
|
}
|
|
|
|
#[DataProvider('dataSignGenerateASha256OfSignedFile')]
|
|
public function testSignGenerateASha256OfSignedFile(string $signedContent):void {
|
|
$service = $this->getService([
|
|
'getEngine',
|
|
'setNewStatusIfNecessary',
|
|
'getNextcloudFile',
|
|
'validateDocMdpAllowsSignatures',
|
|
]);
|
|
|
|
$nextcloudFile = $this->createMock(\OCP\Files\File::class);
|
|
$nextcloudFile->method('getContent')->willReturn($signedContent);
|
|
$service->method('getNextcloudFile')->willReturn($nextcloudFile);
|
|
$service->method('validateDocMdpAllowsSignatures');
|
|
|
|
$pkcs12Handler = $this->createMock(Pkcs12Handler::class);
|
|
$pkcs12Handler->method('sign')->willReturn($nextcloudFile);
|
|
$service->method('getEngine')->willReturn($pkcs12Handler);
|
|
|
|
$expectedHash = hash('sha256', $signedContent);
|
|
|
|
$totalCalls = 0;
|
|
$hashCallback = function ($method, $args) use ($expectedHash, &$totalCalls) {
|
|
switch ($method) {
|
|
case 'setSignedHash':
|
|
$this->assertEquals($expectedHash, $args[0], 'Hash of signed file should match expected SHA-256 value');
|
|
$totalCalls++;
|
|
break;
|
|
case 'getFileId':
|
|
return 1;
|
|
case 'getSigningOrder':
|
|
return 1;
|
|
case 'getDocmdpLevelEnum':
|
|
return \OCA\Libresign\Enum\DocMdpLevel::NOT_CERTIFIED;
|
|
default: return null;
|
|
}
|
|
};
|
|
$signRequest = $this->createMock(SignRequest::class);
|
|
$signRequest->method('__call')->willReturnCallback($hashCallback);
|
|
|
|
$libreSignFile = $this->createMock(\OCA\Libresign\Db\File::class);
|
|
$libreSignFile->method('__call')->willReturnCallback($hashCallback);
|
|
|
|
$service
|
|
->setSignRequest($signRequest)
|
|
->setLibreSignFile($libreSignFile)
|
|
->sign();
|
|
$this->assertEquals(2, $totalCalls, 'setSignedHash should be called twice');
|
|
}
|
|
|
|
public static function dataSignGenerateASha256OfSignedFile(): array {
|
|
return [
|
|
['signed content'],
|
|
['another signed content'],
|
|
];
|
|
}
|
|
|
|
public function testUpdateDatabaseWhenSign(): void {
|
|
$service = $this->getService([
|
|
'getEngine',
|
|
'setNewStatusIfNecessary',
|
|
'computeHash',
|
|
'getNextcloudFile',
|
|
'validateDocMdpAllowsSignatures',
|
|
]);
|
|
|
|
$nextcloudFile = $this->createMock(\OCP\Files\File::class);
|
|
$nextcloudFile->method('getContent')->willReturn('pdf content');
|
|
$service->method('getNextcloudFile')->willReturn($nextcloudFile);
|
|
$service->method('validateDocMdpAllowsSignatures');
|
|
|
|
$this->fileMapper->expects($this->once())->method('update');
|
|
$this->signRequestMapper->expects($this->once())->method('update');
|
|
|
|
$signRequest = $this->createMock(SignRequest::class);
|
|
$signRequest->method('__call')->willReturnCallback(function ($method, $args) {
|
|
switch ($method) {
|
|
case 'getFileId':
|
|
return 1;
|
|
case 'getSigningOrder':
|
|
return 1;
|
|
default: return null;
|
|
}
|
|
});
|
|
$libreSignFile = $this->createMock(\OCA\Libresign\Db\File::class);
|
|
$libreSignFile->method('__call')->willReturnCallback(function ($method) {
|
|
if ($method === 'getDocmdpLevelEnum') {
|
|
return \OCA\Libresign\Enum\DocMdpLevel::NOT_CERTIFIED;
|
|
}
|
|
return null;
|
|
});
|
|
|
|
$service
|
|
->setSignRequest($signRequest)
|
|
->setLibreSignFile($libreSignFile)
|
|
->sign();
|
|
}
|
|
|
|
public function testDispatchEventWhenSign(): void {
|
|
$service = $this->getService([
|
|
'getEngine',
|
|
'setNewStatusIfNecessary',
|
|
'computeHash',
|
|
'getNextcloudFile',
|
|
'validateDocMdpAllowsSignatures',
|
|
]);
|
|
|
|
$nextcloudFile = $this->createMock(\OCP\Files\File::class);
|
|
$nextcloudFile->method('getContent')->willReturn('pdf content');
|
|
$service->method('getNextcloudFile')->willReturn($nextcloudFile);
|
|
$service->method('validateDocMdpAllowsSignatures');
|
|
|
|
$this->eventDispatcher
|
|
->expects($this->once())
|
|
->method('dispatchTyped')
|
|
->with($this->isInstanceOf(SignedEvent::class));
|
|
|
|
$signRequest = $this->createMock(SignRequest::class);
|
|
$signRequest->method('__call')->willReturnCallback(function ($method, $args) {
|
|
switch ($method) {
|
|
case 'getFileId':
|
|
return 1;
|
|
case 'getSigningOrder':
|
|
return 1;
|
|
default: return null;
|
|
}
|
|
});
|
|
$libreSignFile = $this->createMock(\OCA\Libresign\Db\File::class);
|
|
$libreSignFile->method('__call')->willReturnCallback(function ($method) {
|
|
if ($method === 'getDocmdpLevelEnum') {
|
|
return \OCA\Libresign\Enum\DocMdpLevel::NOT_CERTIFIED;
|
|
}
|
|
return null;
|
|
});
|
|
|
|
$service
|
|
->setSignRequest($signRequest)
|
|
->setLibreSignFile($libreSignFile)
|
|
->sign();
|
|
}
|
|
|
|
#[DataProvider('providerCheckStatusAfterSign')]
|
|
public function testCheckStatusAfterSign(array $inputSigners, int $fileStatus, int $finalStatus): void {
|
|
$service = $this->getService([
|
|
'getEngine',
|
|
'computeHash',
|
|
'getSigners',
|
|
'getNextcloudFile',
|
|
]);
|
|
|
|
$nextcloudFile = $this->createMock(\OCP\Files\File::class);
|
|
$nextcloudFile->method('getContent')->willReturn('pdf content');
|
|
$service->method('getNextcloudFile')->willReturn($nextcloudFile);
|
|
|
|
$service->method('getSigners')->willReturn($inputSigners);
|
|
|
|
$signRequestCallback = function ($method, $args) {
|
|
switch ($method) {
|
|
case 'getFileId':
|
|
return 1;
|
|
case 'getSigningOrder':
|
|
return 1;
|
|
default: return null;
|
|
}
|
|
};
|
|
$signRequest = $this->createMock(SignRequest::class);
|
|
$signRequest->method('__call')->willReturnCallback($signRequestCallback);
|
|
$libreSignFile = new \OCA\Libresign\Db\File();
|
|
$libreSignFile->setStatus($fileStatus);
|
|
$libreSignFile->resetUpdatedFields();
|
|
|
|
$service
|
|
->setSignRequest($signRequest)
|
|
->setLibreSignFile($libreSignFile)
|
|
->sign();
|
|
|
|
$this->assertEquals($finalStatus, $libreSignFile->getStatus());
|
|
|
|
$updatedFields = $libreSignFile->getUpdatedFields();
|
|
if ($fileStatus !== $finalStatus) {
|
|
$this->assertArrayHasKey('status', $updatedFields);
|
|
$this->assertTrue($updatedFields['status']);
|
|
} else {
|
|
$this->assertArrayNotHasKey('status', $updatedFields);
|
|
}
|
|
}
|
|
|
|
public static function providerCheckStatusAfterSign(): array {
|
|
return [
|
|
[self::generateSigners(5, 1), File::STATUS_ABLE_TO_SIGN, File::STATUS_PARTIAL_SIGNED],
|
|
[self::generateSigners(5, 1), File::STATUS_PARTIAL_SIGNED, File::STATUS_PARTIAL_SIGNED],
|
|
[self::generateSigners(5, 5), File::STATUS_ABLE_TO_SIGN, File::STATUS_SIGNED],
|
|
[self::generateSigners(3, 0), File::STATUS_ABLE_TO_SIGN, File::STATUS_ABLE_TO_SIGN],
|
|
[self::generateSigners(3, 3), File::STATUS_PARTIAL_SIGNED, File::STATUS_SIGNED],
|
|
[self::generateSigners(2, 2), File::STATUS_SIGNED, File::STATUS_SIGNED],
|
|
[self::generateSigners(4, 3), File::STATUS_ABLE_TO_SIGN, File::STATUS_PARTIAL_SIGNED],
|
|
[self::generateSigners(4, 4), File::STATUS_PARTIAL_SIGNED, File::STATUS_SIGNED],
|
|
[self::generateSigners(1, 1), File::STATUS_ABLE_TO_SIGN, File::STATUS_SIGNED],
|
|
[self::generateSigners(0, 0), File::STATUS_ABLE_TO_SIGN, File::STATUS_ABLE_TO_SIGN],
|
|
];
|
|
}
|
|
|
|
private static function generateSigners(int $total, int $signed): array {
|
|
$signers = [];
|
|
for ($i = 0; $i < $total; $i ++) {
|
|
$signers[] = new SignRequest();
|
|
}
|
|
for ($i = 0; $i < $signed; $i ++) {
|
|
$signers[$i]->setSigned(new DateTime());
|
|
}
|
|
return $signers;
|
|
}
|
|
|
|
#[DataProvider('providerGetEngineWillWorkWithLazyLoadedEngine')]
|
|
public function testGetEngineWillWorkWithLazyLoadedEngine(string $extension, string $instanceOf): void {
|
|
$service = $this->getService([
|
|
'updateSignRequest',
|
|
'updateLibreSignFile',
|
|
'dispatchSignedEvent',
|
|
'getFileToSign',
|
|
'configureEngine',
|
|
'getSignatureParams',
|
|
]);
|
|
|
|
$file = $this->createMock(\OCP\Files\File::class);
|
|
$file->method('getExtension')->willReturn($extension);
|
|
$service->method('getFileToSign')->willReturn($file);
|
|
|
|
$engine = self::invokePrivate($service, 'getEngine');
|
|
|
|
$this->assertInstanceOf($instanceOf, $engine);
|
|
}
|
|
|
|
public static function providerGetEngineWillWorkWithLazyLoadedEngine(): array {
|
|
return [
|
|
['pdf', Pkcs12Handler::class],
|
|
['PDF', Pkcs12Handler::class],
|
|
['odt', Pkcs7Handler::class],
|
|
['ODT', Pkcs7Handler::class],
|
|
['jpg', Pkcs7Handler::class],
|
|
['JPG', Pkcs7Handler::class],
|
|
['png', Pkcs7Handler::class],
|
|
['PNG', Pkcs7Handler::class],
|
|
['txt', Pkcs7Handler::class],
|
|
['TXT', Pkcs7Handler::class],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerGetOrGeneratePfxContent')]
|
|
public function testGetOrGeneratePfxContent(bool $signWithoutPassword, string $occurrency): void {
|
|
$service = $this->getService([
|
|
'getFileToSign',
|
|
'identifyEngine',
|
|
'generateTemporaryPassword',
|
|
'computeHash',
|
|
'updateSignRequest',
|
|
'updateLibreSignFile',
|
|
'dispatchSignedEvent',
|
|
'validateDocMdpAllowsSignatures',
|
|
]);
|
|
|
|
$signEngineHandler = $this->getMockBuilder(Pkcs12Handler::class)
|
|
->disableOriginalConstructor()
|
|
->onlyMethods([
|
|
'getCertificate',
|
|
'getPfxOfCurrentSigner',
|
|
'generateCertificate',
|
|
'sign',
|
|
])
|
|
->getMock();
|
|
|
|
$signEngineHandler->expects($this->{$occurrency}())->method('generateCertificate');
|
|
$service->method('identifyEngine')->willReturn($signEngineHandler);
|
|
|
|
$service
|
|
->setSignWithoutPassword($signWithoutPassword)
|
|
->sign();
|
|
}
|
|
|
|
public static function providerGetOrGeneratePfxContent(): array {
|
|
return [
|
|
[true, 'once'],
|
|
[false, 'never'],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerStoreUserMetadata')]
|
|
public function testStoreUserMetadata(bool $collectMetadata, ?array $previous, array $new, ?array $expected): void {
|
|
$signRequest = new \OCA\Libresign\Db\SignRequest();
|
|
$this->appConfig->setValueBool('libresign', 'collect_metadata', $collectMetadata);
|
|
$signRequest->setMetadata($previous);
|
|
$this->getService()
|
|
->setSignRequest($signRequest)
|
|
->storeUserMetadata($new);
|
|
$this->assertEquals(
|
|
$expected,
|
|
$signRequest->getMetadata()
|
|
);
|
|
}
|
|
|
|
public static function providerStoreUserMetadata(): array {
|
|
return [
|
|
// don't collect metadata
|
|
[false, null, [], null],
|
|
[false, null, ['b' => 2], null],
|
|
[false, null, ['b' => null], null],
|
|
[false, null, ['b' => []], null],
|
|
[false, null, ['b' => ['']], null],
|
|
[false, null, ['b' => ['b' => 1]], null],
|
|
// collect metadata without previous value
|
|
[true, null, [], null],
|
|
[true, null, ['b' => 2], ['b' => 2]],
|
|
[true, null, ['b' => null], ['b' => null]],
|
|
[true, null, ['b' => []], ['b' => []]],
|
|
[true, null, ['b' => ['']], ['b' => ['']]],
|
|
[true, null, ['b' => ['b' => 1]], ['b' => ['b' => 1]]],
|
|
// collect metadata with previous value
|
|
[true, ['a' => 1], ['a' => 2], ['a' => 2]],
|
|
[true, ['a' => 1], ['a' => null], ['a' => null]],
|
|
[true, ['a' => 1], ['a' => []], ['a' => []]],
|
|
[true, ['a' => 1], ['a' => ['']], ['a' => ['']]],
|
|
[true, ['a' => 1], ['a' => ['b' => 1]], ['a' => ['b' => 1]]],
|
|
[true, ['a' => 1], ['b' => 2], ['a' => 1, 'b' => 2]],
|
|
];
|
|
}
|
|
|
|
private function createSignRequestMock(array $methods): MockObject {
|
|
$signRequest = $this->createMock(SignRequest::class);
|
|
$signRequest->method('__call')->willReturnCallback(fn (string $method)
|
|
=> $methods[$method] ?? null
|
|
);
|
|
return $signRequest;
|
|
}
|
|
|
|
#[DataProvider('providerGetSignatureParamsCommonName')]
|
|
public function testGetSignatureParamsCommonName(
|
|
array $certData,
|
|
string $expectedIssuerCN,
|
|
string $expectedSignerCN,
|
|
): void {
|
|
$service = $this->getService(['readCertificate']);
|
|
|
|
$libreSignFile = new \OCA\Libresign\Db\File();
|
|
$libreSignFile->setUuid('uuid');
|
|
$service->setLibreSignFile($libreSignFile);
|
|
|
|
$service->method('readCertificate')->willReturn($certData);
|
|
$service->setCurrentUser(null);
|
|
|
|
$signRequest = $this->createSignRequestMock([
|
|
'getId' => 171,
|
|
'getMetadata' => [],
|
|
]);
|
|
$service->setSignRequest($signRequest);
|
|
|
|
$actual = $this->invokePrivate($service, 'getSignatureParams');
|
|
$this->assertEquals($expectedIssuerCN, $actual['IssuerCommonName']);
|
|
$this->assertEquals($expectedSignerCN, $actual['SignerCommonName']);
|
|
$this->assertEquals('uuid', $actual['DocumentUUID']);
|
|
$this->assertArrayHasKey('DocumentUUID', $actual);
|
|
$this->assertArrayHasKey('LocalSignerTimezone', $actual);
|
|
$this->assertArrayHasKey('LocalSignerSignatureDateTime', $actual);
|
|
}
|
|
|
|
public static function providerGetSignatureParamsCommonName(): array {
|
|
return [
|
|
'simple CNs' => [
|
|
[
|
|
'issuer' => ['CN' => 'LibreCode'],
|
|
'subject' => ['CN' => 'Jane Doe'],
|
|
],
|
|
'LibreCode',
|
|
'Jane Doe',
|
|
],
|
|
'empty CNs' => [
|
|
[
|
|
'issuer' => ['CN' => ''],
|
|
'subject' => ['CN' => ''],
|
|
],
|
|
'',
|
|
'',
|
|
],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerGetSignatureParamsSignerEmail')]
|
|
public function testGetSignatureParamsSignerEmail(
|
|
array $certData,
|
|
string $authenticatedUserEmail,
|
|
array $expected,
|
|
): void {
|
|
$libreSignFile = new \OCA\Libresign\Db\File();
|
|
$libreSignFile->setUuid('uuid');
|
|
$service = $this->getService(['readCertificate']);
|
|
$service->method('readCertificate')
|
|
->willReturn($certData);
|
|
$service->setLibreSignFile($libreSignFile);
|
|
|
|
$signRequest = $this->createMock(SignRequest::class);
|
|
$signRequest
|
|
->method('__call')
|
|
->willReturnCallback(fn (string $method)
|
|
=> match ($method) {
|
|
'getId' => 171,
|
|
'getMetadata' => [],
|
|
}
|
|
);
|
|
$service->setSignRequest($signRequest);
|
|
|
|
if ($authenticatedUserEmail) {
|
|
$user = $this->createMock(\OCP\IUser::class);
|
|
$user->method('getEMailAddress')->willReturn($authenticatedUserEmail);
|
|
} else {
|
|
$user = null;
|
|
}
|
|
$service->setCurrentUser($user);
|
|
|
|
$actual = $this->invokePrivate($service, 'getSignatureParams');
|
|
if (isset($expected['SignerEmail'])) {
|
|
$this->assertArrayHasKey('SignerEmail', $actual);
|
|
$this->assertEquals($expected['SignerEmail'], $actual['SignerEmail']);
|
|
} else {
|
|
$this->assertArrayNotHasKey('SignerEmail', $actual);
|
|
}
|
|
}
|
|
|
|
public static function providerGetSignatureParamsSignerEmail(): array {
|
|
return [
|
|
[
|
|
[], '', [],
|
|
],
|
|
[
|
|
[
|
|
'extensions' => [
|
|
'subjectAltName' => '',
|
|
],
|
|
],
|
|
'',
|
|
[
|
|
],
|
|
],
|
|
[
|
|
[
|
|
'extensions' => [
|
|
'subjectAltName' => 'email:test@email.coop',
|
|
],
|
|
],
|
|
'',
|
|
[
|
|
'SignerEmail' => 'test@email.coop',
|
|
],
|
|
],
|
|
[
|
|
[
|
|
'extensions' => [
|
|
'subjectAltName' => 'email:test@email.coop,otherinfo',
|
|
],
|
|
],
|
|
'',
|
|
[
|
|
'SignerEmail' => 'test@email.coop',
|
|
],
|
|
],
|
|
[
|
|
[
|
|
'extensions' => [
|
|
'subjectAltName' => 'otherinfo,email:test@email.coop',
|
|
],
|
|
],
|
|
'',
|
|
[
|
|
'SignerEmail' => 'test@email.coop',
|
|
],
|
|
],
|
|
[
|
|
[
|
|
'extensions' => [
|
|
'subjectAltName' => 'otherinfo,email:test@email.coop,moreinfo',
|
|
],
|
|
],
|
|
'',
|
|
[
|
|
'SignerEmail' => 'test@email.coop',
|
|
],
|
|
],
|
|
[
|
|
[
|
|
'extensions' => [
|
|
'subjectAltName' => 'test@email.coop',
|
|
],
|
|
],
|
|
'',
|
|
[
|
|
'SignerEmail' => 'test@email.coop',
|
|
],
|
|
],
|
|
[
|
|
[],
|
|
'test@email.coop',
|
|
[
|
|
'SignerEmail' => 'test@email.coop',
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerGetSignatureParamsSignerEmailFallback')]
|
|
public function testGetSignatureParamsSignerEmailFallback(
|
|
string $methodName,
|
|
string $email,
|
|
): void {
|
|
$service = $this->getService(['readCertificate']);
|
|
|
|
$signRequest = $this->createMock(SignRequest::class);
|
|
$signRequest->method('__call')->willReturn(171);
|
|
$service->setSignRequest($signRequest);
|
|
|
|
$identifyMethod = $this->createMock(IIdentifyMethod::class);
|
|
$identifyMethod->method('getName')->willReturn($methodName);
|
|
$entity = new IdentifyMethod();
|
|
$entity->setIdentifierValue($email);
|
|
$identifyMethod->method('getEntity')->willReturn($entity);
|
|
$this->identifyMethodService->method('getIdentifiedMethod')->willReturn($identifyMethod);
|
|
|
|
$actual = $this->invokePrivate($service, 'getSignatureParams');
|
|
if (empty($email)) {
|
|
$this->assertArrayNotHasKey('SignerEmail', $actual);
|
|
} else {
|
|
$this->assertArrayHasKey('SignerEmail', $actual);
|
|
$this->assertEquals($email, $actual['SignerEmail']);
|
|
}
|
|
}
|
|
|
|
public static function providerGetSignatureParamsSignerEmailFallback(): array {
|
|
return [
|
|
['account', '',],
|
|
['email', 'signer@email.tld',],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerGetSignatureParamsMetadata')]
|
|
public function testGetSignatureParamsMetadata(
|
|
array $metadata,
|
|
array $expected,
|
|
): void {
|
|
$service = $this->getService(['readCertificate']);
|
|
$service->method('readCertificate')->willReturn([]);
|
|
|
|
$signRequest = $this->createMock(SignRequest::class);
|
|
$signRequest
|
|
->method('__call')
|
|
->willReturnCallback(fn (string $method)
|
|
=> match ($method) {
|
|
'getId' => 171,
|
|
'getMetadata' => $metadata,
|
|
}
|
|
);
|
|
$service->setSignRequest($signRequest);
|
|
$actual = $this->invokePrivate($service, 'getSignatureParams');
|
|
if (empty($expected)) {
|
|
$this->assertArrayNotHasKey('SignerIP', $actual);
|
|
$this->assertArrayNotHasKey('SignerUserAgent', $actual);
|
|
return;
|
|
}
|
|
if (isset($expected['SignerIP'])) {
|
|
$this->assertArrayHasKey('SignerIP', $actual);
|
|
$this->assertEquals($expected['SignerIP'], $actual['SignerIP']);
|
|
} else {
|
|
$this->assertArrayNotHasKey('SignerIP', $actual);
|
|
}
|
|
if (isset($expected['SignerUserAgent'])) {
|
|
$this->assertArrayHasKey('SignerUserAgent', $actual);
|
|
$this->assertEquals($expected['SignerUserAgent'], $actual['SignerUserAgent']);
|
|
} else {
|
|
$this->assertArrayNotHasKey('SignerUserAgent', $actual);
|
|
}
|
|
}
|
|
|
|
public static function providerGetSignatureParamsMetadata(): array {
|
|
return [
|
|
[[], []],
|
|
[
|
|
[
|
|
'remote-address' => '',
|
|
'user-agent' => '',
|
|
],
|
|
[
|
|
'SignerIP' => '',
|
|
'SignerUserAgent' => '',
|
|
],
|
|
],
|
|
[
|
|
[
|
|
'remote-address' => '127.0.0.1',
|
|
'user-agent' => 'Robot',
|
|
],
|
|
[
|
|
'SignerIP' => '127.0.0.1',
|
|
'SignerUserAgent' => 'Robot',
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerSetVisibleElements')]
|
|
public function testSetVisibleElements(
|
|
array $signerList,
|
|
array $fileElements,
|
|
array $tempFiles,
|
|
array $signatureFile,
|
|
bool $canCreateSignature,
|
|
?string $exception,
|
|
bool $isAuthenticatedSigner,
|
|
): void {
|
|
$service = $this->getService();
|
|
$signRequest = $this->createMock(SignRequest::class);
|
|
$signRequest
|
|
->method('__call')
|
|
->willReturnCallback(fn (string $method)
|
|
=> match ($method) {
|
|
'getFileId' => 171,
|
|
'getId' => 171,
|
|
}
|
|
);
|
|
$service->setSignRequest($signRequest);
|
|
|
|
$fileElements = array_map(function ($value) {
|
|
$fileElement = new FileElement();
|
|
$fileElement->setId($value['id']);
|
|
return $fileElement;
|
|
}, $fileElements);
|
|
$this->fileElementMapper->method('getByFileIdAndSignRequestId')->willReturn($fileElements);
|
|
|
|
$this->signerElementsService->method('canCreateSignature')->willReturn($canCreateSignature);
|
|
|
|
$this->userElementMapper->method('findOne')->willReturnCallback(function () use ($signatureFile) {
|
|
if (!empty($signatureFile)) {
|
|
$userElement = new UserElement();
|
|
$userElement->setFileId(1);
|
|
return $userElement;
|
|
}
|
|
throw new DoesNotExistException('User element not found');
|
|
});
|
|
|
|
$this->folderService->method('getFileById')
|
|
->willReturnCallback(function ($id) use ($signatureFile) {
|
|
if (isset($signatureFile[$id]) && $signatureFile[$id]['valid']) {
|
|
$file = $this->getMockBuilder(\OCP\Files\File::class)->getMock();
|
|
$file->method('getContent')->willReturn($signatureFile[$id]['content']);
|
|
return $file;
|
|
}
|
|
throw new NotFoundException();
|
|
});
|
|
|
|
vfsStream::setup('home');
|
|
$this->tempManager->method('getTemporaryFile')
|
|
->willReturnCallback(function ($postFix) {
|
|
preg_match('/.*(\d+).*/', $postFix, $matches);
|
|
$path = 'vfs://home/_' . $matches[1] . '.png';
|
|
return $path;
|
|
});
|
|
|
|
if ($exception) {
|
|
$this->expectException($exception);
|
|
}
|
|
|
|
if ($isAuthenticatedSigner) {
|
|
$currentUser = $this->createMock(\OCP\IUser::class);
|
|
}
|
|
$service->setCurrentUser($currentUser ?? null);
|
|
|
|
$service->setVisibleElements($signerList);
|
|
|
|
if (!$exception) {
|
|
$visibleElements = $service->getVisibleElements();
|
|
$this->assertCount(count($fileElements), $visibleElements);
|
|
foreach ($fileElements as $key => $element) {
|
|
$this->assertArrayHasKey($key, $visibleElements);
|
|
$this->assertSame($element, $visibleElements[$key]->getFileElement());
|
|
$this->assertEquals(
|
|
isset($signerList[$key], $signerList[$key]['profileNodeId'], $tempFiles[$signerList[$key]['profileNodeId']])
|
|
? $tempFiles[$signerList[$key]['profileNodeId']] . '/_' . $signerList[$key]['profileNodeId'] . '.png'
|
|
: '',
|
|
$visibleElements[$key]->getTempFile(),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function providerSetVisibleElements(): array {
|
|
$validDocumentId = 171;
|
|
$validProfileNodeId = 1;
|
|
$vfsPath = 'vfs://home';
|
|
|
|
return [
|
|
'empty list, can create signature' => self::createScenarioSetVisibleElements(
|
|
signerList: [],
|
|
fileElements: [],
|
|
tempFiles: [],
|
|
signatureFile: [],
|
|
canCreateSignature: true,
|
|
isAuthenticatedSigner: true,
|
|
),
|
|
|
|
'empty list, cannot create signature' => self::createScenarioSetVisibleElements(
|
|
signerList: [],
|
|
fileElements: [],
|
|
tempFiles: [],
|
|
signatureFile: [],
|
|
canCreateSignature: false,
|
|
isAuthenticatedSigner: true,
|
|
),
|
|
|
|
'valid signer with signature file, valid content of signature file' => self::createScenarioSetVisibleElements(
|
|
signerList: [
|
|
['documentElementId' => $validDocumentId, 'profileNodeId' => $validProfileNodeId],
|
|
],
|
|
fileElements: [
|
|
['id' => $validDocumentId],
|
|
],
|
|
tempFiles: [$validProfileNodeId => $vfsPath],
|
|
signatureFile: [$validProfileNodeId => ['valid' => true, 'content' => 'valid content']],
|
|
canCreateSignature: true,
|
|
isAuthenticatedSigner: true,
|
|
),
|
|
|
|
'valid signer with signature file, invalid content of signature file' => self::createScenarioSetVisibleElements(
|
|
signerList: [
|
|
['documentElementId' => $validDocumentId, 'profileNodeId' => $validProfileNodeId],
|
|
],
|
|
fileElements: [
|
|
['id' => $validDocumentId],
|
|
],
|
|
tempFiles: [$validProfileNodeId => false],
|
|
signatureFile: [$validProfileNodeId => ['valid' => true, 'content' => '']],
|
|
canCreateSignature: true,
|
|
isAuthenticatedSigner: true,
|
|
expectedException: LibresignException::class,
|
|
),
|
|
|
|
'unauthenticated signer without profileNodeId' => self::createScenarioSetVisibleElements(
|
|
signerList: [],
|
|
fileElements: [
|
|
['id' => $validDocumentId],
|
|
],
|
|
tempFiles: [$validProfileNodeId => $vfsPath],
|
|
signatureFile: [$validProfileNodeId => ['valid' => true, 'content' => 'valid content']],
|
|
canCreateSignature: true,
|
|
isAuthenticatedSigner: false,
|
|
expectedException: LibresignException::class,
|
|
),
|
|
|
|
'invalid signature file, with invalid field' => self::createScenarioSetVisibleElements(
|
|
signerList: [
|
|
['fake' => 'value', 'profileNodeId' => $validProfileNodeId],
|
|
],
|
|
fileElements: [
|
|
['id' => $validDocumentId],
|
|
],
|
|
tempFiles: [$validProfileNodeId => $vfsPath],
|
|
signatureFile: [$validProfileNodeId => ['valid' => false, 'content' => 'valid content']],
|
|
canCreateSignature: true,
|
|
isAuthenticatedSigner: true,
|
|
expectedException: LibresignException::class,
|
|
),
|
|
|
|
'invalid signature file, with invalid user element' => self::createScenarioSetVisibleElements(
|
|
signerList: [
|
|
['documentElementId' => $validDocumentId, 'profileNodeId' => $validProfileNodeId],
|
|
],
|
|
fileElements: [
|
|
['id' => $validDocumentId],
|
|
],
|
|
tempFiles: [$validProfileNodeId => $vfsPath],
|
|
signatureFile: [$validProfileNodeId => ['valid' => false, 'content' => 'valid content']],
|
|
canCreateSignature: true,
|
|
isAuthenticatedSigner: true,
|
|
expectedException: LibresignException::class,
|
|
),
|
|
|
|
'invalid signature file, with invalid type of profileNodeId' => self::createScenarioSetVisibleElements(
|
|
signerList: [
|
|
['documentElementId' => $validDocumentId, 'profileNodeId' => 'not-a-number'],
|
|
],
|
|
fileElements: [
|
|
['id' => $validDocumentId],
|
|
],
|
|
tempFiles: [$validProfileNodeId => $vfsPath],
|
|
signatureFile: [$validProfileNodeId => ['valid' => false, 'content' => 'valid content']],
|
|
canCreateSignature: true,
|
|
isAuthenticatedSigner: true,
|
|
expectedException: LibresignException::class,
|
|
),
|
|
|
|
'invalid signature file' => self::createScenarioSetVisibleElements(
|
|
signerList: [
|
|
['documentElementId' => $validDocumentId, 'profileNodeId' => $validProfileNodeId],
|
|
],
|
|
fileElements: [
|
|
['id' => $validDocumentId],
|
|
],
|
|
tempFiles: [$validProfileNodeId => $vfsPath],
|
|
signatureFile: [$validProfileNodeId => ['valid' => false, 'content' => 'valid content']],
|
|
canCreateSignature: true,
|
|
isAuthenticatedSigner: true,
|
|
expectedException: LibresignException::class,
|
|
),
|
|
|
|
'missing profileNodeId throws exception' => self::createScenarioSetVisibleElements(
|
|
signerList: [
|
|
['documentElementId' => $validDocumentId],
|
|
],
|
|
fileElements: [
|
|
['id' => $validDocumentId],
|
|
],
|
|
tempFiles: [],
|
|
signatureFile: [],
|
|
canCreateSignature: true,
|
|
isAuthenticatedSigner: true,
|
|
expectedException: LibresignException::class,
|
|
),
|
|
|
|
'cannot create signature, visible element fallback' => self::createScenarioSetVisibleElements(
|
|
signerList: [
|
|
['documentElementId' => $validDocumentId],
|
|
],
|
|
fileElements: [
|
|
['id' => $validDocumentId],
|
|
],
|
|
tempFiles: [],
|
|
signatureFile: [],
|
|
canCreateSignature: false,
|
|
isAuthenticatedSigner: true,
|
|
),
|
|
'no authenticated user, missing session file' => self::createScenarioSetVisibleElements(
|
|
signerList: [['documentElementId' => $validDocumentId, 'profileNodeId' => $validProfileNodeId]],
|
|
fileElements: [['id' => $validDocumentId]],
|
|
tempFiles: [],
|
|
signatureFile: [],
|
|
canCreateSignature: true,
|
|
isAuthenticatedSigner: false,
|
|
expectedException: LibresignException::class,
|
|
),
|
|
'user fallback with valid user element' => self::createScenarioSetVisibleElements(
|
|
signerList: [
|
|
['documentElementId' => $validDocumentId, 'profileNodeId' => $validProfileNodeId],
|
|
],
|
|
fileElements: [
|
|
['id' => $validDocumentId],
|
|
],
|
|
tempFiles: [$validProfileNodeId => $vfsPath],
|
|
signatureFile: [$validProfileNodeId => ['valid' => true, 'content' => 'valid content']],
|
|
canCreateSignature: true,
|
|
isAuthenticatedSigner: true,
|
|
),
|
|
|
|
'authenticated user, file not found' => self::createScenarioSetVisibleElements(
|
|
signerList: [
|
|
['documentElementId' => $validDocumentId, 'profileNodeId' => $validProfileNodeId],
|
|
],
|
|
fileElements: [
|
|
['id' => $validDocumentId],
|
|
],
|
|
tempFiles: [],
|
|
signatureFile: [],
|
|
canCreateSignature: true,
|
|
isAuthenticatedSigner: true,
|
|
expectedException: LibresignException::class,
|
|
),
|
|
];
|
|
}
|
|
|
|
private static function createScenarioSetVisibleElements(
|
|
array $signerList = [],
|
|
array $fileElements = [],
|
|
array $tempFiles = [],
|
|
array $signatureFile = [],
|
|
bool $canCreateSignature = false,
|
|
bool $isAuthenticatedSigner = false,
|
|
?string $expectedException = null,
|
|
): array {
|
|
return [
|
|
$signerList,
|
|
$fileElements,
|
|
$tempFiles,
|
|
$signatureFile,
|
|
$canCreateSignature,
|
|
$expectedException,
|
|
$isAuthenticatedSigner,
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerGetSignedFile')]
|
|
public function testGetSignedFile(
|
|
int $timesCalled,
|
|
string $managerUid,
|
|
?string $ownerUid = null,
|
|
?int $nodeId = null,
|
|
): void {
|
|
$service = $this->getService(['getNodeByIdUsingUid']);
|
|
|
|
$libreSignFile = new \OCA\Libresign\Db\File();
|
|
$libreSignFile->setSignedNodeId($nodeId);
|
|
$libreSignFile->setUserId($managerUid);
|
|
$service->setLibreSignFile($libreSignFile);
|
|
|
|
$fileToSign = $this->createMock(\OCP\Files\File::class);
|
|
$user = $this->createMock(\OCP\IUser::class);
|
|
$user->method('getUID')->willReturn($ownerUid);
|
|
$fileToSign->method('getOwner')->willReturn($user);
|
|
$service
|
|
->expects($this->exactly($timesCalled))
|
|
->method('getNodeByIdUsingUid')
|
|
->willReturn($fileToSign);
|
|
|
|
$this->invokePrivate($service, 'getSignedFile');
|
|
}
|
|
|
|
public static function providerGetSignedFile(): array {
|
|
return [
|
|
[0, 'managerUid', '', null],
|
|
[1, 'managerUid', 'managerUid', 1],
|
|
[2, 'managerUid', 'johndoe', 1],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerGetNodeByIdUsingUid')]
|
|
public function testGetNodeByIdUsingUid(
|
|
string $typeOfNode,
|
|
string $exceptionMessage,
|
|
): void {
|
|
$service = $this->getService();
|
|
if ($exceptionMessage) {
|
|
$this->expectExceptionMessageMatches($exceptionMessage);
|
|
}
|
|
$leaf = $this->createMock($typeOfNode);
|
|
$userFolder = $this->createMock(\OCP\Files\Folder::class);
|
|
$userFolder->method('getFirstNodeById')->willReturn($leaf);
|
|
$this->root->method('getUserFolder')->willReturnCallback(function () use ($userFolder, $exceptionMessage) {
|
|
switch ($exceptionMessage) {
|
|
case '/User not found/':
|
|
throw new NoUserException();
|
|
case '/not have permission/':
|
|
throw new NotPermittedException();
|
|
case '/File not found/':
|
|
return $userFolder;
|
|
default:
|
|
return $userFolder;
|
|
}
|
|
});
|
|
$actual = $this->invokePrivate($service, 'getNodeByIdUsingUid', ['', 1]);
|
|
$this->assertEquals($leaf, $actual);
|
|
}
|
|
|
|
public static function providerGetNodeByIdUsingUid(): array {
|
|
return [
|
|
[\OCP\Files\Folder::class, '/User not found/'],
|
|
[\OCP\Files\Folder::class, '/not have permission/'],
|
|
[\OCP\Files\Folder::class, '/File not found/'],
|
|
[\OCP\Files\File::class, ''],
|
|
];
|
|
}
|
|
|
|
public function testSignThrowsExceptionWhenDocMdpLevel1Detected(): void {
|
|
$this->expectException(LibresignException::class);
|
|
$service = $this->getService(['getNextcloudFile', 'getEngine']);
|
|
|
|
$nextcloudFile = $this->createMock(\OCP\Files\File::class);
|
|
$nextcloudFile->method('getContent')->willReturn(file_get_contents(__DIR__ . '/../../fixtures/pdfs/real_jsignpdf_level1.pdf'));
|
|
$service->method('getNextcloudFile')->willReturn($nextcloudFile);
|
|
|
|
$engineMock = $this->createMock(Pkcs12Handler::class);
|
|
$service->method('getEngine')->willReturn($engineMock);
|
|
|
|
$signRequest = $this->createMock(SignRequest::class);
|
|
$signRequest->method('__call')->willReturnCallback(function ($method) {
|
|
switch ($method) {
|
|
case 'getFileId':
|
|
return 1;
|
|
case 'getSigningOrder':
|
|
return 1;
|
|
default: return null;
|
|
}
|
|
});
|
|
|
|
$libreSignFile = $this->createMock(\OCA\Libresign\Db\File::class);
|
|
$libreSignFile->method('getDocmdpLevelEnum')->willReturn(\OCA\Libresign\Enum\DocMdpLevel::NOT_CERTIFIED);
|
|
|
|
$service
|
|
->setSignRequest($signRequest)
|
|
->setLibreSignFile($libreSignFile)
|
|
->sign();
|
|
}
|
|
|
|
#[DataProvider('provideValidateDocMdpAllowsSignaturesScenarios')]
|
|
public function testValidateDocMdpAllowsSignaturesWithVariousPdfFixtures(
|
|
callable $pdfContentGenerator,
|
|
bool $shouldThrowException,
|
|
): void {
|
|
if (!$shouldThrowException) {
|
|
$this->expectNotToPerformAssertions();
|
|
} else {
|
|
$this->expectException(LibresignException::class);
|
|
}
|
|
|
|
$service = $this->getService(['getLibreSignFileAsResource']);
|
|
|
|
$pdfContent = $pdfContentGenerator($this);
|
|
$resource = fopen('php://memory', 'r+');
|
|
fwrite($resource, $pdfContent);
|
|
rewind($resource);
|
|
|
|
$service->method('getLibreSignFileAsResource')->willReturn($resource);
|
|
|
|
$libreSignFile = $this->createMock(\OCA\Libresign\Db\File::class);
|
|
$libreSignFile->method('getDocmdpLevelEnum')->willReturn(\OCA\Libresign\Enum\DocMdpLevel::NOT_CERTIFIED);
|
|
$service->setLibreSignFile($libreSignFile);
|
|
|
|
self::invokePrivate($service, 'validateDocMdpAllowsSignatures');
|
|
}
|
|
|
|
public static function provideValidateDocMdpAllowsSignaturesScenarios(): array {
|
|
return [
|
|
'Unsigned PDF - should NOT throw exception' => [
|
|
'pdfContentGenerator' => fn (self $test) => \OCA\Libresign\Tests\Fixtures\PdfGenerator::createMinimalPdf(),
|
|
'shouldThrowException' => false,
|
|
],
|
|
'DocMDP level 0 (not certified) - should NOT throw exception' => [
|
|
'pdfContentGenerator' => fn (self $test) => \OCA\Libresign\Tests\Fixtures\PdfGenerator::createPdfWithDocMdp(0, false),
|
|
'shouldThrowException' => false,
|
|
],
|
|
'DocMDP level 1 (no changes allowed) - SHOULD throw exception' => [
|
|
'pdfContentGenerator' => fn (self $test) => \OCA\Libresign\Tests\Fixtures\PdfGenerator::createPdfWithDocMdp(1, false),
|
|
'shouldThrowException' => true,
|
|
],
|
|
'DocMDP level 2 (form filling allowed) - should NOT throw exception' => [
|
|
'pdfContentGenerator' => fn (self $test) => \OCA\Libresign\Tests\Fixtures\PdfGenerator::createPdfWithDocMdp(2, false),
|
|
'shouldThrowException' => false,
|
|
],
|
|
'DocMDP level 3 (annotations allowed) - should NOT throw exception' => [
|
|
'pdfContentGenerator' => fn (self $test) => \OCA\Libresign\Tests\Fixtures\PdfGenerator::createPdfWithDocMdp(3, false),
|
|
'shouldThrowException' => false,
|
|
],
|
|
'DocMDP level 1 with modifications - SHOULD throw exception' => [
|
|
'pdfContentGenerator' => fn (self $test) => \OCA\Libresign\Tests\Fixtures\PdfGenerator::createPdfWithDocMdp(1, true),
|
|
'shouldThrowException' => true,
|
|
],
|
|
];
|
|
}
|
|
}
|