Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.24% covered (success)
90.24%
37 / 41
40.00% covered (danger)
40.00%
2 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Validator
90.24% covered (success)
90.24%
37 / 41
40.00% covered (danger)
40.00%
2 / 5
20.37
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
 validateSignersCount
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
6
 validatePdfRestrictions
81.82% covered (warning)
81.82%
9 / 11
0.00% covered (danger)
0.00%
0 / 1
4.10
 countSigningParticipants
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
4.03
 getDocMdpLevel
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
5.05
1<?php
2
3declare(strict_types=1);
4
5/**
6 * SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
7 * SPDX-License-Identifier: AGPL-3.0-or-later
8 */
9
10namespace OCA\Libresign\Service\DocMdp;
11
12use OCA\Libresign\Db\File;
13use OCA\Libresign\Db\FileMapper;
14use OCA\Libresign\Enum\ParticipantRole;
15use OCA\Libresign\Exception\LibresignException;
16use OCA\Libresign\Service\File\Pdf\PdfValidator;
17use OCP\AppFramework\Db\DoesNotExistException;
18use OCP\Files\IRootFolder;
19use OCP\IL10N;
20
21class Validator {
22    public function __construct(
23        private IL10N $l10n,
24        private FileMapper $fileMapper,
25        private ConfigService $configService,
26        private PdfValidator $pdfValidator,
27        private IRootFolder $root,
28    ) {
29    }
30
31    public function validateSignersCount(array $data): void {
32        if (empty($data['signers'])) {
33            return;
34        }
35
36        $file = null;
37        $docMdpLevel = $this->getDocMdpLevel($data, $file);
38
39        if ($docMdpLevel === 1) {
40            if ($file && $file->getSignedHash()) {
41                throw new LibresignException(
42                    // TRANSLATORS Error shown when trying to add more signers to a document certified with DocMDP level that forbids further changes.
43                    $this->l10n->t('This document has been certified with no changes allowed. You cannot add more signers to this document.')
44                );
45            }
46
47            if ($this->countSigningParticipants($data['signers']) > 1) {
48                throw new LibresignException(
49                    // TRANSLATORS Error shown when trying to add more signers to a document certified with DocMDP level that forbids further changes.
50                    $this->l10n->t('This document has been certified with no changes allowed. You cannot add more signers to this document.')
51                );
52            }
53        }
54    }
55
56    public function validatePdfRestrictions(File $file): void {
57        if (!$file->getSignedHash()) {
58            return;
59        }
60
61        $node = $this->root->getById($file->getNodeId());
62        if (empty($node)) {
63            // TRANSLATORS Error shown when validating DocMDP certification and the document file cannot be found.
64            throw new LibresignException($this->l10n->t('File not found'));
65        }
66
67        $firstNode = current($node);
68        if (!$firstNode instanceof \OCP\Files\File) {
69            // TRANSLATORS Error shown when validating DocMDP certification and the file is not a supported document type.
70            throw new LibresignException($this->l10n->t('Invalid file type'));
71        }
72
73        $content = $firstNode->getContent();
74        $fileName = $firstNode->getName();
75
76        $this->pdfValidator->validate($content, $fileName);
77    }
78
79    /**
80     * @param list<mixed> $signers
81     */
82    private function countSigningParticipants(array $signers): int {
83        $count = 0;
84
85        foreach ($signers as $signer) {
86            if (!is_array($signer)) {
87                continue;
88            }
89
90            $role = ParticipantRole::fromNullable($signer['participantRole'] ?? null);
91            if ($role->canSign()) {
92                $count++;
93            }
94        }
95
96        return $count;
97    }
98
99    private function getDocMdpLevel(array $data, ?File &$file): int {
100        $docMdpLevel = null;
101
102        if (!empty($data['uuid'])) {
103            try {
104                $file = $this->fileMapper->getByUuid($data['uuid']);
105                $docMdpLevel = $file->getDocmdpLevel();
106            } catch (DoesNotExistException) {
107                // File doesn't exist, use global configured level
108            }
109        }
110
111        if ($docMdpLevel === null || $docMdpLevel === 0) {
112            return $this->configService->getLevel()->value;
113        }
114
115        return $docMdpLevel;
116    }
117}