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

LibreSign / libresign / 19124950457

06 Nov 2025 04:35AM UTC coverage: 39.311%. First build
19124950457

Pull #5757

github

web-flow
Merge b88a58dc1 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

61.84
/lib/Service/CrlService.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
7
 * SPDX-License-Identifier: AGPL-3.0-or-later
8
 */
9

10
namespace OCA\Libresign\Service;
11

12
use DateTime;
13
use OCA\Libresign\Db\CrlMapper;
14
use OCA\Libresign\Enum\CRLReason;
15
use OCA\Libresign\Handler\CertificateEngine\CertificateEngineFactory;
16
use Psr\Log\LoggerInterface;
17

18
/**
19
 * RFC 5280 compliant CRL management
20
 */
21
class CrlService {
22

23
        public function __construct(
24
                private CrlMapper $crlMapper,
25
                private LoggerInterface $logger,
26
                private CertificateEngineFactory $certificateEngineFactory,
27
        ) {
28
        }
11✔
29

30
        private static function isValidReasonCode(int $reasonCode): bool {
31
                return CRLReason::isValid($reasonCode);
2✔
32
        }
33

34

35

36
        public function revokeCertificate(
37
                string $serialNumber,
38
                int $reasonCode = CRLReason::UNSPECIFIED->value,
39
                ?string $reasonText = null,
40
                ?string $revokedBy = null,
41
                ?DateTime $invalidityDate = null,
42
        ): bool {
43
                if (!self::isValidReasonCode($reasonCode)) {
2✔
44
                        throw new \InvalidArgumentException("Invalid CRLReason code: {$reasonCode}");
1✔
45
                }
46

47
                $reason = CRLReason::from($reasonCode);
1✔
48
                $crlNumber = $this->getNextCrlNumber();
1✔
49

50
                try {
51
                        $this->crlMapper->revokeCertificate(
1✔
52
                                $serialNumber,
1✔
53
                                $reason,
1✔
54
                                $reasonText,
1✔
55
                                $revokedBy,
1✔
56
                                $invalidityDate,
1✔
57
                                $crlNumber
1✔
58
                        );
1✔
59

60
                        return true;
1✔
61
                } catch (\Exception $e) {
×
62
                        return false;
×
63
                }
64
        }
65

66
        public function getCertificateStatus(string $serialNumber, ?DateTime $checkDate = null): array {
67
                try {
68
                        $certificate = $this->crlMapper->findBySerialNumber($serialNumber);
3✔
69

70
                        if ($certificate->isRevoked()) {
×
71
                                return [
×
72
                                        'status' => 'revoked',
×
73
                                        'reason_code' => $certificate->getReasonCode(),
×
74
                                        'revoked_at' => $certificate->getRevokedAt()?->format('Y-m-d\TH:i:s\Z'),
×
75
                                ];
×
76
                        }
77

78
                        if ($certificate->isExpired()) {
×
79
                                return [
×
80
                                        'status' => 'expired',
×
81
                                        'valid_to' => $certificate->getValidTo()?->format('Y-m-d\TH:i:s\Z'),
×
82
                                ];
×
83
                        }
84

85
                        return ['status' => 'valid'];
×
86

87
                } catch (\OCP\AppFramework\Db\DoesNotExistException $e) {
3✔
88
                        return ['status' => 'unknown'];
3✔
89
                }
90
        }
91

92
        public function getCertificateStatusResponse(string $serialNumber): array {
93
                $statusInfo = $this->getCertificateStatus($serialNumber);
3✔
94

95
                $response = [
3✔
96
                        'serial_number' => $serialNumber,
3✔
97
                        'status' => $statusInfo['status'],
3✔
98
                        'checked_at' => (new \DateTime())->format('Y-m-d\TH:i:s\Z'),
3✔
99
                ];
3✔
100

101
                if ($statusInfo['status'] === 'revoked') {
3✔
102
                        if (isset($statusInfo['reason_code'])) {
×
103
                                $response['reason_code'] = $statusInfo['reason_code'];
×
104
                        }
105
                        if (isset($statusInfo['revoked_at'])) {
×
106
                                $response['revoked_at'] = $statusInfo['revoked_at'];
×
107
                        }
108
                }
109

110
                if ($statusInfo['status'] === 'expired') {
3✔
111
                        if (isset($statusInfo['valid_to'])) {
×
112
                                $response['valid_to'] = $statusInfo['valid_to'];
×
113
                        }
114
                }
115

116
                return $response;
3✔
117
        }
118

119
        public function isInvalidAt(string $serialNumber, ?DateTime $checkDate = null): bool {
120
                return $this->crlMapper->isInvalidAt($serialNumber, $checkDate);
×
121
        }
122

123
        public function getRevokedCertificates(string $instanceId = '', int $generation = 0, string $engine = ''): array {
124
                $certificates = $this->crlMapper->getRevokedCertificates($instanceId, $generation, $engine);
1✔
125

126
                $result = [];
1✔
127
                foreach ($certificates as $certificate) {
1✔
128
                        $result[] = [
1✔
129
                                'serial_number' => $certificate->getSerialNumber(),
1✔
130
                                'owner' => $certificate->getOwner(),
1✔
131
                                'reason_code' => $certificate->getReasonCode(),
1✔
132
                                'description' => $certificate->getReasonCode() ? CRLReason::from($certificate->getReasonCode())->getDescription() : null,
1✔
133
                                'revoked_by' => $certificate->getRevokedBy(),
1✔
134
                                'revoked_at' => $certificate->getRevokedAt()?->format('Y-m-d H:i:s'),
1✔
135
                                'invalidity_date' => $certificate->getInvalidityDate()?->format('Y-m-d H:i:s'),
1✔
136
                                'crl_number' => $certificate->getCrlNumber(),
1✔
137
                        ];
1✔
138
                }
139

140
                return $result;
1✔
141
        }
142

143
        public function getNextCrlNumber(): int {
144
                return $this->crlMapper->getNextCrlNumber();
1✔
145
        }
146

147
        public function cleanupExpiredCertificates(?DateTime $before = null): int {
148
                return $this->crlMapper->cleanupExpiredCertificates($before);
×
149
        }
150

151
        public function getStatistics(): array {
152
                return $this->crlMapper->getStatistics();
1✔
153
        }
154

155
        public function getRevocationStatistics(): array {
156
                return $this->crlMapper->getRevocationStatistics();
×
157
        }
158

159
        public function generateCrlDer(string $instanceId, int $generation, string $engineType): string {
160
                try {
161
                        $revokedCertificates = $this->crlMapper->getRevokedCertificates($instanceId, $generation, $engineType);
1✔
162

163
                        $engine = $this->certificateEngineFactory->getEngine();
1✔
164

165
                        if (!method_exists($engine, 'generateCrlDer')) {
1✔
NEW
166
                                throw new \RuntimeException('Current certificate engine does not support CRL generation');
×
167
                        }
168

169
                        return $engine->generateCrlDer($revokedCertificates, $instanceId, $generation);
1✔
170
                } catch (\Throwable $e) {
×
171
                        $this->logger->error('Failed to generate CRL', [
×
172
                                'exception' => $e,
×
173
                        ]);
×
174
                        throw $e;
×
175
                }
176
        }
177

178
}
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