Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
PfxProvider
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
3 / 3
8
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
 getOrGeneratePfx
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
1 / 1
6
 generateTemporaryPassword
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4/**
5 * SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\Service;
10
11use OCA\Libresign\Handler\SignEngine\SignEngineHandler;
12use OCP\EventDispatcher\IEventDispatcher;
13use OCP\Security\Events\GenerateSecurePasswordEvent;
14use OCP\Security\ISecureRandom;
15
16class PfxProvider {
17    public function __construct(
18        private CertificateValidityPolicy $certificateValidityPolicy,
19        private IEventDispatcher $eventDispatcher,
20        private ISecureRandom $secureRandom,
21    ) {
22    }
23
24    /**
25     * @return array{pfx: string, password: ?string}
26     */
27    public function getOrGeneratePfx(
28        SignEngineHandler $engine,
29        bool $signWithoutPassword,
30        ?string $signatureMethodName,
31        string $userUniqueIdentifier,
32        string $friendlyName,
33        string $password = '',
34    ): array {
35        $effectivePassword = $password;
36        if ($signWithoutPassword) {
37            $effectivePassword = $this->generateTemporaryPassword();
38            $expiryOverride = $this->certificateValidityPolicy->getLeafExpiryDays(
39                $signatureMethodName,
40                $signWithoutPassword,
41            );
42            if ($expiryOverride !== null) {
43                $engine->setLeafExpiryOverrideInDays($expiryOverride);
44            }
45            try {
46                $generatedCertificate = $engine->generateCertificate(
47                    [
48                        'host' => $userUniqueIdentifier,
49                        'uid' => $userUniqueIdentifier,
50                        'name' => $friendlyName,
51                    ],
52                    $effectivePassword,
53                    $friendlyName,
54                );
55            } finally {
56                if ($expiryOverride !== null) {
57                    $engine->setLeafExpiryOverrideInDays(null);
58                }
59            }
60            return [
61                'pfx' => $generatedCertificate,
62                'password' => $effectivePassword,
63            ];
64        }
65
66        $uid = str_starts_with($userUniqueIdentifier, 'account:')
67            ? substr($userUniqueIdentifier, strlen('account:'))
68            : $userUniqueIdentifier;
69
70        return [
71            'pfx' => $engine
72                ->setPassword($effectivePassword)
73                ->getPfxOfCurrentSigner($uid),
74            'password' => $effectivePassword !== '' ? $effectivePassword : null,
75        ];
76    }
77
78    private function generateTemporaryPassword(): string {
79        $passwordEvent = new GenerateSecurePasswordEvent();
80        $this->eventDispatcher->dispatchTyped($passwordEvent);
81        return $passwordEvent->getPassword() ?? $this->secureRandom->generate(20);
82    }
83}