Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
55 / 55
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
Stats
100.00% covered (success)
100.00%
55 / 55
100.00% covered (success)
100.00%
3 / 3
11
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
 configure
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
51 / 51
100.00% covered (success)
100.00%
1 / 1
9
1<?php
2
3declare(strict_types=1);
4
5/**
6 * SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
7 * SPDX-License-Identifier: AGPL-3.0-or-later
8 */
9
10namespace OCA\Libresign\Command\Crl;
11
12use OCA\Libresign\Service\Crl\CrlService;
13use Symfony\Component\Console\Command\Command;
14use Symfony\Component\Console\Input\InputInterface;
15use Symfony\Component\Console\Output\OutputInterface;
16use Symfony\Component\Console\Style\SymfonyStyle;
17
18class Stats extends Command {
19    public function __construct(
20        private CrlService $crlService,
21    ) {
22        parent::__construct();
23    }
24
25    #[\Override]
26    protected function configure(): void {
27        $this
28            ->setName('libresign:crl:stats')
29            ->setDescription('Display Certificate Revocation List statistics');
30    }
31
32    #[\Override]
33    protected function execute(InputInterface $input, OutputInterface $output): int {
34        $io = new SymfonyStyle($input, $output);
35        $io->title('LibreSign CRL Statistics');
36
37        try {
38            $stats = $this->crlService->getStatistics();
39
40            $totalCertificates = array_sum($stats);
41            $validCertificates = $stats['issued'] ?? 0;
42            $revokedCertificates = $stats['revoked'] ?? 0;
43            $expiredCertificates = $stats['expired'] ?? 0;
44
45            $io->section('Database Statistics');
46            $io->table(
47                ['Metric', 'Count'],
48                [
49                    ['Total Certificates', $totalCertificates],
50                    ['Valid Certificates', $validCertificates],
51                    ['Revoked Certificates', $revokedCertificates],
52                    ['Expired Certificates', $expiredCertificates],
53                ]
54            );
55        } catch (\Exception $e) {
56            $io->error('Error retrieving statistics: ' . $e->getMessage());
57            return Command::FAILURE;
58        }
59
60        try {
61            $revocationStats = $this->crlService->getRevocationStatistics();
62            if (!empty($revocationStats)) {
63                $io->section('Revocation Statistics');
64                $revocationTable = [];
65                foreach ($revocationStats as $stat) {
66                    $revocationTable[] = [
67                        $stat['description'] ?? 'Unknown',
68                        $stat['count']
69                    ];
70                }
71                $io->table(['Revocation Reason', 'Count'], $revocationTable);
72            }
73        } catch (\Exception $e) {
74            $io->warning('Error retrieving revocation statistics: ' . $e->getMessage());
75        }
76
77        try {
78            $recentRevoked = $this->crlService->getRevokedCertificates();
79            if (!empty($recentRevoked)) {
80                $io->section('Recent Revocations (Last 10)');
81                $recentTable = [];
82                $count = 0;
83                foreach (array_reverse($recentRevoked) as $cert) {
84                    if ($count >= 10) {
85                        break;
86                    }
87                    $recentTable[] = [
88                        $cert['serial_number'],
89                        $cert['description'] ?? 'N/A',
90                        $cert['revoked_at'] ?? 'N/A',
91                        $cert['revoked_by'] ?? 'N/A'
92                    ];
93                    $count++;
94                }
95                $io->table(['Serial Number', 'Reason', 'Revoked At', 'Revoked By'], $recentTable);
96            }
97        } catch (\Exception $e) {
98            $io->warning('Error retrieving recent revocations: ' . $e->getMessage());
99        }
100
101        return Command::SUCCESS;
102    }
103}