• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LibreSign / libresign / 20801999300

08 Jan 2026 01:11AM UTC coverage: 44.692%. First build
20801999300

Pull #6397

github

web-flow
Merge 8818e7c5a into ef69784e5
Pull Request #6397: fix: translate gateway whatsapp to gowhastaspp

1 of 9 new or added lines in 3 files covered. (11.11%)

6731 of 15061 relevant lines covered (44.69%)

5.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.0
/lib/Service/IdentifyMethodService.php
1
<?php
2

3
declare(strict_types=1);
4
/**
5
 * SPDX-FileCopyrightText: 2020-2024 LibreCode coop and contributors
6
 * SPDX-License-Identifier: AGPL-3.0-or-later
7
 */
8

9
namespace OCA\Libresign\Service;
10

11
use OCA\Libresign\Db\IdentifyMethod;
12
use OCA\Libresign\Db\IdentifyMethodMapper;
13
use OCA\Libresign\Db\SignRequest;
14
use OCA\Libresign\Exception\LibresignException;
15
use OCA\Libresign\Service\IdentifyMethod\Account;
16
use OCA\Libresign\Service\IdentifyMethod\Email;
17
use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod;
18
use OCA\Libresign\Service\IdentifyMethod\Signal;
19
use OCA\Libresign\Service\IdentifyMethod\Sms;
20
use OCA\Libresign\Service\IdentifyMethod\Telegram;
21
use OCA\Libresign\Service\IdentifyMethod\Whatsapp;
22
use OCA\Libresign\Service\IdentifyMethod\Xmpp;
23
use OCP\IConfig;
24
use OCP\IL10N;
25
use OCP\IUserManager;
26

27
class IdentifyMethodService {
28
        public const IDENTIFY_ACCOUNT = 'account';
29
        public const IDENTIFY_EMAIL = 'email';
30
        public const IDENTIFY_SIGNAL = 'signal';
31
        public const IDENTIFY_TELEGRAM = 'telegram';
32
        public const IDENTIFY_SMS = 'sms';
33
        public const IDENTIFY_WHATSAPP = 'whatsapp';
34
        public const IDENTIFY_XMPP = 'xmpp';
35
        public const IDENTIFY_PASSWORD = 'password';
36
        public const IDENTIFY_CLICK_TO_SIGN = 'clickToSign';
37
        public const IDENTIFY_METHODS = [
38
                self::IDENTIFY_ACCOUNT,
39
                self::IDENTIFY_EMAIL,
40
                self::IDENTIFY_SIGNAL,
41
                self::IDENTIFY_TELEGRAM,
42
                self::IDENTIFY_SMS,
43
                self::IDENTIFY_WHATSAPP,
44
                self::IDENTIFY_XMPP,
45
                self::IDENTIFY_PASSWORD,
46
                self::IDENTIFY_CLICK_TO_SIGN,
47
        ];
48
        private bool $isRequest = true;
49
        private ?IdentifyMethod $currentIdentifyMethod = null;
50
        private array $identifyMethodsSettings = [];
51
        /**
52
         * @var array<string,array<IIdentifyMethod>>
53
         */
54
        private array $identifyMethods = [];
55

56
        public function __construct(
57
                private IConfig $config,
58
                private IdentifyMethodMapper $identifyMethodMapper,
59
                private IL10N $l10n,
60
                private IUserManager $userManager,
61
                private Account $account,
62
                private Email $email,
63
                private Signal $signal,
64
                private Sms $sms,
65
                private Telegram $telegram,
66
                private Whatsapp $Whatsapp,
67
                private Xmpp $xmpp,
68
        ) {
69
        }
47✔
70

71
        public function clearCache(): void {
72
                $this->identifyMethods = [];
3✔
73
                $this->currentIdentifyMethod = null;
3✔
74
        }
75

76
        public function setIsRequest(bool $isRequest): self {
77
                $this->isRequest = $isRequest;
2✔
78
                return $this;
2✔
79
        }
80

81
        public function getInstanceOfIdentifyMethod(string $name, ?string $identifyValue = null): IIdentifyMethod {
82
                if ($identifyValue && isset($this->identifyMethods[$name])) {
13✔
83
                        foreach ($this->identifyMethods[$name] as $identifyMethod) {
10✔
84
                                if ($identifyMethod->getEntity()->getIdentifierValue() === $identifyValue) {
10✔
85
                                        $identifyMethod = $this->mergeWithCurrentIdentifyMethod($identifyMethod);
10✔
86
                                        return $identifyMethod;
10✔
87
                                }
88
                        }
89
                }
90
                $identifyMethod = $this->getNewInstanceOfMethod($name);
13✔
91

92
                $entity = $identifyMethod->getEntity();
13✔
93
                if (!$entity->getId()) {
13✔
94
                        $entity->setIdentifierKey($name);
13✔
95
                        $entity->setIdentifierValue($identifyValue);
13✔
96
                        $entity->setMandatory($this->isMandatoryMethod($name) ? 1 : 0);
13✔
97
                }
98
                if ($identifyValue && $this->isRequest) {
13✔
99
                        $identifyMethod->validateToRequest();
13✔
100
                }
101

102
                $this->identifyMethods[$name][] = $identifyMethod;
13✔
103
                return $identifyMethod;
13✔
104
        }
105

106
        private function mergeWithCurrentIdentifyMethod(IIdentifyMethod $identifyMethod): IIdentifyMethod {
107
                if ($this->currentIdentifyMethod === null) {
10✔
108
                        return $identifyMethod;
1✔
109
                }
110
                if ($this->currentIdentifyMethod->getIdentifierKey() === $identifyMethod->getEntity()->getIdentifierKey()
9✔
111
                        && $this->currentIdentifyMethod->getIdentifierValue() === $identifyMethod->getEntity()->getIdentifierValue()
9✔
112
                ) {
113
                        $identifyMethod->setEntity($this->currentIdentifyMethod);
9✔
114
                }
115
                return $identifyMethod;
9✔
116
        }
117

118
        private function getNewInstanceOfMethod(string $name): IIdentifyMethod {
119
                $className = 'OCA\Libresign\Service\IdentifyMethod\\' . ucfirst($name);
13✔
120
                if (!class_exists($className)) {
13✔
121
                        $className = 'OCA\Libresign\Service\IdentifyMethod\\SignatureMethod\\' . ucfirst($name);
×
122
                        if (!class_exists($className)) {
×
123
                                // TRANSLATORS When is requested to a person to sign a file, is
124
                                // necessary identify what is the identification method. The
125
                                // identification method is used to define how will be the sign
126
                                // flow.
127
                                throw new LibresignException($this->l10n->t('Invalid identification method'));
×
128
                        }
129
                }
130
                /** @var IIdentifyMethod */
131
                $identifyMethod = clone \OCP\Server::get($className);
13✔
132
                if (empty($this->currentIdentifyMethod)) {
13✔
133
                        $identifyMethod->cleanEntity();
13✔
134
                } else {
135
                        $identifyMethod->setEntity($this->currentIdentifyMethod);
×
136
                }
137
                $identifyMethod->getSettings();
13✔
138
                return $identifyMethod;
13✔
139
        }
140

141
        private function setEntityData(string $method, string $identifyValue): void {
142
                // @todo Replace by enum when PHP 8.1 is the minimum version acceptable
143
                // at server. Check file lib/versioncheck.php of server repository
144
                if (!in_array($method, IdentifyMethodService::IDENTIFY_METHODS)) {
1✔
145
                        // TRANSLATORS When is requested to a person to sign a file, is
146
                        // necessary identify what is the identification method. The
147
                        // identification method is used to define how will be the sign
148
                        // flow.
149
                        throw new LibresignException($this->l10n->t('Invalid identification method'));
×
150
                }
151
                $identifyMethod = $this->getInstanceOfIdentifyMethod($method, $identifyValue);
1✔
152
                $identifyMethod->validateToRequest();
1✔
153
        }
154

155
        public function setAllEntityData(array $user): void {
156
                foreach ($user['identify'] as $method => $identifyValue) {
1✔
157
                        $this->setEntityData($method, $identifyValue);
1✔
158
                }
159
        }
160

161
        private function isMandatoryMethod(string $methodName): bool {
162
                $settings = $this->getIdentifyMethodsSettings();
13✔
163
                foreach ($settings as $setting) {
13✔
164
                        if ($setting['name'] === $methodName) {
13✔
165
                                return $setting['mandatory'];
13✔
166
                        }
167
                }
168
                return false;
×
169
        }
170

171
        /**
172
         * @return array<IIdentifyMethod>
173
         */
174
        public function getByUserData(array $data) {
175
                $return = [];
13✔
176
                foreach ($data as $method => $identifyValue) {
13✔
177
                        $this->setCurrentIdentifyMethod();
13✔
178
                        $return[] = $this->getInstanceOfIdentifyMethod($method, $identifyValue);
13✔
179
                }
180
                return $return;
13✔
181
        }
182

183
        public function setCurrentIdentifyMethod(?IdentifyMethod $entity = null): self {
184
                $this->currentIdentifyMethod = $entity;
13✔
185
                return $this;
13✔
186
        }
187

188
        /**
189
         * @return array<string,array<IIdentifyMethod>>
190
         */
191
        public function getIdentifyMethodsFromSignRequestId(int $signRequestId): array {
192
                $entities = $this->identifyMethodMapper->getIdentifyMethodsFromSignRequestId($signRequestId);
9✔
193
                foreach ($entities as $entity) {
9✔
194
                        $this->setCurrentIdentifyMethod($entity);
9✔
195
                        $this->getInstanceOfIdentifyMethod(
9✔
196
                                $entity->getIdentifierKey(),
9✔
197
                                $entity->getIdentifierValue(),
9✔
198
                        );
9✔
199
                }
200
                $return = [];
9✔
201
                foreach ($this->identifyMethods as $methodName => $list) {
9✔
202
                        foreach ($list as $method) {
9✔
203
                                if ($method->getEntity()->getSignRequestId() === $signRequestId) {
9✔
204
                                        $return[$methodName][] = $method;
9✔
205
                                }
206
                        }
207
                }
208
                return $return;
9✔
209
        }
210

211
        public function getIdentifiedMethod(int $signRequestId): IIdentifyMethod {
212
                $matrix = $this->getIdentifyMethodsFromSignRequestId($signRequestId);
×
213
                foreach ($matrix as $identifyMethods) {
×
214
                        foreach ($identifyMethods as $identifyMethod) {
×
215
                                if ($identifyMethod->getEntity()->getIdentifiedAtDate()) {
×
216
                                        return $identifyMethod;
×
217
                                }
218
                        }
219
                }
220
                throw new LibresignException($this->l10n->t('Invalid identification method'), 1);
×
221
        }
222

223
        public function deleteBySignRequestId(int $signRequestId): void {
224
                $this->identifyMethodMapper->deleteBySignRequestId($signRequestId);
3✔
225
                $this->clearCache();
3✔
226
        }
227

228
        public function getSignMethodsOfIdentifiedFactors(int $signRequestId): array {
229
                $matrix = $this->getIdentifyMethodsFromSignRequestId($signRequestId);
2✔
230
                $return = [];
2✔
231
                foreach ($matrix as $identifyMethods) {
2✔
232
                        foreach ($identifyMethods as $identifyMethod) {
2✔
233
                                $signatureMethods = $identifyMethod->getSignatureMethods();
2✔
234
                                foreach ($signatureMethods as $signatureMethod) {
2✔
235
                                        if (!$signatureMethod->isEnabled()) {
2✔
236
                                                continue;
2✔
237
                                        }
238
                                        $signatureMethod->setEntity($identifyMethod->getEntity());
2✔
239
                                        $return[$signatureMethod->getName()] = $signatureMethod->toArray();
2✔
240
                                }
241
                        }
242
                }
243
                return $return;
2✔
244
        }
245

246
        public function save(SignRequest $signRequest, bool $notify = true): void {
247
                foreach ($this->identifyMethods as $methods) {
×
248
                        foreach ($methods as $identifyMethod) {
×
249
                                $entity = $identifyMethod->getEntity();
×
250
                                $entity->setSignRequestId($signRequest->getId());
×
251
                                if ($entity->getId()) {
×
252
                                        $entity = $this->identifyMethodMapper->update($entity);
×
253
                                        if ($notify) {
×
254
                                                $identifyMethod->willNotifyUser(false);
×
255
                                                $identifyMethod->notify();
×
256
                                        }
257
                                } else {
258
                                        $entity = $this->identifyMethodMapper->insert($entity);
×
259
                                        if ($notify) {
×
260
                                                $identifyMethod->willNotifyUser(true);
×
261
                                                $identifyMethod->notify();
×
262
                                        }
263
                                }
264
                        }
265
                }
266
        }
267

268
        public function getIdentifyMethodsSettings(): array {
269
                if ($this->identifyMethodsSettings) {
13✔
270
                        return $this->identifyMethodsSettings;
1✔
271
                }
272
                $this->identifyMethodsSettings = [
13✔
273
                        $this->account->getSettings(),
13✔
274
                        $this->email->getSettings(),
13✔
275
                ];
13✔
276
                if ($this->signal->isTwofactorGatewayEnabled()) {
13✔
277
                        $this->identifyMethodsSettings[] = $this->signal->getSettings();
×
278
                }
279
                if ($this->sms->isTwofactorGatewayEnabled()) {
13✔
280
                        $this->identifyMethodsSettings[] = $this->sms->getSettings();
×
281
                }
282
                if ($this->telegram->isTwofactorGatewayEnabled()) {
13✔
283
                        $this->identifyMethodsSettings[] = $this->telegram->getSettings();
×
284
                }
285
                if ($this->Whatsapp->isTwofactorGatewayEnabled()) {
13✔
NEW
286
                        $this->identifyMethodsSettings[] = $this->Whatsapp->getSettings();
×
287
                }
288
                if ($this->xmpp->isTwofactorGatewayEnabled()) {
13✔
289
                        $this->identifyMethodsSettings[] = $this->xmpp->getSettings();
×
290
                }
291
                return $this->identifyMethodsSettings;
13✔
292
        }
293

294
        /**
295
         * Resolve UID from certificate chain data
296
         *
297
         * Extracts and resolves the identifier from certificate subject or extensions.
298
         * Supports fallbacks for older LibreSign versions and converts to standard
299
         * identifier format (account:uid or email:value).
300
         *
301
         * @param array $chainArr Certificate chain array with subject and extensions
302
         * @param string $host Host domain for email matching
303
         * @return string|null Resolved identifier in format "type:value" or null
304
         */
305
        public function resolveUid(array $chainArr, string $host): ?string {
306
                if (!empty($chainArr['subject']['UID'])) {
×
307
                        return $chainArr['subject']['UID'];
×
308
                }
309
                if (!empty($chainArr['subject']['CN'])) {
×
310
                        $cn = $chainArr['subject']['CN'];
×
311
                        if (is_array($cn)) {
×
312
                                $cn = $cn[0];
×
313
                        }
314
                        if (preg_match('/^(?<key>.*):(?<value>.*), /', (string)$cn, $matches)) {
×
315
                                return $matches['key'] . ':' . $matches['value'];
×
316
                        }
317
                }
318
                if (!empty($chainArr['extensions']['subjectAltName'])) {
×
319
                        $subjectAltName = $chainArr['extensions']['subjectAltName'];
×
320
                        if (is_array($subjectAltName)) {
×
321
                                $subjectAltName = $subjectAltName[0];
×
322
                        }
323
                        preg_match('/^(?<key>(email|account)):(?<value>.*)$/', (string)$subjectAltName, $matches);
×
324
                        if ($matches) {
×
325
                                if (str_ends_with($matches['value'], $host)) {
×
326
                                        $uid = str_replace('@' . $host, '', $matches['value']);
×
327
                                        $userFound = $this->userManager->get($uid);
×
328
                                        if ($userFound) {
×
329
                                                return 'account:' . $uid;
×
330
                                        } else {
331
                                                $userFound = $this->userManager->getByEmail($matches['value']);
×
332
                                                if ($userFound) {
×
333
                                                        $userFound = current($userFound);
×
334
                                                        return 'account:' . $userFound->getUID();
×
335
                                                } else {
336
                                                        return 'email:' . $matches['value'];
×
337
                                                }
338
                                        }
339
                                } else {
340
                                        $userFound = $this->userManager->getByEmail($matches['value']);
×
341
                                        if ($userFound) {
×
342
                                                $userFound = current($userFound);
×
343
                                                return 'account:' . $userFound->getUID();
×
344
                                        } else {
345
                                                $userFound = $this->userManager->get($matches['value']);
×
346
                                                if ($userFound) {
×
347
                                                        return 'account:' . $userFound->getUID();
×
348
                                                } else {
349
                                                        return $matches['key'] . ':' . $matches['value'];
×
350
                                                }
351
                                        }
352
                                }
353
                        }
354
                }
355
                return null;
×
356
        }
357
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc