Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
75.00% covered (warning)
75.00%
45 / 60
45.45% covered (danger)
45.45%
5 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
SigningRequestValidator
75.00% covered (warning)
75.00%
45 / 60
45.45% covered (danger)
45.45%
5 / 11
48.00
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validateWorkflowIsNotClosedByFileId
50.00% covered (danger)
50.00%
2 / 4
0.00% covered (danger)
0.00%
0 / 1
2.50
 validateWorkflowIsNotClosedByUuid
50.00% covered (danger)
50.00%
2 / 4
0.00% covered (danger)
0.00%
0 / 1
2.50
 validateWorkflowIsNotClosed
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 fileCanBeSigned
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 canRequestSign
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 iRequestedSignThisFile
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 validateFileStatus
68.75% covered (warning)
68.75%
11 / 16
0.00% covered (danger)
0.00%
0 / 1
14.69
 validateExistingFile
83.33% covered (warning)
83.33%
10 / 12
0.00% covered (danger)
0.00%
0 / 1
4.07
 validateFileUuid
33.33% covered (danger)
33.33%
1 / 3
0.00% covered (danger)
0.00%
0 / 1
3.19
 validateIsSignerOfFile
33.33% covered (danger)
33.33%
1 / 3
0.00% covered (danger)
0.00%
0 / 1
3.19
1<?php
2
3declare(strict_types=1);
4/**
5 * SPDX-FileCopyrightText: 2020-2024 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\Service\Validation;
10
11use OCA\Libresign\Db\File;
12use OCA\Libresign\Db\FileMapper;
13use OCA\Libresign\Db\SignRequestMapper;
14use OCA\Libresign\Enum\FileStatus;
15use OCA\Libresign\Exception\LibresignException;
16use OCA\Libresign\Helper\JSActions;
17use OCA\Libresign\Service\Policy\RequestSignAuthorizationService;
18use OCP\AppFramework\Http;
19use OCP\IL10N;
20use OCP\IUser;
21
22class SigningRequestValidator {
23    public function __construct(
24        private IL10N $l10n,
25        private FileMapper $fileMapper,
26        private SignRequestMapper $signRequestMapper,
27        private RequestSignAuthorizationService $requestSignAuthorizationService,
28        private FileInputValidator $fileInputValidator,
29    ) {
30    }
31
32    public function validateWorkflowIsNotClosedByFileId(int $fileId): void {
33        try {
34            $file = $this->fileMapper->getById($fileId);
35        } catch (\Throwable) {
36            return;
37        }
38        $this->validateWorkflowIsNotClosed($file);
39    }
40
41    public function validateWorkflowIsNotClosedByUuid(string $uuid): void {
42        try {
43            $file = $this->fileMapper->getByUuid($uuid);
44        } catch (\Throwable) {
45            return;
46        }
47        $this->validateWorkflowIsNotClosed($file);
48    }
49
50    public function validateWorkflowIsNotClosed(File $file): void {
51        if ($file->getStatus() === FileStatus::CANCELED->value) {
52            throw new LibresignException($this->l10n->t('The signing workflow of this document is already closed.'));
53        }
54    }
55
56    public function fileCanBeSigned(File $file): void {
57        $this->validateWorkflowIsNotClosed($file);
58        if (!in_array($file->getStatus(), [FileStatus::ABLE_TO_SIGN->value, FileStatus::PARTIAL_SIGNED->value], true)) {
59            $statusText = $this->fileMapper->getTextOfStatus($file->getStatus());
60            throw new LibresignException($this->l10n->t('This file cannot be signed. Invalid status: %s', $statusText));
61        }
62    }
63
64    public function canRequestSign(IUser $user): void {
65        if (!$this->requestSignAuthorizationService->canRequestSign($user)) {
66            throw new LibresignException(
67                json_encode([
68                    'action' => JSActions::ACTION_DO_NOTHING,
69                    'errors' => [['message' => $this->l10n->t('You are not allowed to create signature requests')]],
70                ]),
71                Http::STATUS_UNPROCESSABLE_ENTITY,
72            );
73        }
74    }
75
76    public function iRequestedSignThisFile(IUser $user, int $fileId): void {
77        $libresignFile = $this->fileMapper->getById($fileId);
78        if ($libresignFile->getUserId() !== $user->getUID()) {
79            throw new LibresignException($this->l10n->t('You do not have permission for this action.'));
80        }
81    }
82
83    public function validateFileStatus(array $data): void {
84        if (!array_key_exists('status', $data)) {
85            return;
86        }
87        $validStatusList = [FileStatus::DRAFT->value, FileStatus::ABLE_TO_SIGN->value, FileStatus::DELETED->value];
88        if (!in_array($data['status'], $validStatusList, true)) {
89            throw new LibresignException($this->l10n->t('Invalid status code for file.'));
90        }
91
92        $file = null;
93        if (!empty($data['uuid'])) {
94            $file = $this->fileMapper->getByUuid($data['uuid']);
95        } elseif (!empty($data['file']['fileId'])) {
96            try {
97                $file = $this->fileMapper->getById($data['file']['fileId']);
98            } catch (\Throwable) {
99            }
100        }
101        if ($file instanceof File) {
102            if ($data['status'] > $file->getStatus() && $file->getStatus() >= FileStatus::ABLE_TO_SIGN->value && $data['status'] !== FileStatus::DELETED->value) {
103                throw new LibresignException($this->l10n->t('Sign process already started. Unable to change status.'));
104            }
105        } elseif ($data['status'] === FileStatus::DELETED->value) {
106            throw new LibresignException($this->l10n->t('Invalid status code for file.'));
107        }
108    }
109
110    public function validateExistingFile(array $data): void {
111        if (isset($data['uuid'])) {
112            $this->validateFileUuid($data);
113            $file = $this->fileMapper->getByUuid($data['uuid']);
114            $this->iRequestedSignThisFile($data['userManager'], $file->getId());
115            return;
116        }
117        if (isset($data['file'])) {
118            if (!isset($data['file']['fileId'])) {
119                throw new LibresignException($this->l10n->t('Invalid fileID'));
120            }
121            $this->fileInputValidator->validateLibreSignFileId($data['file']['fileId']);
122            $this->iRequestedSignThisFile($data['userManager'], $data['file']['fileId']);
123            return;
124        }
125        throw new LibresignException($this->l10n->t('Please provide either UUID or File object'));
126    }
127
128    public function validateFileUuid(array $data): void {
129        try {
130            $this->fileMapper->getByUuid($data['uuid']);
131        } catch (\Throwable) {
132            throw new LibresignException($this->l10n->t('Invalid UUID file'));
133        }
134    }
135
136    public function validateIsSignerOfFile(int $signRequestId, int $fileId): void {
137        try {
138            $this->signRequestMapper->getByFileIdAndSignRequestId($fileId, $signRequestId);
139        } catch (\Throwable) {
140            throw new LibresignException($this->l10n->t('Signer not associated to this file'));
141        }
142    }
143}