Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
66 / 66
100.00% covered (success)
100.00%
27 / 27
CRAP
100.00% covered (success)
100.00%
1 / 1
AccountService
100.00% covered (success)
100.00%
66 / 66
100.00% covered (success)
100.00%
27 / 27
42
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validateCreateToSign
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
9
 getFileByUuid
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validateCertificateData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSignRequestByUuid
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createToSign
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
4
 getCertificateEngineName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isSetupOk
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getConfig
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getConfigFilters
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getConfigSorting
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 updateIdentifyMethodToAccount
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 getPhoneNumber
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasSignatureFile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPdfByUuid
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFileByNodeId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 canRequestSign
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSettings
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 addFilesToAccount
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 deleteFileFromAccount
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 saveVisibleElements
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 saveVisibleElement
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 deleteSignatureElement
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 uploadPfx
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 deletePfx
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 updatePfxPassword
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 readPfxData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
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;
10
11use InvalidArgumentException;
12use OCA\Libresign\Db\IdentifyMethodMapper;
13use OCA\Libresign\Db\SignRequest;
14use OCA\Libresign\Exception\LibresignException;
15use OCA\Libresign\Service\File\AccountSettingsProvider;
16use OCA\Libresign\Service\Policy\RequestSignAuthorizationService;
17use OCA\Settings\Mailer\NewUserMailHelper;
18use OCP\Files\File;
19use OCP\IAppConfig;
20use OCP\IL10N;
21use OCP\IUser;
22use OCP\IUserManager;
23use Sabre\DAV\UUIDUtil;
24
25class AccountService {
26    public function __construct(
27        private IL10N $l10n,
28        private AccountFileService $accountFileService,
29        private AccountCertificateService $accountCertificateService,
30        private AccountSettingsProvider $accountSettingsProvider,
31        private IUserManager $userManager,
32        private IAppConfig $appConfig,
33        private NewUserMailHelper $newUserMail,
34        private IdentifyMethodService $identifyMethodService,
35        private IdentifyMethodMapper $identifyMethodMapper,
36        private IdDocsService $idDocsService,
37        private SignerElementsService $signerElementsService,
38        private RequestSignAuthorizationService $requestSignAuthorizationService,
39    ) {
40    }
41
42    public function validateCreateToSign(array $data): void {
43        if (!UUIDUtil::validateUUID($data['uuid'])) {
44            // TRANSLATORS Account setup error when the signature-request UUID used to create a guest account is invalid.
45            throw new LibresignException($this->l10n->t('Invalid UUID'), 1);
46        }
47        try {
48            $signRequest = $this->getSignRequestByUuid($data['uuid']);
49        } catch (\Throwable) {
50            // TRANSLATORS Account setup error when the signature-request UUID used to create a guest account cannot be found.
51            throw new LibresignException($this->l10n->t('UUID not found'), 1);
52        }
53        $identifyMethods = $this->identifyMethodService->getIdentifyMethodsFromSignRequestId($signRequest->getId());
54        if (!array_key_exists('identify', $data['user'])) {
55            // TRANSLATORS Account setup error when the identification method linked to the signature request is invalid for account creation.
56            throw new LibresignException($this->l10n->t('Invalid identification method'), 1);
57        }
58        foreach ($data['user']['identify'] as $method => $value) {
59            if (!array_key_exists($method, $identifyMethods)) {
60                // TRANSLATORS Account setup error when the identification method linked to the signature request is invalid for account creation.
61                throw new LibresignException($this->l10n->t('Invalid identification method'), 1);
62            }
63            foreach ($identifyMethods[$method] as $identifyMethod) {
64                $identifyMethod->validateToCreateAccount($value);
65            }
66        }
67        if (empty($data['password'])) {
68            // TRANSLATORS Account setup error when creating a guest account to sign and the Nextcloud password is missing.
69            throw new LibresignException($this->l10n->t('Password is mandatory'), 1);
70        }
71        $file = $this->getFileByUuid($data['uuid']);
72        if (empty($file['fileToSign'])) {
73            // TRANSLATORS Account setup error when the document linked to the signature-request UUID cannot be found.
74            throw new LibresignException($this->l10n->t('File not found'));
75        }
76    }
77
78    public function getFileByUuid(string $uuid): array {
79        return $this->accountFileService->getFileByUuid($uuid);
80    }
81
82    public function validateCertificateData(array $data): void {
83        $this->accountCertificateService->validateCertificateData($data);
84    }
85
86    public function getSignRequestByUuid(string $uuid): SignRequest {
87        return $this->accountFileService->getSignRequestByUuid($uuid);
88    }
89
90    public function createToSign(string $uuid, string $email, string $password, ?string $signPassword): void {
91        $signRequest = $this->getSignRequestByUuid($uuid);
92
93        $newUser = $this->userManager->createUser($email, $password);
94        $newUser->setDisplayName($signRequest->getDisplayName());
95        $newUser->setSystemEMailAddress($email);
96
97        $this->updateIdentifyMethodToAccount($signRequest->getId(), $email, $newUser->getUID());
98
99        if ($this->appConfig->getValueString('core', 'newUser.sendEmail', 'yes') === 'yes') {
100            try {
101                $emailTemplate = $this->newUserMail->generateTemplate($newUser, false);
102                $this->newUserMail->sendMail($newUser, $emailTemplate);
103            } catch (\Exception) {
104                throw new LibresignException('Unable to send the invitation', 1);
105            }
106        }
107
108        if ($signPassword) {
109            $this->accountCertificateService->createForUser($newUser, $signPassword);
110        }
111    }
112
113    public function getCertificateEngineName(): string {
114        return $this->accountCertificateService->getCertificateEngineName();
115    }
116
117    public function isSetupOk(): bool {
118        return $this->accountCertificateService->isSetupOk();
119    }
120
121    /**
122     * @return array<string, mixed>
123     */
124    public function getConfig(?IUser $user = null): array {
125        return $this->accountSettingsProvider->getConfig($user);
126    }
127
128    public function getConfigFilters(?IUser $user = null): array {
129        return $this->accountSettingsProvider->getConfigFilters($user);
130    }
131
132    public function getConfigSorting(?IUser $user = null): array {
133        return $this->accountSettingsProvider->getConfigSorting($user);
134    }
135
136    private function updateIdentifyMethodToAccount(int $signRequestId, string $email, string $uid): void {
137        $identifyMethods = $this->identifyMethodService->getIdentifyMethodsFromSignRequestId($signRequestId);
138        foreach ($identifyMethods as $name => $methods) {
139            if ($name === IdentifyMethodService::IDENTIFY_EMAIL) {
140                foreach ($methods as $identifyMethod) {
141                    $entity = $identifyMethod->getEntity();
142                    if ($entity->getIdentifierValue() === $email) {
143                        $entity->setIdentifierKey(IdentifyMethodService::IDENTIFY_ACCOUNT);
144                        $entity->setIdentifierValue($uid);
145                        $this->identifyMethodMapper->update($entity);
146                    }
147                }
148            }
149        }
150    }
151
152    private function getPhoneNumber(?IUser $user): string {
153        return $this->accountSettingsProvider->getPhoneNumber($user);
154    }
155
156    public function hasSignatureFile(?IUser $user = null): bool {
157        return $this->accountSettingsProvider->hasSignatureFile($user);
158    }
159
160    public function getPdfByUuid(string $uuid): File {
161        return $this->accountFileService->getPdfByUuid($uuid);
162    }
163
164    public function getFileByNodeId(int $nodeId): File {
165        return $this->accountFileService->getFileByNodeId($nodeId);
166    }
167
168    public function canRequestSign(?IUser $user = null): bool {
169        return $this->requestSignAuthorizationService->canRequestSign($user);
170    }
171
172    public function getSettings(?IUser $user = null): array {
173        $return['canRequestSign'] = $this->canRequestSign($user);
174        $return['hasSignatureFile'] = $this->hasSignatureFile($user);
175        $return['phoneNumber'] = $this->getPhoneNumber($user);
176        return $return;
177    }
178
179    public function addFilesToAccount(array $files, IUser $user): void {
180        $this->idDocsService->addIdDocs($files, $user);
181    }
182
183    public function deleteFileFromAccount(int $nodeId, IUser $user): void {
184        $this->idDocsService->deleteIdDoc($nodeId, $user);
185    }
186
187    public function saveVisibleElements(array $elements, string $sessionId, ?IUser $user): void {
188        $this->signerElementsService->saveVisibleElements($elements, $sessionId, $user);
189    }
190
191    public function saveVisibleElement(array $data, string $sessionId, ?IUser $user): void {
192        $this->signerElementsService->saveVisibleElement($data, $sessionId, $user);
193    }
194
195    public function deleteSignatureElement(?IUser $user, string $sessionId, int $nodeId): void {
196        $this->signerElementsService->deleteSignatureElement($user, $sessionId, $nodeId);
197    }
198
199    /**
200     * @throws LibresignException at savePfx
201     * @throws InvalidArgumentException
202     */
203    public function uploadPfx(array $file, IUser $user): void {
204        $this->accountCertificateService->uploadPfx($file, $user);
205    }
206
207    public function deletePfx(IUser $user): void {
208        $this->accountCertificateService->deletePfx($user);
209    }
210
211    /**
212     * @throws LibresignException when have not a certificate file
213     */
214    public function updatePfxPassword(IUser $user, string $current, string $new): void {
215        $this->accountCertificateService->updatePfxPassword($user, $current, $new);
216    }
217
218    /**
219     * @throws LibresignException when have not a certificate file
220     */
221    public function readPfxData(IUser $user, string $password): array {
222        return $this->accountCertificateService->readPfxData($user, $password);
223    }
224}