• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

LibreSign / libresign / 19341028102

13 Nov 2025 05:58PM UTC coverage: 39.393%. First build
19341028102

Pull #5770

github

web-flow
Merge 68c87ae87 into 24d75d33d
Pull Request #5770: feat: validate crl

49 of 184 new or added lines in 8 files covered. (26.63%)

4633 of 11761 relevant lines covered (39.39%)

3.09 hits per line

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

16.16
/lib/Db/CrlMapper.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\Db;
11

12
use DateTime;
13
use OCA\Libresign\Enum\CRLReason;
14
use OCA\Libresign\Enum\CRLStatus;
15
use OCP\AppFramework\Db\DoesNotExistException;
16
use OCP\AppFramework\Db\QBMapper;
17
use OCP\DB\QueryBuilder\IQueryBuilder;
18
use OCP\IDBConnection;
19

20
/**
21
 * @template-extends QBMapper<Crl>
22
 */
23
class CrlMapper extends QBMapper {
24
        public function __construct(IDBConnection $db) {
25
                parent::__construct($db, 'libresign_crl');
56✔
26
        }
27

28
        public function findBySerialNumber(string $serialNumber): Crl {
29
                $qb = $this->db->getQueryBuilder();
3✔
30

31
                $qb->select('*')
3✔
32
                        ->from($this->getTableName())
3✔
33
                        ->where($qb->expr()->eq('serial_number', $qb->createNamedParameter($serialNumber)));
3✔
34

35
                /** @var Crl */
36
                return $this->findEntity($qb);
3✔
37
        }
38

39
        public function createCertificate(
40
                string $serialNumber,
41
                string $owner,
42
                string $engine,
43
                string $instanceId,
44
                int $generation,
45
                DateTime $issuedAt,
46
                ?DateTime $validTo = null,
47
        ): Crl {
48
                $certificate = new Crl();
14✔
49
                $certificate->setSerialNumber($serialNumber);
14✔
50
                $certificate->setOwner($owner);
14✔
51
                $certificate->setStatus(CRLStatus::ISSUED);
14✔
52
                $certificate->setIssuedAt($issuedAt);
14✔
53
                $certificate->setValidTo($validTo);
14✔
54
                $certificate->setEngine($engine);
14✔
55
                $certificate->setInstanceId($instanceId);
14✔
56
                $certificate->setGeneration($generation);
14✔
57

58
                /** @var Crl */
59
                return $this->insert($certificate);
14✔
60
        }
61

62
        public function revokeCertificate(
63
                string $serialNumber,
64
                CRLReason $reason = CRLReason::UNSPECIFIED,
65
                ?string $comment = null,
66
                ?string $revokedBy = null,
67
                ?DateTime $invalidityDate = null,
68
                ?int $crlNumber = null,
69
        ): Crl {
70
                $certificate = $this->findBySerialNumber($serialNumber);
×
71

72
                if (CRLStatus::from($certificate->getStatus()) !== CRLStatus::ISSUED) {
×
73
                        throw new \InvalidArgumentException('Certificate is not in issued status');
×
74
                }
75

76
                $certificate->setStatus(CRLStatus::REVOKED);
×
77
                $certificate->setReasonCode($reason->value);
×
78
                $certificate->setComment($comment);
×
79
                $certificate->setRevokedBy($revokedBy);
×
80
                $certificate->setRevokedAt(new DateTime());
×
81
                $certificate->setInvalidityDate($invalidityDate);
×
82
                $certificate->setCrlNumber($crlNumber);
×
83

84
                /** @var Crl */
85
                return $this->update($certificate);
×
86
        }
87

88
        public function getRevokedCertificates(string $instanceId = '', int $generation = 0, string $engineType = ''): array {
89
                $qb = $this->db->getQueryBuilder();
×
90

91
                $qb->select('*')
×
92
                        ->from($this->getTableName())
×
93
                        ->where($qb->expr()->eq('status', $qb->createNamedParameter(CRLStatus::REVOKED->value)))
×
94
                        ->orderBy('revoked_at', 'DESC');
×
95

96
                if ($instanceId !== '') {
×
97
                        $qb->andWhere($qb->expr()->eq('instance_id', $qb->createNamedParameter($instanceId)));
×
98
                }
99
                if ($generation !== 0) {
×
100
                        $qb->andWhere($qb->expr()->eq('generation', $qb->createNamedParameter($generation, IQueryBuilder::PARAM_INT)));
×
101
                }
102
                if ($engineType !== '') {
×
103
                        $engineName = match($engineType) {
×
104
                                'o' => 'openssl',
×
105
                                'c' => 'cfssl',
×
106
                                'openssl', 'cfssl' => $engineType,
×
107
                                default => throw new \InvalidArgumentException("Invalid engine type: $engineType"),
×
108
                        };
×
109
                        $qb->andWhere($qb->expr()->eq('engine', $qb->createNamedParameter($engineName)));
×
110
                }
111

112
                return $this->findEntities($qb);
×
113
        }
114

115
        public function isInvalidAt(string $serialNumber, ?DateTime $checkDate = null): bool {
116
                $checkDate = $checkDate ?? new DateTime();
×
117

118
                try {
119
                        $certificate = $this->findBySerialNumber($serialNumber);
×
120
                } catch (DoesNotExistException $e) {
×
121
                        return false;
×
122
                }
123

124
                if ($certificate->isRevoked()) {
×
125
                        return true;
×
126
                }
127

128
                if ($certificate->getInvalidityDate() && $certificate->getInvalidityDate() <= $checkDate) {
×
129
                        return true;
×
130
                }
131

132
                return false;
×
133
        }
134

135
        public function cleanupExpiredCertificates(?DateTime $before = null): int {
136
                $before = $before ?? new DateTime('-1 year');
×
137

138
                $qb = $this->db->getQueryBuilder();
×
139

140
                return $qb->delete($this->getTableName())
×
141
                        ->where($qb->expr()->isNotNull('valid_to'))
×
142
                        ->andWhere($qb->expr()->lt('valid_to', $qb->createNamedParameter($before, 'datetime')))
×
143
                        ->executeStatement();
×
144
        }
145

146
        public function getStatistics(): array {
147
                $qb = $this->db->getQueryBuilder();
×
148

149
                $result = $qb->select('status', $qb->func()->count('*', 'count'))
×
150
                        ->from($this->getTableName())
×
151
                        ->groupBy('status')
×
152
                        ->executeQuery();
×
153

154
                $stats = [];
×
155
                while ($row = $result->fetch()) {
×
156
                        $stats[$row['status']] = (int)$row['count'];
×
157
                }
158

159
                $result->closeCursor();
×
160
                return $stats;
×
161
        }
162

163
        public function getRevocationStatistics(): array {
164
                $qb = $this->db->getQueryBuilder();
×
165

166
                $result = $qb->select('reason_code', $qb->func()->count('*', 'count'))
×
167
                        ->from($this->getTableName())
×
168
                        ->where($qb->expr()->eq('status', $qb->createNamedParameter(CRLStatus::REVOKED->value)))
×
169
                        ->andWhere($qb->expr()->isNotNull('reason_code'))
×
170
                        ->groupBy('reason_code')
×
171
                        ->executeQuery();
×
172

173
                $stats = [];
×
174
                while ($row = $result->fetch()) {
×
175
                        $reasonCode = (int)$row['reason_code'];
×
176
                        $reason = CRLReason::tryFrom($reasonCode);
×
177
                        $stats[$reasonCode] = [
×
178
                                'code' => $reasonCode,
×
179
                                'description' => $reason?->getDescription() ?? 'unknown',
×
180
                                'count' => (int)$row['count'],
×
181
                        ];
×
182
                }
183

184
                $result->closeCursor();
×
185
                return $stats;
×
186
        }
187

188
        public function getLastCrlNumber(string $instanceId, int $generation, string $engineType): int {
NEW
189
                $qb = $this->db->getQueryBuilder();
×
190

NEW
191
                $qb->select($qb->func()->max('crl_number'))
×
NEW
192
                        ->from($this->getTableName())
×
NEW
193
                        ->where($qb->expr()->eq('instance_id', $qb->createNamedParameter($instanceId)))
×
NEW
194
                        ->andWhere($qb->expr()->eq('generation', $qb->createNamedParameter($generation, IQueryBuilder::PARAM_INT)))
×
NEW
195
                        ->andWhere($qb->expr()->eq('engine', $qb->createNamedParameter($engineType)))
×
NEW
196
                        ->andWhere($qb->expr()->isNotNull('crl_number'));
×
197

NEW
198
                $result = $qb->executeQuery();
×
NEW
199
                $maxCrlNumber = $result->fetchOne();
×
NEW
200
                $result->closeCursor();
×
201

NEW
202
                return (int)($maxCrlNumber ?? 0);
×
203
        }
204
}
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