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

LibreSign / libresign / 19137174086

06 Nov 2025 01:25PM UTC coverage: 39.311%. First build
19137174086

Pull #5757

github

web-flow
Merge b64de61b5 into f56fcda6c
Pull Request #5757: feat: use crl by cert

54 of 116 new or added lines in 11 files covered. (46.55%)

4599 of 11699 relevant lines covered (39.31%)

3.08 hits per line

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

45.28
/lib/Handler/CertificateEngine/AEngineHandler.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\Handler\CertificateEngine;
10

11
use OCA\Libresign\AppInfo\Application;
12
use OCA\Libresign\Exception\EmptyCertificateException;
13
use OCA\Libresign\Exception\InvalidPasswordException;
14
use OCA\Libresign\Exception\LibresignException;
15
use OCA\Libresign\Helper\ConfigureCheckHelper;
16
use OCA\Libresign\Helper\MagicGetterSetterTrait;
17
use OCA\Libresign\Service\CaIdentifierService;
18
use OCA\Libresign\Service\CertificatePolicyService;
19
use OCP\Files\AppData\IAppDataFactory;
20
use OCP\Files\IAppData;
21
use OCP\Files\SimpleFS\ISimpleFolder;
22
use OCP\IAppConfig;
23
use OCP\IConfig;
24
use OCP\IDateTimeFormatter;
25
use OCP\ITempManager;
26
use OCP\IURLGenerator;
27
use OpenSSLAsymmetricKey;
28
use OpenSSLCertificate;
29
use ReflectionClass;
30

31
/**
32
 * @method IEngineHandler setPassword(string $password)
33
 * @method string getPassword()
34
 * @method IEngineHandler setCommonName(string $commonName)
35
 * @method string getCommonName()
36
 * @method IEngineHandler setHosts(array $hosts)
37
 * @method array getHosts()
38
 * @method IEngineHandler setFriendlyName(string $friendlyName)
39
 * @method string getFriendlyName()
40
 * @method IEngineHandler setCountry(string $country)
41
 * @method string getCountry()
42
 * @method IEngineHandler setState(string $state)
43
 * @method string getState()
44
 * @method IEngineHandler setLocality(string $locality)
45
 * @method string getLocality()
46
 * @method IEngineHandler setOrganization(string $organization)
47
 * @method string getOrganization()
48
 * @method IEngineHandler setOrganizationalUnit(array $organizationalUnit)
49
 * @method array getOrganizationalUnit()
50
 * @method IEngineHandler setUID(string $UID)
51
 * @method string getName()
52
 */
53
abstract class AEngineHandler implements IEngineHandler {
54
        use MagicGetterSetterTrait;
55
        use OrderCertificatesTrait;
56

57
        protected string $commonName = '';
58
        protected array $hosts = [];
59
        protected string $friendlyName = '';
60
        protected string $country = '';
61
        protected string $state = '';
62
        protected string $locality = '';
63
        protected string $organization = '';
64
        protected array $organizationalUnit = [];
65
        protected string $UID = '';
66
        protected string $password = '';
67
        protected string $configPath = '';
68
        protected string $engine = '';
69
        protected string $certificate = '';
70
        protected string $currentCaId = '';
71
        protected IAppData $appData;
72

73
        public function __construct(
74
                protected IConfig $config,
75
                protected IAppConfig $appConfig,
76
                protected IAppDataFactory $appDataFactory,
77
                protected IDateTimeFormatter $dateTimeFormatter,
78
                protected ITempManager $tempManager,
79
                protected CertificatePolicyService $certificatePolicyService,
80
                protected IURLGenerator $urlGenerator,
81
                protected CaIdentifierService $caIdentifierService,
82
        ) {
83
                $this->appData = $appDataFactory->get('libresign');
64✔
84
        }
85

86
        protected function exportToPkcs12(
87
                OpenSSLCertificate|string $certificate,
88
                OpenSSLAsymmetricKey|OpenSSLCertificate|string $privateKey,
89
                array $options = [],
90
        ): string {
91
                if (empty($certificate) || empty($privateKey)) {
7✔
92
                        throw new EmptyCertificateException();
×
93
                }
94
                $certContent = null;
7✔
95
                try {
96
                        openssl_pkcs12_export(
7✔
97
                                $certificate,
7✔
98
                                $certContent,
7✔
99
                                $privateKey,
7✔
100
                                $this->getPassword(),
7✔
101
                                $options,
7✔
102
                        );
7✔
103
                        if (!$certContent) {
7✔
104
                                throw new \Exception();
7✔
105
                        }
106
                } catch (\Throwable) {
×
107
                        throw new LibresignException('Error while creating certificate file', 500);
×
108
                }
109

110
                return $certContent;
7✔
111
        }
112

113
        #[\Override]
114
        public function updatePassword(string $certificate, string $currentPrivateKey, string $newPrivateKey): string {
115
                if (empty($certificate) || empty($currentPrivateKey) || empty($newPrivateKey)) {
×
116
                        throw new EmptyCertificateException();
×
117
                }
118
                $certContent = $this->opensslPkcs12Read($certificate, $currentPrivateKey);
×
119
                $this->setPassword($newPrivateKey);
×
120
                $certContent = self::exportToPkcs12($certContent['cert'], $certContent['pkey']);
×
121
                return $certContent;
×
122
        }
123

124
        #[\Override]
125
        public function readCertificate(string $certificate, string $privateKey): array {
126
                if (empty($certificate) || empty($privateKey)) {
9✔
127
                        throw new EmptyCertificateException();
1✔
128
                }
129
                $certContent = $this->opensslPkcs12Read($certificate, $privateKey);
8✔
130

131
                $return = $this->parseX509($certContent['cert']);
6✔
132
                if (isset($certContent['extracerts'])) {
6✔
133
                        foreach ($certContent['extracerts'] as $extraCert) {
6✔
134
                                $return['extracerts'][] = $this->parseX509($extraCert);
6✔
135
                        }
136
                        $return['extracerts'] = $this->orderCertificates($return['extracerts']);
6✔
137
                }
138
                return $return;
6✔
139
        }
140

141
        public function getCaId(): string {
142
                $caId = $this->caIdentifierService->getCaId();
9✔
143
                if (empty($caId)) {
9✔
144
                        $caId = $this->caIdentifierService->generateCaId($this->getName());
2✔
145
                }
146
                return $caId;
9✔
147
        }
148

149
        private function parseX509(string $x509): array {
150
                $parsed = openssl_x509_parse(openssl_x509_read($x509));
6✔
151

152
                $return = self::convertArrayToUtf8($parsed);
6✔
153

154
                foreach (['subject', 'issuer'] as $actor) {
6✔
155
                        foreach ($return[$actor] as $part => $value) {
6✔
156
                                if (is_string($value) && str_contains($value, ', ')) {
6✔
157
                                        $return[$actor][$part] = explode(', ', $value);
×
158
                                } else {
159
                                        $return[$actor][$part] = $value;
6✔
160
                                }
161
                        }
162
                }
163

164
                $return['valid_from'] = $this->dateTimeFormatter->formatDateTime($parsed['validFrom_time_t']);
6✔
165
                $return['valid_to'] = $this->dateTimeFormatter->formatDateTime($parsed['validTo_time_t']);
6✔
166
                return $return;
6✔
167
        }
168

169
        private static function convertArrayToUtf8($array) {
170
                foreach ($array as $key => $value) {
6✔
171
                        if (is_array($value)) {
6✔
172
                                $array[$key] = self::convertArrayToUtf8($value);
6✔
173
                        } elseif (is_string($value)) {
6✔
174
                                $array[$key] = mb_convert_encoding($value, 'UTF-8', 'UTF-8');
6✔
175
                        }
176
                }
177
                return $array;
6✔
178
        }
179

180
        public function opensslPkcs12Read(string &$certificate, string $privateKey): array {
181
                openssl_pkcs12_read($certificate, $certContent, $privateKey);
8✔
182
                if (!empty($certContent)) {
8✔
183
                        return $certContent;
6✔
184
                }
185
                /**
186
                 * Reference:
187
                 *
188
                 * https://github.com/php/php-src/issues/12128
189
                 * https://www.php.net/manual/en/function.openssl-pkcs12-read.php#128992
190
                 */
191
                $msg = openssl_error_string();
2✔
192
                if ($msg === 'error:0308010C:digital envelope routines::unsupported') {
2✔
193
                        $tempPassword = $this->tempManager->getTemporaryFile();
×
194
                        $tempEncriptedOriginal = $this->tempManager->getTemporaryFile();
×
195
                        $tempEncriptedRepacked = $this->tempManager->getTemporaryFile();
×
196
                        $tempDecrypted = $this->tempManager->getTemporaryFile();
×
197
                        file_put_contents($tempPassword, $privateKey);
×
198
                        file_put_contents($tempEncriptedOriginal, $certificate);
×
199
                        shell_exec(<<<REPACK_COMMAND
×
200
                                cat $tempPassword | openssl pkcs12 -legacy -in $tempEncriptedOriginal -nodes -out $tempDecrypted -passin stdin &&
×
201
                                cat $tempPassword | openssl pkcs12 -in $tempDecrypted -export -out $tempEncriptedRepacked -passout stdin
×
202
                                REPACK_COMMAND
×
203
                        );
×
204
                        $certificateRepacked = file_get_contents($tempEncriptedRepacked);
×
205
                        openssl_pkcs12_read($certificateRepacked, $certContent, $privateKey);
×
206
                        if (!empty($certContent)) {
×
207
                                $certificate = $certificateRepacked;
×
208
                                return $certContent;
×
209
                        }
210
                }
211
                throw new InvalidPasswordException();
2✔
212
        }
213

214
        /**
215
         * @param (int|string) $name
216
         *
217
         * @psalm-param array-key $name
218
         */
219
        public function translateToLong($name): string {
220
                return match ($name) {
3✔
221
                        'CN' => 'CommonName',
×
222
                        'C' => 'Country',
3✔
223
                        'ST' => 'State',
×
224
                        'L' => 'Locality',
×
225
                        'O' => 'Organization',
3✔
226
                        'OU' => 'OrganizationalUnit',
2✔
227
                        'UID' => 'UserIdentifier',
×
228
                        default => '',
3✔
229
                };
3✔
230
        }
231

232
        public function setEngine(string $engine): void {
233
                $this->appConfig->setValueString(Application::APP_ID, 'certificate_engine', $engine);
×
234
                $this->engine = $engine;
×
235
        }
236

237
        #[\Override]
238
        public function getEngine(): string {
239
                if ($this->engine) {
×
240
                        return $this->engine;
×
241
                }
242
                $this->engine = $this->appConfig->getValueString(Application::APP_ID, 'certificate_engine', 'openssl');
×
243
                return $this->engine;
×
244
        }
245

246
        #[\Override]
247
        public function populateInstance(array $rootCert): IEngineHandler {
248
                if (empty($rootCert)) {
9✔
249
                        $rootCert = $this->appConfig->getValueArray(Application::APP_ID, 'rootCert');
9✔
250
                }
251
                if (!$rootCert) {
9✔
252
                        return $this;
9✔
253
                }
254
                if (!empty($rootCert['names'])) {
×
255
                        foreach ($rootCert['names'] as $id => $customName) {
×
256
                                $longCustomName = $this->translateToLong($id);
×
257
                                // Prevent to save a property that don't exists
258
                                if (!property_exists($this, lcfirst($longCustomName))) {
×
259
                                        continue;
×
260
                                }
261
                                $this->{'set' . ucfirst($longCustomName)}($customName['value']);
×
262
                        }
263
                }
264
                if (!$this->getCommonName()) {
×
265
                        $this->setCommonName($rootCert['commonName']);
×
266
                }
267
                return $this;
×
268
        }
269

270
        #[\Override]
271
        public function getCurrentConfigPath(): string {
272
                if ($this->configPath) {
16✔
273
                        return $this->configPath;
15✔
274
                }
275

276
                $customConfigPath = $this->appConfig->getValueString(Application::APP_ID, 'config_path');
16✔
277
                if ($customConfigPath && is_dir($customConfigPath)) {
16✔
278
                        $this->configPath = $customConfigPath;
8✔
279
                        return $this->configPath;
8✔
280
                }
281

282
                $this->configPath = $this->initializePkiConfigPath();
9✔
283
                if (!empty($this->configPath)) {
9✔
284
                        $this->appConfig->setValueString(Application::APP_ID, 'config_path', $this->configPath);
9✔
285
                }
286
                return $this->configPath;
9✔
287
        }
288

289
        #[\Override]
290
        public function getConfigPathByParams(string $instanceId, int $generation): string {
NEW
291
                $engineName = $this->getName();
×
292

NEW
293
                $pkiDirName = $this->caIdentifierService->generatePkiDirectoryNameFromParams($instanceId, $generation, $engineName);
×
NEW
294
                $dataDir = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data/');
×
NEW
295
                $systemInstanceId = $this->config->getSystemValue('instanceid');
×
NEW
296
                $pkiPath = $dataDir . '/appdata_' . $systemInstanceId . '/libresign/' . $pkiDirName;
×
297

NEW
298
                if (!is_dir($pkiPath)) {
×
NEW
299
                        throw new \RuntimeException("Config path does not exist for instanceId: {$instanceId}, generation: {$generation}");
×
300
                }
301

NEW
302
                return $pkiPath;
×
303
        }
304

305
        private function initializePkiConfigPath(): string {
306
                $caId = $this->getCaId();
9✔
307
                if (empty($caId)) {
9✔
308
                        return '';
×
309
                }
310
                $pkiDirName = $this->caIdentifierService->generatePkiDirectoryName($caId);
9✔
311
                $dataDir = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data/');
9✔
312
                $instanceId = $this->config->getSystemValue('instanceid');
9✔
313
                $pkiPath = $dataDir . '/appdata_' . $instanceId . '/libresign/' . $pkiDirName;
9✔
314

315
                if (!is_dir($pkiPath)) {
9✔
316
                        $this->createDirectoryWithCorrectOwnership($pkiPath);
9✔
317
                }
318

319
                return $pkiPath;
9✔
320
        }
321

322
        private function createDirectoryWithCorrectOwnership(string $path): void {
323
                $ownerInfo = $this->getFilesOwnerInfo();
9✔
324
                $fullCommand = 'mkdir -p ' . escapeshellarg($path);
9✔
325

326
                if (posix_getuid() !== $ownerInfo['uid']) {
9✔
327
                        $fullCommand = 'runuser -u ' . $ownerInfo['name'] . ' -- ' . $fullCommand;
×
328
                }
329

330
                exec($fullCommand);
9✔
331
        }
332

333
        private function getFilesOwnerInfo(): array {
334
                $currentFile = realpath(__DIR__);
9✔
335
                $owner = fileowner($currentFile);
9✔
336
                if ($owner === false) {
9✔
337
                        throw new \RuntimeException('Unable to get file information');
×
338
                }
339
                $ownerInfo = posix_getpwuid($owner);
9✔
340
                if ($ownerInfo === false) {
9✔
341
                        throw new \RuntimeException('Unable to get file owner information');
×
342
                }
343

344
                return $ownerInfo;
9✔
345
        }
346

347
        /**
348
         * @todo check a best solution to don't use reflection
349
         */
350
        private function getInternalPathOfFolder(ISimpleFolder $node): string {
351
                $reflection = new \ReflectionClass($node);
×
352
                $reflectionProperty = $reflection->getProperty('folder');
×
353
                $folder = $reflectionProperty->getValue($node);
×
354
                $path = $folder->getInternalPath();
×
355
                return $path;
×
356
        }
357

358
        #[\Override]
359
        public function setConfigPath(string $configPath): IEngineHandler {
360
                if (!$configPath) {
×
361
                        $this->appConfig->deleteKey(Application::APP_ID, 'config_path');
×
362
                } else {
363
                        if (!is_dir($configPath)) {
×
364
                                mkdir(
×
365
                                        directory: $configPath,
×
366
                                        recursive: true,
×
367
                                );
×
368
                        }
369
                        $this->appConfig->setValueString(Application::APP_ID, 'config_path', $configPath);
×
370
                }
371
                $this->configPath = $configPath;
×
372
                return $this;
×
373
        }
374

375
        public function getName(): string {
376
                $reflect = new ReflectionClass($this);
2✔
377
                $className = $reflect->getShortName();
2✔
378
                $name = strtolower(substr($className, 0, -7));
2✔
379
                return $name;
2✔
380
        }
381

382
        protected function getNames(): array {
383
                $names = [
15✔
384
                        'C' => $this->getCountry(),
15✔
385
                        'ST' => $this->getState(),
15✔
386
                        'L' => $this->getLocality(),
15✔
387
                        'O' => $this->getOrganization(),
15✔
388
                        'OU' => $this->getOrganizationalUnit(),
15✔
389
                ];
15✔
390
                if ($uid = $this->getUID()) {
15✔
391
                        $names['UID'] = $uid;
×
392
                }
393
                $names = array_filter($names, fn ($v) => !empty($v));
15✔
394
                return $names;
15✔
395
        }
396

397
        public function getUID(): string {
398
                return str_replace(' ', '+', $this->UID);
15✔
399
        }
400

401
        #[\Override]
402
        public function getLeafExpiryInDays(): int {
403
                $exp = $this->appConfig->getValueInt(Application::APP_ID, 'expiry_in_days', 365);
7✔
404
                return $exp > 0 ? $exp : 365;
7✔
405
        }
406

407
        #[\Override]
408
        public function getCaExpiryInDays(): int {
409
                $exp = $this->appConfig->getValueInt(Application::APP_ID, 'ca_expiry_in_days', 3650); // 10 years
14✔
410
                return $exp > 0 ? $exp : 3650;
14✔
411
        }
412

413
        private function getCertificatePolicy(): array {
414
                $return = ['policySection' => []];
1✔
415
                $oid = $this->certificatePolicyService->getOid();
1✔
416
                $cps = $this->certificatePolicyService->getCps();
1✔
417
                if ($oid && $cps) {
1✔
418
                        $return['policySection'][] = [
×
419
                                'OID' => $oid,
×
420
                                'CPS' => $cps,
×
421
                        ];
×
422
                }
423
                return $return;
1✔
424
        }
425

426
        abstract protected function getConfigureCheckResourceName(): string;
427

428
        abstract protected function getCertificateRegenerationTip(): string;
429

430
        abstract protected function getEngineSpecificChecks(): array;
431

432
        abstract protected function getSetupSuccessMessage(): string;
433

434
        abstract protected function getSetupErrorMessage(): string;
435

436
        abstract protected function getSetupErrorTip(): string;
437

438
        #[\Override]
439
        public function configureCheck(): array {
440
                $checks = $this->getEngineSpecificChecks();
1✔
441

442
                if (!$this->isSetupOk()) {
1✔
443
                        return array_merge($checks, [
1✔
444
                                (new ConfigureCheckHelper())
1✔
445
                                        ->setErrorMessage($this->getSetupErrorMessage())
1✔
446
                                        ->setResource($this->getConfigureCheckResourceName())
1✔
447
                                        ->setTip($this->getSetupErrorTip())
1✔
448
                        ]);
1✔
449
                }
450

451
                $checks[] = (new ConfigureCheckHelper())
×
452
                        ->setSuccessMessage($this->getSetupSuccessMessage())
×
453
                        ->setResource($this->getConfigureCheckResourceName());
×
454

455
                $modernFeaturesCheck = $this->checkRootCertificateModernFeatures();
×
456
                if ($modernFeaturesCheck) {
×
457
                        $checks[] = $modernFeaturesCheck;
×
458
                }
459

460
                return $checks;
×
461
        }
462

463
        protected function checkRootCertificateModernFeatures(): ?ConfigureCheckHelper {
NEW
464
                $configPath = $this->getCurrentConfigPath();
×
465
                $caCertPath = $configPath . DIRECTORY_SEPARATOR . 'ca.pem';
×
466

467
                try {
468
                        $certContent = file_get_contents($caCertPath);
×
469
                        if (!$certContent) {
×
470
                                return (new ConfigureCheckHelper())
×
471
                                        ->setErrorMessage('Failed to read root certificate file')
×
472
                                        ->setResource($this->getConfigureCheckResourceName())
×
473
                                        ->setTip('Check file permissions and disk space');
×
474
                        }
475

476
                        $x509Resource = openssl_x509_read($certContent);
×
477
                        if (!$x509Resource) {
×
478
                                return (new ConfigureCheckHelper())
×
479
                                        ->setErrorMessage('Failed to parse root certificate')
×
480
                                        ->setResource($this->getConfigureCheckResourceName())
×
481
                                        ->setTip('Root certificate file may be corrupted or invalid');
×
482
                        }
483

484
                        $parsed = openssl_x509_parse($x509Resource);
×
485
                        if (!$parsed) {
×
486
                                return (new ConfigureCheckHelper())
×
487
                                        ->setErrorMessage('Failed to extract root certificate information')
×
488
                                        ->setResource($this->getConfigureCheckResourceName())
×
489
                                        ->setTip('Root certificate may be in an unsupported format');
×
490
                        }
491

492
                        $criticalIssues = [];
×
493
                        $minorIssues = [];
×
494

495
                        if (isset($parsed['serialNumber'])) {
×
496
                                $serialNumber = $parsed['serialNumber'];
×
497
                                $serialDecimal = hexdec($serialNumber);
×
498
                                if ($serialDecimal <= 1) {
×
499
                                        $minorIssues[] = 'Serial number is simple (zero or one)';
×
500
                                }
501
                        } else {
502
                                $criticalIssues[] = 'Serial number is missing';
×
503
                        }
504

505
                        $missingExtensions = [];
×
506
                        if (!isset($parsed['extensions']['subjectKeyIdentifier'])) {
×
507
                                $missingExtensions[] = 'Subject Key Identifier (SKI)';
×
508
                        }
509

510
                        $isSelfSigned = (isset($parsed['issuer']) && isset($parsed['subject'])
×
511
                                                        && $parsed['issuer'] === $parsed['subject']);
×
512

513
                        /**
514
                         * @todo workarround for missing AKI at certificates generated by CFSSL.
515
                         *
516
                         * CFSSL does not add Authority Key Identifier (AKI) to self-signed root certificates.
517
                         */
518
                        if (!$isSelfSigned && !isset($parsed['extensions']['authorityKeyIdentifier'])) {
×
519
                                $missingExtensions[] = 'Authority Key Identifier (AKI)';
×
520
                        }
521

522
                        if (!isset($parsed['extensions']['crlDistributionPoints'])) {
×
523
                                $missingExtensions[] = 'CRL Distribution Points';
×
524
                        }
525

526
                        if (!empty($missingExtensions)) {
×
527
                                $extensionsList = implode(', ', $missingExtensions);
×
528
                                $minorIssues[] = "Missing modern extensions: {$extensionsList}";
×
529
                        }
530

531
                        $hasLibresignCaUuid = $this->validateLibresignCaUuidInCertificate($parsed);
×
532
                        if (!$hasLibresignCaUuid) {
×
533
                                $minorIssues[] = 'LibreSign CA UUID not found in Organizational Unit';
×
534
                        }
535

536
                        if (!empty($criticalIssues)) {
×
537
                                $issuesList = implode(', ', $criticalIssues);
×
538
                                return (new ConfigureCheckHelper())
×
539
                                        ->setErrorMessage("Root certificate has critical issues: {$issuesList}")
×
540
                                        ->setResource($this->getConfigureCheckResourceName())
×
541
                                        ->setTip($this->getCertificateRegenerationTip());
×
542
                        }
543

544
                        if (!empty($minorIssues)) {
×
545
                                $issuesList = implode(', ', $minorIssues);
×
546
                                return (new ConfigureCheckHelper())
×
547
                                        ->setInfoMessage("Root certificate could benefit from modern features: {$issuesList}")
×
548
                                        ->setResource($this->getConfigureCheckResourceName())
×
549
                                        ->setTip($this->getCertificateRegenerationTip() . ' (recommended but not required)');
×
550
                        }
551

552
                        return null;
×
553

554
                } catch (\Exception $e) {
×
555
                        return (new ConfigureCheckHelper())
×
556
                                ->setErrorMessage('Failed to analyze root certificate: ' . $e->getMessage())
×
557
                                ->setResource($this->getConfigureCheckResourceName())
×
558
                                ->setTip('Check if the root certificate file is valid');
×
559
                }
560
        }
561

562
        private function validateLibresignCaUuidInCertificate(array $parsed): bool {
563
                if (!isset($parsed['subject']['OU'])) {
×
564
                        return false;
×
565
                }
566

567
                $instanceId = $this->getLibreSignInstanceId();
×
568
                if (empty($instanceId)) {
×
569
                        return false;
×
570
                }
571

572
                $organizationalUnits = $parsed['subject']['OU'];
×
573

574
                if (is_string($organizationalUnits)) {
×
575
                        if (str_contains($organizationalUnits, ', ')) {
×
576
                                $organizationalUnits = explode(', ', $organizationalUnits);
×
577
                        } else {
578
                                $organizationalUnits = [$organizationalUnits];
×
579
                        }
580
                }
581

582
                foreach ($organizationalUnits as $ou) {
×
583
                        $ou = trim($ou);
×
584
                        if ($this->caIdentifierService->isValidCaId($ou, $instanceId)) {
×
585
                                return true;
×
586
                        }
587
                }
588

589
                return false;
×
590
        }
591

592
        private function getLibreSignInstanceId(): string {
593
                $instanceId = $this->appConfig->getValueString(Application::APP_ID, 'instance_id', '');
×
594
                if (strlen($instanceId) === 10) {
×
595
                        return $instanceId;
×
596
                }
597
                return '';
×
598
        }
599

600
        #[\Override]
601
        public function toArray(): array {
602
                $return = [
1✔
603
                        'configPath' => $this->getCurrentConfigPath(),
1✔
604
                        'generated' => $this->isSetupOk(),
1✔
605
                        'rootCert' => [
1✔
606
                                'commonName' => $this->getCommonName(),
1✔
607
                                'names' => [],
1✔
608
                        ],
1✔
609
                ];
1✔
610
                $return = array_merge(
1✔
611
                        $return,
1✔
612
                        $this->getCertificatePolicy(),
1✔
613
                );
1✔
614
                $names = $this->getNames();
1✔
615
                foreach ($names as $name => $value) {
1✔
616
                        $return['rootCert']['names'][] = [
×
617
                                'id' => $name,
×
618
                                'value' => $value,
×
619
                        ];
×
620
                }
621
                return $return;
1✔
622
        }
623

624
        protected function getCrlDistributionUrl(): string {
625
                $caIdParsed = $this->caIdentifierService->getCaIdParsed();
14✔
626
                return $this->urlGenerator->linkToRouteAbsolute('libresign.crl.getRevocationList', [
14✔
627
                        'instanceId' => $caIdParsed['instanceId'],
14✔
628
                        'generation' => $caIdParsed['generation'],
14✔
629
                        'engineType' => $caIdParsed['engineType'],
14✔
630
                ]);
14✔
631
        }
632
}
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