Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
36.73% covered (danger)
36.73%
18 / 49
33.33% covered (danger)
33.33%
2 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
TwofactorGatewayToken
36.73% covered (danger)
36.73%
18 / 49
33.33% covered (danger)
33.33%
2 / 6
48.46
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 validateToSign
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 toArray
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
12
 blurIdentifier
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 requestCode
87.50% covered (warning)
87.50%
14 / 16
0.00% covered (danger)
0.00%
0 / 1
4.03
 getGatewayName
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: 2025 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\Service\IdentifyMethod\SignatureMethod;
10
11use OCA\Libresign\Exception\LibresignException;
12use OCA\Libresign\Service\IdentifyMethod\IdentifyService;
13use OCA\Libresign\Service\IdentifyMethodService;
14
15class TwofactorGatewayToken extends AbstractSignatureMethod implements IToken {
16    private const int VISIBILITY_START = 2;
17    private const int VISIBILITY_END = 2;
18
19    public function __construct(
20        protected IdentifyService $identifyService,
21        protected TokenService $tokenService,
22    ) {
23        parent::__construct(
24            $identifyService,
25        );
26    }
27
28    #[\Override]
29    public function validateToSign(): void {
30        $this->throwIfInvalidToken();
31    }
32
33    #[\Override]
34    public function toArray(): array {
35        $entity = $this->getEntity();
36
37        $identifier = match ($entity->getIdentifierKey()) {
38            'identifier' => $entity->getIdentifierValue(),
39            'account' => $this->identifyService->getUserManager()->get($entity->getIdentifierValue())
40                ?->getEMailAddress() ?? '',
41            default => '',
42        };
43
44        $code = $entity->getCode();
45        $identifiedAt = $entity->getIdentifiedAtDate();
46        $codeSentByUser = $this->codeSentByUser;
47
48        $hasConfirmCode = !empty($code);
49        $needCode = empty($code)
50            || empty($identifiedAt)
51            || empty($codeSentByUser);
52
53        $return = parent::toArray();
54        $return['identifyMethod'] = $entity->getIdentifierKey();
55        $return['needCode'] = $needCode;
56        $return['hasConfirmCode'] = $hasConfirmCode;
57        $return['blurredIdentifier'] = $this->blurIdentifier($identifier);
58        $return['hashOfIdentifier'] = md5($identifier);
59        return $return;
60    }
61
62    private function blurIdentifier(
63        string $identifier,
64        int $visibleStart = self::VISIBILITY_START,
65        int $visibleEnd = self::VISIBILITY_END,
66    ): string {
67        $length = mb_strlen($identifier);
68
69        if ($length <= $visibleStart + $visibleEnd) {
70            return str_repeat('*', $length);
71        }
72
73        $start = mb_substr($identifier, 0, $visibleStart);
74        $end = mb_substr($identifier, -$visibleEnd);
75
76        $maskedLength = $length - ($visibleStart + $visibleEnd);
77
78        return $start . str_repeat('*', $maskedLength) . $end;
79    }
80
81    #[\Override]
82    public function requestCode(): void {
83        $entity = $this->getEntity();
84        $identifier = $entity->getIdentifierValue();
85        $method = $entity->getIdentifierKey();
86
87        if (trim($identifier) === '') {
88            // TRANSLATORS Generic error shown when a verification code cannot be delivered.
89            throw new LibresignException($this->identifyService->getL10n()->t('Unable to send verification code.'));
90        }
91
92        $signRequestMapper = $this->identifyService->getSignRequestMapper();
93        $signRequest = $signRequestMapper->getById($this->getEntity()->getSignRequestId());
94        $displayName = $signRequest->getDisplayName();
95        if ($identifier === $displayName) {
96            $displayName = '';
97        }
98        if ($method === 'email') {
99            $code = $this->tokenService->sendCodeByEmail($identifier, $displayName);
100        } else {
101            $gatewayName = $this->getGatewayName($method);
102            $code = $this->tokenService->sendCodeByGateway($identifier, $gatewayName);
103        }
104        $this->getEntity()->setCode($code);
105        $this->identifyService->save($this->getEntity());
106    }
107
108    private function getGatewayName(string $identifyMethod): string {
109        return IdentifyMethodService::resolveTwofactorGatewayName($identifyMethod);
110    }
111}