Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
79.17% covered (warning)
79.17%
57 / 72
60.00% covered (warning)
60.00%
12 / 20
CRAP
0.00% covered (danger)
0.00%
0 / 1
IdentifyService
79.17% covered (warning)
79.17%
57 / 72
60.00% covered (warning)
60.00%
12 / 20
52.75
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
 save
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 propagateIdentifiedDateToEnvelopeChildren
91.67% covered (success)
91.67%
22 / 24
0.00% covered (danger)
0.00%
0 / 1
10.06
 delete
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 refreshIdFromDatabaseIfNecessary
70.00% covered (warning)
70.00%
7 / 10
0.00% covered (danger)
0.00%
0 / 1
5.68
 getSavedSettings
69.23% covered (warning)
69.23%
9 / 13
0.00% covered (danger)
0.00%
0 / 1
5.73
 getEventDispatcher
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSessionService
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTimeFactory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRootFolder
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAppConfig
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSignRequestMapper
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getL10n
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFileMapper
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHasher
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getUserManager
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUrlGenerator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLogger
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getFolderService
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPolicyService
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\IdentifyMethod;
10
11use OCA\Libresign\Db\FileMapper;
12use OCA\Libresign\Db\IdentifyMethod;
13use OCA\Libresign\Db\IdentifyMethodMapper;
14use OCA\Libresign\Db\SignRequestMapper;
15use OCA\Libresign\Service\FolderService;
16use OCA\Libresign\Service\IdentifyMethodService;
17use OCA\Libresign\Service\Policy\PolicyService;
18use OCA\Libresign\Service\Policy\Provider\IdentifyMethods\IdentifyMethodsPolicy;
19use OCA\Libresign\Service\Policy\Provider\IdentifyMethods\IdentifyMethodsPolicyValue;
20use OCA\Libresign\Service\SessionService;
21use OCP\AppFramework\Utility\ITimeFactory;
22use OCP\EventDispatcher\IEventDispatcher;
23use OCP\Files\IRootFolder;
24use OCP\IAppConfig;
25use OCP\IL10N;
26use OCP\IURLGenerator;
27use OCP\IUserManager;
28use OCP\Security\IHasher;
29use Psr\Log\LoggerInterface;
30
31class IdentifyService {
32    private ?array $savedSettings = null;
33    public function __construct(
34        private IdentifyMethodMapper $identifyMethodMapper,
35        private SessionService $sessionService,
36        private ITimeFactory $timeFactory,
37        private IEventDispatcher $eventDispatcher,
38        private IRootFolder $root,
39        private IAppConfig $appConfig,
40        private SignRequestMapper $signRequestMapper,
41        private IL10N $l10n,
42        private FileMapper $fileMapper,
43        private IHasher $hasher,
44        private IUserManager $userManager,
45        private IURLGenerator $urlGenerator,
46        private LoggerInterface $logger,
47        private FolderService $folderService,
48        private PolicyService $policyService,
49    ) {
50    }
51
52    public function save(IdentifyMethod $identifyMethod): void {
53        $this->refreshIdFromDatabaseIfNecessary($identifyMethod);
54        if ($identifyMethod->getId()) {
55            $this->identifyMethodMapper->update($identifyMethod);
56            $this->propagateIdentifiedDateToEnvelopeChildren($identifyMethod);
57            return;
58        }
59        $this->identifyMethodMapper->insertOrUpdate($identifyMethod);
60        $this->propagateIdentifiedDateToEnvelopeChildren($identifyMethod);
61        return;
62    }
63
64    private function propagateIdentifiedDateToEnvelopeChildren(IdentifyMethod $identifyMethod): void {
65        if (!$identifyMethod->getIdentifiedAtDate()) {
66            return;
67        }
68
69        if (!$identifyMethod->getSignRequestId()) {
70            return;
71        }
72
73        $signRequest = $this->signRequestMapper->getById($identifyMethod->getSignRequestId());
74        $fileEntity = $this->fileMapper->getById($signRequest->getFileId());
75
76        if ($fileEntity->getNodeType() === 'envelope') {
77            $envelopeId = $fileEntity->getId();
78        } elseif ($fileEntity->hasParent()) {
79            $envelopeId = $fileEntity->getParentFileId();
80        } else {
81            return;
82        }
83
84        $children = $this->signRequestMapper->getByEnvelopeChildrenAndIdentifyMethod(
85            $envelopeId,
86            $signRequest->getId(),
87        );
88
89        foreach ($children as $childSignRequest) {
90            // Skip the current sign request to avoid updating it twice
91            if ($childSignRequest->getId() === $signRequest->getId()) {
92                continue;
93            }
94
95            $childMethods = $this->identifyMethodMapper->getIdentifyMethodsFromSignRequestId($childSignRequest->getId());
96
97            foreach ($childMethods as $childEntity) {
98                if (
99                    $childEntity->getIdentifierKey() === $identifyMethod->getIdentifierKey()
100                    && $childEntity->getIdentifierValue() === $identifyMethod->getIdentifierValue()
101                ) {
102                    $childEntity->setIdentifiedAtDate($identifyMethod->getIdentifiedAtDate());
103                    $this->identifyMethodMapper->update($childEntity);
104                }
105            }
106        }
107    }
108
109    public function delete(IdentifyMethod $identifyMethod): void {
110        if ($identifyMethod->getId()) {
111            $this->identifyMethodMapper->delete($identifyMethod);
112        }
113    }
114
115    private function refreshIdFromDatabaseIfNecessary(IdentifyMethod $identifyMethod): void {
116        if ($identifyMethod->getId()) {
117            return;
118        }
119        if (!$identifyMethod->getSignRequestId() || !$identifyMethod->getIdentifierKey()) {
120            return;
121        }
122
123        $identifyMethods = $this->identifyMethodMapper->getIdentifyMethodsFromSignRequestId($identifyMethod->getSignRequestId());
124        $exists = array_filter($identifyMethods, fn (IdentifyMethod $current): bool => $current->getIdentifierKey() === $identifyMethod->getIdentifierKey());
125        if (!$exists) {
126            return;
127        }
128        $exists = current($exists);
129        $identifyMethod->setId($exists->getId());
130    }
131
132    public function getSavedSettings(): array {
133        if ($this->savedSettings !== null) {
134            return $this->savedSettings;
135        }
136
137        $resolved = $this->getPolicyService()->resolve(IdentifyMethodsPolicy::KEY)->getEffectiveValue();
138        $normalizedPayload = IdentifyMethodsPolicyValue::normalize($resolved);
139        $settings = IdentifyMethodsPolicyValue::extractFactors($normalizedPayload);
140        $globalCanCreateAccount = IdentifyMethodsPolicyValue::resolveGlobalCanCreateAccount($normalizedPayload);
141
142        if ($globalCanCreateAccount !== null) {
143            foreach ($settings as &$setting) {
144                if (($setting['name'] ?? null) === IdentifyMethodService::IDENTIFY_EMAIL) {
145                    $setting['can_create_account'] = $globalCanCreateAccount;
146                }
147            }
148            unset($setting);
149        }
150
151        $this->savedSettings = $settings;
152        return $this->savedSettings;
153    }
154
155    public function getEventDispatcher(): IEventDispatcher {
156        return $this->eventDispatcher;
157    }
158
159    public function getSessionService(): SessionService {
160        return $this->sessionService;
161    }
162
163    public function getTimeFactory(): ITimeFactory {
164        return $this->timeFactory;
165    }
166
167    public function getRootFolder(): IRootFolder {
168        return $this->root;
169    }
170
171    public function getAppConfig(): IAppConfig {
172        return $this->appConfig;
173    }
174
175    public function getSignRequestMapper(): SignRequestMapper {
176        return $this->signRequestMapper;
177    }
178
179    public function getL10n(): IL10N {
180        return $this->l10n;
181    }
182
183    public function getFileMapper(): FileMapper {
184        return $this->fileMapper;
185    }
186
187    public function getHasher(): IHasher {
188        return $this->hasher;
189    }
190
191    public function getUserManager(): IUserManager {
192        return $this->userManager;
193    }
194
195    public function getUrlGenerator(): IURLGenerator {
196        return $this->urlGenerator;
197    }
198
199    public function getLogger(): LoggerInterface {
200        return $this->logger;
201    }
202
203    public function getFolderService(): FolderService {
204        return $this->folderService;
205    }
206
207    public function getPolicyService(): PolicyService {
208        return $this->policyService;
209    }
210}