From d8512ab017409b489fd1287c9d8dcefbe98d9e2d Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 19 Apr 2024 10:26:03 -0300 Subject: [PATCH 01/10] chore: split controller Split AccountController to move visible signature methods to a dedicated controller Add specific header to identify the signer at public endpoints. This is used when sign a document using email without account. Signed-off-by: Vitor Mattos --- appinfo/routes.php | 1 + appinfo/routes/routesAccountController.php | 7 - .../routesSignatureElementsController.php | 16 ++ lib/Controller/AccountController.php | 173 ------------- .../SignatureElementsController.php | 237 ++++++++++++++++++ src/store/signatureElements.js | 33 ++- .../features/account/signature.feature | 12 +- 7 files changed, 287 insertions(+), 192 deletions(-) create mode 100644 appinfo/routes/routesSignatureElementsController.php create mode 100644 lib/Controller/SignatureElementsController.php diff --git a/appinfo/routes.php b/appinfo/routes.php index ca5716fb02..9c5d9812d9 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -24,5 +24,6 @@ include(__DIR__ . '/routes/routesPageController.php'), include(__DIR__ . '/routes/routesRequestSignatureController.php'), include(__DIR__ . '/routes/routesSettingsController.php'), + include(__DIR__ . '/routes/routesSignatureElementsController.php'), include(__DIR__ . '/routes/routesSignFileController.php'), ); diff --git a/appinfo/routes/routesAccountController.php b/appinfo/routes/routesAccountController.php index 00d2849a4f..762b369050 100644 --- a/appinfo/routes/routesAccountController.php +++ b/appinfo/routes/routesAccountController.php @@ -17,12 +17,5 @@ ['name' => 'account#addFiles', 'url' => '/api/{apiVersion}/account/files', 'verb' => 'POST', 'requirements' => $requirements], ['name' => 'account#deleteFile', 'url' => '/api/{apiVersion}/account/files', 'verb' => 'DELETE', 'requirements' => $requirements], ['name' => 'account#accountFileListToOwner', 'url' => '/api/{apiVersion}/account/files', 'verb' => 'GET', 'requirements' => $requirements], - ['name' => 'account#accountFileListToApproval', 'url' => '/api/{apiVersion}/account/files/approval/list', 'verb' => 'GET', 'requirements' => $requirements], - ['name' => 'account#createSignatureElement', 'url' => '/api/{apiVersion}/account/signature/elements', 'verb' => 'POST', 'requirements' => $requirements], - ['name' => 'account#getSignatureElements', 'url' => '/api/{apiVersion}/account/signature/elements', 'verb' => 'GET', 'requirements' => $requirements], - ['name' => 'account#getSignatureElementPreview','url' => '/api/{apiVersion}/account/signature/elements/preview/{nodeId}', 'verb' => 'GET', 'requirements' => $requirements], - ['name' => 'account#getSignatureElement', 'url' => '/api/{apiVersion}/account/signature/elements/{nodeId}', 'verb' => 'GET', 'requirements' => $requirements], - ['name' => 'account#patchSignatureElement', 'url' => '/api/{apiVersion}/account/signature/elements/{nodeId}', 'verb' => 'PATCH', 'requirements' => $requirements], - ['name' => 'account#deleteSignatureElement', 'url' => '/api/{apiVersion}/account/signature/elements/{nodeId}', 'verb' => 'DELETE', 'requirements' => $requirements], ], ]; diff --git a/appinfo/routes/routesSignatureElementsController.php b/appinfo/routes/routesSignatureElementsController.php new file mode 100644 index 0000000000..8a09df7a0e --- /dev/null +++ b/appinfo/routes/routesSignatureElementsController.php @@ -0,0 +1,16 @@ + 'v1', +]; + +return [ + 'ocs' => [ + ['name' => 'SignatureElements#createSignatureElement', 'url' => '/api/{apiVersion}/signature/elements', 'verb' => 'POST', 'requirements' => $requirements], + ['name' => 'SignatureElements#getSignatureElements', 'url' => '/api/{apiVersion}/signature/elements', 'verb' => 'GET', 'requirements' => $requirements], + ['name' => 'SignatureElements#getSignatureElementPreview','url' => '/api/{apiVersion}/signature/elements/preview/{nodeId}', 'verb' => 'GET', 'requirements' => $requirements], + ['name' => 'SignatureElements#getSignatureElement', 'url' => '/api/{apiVersion}/signature/elements/{nodeId}', 'verb' => 'GET', 'requirements' => $requirements], + ['name' => 'SignatureElements#patchSignatureElement', 'url' => '/api/{apiVersion}/signature/elements/{nodeId}', 'verb' => 'PATCH', 'requirements' => $requirements], + ['name' => 'SignatureElements#deleteSignatureElement', 'url' => '/api/{apiVersion}/signature/elements/{nodeId}', 'verb' => 'DELETE', 'requirements' => $requirements], + ], +]; diff --git a/lib/Controller/AccountController.php b/lib/Controller/AccountController.php index aa6e7c5c32..1d8c8d1c61 100644 --- a/lib/Controller/AccountController.php +++ b/lib/Controller/AccountController.php @@ -40,21 +40,17 @@ use OCA\Libresign\Service\SignFileService; use OCP\Accounts\IAccountManager; use OCP\AppFramework\ApiController; -use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\CORS; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\Attribute\UseSession; -use OCP\AppFramework\Http\DataResponse; -use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Http\JSONResponse; use OCP\IL10N; use OCP\IPreview; use OCP\IRequest; use OCP\IURLGenerator; -use OCP\IUser; use OCP\IUserSession; use Psr\Log\LoggerInterface; @@ -258,175 +254,6 @@ public function me(): JSONResponse { ); } - #[NoAdminRequired] - #[NoCSRFRequired] - #[PublicPage] - public function createSignatureElement(array $elements): JSONResponse { - try { - $this->validateHelper->validateVisibleElements($elements, $this->validateHelper::TYPE_VISIBLE_ELEMENT_USER); - $this->accountService->saveVisibleElements( - elements: $elements, - sessionId: $this->sessionService->getSessionId(), - user: $this->userSession->getUser(), - ); - } catch (\Throwable $th) { - return new JSONResponse( - [ - 'message' => $th->getMessage(), - ], - Http::STATUS_UNPROCESSABLE_ENTITY - ); - } - return new JSONResponse( - [ - 'message' => $this->l10n->n( - 'Element created with success', - 'Elements created with success', - count($elements) - ), - 'elements' => - ( - $this->userSession->getUser() instanceof IUser - ? $this->signerElementsService->getUserElements($this->userSession->getUser()->getUID()) - : $this->signerElementsService->getElementsFromSessionAsArray() - ), - ], - Http::STATUS_OK - ); - } - - #[NoAdminRequired] - #[NoCSRFRequired] - #[PublicPage] - public function getSignatureElements(): JSONResponse { - $userId = $this->userSession->getUser()?->getUID(); - try { - return new JSONResponse( - [ - 'elements' => - ( - $userId - ? $this->signerElementsService->getUserElements($userId) - : $this->signerElementsService->getElementsFromSessionAsArray() - ) - ], - Http::STATUS_OK - ); - } catch (\Throwable $th) { - return new JSONResponse( - [ - 'message' => $this->l10n->t('Elements not found') - ], - Http::STATUS_NOT_FOUND - ); - } - } - - #[NoAdminRequired] - #[PublicPage] - #[NoCSRFRequired] - public function getSignatureElementPreview(int $nodeId) { - try { - $node = $this->accountService->getFileByNodeIdAndSessionId( - $nodeId, - $this->sessionService->getSessionId() - ); - } catch (DoesNotExistException $th) { - return new DataResponse([], Http::STATUS_NOT_FOUND); - } - $preview = $this->preview->getPreview( - file: $node, - width: SignerElementsService::ELEMENT_SIGN_WIDTH, - height: SignerElementsService::ELEMENT_SIGN_HEIGHT, - ); - $response = new FileDisplayResponse($preview, Http::STATUS_OK, [ - 'Content-Type' => $preview->getMimeType(), - ]); - return $response; - } - - #[NoAdminRequired] - #[NoCSRFRequired] - public function getSignatureElement(int $nodeId): JSONResponse { - $userId = $this->userSession->getUser()->getUID(); - try { - return new JSONResponse( - $this->signerElementsService->getUserElementByNodeId($userId, $nodeId), - Http::STATUS_OK - ); - } catch (\Throwable $th) { - return new JSONResponse( - [ - 'message' => $this->l10n->t('Element not found') - ], - Http::STATUS_NOT_FOUND - ); - } - } - - #[NoAdminRequired] - #[NoCSRFRequired] - public function patchSignatureElement($nodeId, string $type = '', array $file = []): JSONResponse { - try { - $element['nodeId'] = $nodeId; - if ($type) { - $element['type'] = $type; - } - if ($file) { - $element['file'] = $file; - } - $this->validateHelper->validateVisibleElement($element, $this->validateHelper::TYPE_VISIBLE_ELEMENT_USER); - $user = $this->userSession->getUser(); - if ($user instanceof IUser) { - $userElement = $this->signerElementsService->getUserElementByNodeId( - $user->getUID(), - $nodeId, - ); - $element['elementId'] = $userElement['id']; - } - $this->accountService->saveVisibleElement($element, $this->sessionService->getSessionId(), $user); - return new JSONResponse( - [ - 'message' => $this->l10n->t('Element updated with success') - ], - Http::STATUS_OK - ); - } catch (\Throwable $th) { - return new JSONResponse( - [ - 'message' => $th->getMessage() - ], - Http::STATUS_UNPROCESSABLE_ENTITY - ); - } - } - - #[NoAdminRequired] - #[NoCSRFRequired] - #[PublicPage] - public function deleteSignatureElement(int $nodeId): JSONResponse { - try { - $this->accountService->deleteSignatureElement( - user: $this->userSession->getUser(), - nodeId: $nodeId, - sessionId: $this->sessionService->getSessionId(), - ); - } catch (\Throwable $th) { - return new JSONResponse( - [ - 'message' => $this->l10n->t('Element not found') - ], - Http::STATUS_NOT_FOUND - ); - } - return new JSONResponse( - [ - 'message' => $this->l10n->t('Visible element deleted') - ], - Http::STATUS_OK - ); - } - #[NoAdminRequired] #[NoCSRFRequired] public function accountFileListToOwner(array $filter = [], $page = null, $length = null): JSONResponse { diff --git a/lib/Controller/SignatureElementsController.php b/lib/Controller/SignatureElementsController.php new file mode 100644 index 0000000000..c9e76d341b --- /dev/null +++ b/lib/Controller/SignatureElementsController.php @@ -0,0 +1,237 @@ + + * + * @author Vitor Mattos + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCA\Libresign\Controller; + +use OCA\Libresign\AppInfo\Application; +use OCA\Libresign\Helper\ValidateHelper; +use OCA\Libresign\Middleware\Attribute\RequireSignRequestUuid; +use OCA\Libresign\Service\AccountFileService; +use OCA\Libresign\Service\AccountService; +use OCA\Libresign\Service\SessionService; +use OCA\Libresign\Service\SignerElementsService; +use OCP\AppFramework\ApiController; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Attribute\PublicPage; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\FileDisplayResponse; +use OCP\AppFramework\Http\JSONResponse; +use OCP\IL10N; +use OCP\IPreview; +use OCP\IRequest; +use OCP\IUser; +use OCP\IUserSession; + +class SignatureElementsController extends ApiController implements ISignatureUuid { + use LibresignTrait; + public function __construct( + IRequest $request, + protected IL10N $l10n, + private AccountService $accountService, + private AccountFileService $accountFileService, + private SignerElementsService $signerElementsService, + protected IUserSession $userSession, + protected SessionService $sessionService, + private IPreview $preview, + private ValidateHelper $validateHelper + ) { + parent::__construct(Application::APP_ID, $request); + } + + #[NoAdminRequired] + #[NoCSRFRequired] + #[PublicPage] + #[RequireSignRequestUuid] + public function createSignatureElement(array $elements): JSONResponse { + try { + $this->validateHelper->validateVisibleElements($elements, $this->validateHelper::TYPE_VISIBLE_ELEMENT_USER); + $this->accountService->saveVisibleElements( + elements: $elements, + sessionId: $this->sessionService->getSessionId(), + user: $this->userSession->getUser(), + ); + } catch (\Throwable $th) { + return new JSONResponse( + [ + 'message' => $th->getMessage(), + ], + Http::STATUS_UNPROCESSABLE_ENTITY + ); + } + return new JSONResponse( + [ + 'message' => $this->l10n->n( + 'Element created with success', + 'Elements created with success', + count($elements) + ), + 'elements' => + ( + $this->userSession->getUser() instanceof IUser + ? $this->signerElementsService->getUserElements($this->userSession->getUser()->getUID()) + : $this->signerElementsService->getElementsFromSessionAsArray() + ), + ], + Http::STATUS_OK + ); + } + + #[NoAdminRequired] + #[NoCSRFRequired] + #[PublicPage] + #[RequireSignRequestUuid] + public function getSignatureElements(): JSONResponse { + $userId = $this->userSession->getUser()?->getUID(); + try { + return new JSONResponse( + [ + 'elements' => + ( + $userId + ? $this->signerElementsService->getUserElements($userId) + : $this->signerElementsService->getElementsFromSessionAsArray() + ) + ], + Http::STATUS_OK + ); + } catch (\Throwable $th) { + return new JSONResponse( + [ + 'message' => $this->l10n->t('Elements not found') + ], + Http::STATUS_NOT_FOUND + ); + } + } + + #[NoAdminRequired] + #[PublicPage] + #[NoCSRFRequired] + #[RequireSignRequestUuid] + public function getSignatureElementPreview(int $nodeId) { + try { + $node = $this->accountService->getFileByNodeIdAndSessionId( + $nodeId, + $this->sessionService->getSessionId() + ); + } catch (DoesNotExistException $th) { + return new DataResponse([], Http::STATUS_NOT_FOUND); + } + $preview = $this->preview->getPreview( + file: $node, + width: SignerElementsService::ELEMENT_SIGN_WIDTH, + height: SignerElementsService::ELEMENT_SIGN_HEIGHT, + ); + $response = new FileDisplayResponse($preview, Http::STATUS_OK, [ + 'Content-Type' => $preview->getMimeType(), + ]); + return $response; + } + + #[NoAdminRequired] + #[NoCSRFRequired] + public function getSignatureElement(int $nodeId): JSONResponse { + $userId = $this->userSession->getUser()->getUID(); + try { + return new JSONResponse( + $this->signerElementsService->getUserElementByNodeId($userId, $nodeId), + Http::STATUS_OK + ); + } catch (\Throwable $th) { + return new JSONResponse( + [ + 'message' => $this->l10n->t('Element not found') + ], + Http::STATUS_NOT_FOUND + ); + } + } + + #[NoAdminRequired] + #[NoCSRFRequired] + public function patchSignatureElement($nodeId, string $type = '', array $file = []): JSONResponse { + try { + $element['nodeId'] = $nodeId; + if ($type) { + $element['type'] = $type; + } + if ($file) { + $element['file'] = $file; + } + $this->validateHelper->validateVisibleElement($element, $this->validateHelper::TYPE_VISIBLE_ELEMENT_USER); + $user = $this->userSession->getUser(); + if ($user instanceof IUser) { + $userElement = $this->signerElementsService->getUserElementByNodeId( + $user->getUID(), + $nodeId, + ); + $element['elementId'] = $userElement['id']; + } + $this->accountService->saveVisibleElement($element, $this->sessionService->getSessionId(), $user); + return new JSONResponse( + [ + 'message' => $this->l10n->t('Element updated with success') + ], + Http::STATUS_OK + ); + } catch (\Throwable $th) { + return new JSONResponse( + [ + 'message' => $th->getMessage() + ], + Http::STATUS_UNPROCESSABLE_ENTITY + ); + } + } + + #[NoAdminRequired] + #[NoCSRFRequired] + #[PublicPage] + #[RequireSignRequestUuid] + public function deleteSignatureElement(int $nodeId): JSONResponse { + try { + $this->accountService->deleteSignatureElement( + user: $this->userSession->getUser(), + nodeId: $nodeId, + sessionId: $this->sessionService->getSessionId(), + ); + } catch (\Throwable $th) { + return new JSONResponse( + [ + 'message' => $this->l10n->t('Element not found') + ], + Http::STATUS_NOT_FOUND + ); + } + return new JSONResponse( + [ + 'message' => $this->l10n->t('Visible element deleted') + ], + Http::STATUS_OK + ); + } +} diff --git a/src/store/signatureElements.js b/src/store/signatureElements.js index 953a76ce7c..3d69ff7169 100644 --- a/src/store/signatureElements.js +++ b/src/store/signatureElements.js @@ -57,7 +57,16 @@ export const useSignatureElementsStore = function(...args) { }) return } - await axios.get(generateOcsUrl('/apps/libresign/api/v1/account/signature/elements')) + const config = { + url: generateOcsUrl('/apps/libresign/api/v1/signature/elements'), + method: 'get', + } + if (this.uuid !== null) { + config.headers = { + 'LibreSign-sign-request-uuid': this.uuid + } + } + await axios(config) .then(({ data }) => { data.elements.forEach(current => { set(this.signs, current.type, current) @@ -73,7 +82,7 @@ export const useSignatureElementsStore = function(...args) { async save(type, base64) { const config = {} if (this.signs[type].id > 0) { - config.url = generateOcsUrl('/apps/libresign/api/v1/account/signature/elements/{nodeId}', { + config.url = generateOcsUrl('/apps/libresign/api/v1/signature/elements/{nodeId}', { nodeId: this.signs[type].file.nodeId, }) config.data = { @@ -82,7 +91,7 @@ export const useSignatureElementsStore = function(...args) { } config.method = 'patch' } else { - config.url = generateOcsUrl('/apps/libresign/api/v1/account/signature/elements') + config.url = generateOcsUrl('/apps/libresign/api/v1/signature/elements') config.data = { elements: [ { @@ -95,6 +104,11 @@ export const useSignatureElementsStore = function(...args) { } config.method = 'post' } + if (this.uuid !== null) { + config.headers = { + 'LibreSign-sign-request-uuid': this.uuid + } + } await axios(config) .then(({ data }) => { if (Object.hasOwn(data, 'elements')) { @@ -114,11 +128,18 @@ export const useSignatureElementsStore = function(...args) { }) }, async delete(type) { - await axios.delete( - generateOcsUrl('/apps/libresign/api/v1/account/signature/elements/{nodeId}', { + const config = { + url: generateOcsUrl('/apps/libresign/api/v1/signature/elements/{nodeId}', { nodeId: this.signs[type].file.nodeId, }), - ) + method: 'delete', + } + if (this.uuid !== null) { + config.headers = { + 'LibreSign-sign-request-uuid': this.uuid + } + } + await axios(config) .then(({ data }) => { this.signs[type] = emptyElement this.success = data.message diff --git a/tests/integration/features/account/signature.feature b/tests/integration/features/account/signature.feature index 7115ff518b..37ecc07544 100644 --- a/tests/integration/features/account/signature.feature +++ b/tests/integration/features/account/signature.feature @@ -132,15 +132,15 @@ Feature: account/signature Given guest "guest@test.coop" exists And run the command "config:app:set guests whitelist --value libresign" with result code 0 And as user "guest@test.coop" - When sending "post" to ocs "/apps/libresign/api/v1/account/signature/elements" + When sending "post" to ocs "/apps/libresign/api/v1/signature/elements" | elements | [{"type":"signature","file":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="}}] | Then the response should have a status code 200 - When sending "get" to ocs "/apps/libresign/api/v1/account/signature/elements" + When sending "get" to ocs "/apps/libresign/api/v1/signature/elements" Then the response should be a JSON array with the following mandatory values | key | value | | elements | (jq).[]\|.type == "signature" | And fetch field "(NODE_ID)elements.0.file.nodeId" from prevous JSON response - When sending "delete" to ocs "/apps/libresign/api/v1/account/signature/elements/" + When sending "delete" to ocs "/apps/libresign/api/v1/signature/elements/" Then the response should have a status code 200 Scenario: CRUD of signature element to signer by email without account @@ -155,13 +155,13 @@ Feature: account/signature | users | [{"identify":{"email":"guest@test.coop"}}] | | name | document | And as user "" - When sending "post" to ocs "/apps/libresign/api/v1/account/signature/elements" + When sending "post" to ocs "/apps/libresign/api/v1/signature/elements" | elements | [{"type":"signature","file":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="}}] | Then the response should have a status code 200 - When sending "get" to ocs "/apps/libresign/api/v1/account/signature/elements" + When sending "get" to ocs "/apps/libresign/api/v1/signature/elements" Then the response should be a JSON array with the following mandatory values | key | value | | elements | (jq).[]\|.type == "signature" | And fetch field "(NODE_ID)elements.0.file.nodeId" from prevous JSON response - When sending "delete" to ocs "/apps/libresign/api/v1/account/signature/elements/" + When sending "delete" to ocs "/apps/libresign/api/v1/signature/elements/" Then the response should have a status code 200 From 677bdb0a76c046098f991d0890e6d2f30193b6af Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 19 Apr 2024 11:10:02 -0300 Subject: [PATCH 02/10] chore: Remove unecessary file Signed-off-by: Vitor Mattos --- src/Components/Draw/index.js | 3 --- src/Components/Request/VisibleElements.vue | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 src/Components/Draw/index.js diff --git a/src/Components/Draw/index.js b/src/Components/Draw/index.js deleted file mode 100644 index 2e0a6010b3..0000000000 --- a/src/Components/Draw/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import Draw from './Draw.vue' -export * from './options.js' -export default Draw diff --git a/src/Components/Request/VisibleElements.vue b/src/Components/Request/VisibleElements.vue index df6f94fef2..10c06d3868 100644 --- a/src/Components/Request/VisibleElements.vue +++ b/src/Components/Request/VisibleElements.vue @@ -83,7 +83,7 @@ import { subscribe, unsubscribe, emit } from '@nextcloud/event-bus' import { SIGN_STATUS } from '../../domains/sign/enum.js' import Signer from '../Signers/Signer.vue' import { showResponseError } from '../../helpers/errors.js' -import { SignatureImageDimensions } from '../Draw/index.js' +import { SignatureImageDimensions } from '../Draw/options.js' import Chip from '../Chip.vue' import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js' import PdfEditor from '../PdfEditor/PdfEditor.vue' From bff9f54a670b2c4fcda4f67b8e48e03cb32ae764 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 19 Apr 2024 11:10:53 -0300 Subject: [PATCH 03/10] fix: route name Signed-off-by: Vitor Mattos --- lib/Service/SignerElementsService.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Service/SignerElementsService.php b/lib/Service/SignerElementsService.php index f656025a66..5b264e4c13 100644 --- a/lib/Service/SignerElementsService.php +++ b/lib/Service/SignerElementsService.php @@ -52,7 +52,7 @@ public function getUserElementByNodeId(string $userId, $nodeId): array { 'id' => $element->getId(), 'type' => $element->getType(), 'file' => [ - 'url' => $this->urlGenerator->linkToRoute('ocs.libresign.account.getSignatureElementPreview', [ + 'url' => $this->urlGenerator->linkToRoute('ocs.libresign.SignatureService.getSignatureElementPreview', [ 'apiVersion' => 'v1', 'nodeId' => $element->getFileId(), ]), @@ -76,7 +76,7 @@ public function getUserElements(string $userId): array { 'id' => $element->getId(), 'type' => $element->getType(), 'file' => [ - 'url' => $this->urlGenerator->linkToRoute('ocs.libresign.account.getSignatureElementPreview', [ + 'url' => $this->urlGenerator->linkToRoute('ocs.libresign.SignatureService.getSignatureElementPreview', [ 'apiVersion' => 'v1', 'nodeId' => $element->getFileId(), ]), @@ -119,7 +119,7 @@ public function getElementsFromSessionAsArray(): array { $return[] = [ 'type' => $type, 'file' => [ - 'url' => $this->urlGenerator->linkToRoute('ocs.libresign.account.getSignatureElementPreview', [ + 'url' => $this->urlGenerator->linkToRoute('ocs.libresign.SignatureService.getSignatureElementPreview', [ 'apiVersion' => 'v1', 'nodeId' => $fileElement->getId(), ]), From 87aae4a5dbdadfb60ebbccb244af33cb3b9b3b4f Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 19 Apr 2024 12:34:34 -0300 Subject: [PATCH 04/10] chore: send header to API at signature image CRUD Signed-off-by: Vitor Mattos --- lib/Controller/PageController.php | 1 + .../SignatureElementsController.php | 16 ++++++-- lib/Middleware/InjectionMiddleware.php | 16 +++++--- lib/Service/AccountService.php | 21 ++++++++++ lib/Service/SignerElementsService.php | 7 ++-- src/Components/Draw/Draw.vue | 1 + .../PreviewSignature/PreviewSignature.vue | 39 ++++++++++++++++++- src/store/signatureElements.js | 25 +++++++----- src/views/Account/partials/Signature.vue | 1 + src/views/Account/partials/Signatures.vue | 5 --- src/views/SignPDF/_partials/Sign.vue | 10 ++++- 11 files changed, 114 insertions(+), 28 deletions(-) diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 985db98846..36b1e28e1b 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -189,6 +189,7 @@ public function sign(string $uuid): TemplateResponse { $this->initialState->provideInitialState('status', $file['status']); $this->initialState->provideInitialState('statusText', $file['statusText']); $this->initialState->provideInitialState('signers', $file['signers']); + $this->initialState->provideInitialState('sign_request_uuid', $uuid); $this->provideSignerSignatues(); $this->initialState->provideInitialState('token_length', TokenService::TOKEN_LENGTH); $this->initialState->provideInitialState('description', $this->getSignRequestEntity()->getDescription() ?? ''); diff --git a/lib/Controller/SignatureElementsController.php b/lib/Controller/SignatureElementsController.php index c9e76d341b..017fc50a03 100644 --- a/lib/Controller/SignatureElementsController.php +++ b/lib/Controller/SignatureElementsController.php @@ -2,7 +2,7 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Vitor Mattos + * @copyright Copyright (c) 2024 Vitor Mattos * * @author Vitor Mattos * @@ -31,6 +31,7 @@ use OCA\Libresign\Service\AccountService; use OCA\Libresign\Service\SessionService; use OCA\Libresign\Service\SignerElementsService; +use OCA\Libresign\Service\SignFileService; use OCP\AppFramework\ApiController; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; @@ -56,6 +57,7 @@ public function __construct( private SignerElementsService $signerElementsService, protected IUserSession $userSession, protected SessionService $sessionService, + protected SignFileService $signFileService, private IPreview $preview, private ValidateHelper $validateHelper ) { @@ -172,8 +174,10 @@ public function getSignatureElement(int $nodeId): JSONResponse { } #[NoAdminRequired] + #[PublicPage] #[NoCSRFRequired] - public function patchSignatureElement($nodeId, string $type = '', array $file = []): JSONResponse { + #[RequireSignRequestUuid] + public function patchSignatureElement(int $nodeId, string $type = '', array $file = []): JSONResponse { try { $element['nodeId'] = $nodeId; if ($type) { @@ -194,7 +198,13 @@ public function patchSignatureElement($nodeId, string $type = '', array $file = $this->accountService->saveVisibleElement($element, $this->sessionService->getSessionId(), $user); return new JSONResponse( [ - 'message' => $this->l10n->t('Element updated with success') + 'message' => $this->l10n->t('Element updated with success'), + 'elements' => + ( + $this->userSession->getUser() instanceof IUser + ? $this->signerElementsService->getUserElements($this->userSession->getUser()->getUID()) + : $this->signerElementsService->getElementsFromSessionAsArray() + ), ], Http::STATUS_OK ); diff --git a/lib/Middleware/InjectionMiddleware.php b/lib/Middleware/InjectionMiddleware.php index dfd70fb554..54a954d9f2 100644 --- a/lib/Middleware/InjectionMiddleware.php +++ b/lib/Middleware/InjectionMiddleware.php @@ -106,29 +106,35 @@ private function handleUuid(Controller $controller, \ReflectionMethod $reflectio return; } + $uuid = $this->getUuidFromRequest(); + if (!empty($reflectionMethod->getAttributes(CanSignRequestUuid::class))) { /** @var AEnvironmentPageAwareController $controller */ $controller->validateRenewSigner( - uuid: $this->request->getParam('uuid', ''), + uuid: $uuid, ); /** @var AEnvironmentPageAwareController $controller */ $controller->loadNextcloudFileFromSignRequestUuid( - uuid: $this->request->getParam('uuid', ''), + uuid: $uuid, ); } if (!empty($reflectionMethod->getAttributes(RequireSignRequestUuid::class))) { /** @var AEnvironmentPageAwareController $controller */ $controller->validateSignRequestUuid( - uuid: $this->request->getParam('uuid', ''), + uuid: $uuid, ); /** @var AEnvironmentPageAwareController $controller */ $controller->loadNextcloudFileFromSignRequestUuid( - uuid: $this->request->getParam('uuid', ''), + uuid: $uuid, ); } } + private function getUuidFromRequest(): ?string { + return $this->request->getParam('uuid', $this->request->getHeader('LibreSign-sign-request-uuid', '')); + } + private function getLoggedIn(): void { $user = $this->userSession->getUser(); if (!$user instanceof IUser) { @@ -138,7 +144,7 @@ private function getLoggedIn(): void { } private function requireSigner(): void { - $uuid = $this->request->getParam('uuid', ''); + $uuid = $this->getUuidFromRequest(); try { $user = $this->userSession->getUser(); diff --git a/lib/Service/AccountService.php b/lib/Service/AccountService.php index fa908dcc25..a1966b084d 100644 --- a/lib/Service/AccountService.php +++ b/lib/Service/AccountService.php @@ -90,6 +90,7 @@ public function __construct( private Pkcs12Handler $pkcs12Handler, private IGroupManager $groupManager, private AccountFileService $accountFileService, + private SignerElementsService $signerElementsService, private UserElementMapper $userElementMapper, private FolderService $folderService, private IClientService $clientService, @@ -408,6 +409,26 @@ private function saveFileOfVisibleElementUsingUser(array $data, IUser $user): Fi } private function saveFileOfVisibleElementUsingSession(array $data, string $sessionId): File { + if ($data['nodeId']) { + return $this->updateFileOfVisibleElementUsingSession($data, $sessionId); + } + return $this->createFileOfVisibleElementUsingSession($data, $sessionId); + } + + private function updateFileOfVisibleElementUsingSession(array $data, string $sessionId): File { + $fileList = $this->signerElementsService->getElementsFromSession(); + $element = array_filter($fileList, function (File $element) use ($data) { + return $element->getId() === $data['nodeId']; + }); + $element = current($element); + if (!$element instanceof File) { + throw new \Exception($this->l10n->t('File not found')); + } + $element->putContent($this->getFileRaw($data)); + return $element; + } + + private function createFileOfVisibleElementUsingSession(array $data, string $sessionId): File { $rootSignatureFolder = $this->folderService->getFolder(); $folderName = $sessionId; if ($rootSignatureFolder->nodeExists($folderName)) { diff --git a/lib/Service/SignerElementsService.php b/lib/Service/SignerElementsService.php index 5b264e4c13..ac201e7f87 100644 --- a/lib/Service/SignerElementsService.php +++ b/lib/Service/SignerElementsService.php @@ -52,7 +52,7 @@ public function getUserElementByNodeId(string $userId, $nodeId): array { 'id' => $element->getId(), 'type' => $element->getType(), 'file' => [ - 'url' => $this->urlGenerator->linkToRoute('ocs.libresign.SignatureService.getSignatureElementPreview', [ + 'url' => $this->urlGenerator->linkToRoute('ocs.libresign.SignatureElements.getSignatureElementPreview', [ 'apiVersion' => 'v1', 'nodeId' => $element->getFileId(), ]), @@ -76,7 +76,7 @@ public function getUserElements(string $userId): array { 'id' => $element->getId(), 'type' => $element->getType(), 'file' => [ - 'url' => $this->urlGenerator->linkToRoute('ocs.libresign.SignatureService.getSignatureElementPreview', [ + 'url' => $this->urlGenerator->linkToRoute('ocs.libresign.SignatureElements.getSignatureElementPreview', [ 'apiVersion' => 'v1', 'nodeId' => $element->getFileId(), ]), @@ -119,9 +119,10 @@ public function getElementsFromSessionAsArray(): array { $return[] = [ 'type' => $type, 'file' => [ - 'url' => $this->urlGenerator->linkToRoute('ocs.libresign.SignatureService.getSignatureElementPreview', [ + 'url' => $this->urlGenerator->linkToRoute('ocs.libresign.SignatureElements.getSignatureElementPreview', [ 'apiVersion' => 'v1', 'nodeId' => $fileElement->getId(), + 'mtime' => $fileElement->getMTime(), ]), 'nodeId' => $fileElement->getId(), ], diff --git a/src/Components/Draw/Draw.vue b/src/Components/Draw/Draw.vue index 25c0842989..8f30a4e724 100644 --- a/src/Components/Draw/Draw.vue +++ b/src/Components/Draw/Draw.vue @@ -101,6 +101,7 @@ export default { this.$emit('close') }, async save(base64) { + this.signatureElementsStore.loadSignatures() await this.signatureElementsStore.save(this.type, base64) this.$emit('save') this.close() diff --git a/src/Components/PreviewSignature/PreviewSignature.vue b/src/Components/PreviewSignature/PreviewSignature.vue index 8d048ad27a..99075d22db 100644 --- a/src/Components/PreviewSignature/PreviewSignature.vue +++ b/src/Components/PreviewSignature/PreviewSignature.vue @@ -2,13 +2,14 @@
diff --git a/src/views/SignPDF/_partials/Sign.vue b/src/views/SignPDF/_partials/Sign.vue index 06a1d6cd61..e33f781ad2 100644 --- a/src/views/SignPDF/_partials/Sign.vue +++ b/src/views/SignPDF/_partials/Sign.vue @@ -78,6 +78,7 @@ :draw-editor="true" :text-editor="true" :file-editor="true" + :sign-request-uuid="signRequestUuid" type="signature" @save="saveSignature" @close="signMethodsStore.closeModal('createSignature')" /> @@ -179,9 +180,15 @@ export default { } return true }, + signRequestUuid() { + const signer = this.signStore.document.signers.find(row => row.me) || {} + return signer.sign_uuid + }, }, mounted() { this.loading = true + this.signatureElementsStore.signRequestUuid = this.signRequestUuid + this.signatureElementsStore.loadSignatures() Promise.all([ this.loadUser(), @@ -252,8 +259,7 @@ export default { if (this.signStore.document.fileId > 0) { url = generateOcsUrl('/apps/libresign/api/v1/sign/file_id/{nodeId}', { fileId: this.signStore.document.nodeId }) } else { - const signer = this.signStore.document.signers.find(row => row.me) || {} - url = generateOcsUrl('/apps/libresign/api/v1/sign/uuid/{uuid}', { uuid: signer.sign_uuid }) + url = generateOcsUrl('/apps/libresign/api/v1/sign/uuid/{uuid}', { uuid: this.signRequestUuid }) } await axios.post(url, payload) From fd137025f0ebf635d0440ffe0153422046c98888 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 19 Apr 2024 14:03:45 -0300 Subject: [PATCH 05/10] fix: unit tests Signed-off-by: Vitor Mattos --- tests/Unit/Service/AccountServiceTest.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/Unit/Service/AccountServiceTest.php b/tests/Unit/Service/AccountServiceTest.php index d5d89b38c5..3c4b466318 100644 --- a/tests/Unit/Service/AccountServiceTest.php +++ b/tests/Unit/Service/AccountServiceTest.php @@ -18,6 +18,7 @@ use OCA\Libresign\Service\FolderService; use OCA\Libresign\Service\IdentifyMethodService; use OCA\Libresign\Service\RequestSignatureService; +use OCA\Libresign\Service\SignerElementsService; use OCA\Libresign\Service\SignFileService; use OCA\Settings\Mailer\NewUserMailHelper; use OCP\Accounts\IAccountManager; @@ -58,10 +59,11 @@ final class AccountServiceTest extends \OCA\Libresign\Tests\Unit\TestCase { private ValidateHelper|MockObject $validateHelper; private IURLGenerator|MockObject $urlGenerator; private IGroupManager|MockObject $groupManager; - private AccountFileService $accountFileService; - private UserElementMapper $userElementMapper; - private FolderService $folderService; - private ClientService $clientService; + private AccountFileService|MockObject $accountFileService; + private SignerElementsService|MockObject $signerElementsService; + private UserElementMapper|MockObject $userElementMapper; + private FolderService|MockObject $folderService; + private ClientService|MockObject $clientService; private TimeFactory|MockObject $timeFactory; private RequestSignatureService|MockObject $requestSignatureService; private Pkcs12Handler|MockObject $pkcs12Handler; @@ -94,6 +96,7 @@ public function setUp(): void { $this->pkcs12Handler = $this->createMock(Pkcs12Handler::class); $this->groupManager = $this->createMock(IGroupManager::class); $this->accountFileService = $this->createMock(AccountFileService::class); + $this->signerElementsService = $this->createMock(SignerElementsService::class); $this->userElementMapper = $this->createMock(UserElementMapper::class); $this->folderService = $this->createMock(FolderService::class); $this->clientService = $this->createMock(ClientService::class); @@ -125,6 +128,7 @@ private function getService(): AccountService { $this->pkcs12Handler, $this->groupManager, $this->accountFileService, + $this->signerElementsService, $this->userElementMapper, $this->folderService, $this->clientService, From 0c5ee4696a2a5bd36b2a2462cc8486f8a73ed51f Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 19 Apr 2024 16:18:26 -0300 Subject: [PATCH 06/10] fix: do not use header with signer_uuid when is authenticated Endpoints of CRUD to visible signatures need to be acessed by authenticated user or if not authenticated, will be necessary use the signer_uuid Signed-off-by: Vitor Mattos --- .../SignatureElementsController.php | 10 ++++---- .../Attribute/RequireSignRequestUuid.php | 8 ++++++ lib/Middleware/InjectionMiddleware.php | 25 ++++++++++++------- .../features/account/signature.feature | 10 +++++--- .../features/bootstrap/FeatureContext.php | 23 +++++++++++++---- 5 files changed, 53 insertions(+), 23 deletions(-) diff --git a/lib/Controller/SignatureElementsController.php b/lib/Controller/SignatureElementsController.php index 017fc50a03..d749e82ffe 100644 --- a/lib/Controller/SignatureElementsController.php +++ b/lib/Controller/SignatureElementsController.php @@ -67,7 +67,7 @@ public function __construct( #[NoAdminRequired] #[NoCSRFRequired] #[PublicPage] - #[RequireSignRequestUuid] + #[RequireSignRequestUuid(skipIfAuthenticated: true)] public function createSignatureElement(array $elements): JSONResponse { try { $this->validateHelper->validateVisibleElements($elements, $this->validateHelper::TYPE_VISIBLE_ELEMENT_USER); @@ -105,7 +105,7 @@ public function createSignatureElement(array $elements): JSONResponse { #[NoAdminRequired] #[NoCSRFRequired] #[PublicPage] - #[RequireSignRequestUuid] + #[RequireSignRequestUuid(skipIfAuthenticated: true)] public function getSignatureElements(): JSONResponse { $userId = $this->userSession->getUser()?->getUID(); try { @@ -133,7 +133,7 @@ public function getSignatureElements(): JSONResponse { #[NoAdminRequired] #[PublicPage] #[NoCSRFRequired] - #[RequireSignRequestUuid] + #[RequireSignRequestUuid(skipIfAuthenticated: true)] public function getSignatureElementPreview(int $nodeId) { try { $node = $this->accountService->getFileByNodeIdAndSessionId( @@ -176,7 +176,7 @@ public function getSignatureElement(int $nodeId): JSONResponse { #[NoAdminRequired] #[PublicPage] #[NoCSRFRequired] - #[RequireSignRequestUuid] + #[RequireSignRequestUuid(skipIfAuthenticated: true)] public function patchSignatureElement(int $nodeId, string $type = '', array $file = []): JSONResponse { try { $element['nodeId'] = $nodeId; @@ -221,7 +221,7 @@ public function patchSignatureElement(int $nodeId, string $type = '', array $fil #[NoAdminRequired] #[NoCSRFRequired] #[PublicPage] - #[RequireSignRequestUuid] + #[RequireSignRequestUuid(skipIfAuthenticated: true)] public function deleteSignatureElement(int $nodeId): JSONResponse { try { $this->accountService->deleteSignatureElement( diff --git a/lib/Middleware/Attribute/RequireSignRequestUuid.php b/lib/Middleware/Attribute/RequireSignRequestUuid.php index 8841882ce6..343417eea9 100644 --- a/lib/Middleware/Attribute/RequireSignRequestUuid.php +++ b/lib/Middleware/Attribute/RequireSignRequestUuid.php @@ -28,4 +28,12 @@ #[Attribute(Attribute::TARGET_METHOD)] class RequireSignRequestUuid { + public function __construct( + protected bool $skipIfAuthenticated = false, + ) { + } + + public function skipIfAuthenticated(): bool { + return $this->skipIfAuthenticated; + } } diff --git a/lib/Middleware/InjectionMiddleware.php b/lib/Middleware/InjectionMiddleware.php index 54a954d9f2..db1f8b7950 100644 --- a/lib/Middleware/InjectionMiddleware.php +++ b/lib/Middleware/InjectionMiddleware.php @@ -119,15 +119,22 @@ private function handleUuid(Controller $controller, \ReflectionMethod $reflectio ); } - if (!empty($reflectionMethod->getAttributes(RequireSignRequestUuid::class))) { - /** @var AEnvironmentPageAwareController $controller */ - $controller->validateSignRequestUuid( - uuid: $uuid, - ); - /** @var AEnvironmentPageAwareController $controller */ - $controller->loadNextcloudFileFromSignRequestUuid( - uuid: $uuid, - ); + if (!empty($attribute = $reflectionMethod->getAttributes(RequireSignRequestUuid::class))) { + $attribute = $reflectionMethod->getAttributes(RequireSignRequestUuid::class); + $attribute = current($attribute); + /** @var RequireSignRequestUuid $intance */ + $intance = $attribute->newInstance(); + $user = $this->userSession->getUser(); + if (!($intance->skipIfAuthenticated() && $user instanceof IUser)) { + /** @var AEnvironmentPageAwareController $controller */ + $controller->validateSignRequestUuid( + uuid: $uuid, + ); + /** @var AEnvironmentPageAwareController $controller */ + $controller->loadNextcloudFileFromSignRequestUuid( + uuid: $uuid, + ); + } } } diff --git a/tests/integration/features/account/signature.feature b/tests/integration/features/account/signature.feature index 37ecc07544..4eb171cf1e 100644 --- a/tests/integration/features/account/signature.feature +++ b/tests/integration/features/account/signature.feature @@ -144,17 +144,19 @@ Feature: account/signature Then the response should have a status code 200 Scenario: CRUD of signature element to signer by email without account - Given guest "guest@test.coop" exists - And run the command "config:app:set guests whitelist --value libresign" with result code 0 + Given run the command "config:app:set guests whitelist --value libresign" with result code 0 And run the command "libresign:configure:openssl --cn test" with result code 0 And as user "admin" And sending "post" to ocs "/apps/provisioning_api/api/v1/config/apps/libresign/identify_methods" | value | (string)[{"name":"email","enabled":true,"mandatory":true,"can_create_account":false}] | And sending "post" to ocs "/apps/libresign/api/v1/request-signature" | file | {"url":"/apps/libresign/develop/pdf"} | - | users | [{"identify":{"email":"guest@test.coop"}}] | + | users | [{"identify":{"email":"signer@test.coop"}}] | | name | document | - And as user "" + When as user "" + And I open the latest email to "signer@test.coop" with subject "LibreSign: There is a file for you to sign" + And I fetch the signer UUID from opened email + And set the custom http header "LibreSign-sign-request-uuid" with "" as value to next request When sending "post" to ocs "/apps/libresign/api/v1/signature/elements" | elements | [{"type":"signature","file":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="}}] | Then the response should have a status code 200 diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index 6e82696022..fd8af010e3 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -1,5 +1,6 @@ customHeaders); parent::sendRequest($verb, $url, $body, $headers, $options); } + /** + * @Given /^set the custom http header "([^"]*)" with "([^"]*)" as value to next request$/ + */ + public function setTheCustomHttpHeaderAsValueToNextRequest(string $header, string $value) + { + if (empty($value)) { + unset($this->customHeaders[$header]); + return; + } + $this->customHeaders[$header] = $this->parseText($value); + } + + /** * @Then /^the signer "([^"]*)" have a file to sign$/ */ @@ -154,9 +170,6 @@ protected function beforeRequest(string $fullUrl, array $options): array { protected function parseText(string $text): string { $fields = $this->fields; - if (!empty($this->signer['sign_uuid'])) { - $fields['SIGN_UUID'] = $this->signer['sign_uuid']; - } $fields['BASE_URL'] = $this->baseUrl . '/index.php'; foreach ($fields as $key => $value) { $patterns[] = '/<' . $key . '>/'; @@ -200,7 +213,7 @@ public function iFetchTheLinkOnOpenedEmail(): void { $openedEmail = $this->openedEmailStorage->getOpenedEmail(); preg_match('/p\/sign\/(?[\w-]+)"/', $openedEmail->body, $matches); Assert::assertArrayHasKey('uuid', $matches, 'UUID not found on email'); - $this->signer['sign_uuid'] = $matches['uuid']; + $this->fields['SIGN_UUID'] = $matches['uuid']; } /** @@ -340,6 +353,6 @@ function ($notification) { preg_match('/p\/sign\/(?[\w-]+)$/', $found['link'], $matches); Assert::assertArrayHasKey('uuid', $matches, 'UUID not found on email'); - $this->signer['sign_uuid'] = $matches['uuid']; + $this->fields['SIGN_UUID'] = $matches['uuid']; } } From 001decc4f5f3b24cf7e11d26622380d224c43638 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 19 Apr 2024 16:20:10 -0300 Subject: [PATCH 07/10] chore: bump dependency Signed-off-by: Vitor Mattos --- tests/integration/composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/integration/composer.lock b/tests/integration/composer.lock index 010bc9ef91..d20ea97342 100644 --- a/tests/integration/composer.lock +++ b/tests/integration/composer.lock @@ -5184,16 +5184,16 @@ }, { "name": "libresign/nextcloud-behat", - "version": "v0.15.0", + "version": "v0.15.1", "source": { "type": "git", "url": "https://github.com/LibreSign/nextcloud-behat.git", - "reference": "96829df411c2cffeb032281eb90acaf63531da70" + "reference": "c4de17091a1e4f8b897d408fde20ee9191dbceba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/LibreSign/nextcloud-behat/zipball/96829df411c2cffeb032281eb90acaf63531da70", - "reference": "96829df411c2cffeb032281eb90acaf63531da70", + "url": "https://api.github.com/repos/LibreSign/nextcloud-behat/zipball/c4de17091a1e4f8b897d408fde20ee9191dbceba", + "reference": "c4de17091a1e4f8b897d408fde20ee9191dbceba", "shasum": "" }, "require": { @@ -5238,9 +5238,9 @@ ], "support": { "issues": "https://github.com/LibreSign/nextcloud-behat/issues", - "source": "https://github.com/LibreSign/nextcloud-behat/tree/v0.15.0" + "source": "https://github.com/LibreSign/nextcloud-behat/tree/v0.15.1" }, - "time": "2024-04-10T13:35:20+00:00" + "time": "2024-04-19T18:46:01+00:00" }, { "name": "symfony/process", From d2509274cfb58b7e80b7e148eded1d474bae75b0 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 19 Apr 2024 16:21:49 -0300 Subject: [PATCH 08/10] fix: cs Signed-off-by: Vitor Mattos --- tests/integration/features/bootstrap/FeatureContext.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index fd8af010e3..afc379221f 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -1,6 +1,5 @@ customHeaders[$header]); return; From 00a45691d549f47bf6ef0e3b7998a7975addef33 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 19 Apr 2024 16:56:02 -0300 Subject: [PATCH 09/10] fix: revert previous change Signed-off-by: Vitor Mattos --- appinfo/routes/routesAccountController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/appinfo/routes/routesAccountController.php b/appinfo/routes/routesAccountController.php index 762b369050..c9c6c9368d 100644 --- a/appinfo/routes/routesAccountController.php +++ b/appinfo/routes/routesAccountController.php @@ -17,5 +17,6 @@ ['name' => 'account#addFiles', 'url' => '/api/{apiVersion}/account/files', 'verb' => 'POST', 'requirements' => $requirements], ['name' => 'account#deleteFile', 'url' => '/api/{apiVersion}/account/files', 'verb' => 'DELETE', 'requirements' => $requirements], ['name' => 'account#accountFileListToOwner', 'url' => '/api/{apiVersion}/account/files', 'verb' => 'GET', 'requirements' => $requirements], + ['name' => 'account#accountFileListToApproval', 'url' => '/api/{apiVersion}/account/files/approval/list', 'verb' => 'GET', 'requirements' => $requirements], ], ]; From 18f6402271bf4f90391a55118848dba9425e996e Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 19 Apr 2024 18:02:39 -0300 Subject: [PATCH 10/10] fix: remove unecessary steps After implementation of integration with jq and the step to fetch a field from previous response was possible to remove some steps. Signed-off-by: Vitor Mattos --- .../features/bootstrap/FeatureContext.php | 65 ------------------- .../page/sign_identify_account.feature | 38 +++++------ 2 files changed, 15 insertions(+), 88 deletions(-) diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index afc379221f..61080e1668 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -115,50 +115,6 @@ public function setTheCustomHttpHeaderAsValueToNextRequest(string $header, strin $this->customHeaders[$header] = $this->parseText($value); } - - /** - * @Then /^the signer "([^"]*)" have a file to sign$/ - */ - public function theSignerHaveAFileToSign(string $signer): void { - $this->setCurrentUser($signer); - $this->sendOCSRequest('get', '/apps/libresign/api/v1/file/list'); - $response = json_decode($this->response->getBody()->getContents(), true); - Assert::assertGreaterThan(0, $response['data'], 'Haven\'t files to sign'); - $this->signer = []; - $this->file = []; - foreach (array_reverse($response['data']) as $file) { - $currentSigner = array_filter($file['signers'], function ($signer): bool { - return $signer['me']; - }); - if (count($currentSigner) === 1) { - $this->signer = end($currentSigner); - $this->file = $file; - break; - } - } - Assert::assertGreaterThan(1, $this->signer, $signer . ' don\'t will sign a file'); - Assert::assertGreaterThan(1, $this->file, 'The /file/list didn\'t returned a file assigned to ' . $signer); - } - - /** - * @Then /^the file to sign contains$/ - * - * @param string $name - */ - public function theFileToSignContains(TableNode $table): void { - if (!$this->file) { - $this->theSignerHaveAFileToSign($this->currentUser); - } - $expectedValues = $table->getColumnsHash(); - foreach ($expectedValues as $value) { - Assert::assertArrayHasKey($value['key'], $this->file); - if ($value['value'] === '') { - continue; - } - Assert::assertEquals($value['value'], $this->file[$value['key']]); - } - } - protected function beforeRequest(string $fullUrl, array $options): array { list($fullUrl, $options) = parent::beforeRequest($fullUrl, $options); $options = $this->parseFormParams($options); @@ -178,27 +134,6 @@ protected function parseText(string $text): string { return $text; } - /** - * @Given the signer contains - */ - public function theSignerContains(TableNode $table): void { - if (!$this->signer) { - $this->theSignerHaveAFileToSign($this->currentUser); - } - $expectedValues = $table->getColumnsHash(); - foreach ($expectedValues as $value) { - Assert::assertArrayHasKey($value['key'], $this->signer); - if ($value['value'] === '') { - continue; - } - $actual = $this->signer[$value['key']]; - if (is_array($this->signer[$value['key']]) || is_object($this->signer[$value['key']])) { - $actual = json_encode($actual); - } - Assert::assertEquals($value['value'], $actual, sprintf('The actual value of key "%s" is different of expected', $value['key'])); - } - } - /** * @When I fetch the signer UUID from opened email */ diff --git a/tests/integration/features/page/sign_identify_account.feature b/tests/integration/features/page/sign_identify_account.feature index c41ff17e1b..e2ef4e950b 100644 --- a/tests/integration/features/page/sign_identify_account.feature +++ b/tests/integration/features/page/sign_identify_account.feature @@ -1,6 +1,6 @@ Feature: page/sign_identify_account Background: Make setup ok - Given run the command "config:app:set libresign authkey --value dummy" with result code 0 + Given run the command "libresign:configure:openssl --cn test" with result code 0 Scenario: Open sign file with invalid account data Given user "signer1" exists @@ -21,17 +21,13 @@ Feature: page/sign_identify_account | ocs | (jq).data\|.[].subject == "admin requested your signature on document"| When sending "get" to ocs "/apps/libresign/api/v1/file/list" And the response should have a status code 200 - And the file to sign contains - | key | value | - | uuid | | - | name | document | - | status | 1 | - | statusText | available for signature | - And the signer contains - | key | value | - | email | | - | me | true | - | identifyMethods | [{"method":"account","value":"signer1","mandatory":1}] | + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq).data\|.[].statusText | available for signature | + | (jq).data\|.[].signers | (jq).[].me == true | + | (jq).data\|.[].signers\|.[].identifyMethods\|.[].method | account | + | (jq).data\|.[].signers\|.[].identifyMethods\|.[].value | signer1 | + And fetch field "(SIGN_UUID)data.0.signers.0.sign_uuid" from prevous JSON response # invalid UUID, need to be the signer UUID When as user "signer1" And sending "get" to "/apps/libresign/p/sign/" @@ -75,17 +71,13 @@ Feature: page/sign_identify_account | ocs | (jq).data\|.[].subject == "admin requested your signature on document"| When sending "get" to ocs "/apps/libresign/api/v1/file/list" And the response should have a status code 200 - And the file to sign contains - | key | value | - | uuid | | - | name | document | - | status | 1 | - | statusText | available for signature | - And the signer contains - | key | value | - | email | | - | me | true | - | identifyMethods | [{"method":"account","value":"signer1","mandatory":1}] | + And the response should be a JSON array with the following mandatory values + | key | value | + | (jq).data\|.[].statusText | available for signature | + | (jq).data\|.[].signers | (jq).[].me == true | + | (jq).data\|.[].signers\|.[].identifyMethods\|.[].method | account | + | (jq).data\|.[].signers\|.[].identifyMethods\|.[].value | signer1 | + And fetch field "(SIGN_UUID)data.0.signers.0.sign_uuid" from prevous JSON response When as user "signer1" And sending "get" to "/apps/libresign/p/sign/" And the response should contain the initial state "libresign-action" with the following values: