OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

index.php

16 KB
<?php
$this->layout('layouts/main', [
    'page_title'    => $page_title,
    'current_route' => $current_route,
    'full_width'    => true,
    'extra_css'     => ['/assets/css/ssl.css?v=' . filemtime(dirname(__DIR__, 2) . '/public/assets/css/ssl.css')],
    'extra_js'      => ['/assets/js/ssl.js?v=' . filemtime(dirname(__DIR__, 2) . '/public/assets/js/ssl.js')],
]);

$entries    = $entries ?? [];
$stats      = $stats ?? ['total' => 0, 'ok' => 0, 'soon' => 0, 'critical' => 0, 'problem' => 0];
$allTags    = $all_tags ?? [];
$activeTag  = $active_tag ?? '';
$users      = $users ?? [];
$transports = $transports ?? ['tls'];
$canManage  = $can_manage ?? false;
$view       = $this;

$gradeChip = function (?string $g) use ($view): string {
    if (!$g) {
        return '<span class="ssl-grade ssl-grade-na">–</span>';
    }
    $cls = in_array($g, ['A', 'B', 'C', 'F'], true) ? $g : 'na';
    return '<span class="ssl-grade ssl-grade-' . $cls . '">' . $view->e($g) . '</span>';
};
$isInsecure = function (array $e): bool {
    $t = $e['trust_valid'] ?? null;
    $h = $e['hostname_match'] ?? null;
    return ($t !== null && (int)$t === 0) || ($h !== null && (int)$h === 0);
};
$transportLabel = ['tls' => 'TLS', 'smtp' => 'SMTP', 'imap' => 'IMAP', 'pop3' => 'POP3'];
?>

<div class="ssl-wrap">
    <div class="content-header">
        <div>
            <h1><?= $this->te('SSL-Zertifikate') ?></h1>
            <ol class="breadcrumb">
                <li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
                <li class="breadcrumb-item active"><?= $this->te('SSL-Zertifikate') ?></li>
            </ol>
        </div>
        <div class="content-header-actions">
            <button class="btn btn-default btn-sm" id="sslCheckAllBtn" title="<?= $this->te('Alle Zertifikate neu prüfen') ?>"><?= $this->te('&#8635; Alle prüfen') ?></button>
            <?php if ($canManage): ?>
            <button type="button" class="btn btn-primary" data-ssl-add-open>
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18" aria-hidden="true" focusable="false"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
                <?= $this->te('Zertifikat hinzufügen') ?>
            </button>
            <?php endif; ?>
        </div>
    </div>

    <div class="page-body">

        <!-- KPI Small Boxes -->
        <div class="kpi-row">
            <div class="kpi-col">
                <a href="/ssl">
                <div class="small-box bg-info">
                    <div class="inner"><h3><?= (int)$stats['total'] ?></h3><p><?= $this->te('Überwacht') ?></p></div>
                    <div class="icon"><i class="fas fa-lock"></i></div>
                </div>
                </a>
            </div>
            <div class="kpi-col">
                <div class="small-box bg-success">
                    <div class="inner"><h3><?= (int)$stats['ok'] ?></h3><p>OK</p></div>
                    <div class="icon"><i class="fas fa-check-circle"></i></div>
                </div>
            </div>
            <div class="kpi-col">
                <div class="small-box bg-warning">
                    <div class="inner"><h3><?= (int)$stats['soon'] ?></h3><p><?= $this->te('≤ 30 Tage') ?></p></div>
                    <div class="icon"><i class="fas fa-clock"></i></div>
                </div>
            </div>
            <div class="kpi-col">
                <div class="small-box bg-danger">
                    <div class="inner"><h3><?= (int)$stats['critical'] ?></h3><p><?= $this->te('≤ 14 Tage') ?></p></div>
                    <div class="icon"><i class="fas fa-exclamation-triangle"></i></div>
                </div>
            </div>
            <div class="kpi-col">
                <div class="small-box bg-secondary">
                    <div class="inner"><h3><?= (int)$stats['problem'] ?></h3><p><?= $this->te('Fehler / Unsicher') ?></p></div>
                    <div class="icon"><i class="fas fa-times-circle"></i></div>
                </div>
            </div>
        </div>

        <!-- Suchleiste -->
        <div class="ssl-searchbar card card-outline card-primary">
            <div class="filter-card-body">
                <div class="ssl-search-field">
                    <svg class="ssl-search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15" aria-hidden="true" focusable="false"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
                    <label for="sslSearch" class="sr-only"><?= $this->te('Zertifikate durchsuchen') ?></label>
                    <input type="search" id="sslSearch" class="form-control ssl-search-input" placeholder="<?= $this->te('Host, Bezeichnung, Aussteller, Owner oder Tag suchen…') ?>" autocomplete="off">
                </div>
            </div>
        </div>

        <!-- Tag-Filter -->
        <?php if (!empty($allTags)): ?>
        <div class="ssl-toolbar">
            <div class="ssl-tags">
                <a href="/ssl" class="ssl-tag-chip <?= $activeTag === '' ? 'active' : '' ?>"><?= $this->te('Alle') ?></a>
                <?php foreach ($allTags as $t): ?>
                    <a href="/ssl?tag=<?= urlencode($t) ?>" class="ssl-tag-chip <?= $activeTag === $t ? 'active' : '' ?>"><?= $view->e($t) ?></a>
                <?php endforeach; ?>
            </div>
        </div>
        <?php endif; ?>

        <div class="table-wrapper">
            <?php if (empty($entries)): ?>
                <div class="empty-state"><p><?= $this->te('Noch keine Domains überwacht. Füge oben eine Domain hinzu.') ?></p></div>
            <?php else: ?>
            <table class="data-table" id="sslTable">
                <thead>
                    <tr>
                        <th scope="col"><?= $this->te('Grade') ?></th>
                        <th scope="col"><?= $this->te('Bezeichnung / Host') ?></th>
                        <th scope="col"><?= $this->te('Status') ?></th>
                        <th scope="col"><?= $this->te('Ablauf') ?></th>
                        <th scope="col"><?= $this->te('Sicherheit') ?></th>
                        <th scope="col"><?= $this->te('Aussteller') ?></th>
                        <th scope="col"><?= $this->te('Owner') ?></th>
                        <th scope="col"><?= $this->te('Letzte Prüfung') ?></th>
                        <th scope="col"></th>
                    </tr>
                </thead>
                <tbody>
                <?php foreach ($entries as $e):
                    $days     = $e['cert_days_left'];
                    $hasError = !empty($e['check_error']);
                    $insecure = $isInsecure($e);
                    if ($hasError) {
                        $badgeClass = 'status-pill--neutral'; $badgeLabel = 'Fehler';
                    } elseif ($insecure) {
                        $badgeClass = 'status-pill--danger';  $badgeLabel = 'Unsicher';
                    } elseif ($days === null) {
                        $badgeClass = 'status-pill--neutral'; $badgeLabel = 'Nicht geprüft';
                    } elseif ($days <= 14) {
                        $badgeClass = 'status-pill--danger';  $badgeLabel = 'Kritisch';
                    } elseif ($days <= 30) {
                        $badgeClass = 'status-pill--warning'; $badgeLabel = 'Bald';
                    } else {
                        $badgeClass = 'status-pill--success'; $badgeLabel = 'OK';
                    }
                    $searchText = mb_strtolower(trim(implode(' ', array_filter([
                        $e['label'] ?? '',
                        $e['host'] ?? '',
                        (string)($e['port'] ?? ''),
                        $e['cert_issuer_cn'] ?? '',
                        $e['owner_name'] ?? '',
                        $e['tags'] ?? '',
                        $transportLabel[$e['transport'] ?? 'tls'] ?? '',
                    ]))));
                ?>
                    <tr id="sslRow<?= (int)$e['id'] ?>" class="clickable" data-href="/ssl/entries/<?= (int)$e['id'] ?>" data-search="<?= $view->e($searchText) ?>">
                        <td><?= $gradeChip($e['grade'] ?? null) ?></td>
                        <td>
                            <div class="ssl-host-name"><?= $view->e($e['label']) ?></div>
                            <div class="ssl-host-sub">
                                <?= $view->e($e['host']) ?>:<?= (int)$e['port'] ?>
                                <?php if (($e['transport'] ?? 'tls') !== 'tls'): ?>
                                    · <?= $view->e($transportLabel[$e['transport']] ?? strtoupper($e['transport'])) ?>
                                <?php endif; ?>
                                <?php if (!empty($e['cert_is_wildcard'])): ?>
                                    <span class="status-pill status-pill--purple"><?= $this->te('Wildcard') ?></span>
                                <?php endif; ?>
                            </div>
                        </td>
                        <td><span class="status-pill <?= $badgeClass ?>"><?= $badgeLabel ?></span></td>
                        <td>
                            <?php if ($e['cert_valid_to']): ?>
                                <div style="<?= $days !== null && $days <= 30 ? 'color:var(--red-700);font-weight:600;' : '' ?>"><?= date('d.m.Y', strtotime($e['cert_valid_to'])) ?></div>
                                <?php if ($days !== null): ?><div class="ssl-host-sub"><?= (int)$days ?> Tage</div><?php endif; ?>
                            <?php else: ?>
                                <span class="ssl-muted">—</span>
                            <?php endif; ?>
                        </td>
                        <td>
                            <div class="pill-row">
                                <?php if (($e['trust_valid'] ?? null) !== null): ?>
                                    <span class="status-pill <?= (int)$e['trust_valid'] === 1 ? 'status-pill--success' : 'status-pill--danger' ?>" title="<?= $this->te('Vertrauenskette') ?>"><?= (int)$e['trust_valid'] === 1 ? '✓ Trust' : '✗ Trust' ?></span>
                                <?php endif; ?>
                                <?php if (($e['hostname_match'] ?? null) !== null): ?>
                                    <span class="status-pill <?= (int)$e['hostname_match'] === 1 ? 'status-pill--success' : 'status-pill--danger' ?>" title="<?= $this->te('Hostname-Übereinstimmung') ?>"><?= (int)$e['hostname_match'] === 1 ? '✓ Host' : '✗ Host' ?></span>
                                <?php endif; ?>
                                <?php if (!empty($e['tls_version'])): ?>
                                    <span class="status-pill status-pill--neutral"><?= $view->e($e['tls_version']) ?></span>
                                <?php endif; ?>
                            </div>
                        </td>
                        <td style="font-size:0.8rem;"><?= $view->e($e['cert_issuer_cn'] ?: '—') ?></td>
                        <td style="font-size:0.8rem;"><?= $view->e($e['owner_name'] ?: '—') ?></td>
                        <td class="ssl-muted" style="font-size:0.78rem;white-space:nowrap;">
                            <?= $e['last_checked_at'] ? date('d.m.Y H:i', strtotime($e['last_checked_at'])) : 'Nie' ?>
                        </td>
                        <td style="white-space:nowrap;">
                            <button class="btn btn-default btn-xs ssl-check-btn" data-id="<?= (int)$e['id'] ?>" title="<?= $this->te('Jetzt prüfen') ?>">&#8635;</button>
                            <?php if ($canManage): ?>
                            <button class="btn btn-danger btn-xs ssl-delete-btn" data-id="<?= (int)$e['id'] ?>" data-host="<?= $view->e($e['host']) ?>" title="<?= $this->te('Entfernen') ?>">&#215;</button>
                            <?php endif; ?>
                        </td>
                    </tr>
                <?php endforeach; ?>
                </tbody>
            </table>
            <div class="empty-state" id="sslNoResults" hidden><p><?= $this->te('Keine Zertifikate gefunden.') ?></p></div>
            <?php endif; ?>
        </div>

    </div>
</div>

<?php if ($canManage): ?>
<!-- Modal: Zertifikat hinzufügen -->
<div id="sslAddModal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="sslAddModalTitle" style="display:none;">
    <div class="modal-card">
        <div class="modal-header">
            <h2 id="sslAddModalTitle"><?= $this->te('Zertifikat hinzufügen') ?></h2>
            <button type="button" class="modal-close" data-ssl-modal-close aria-label="<?= $this->te('Dialog schließen') ?>">&times;</button>
        </div>
        <form class="ssl-add-form" id="sslAddForm">
            <div class="modal-body">
                <div class="form-group">
                    <label for="ssl-add-host"><?= $this->te('Host / URL') ?> <span style="color:var(--red-500);">*</span></label>
                    <input type="text" id="ssl-add-host" name="host" class="form-control" placeholder="<?= $this->te('domain.de oder https://domain.de') ?>" autocomplete="off" required>
                </div>
                <div class="ssl-form-row">
                    <div class="form-group ssl-form-col-port">
                        <label for="ssl-add-port"><?= $this->te('Port') ?></label>
                        <input type="number" id="ssl-add-port" name="port" class="form-control" value="443" min="1" max="65535">
                    </div>
                    <div class="form-group ssl-form-col">
                        <label for="ssl-add-transport"><?= $this->te('Transport') ?></label>
                        <select id="ssl-add-transport" name="transport" class="form-control">
                            <?php foreach ($transports as $tr): ?>
                                <option value="<?= $view->e($tr) ?>"><?= $view->e($transportLabel[$tr] ?? strtoupper($tr)) ?></option>
                            <?php endforeach; ?>
                        </select>
                    </div>
                </div>
                <div class="form-group">
                    <label for="ssl-add-label"><?= $this->te('Bezeichnung') ?> <span class="form-hint"><?= $this->te('(optional)') ?></span></label>
                    <input type="text" id="ssl-add-label" name="label" class="form-control" placeholder="<?= $this->te('Optionaler Anzeigename') ?>">
                </div>
                <div class="ssl-form-row">
                    <div class="form-group ssl-form-col">
                        <label for="ssl-add-owner"><?= $this->te('Verantwortlich') ?></label>
                        <select id="ssl-add-owner" name="owner_user_id" class="form-control">
                            <option value=""><?= $this->te('— Owner —') ?></option>
                            <?php foreach ($users as $u): ?>
                                <option value="<?= (int)$u['id'] ?>"><?= $view->e($u['display_name']) ?></option>
                            <?php endforeach; ?>
                        </select>
                    </div>
                    <div class="form-group ssl-form-col-warn">
                        <label for="ssl-add-warn"><?= $this->te('Warn-Tage') ?></label>
                        <input type="number" id="ssl-add-warn" name="warn_days" class="form-control" placeholder="<?= $this->te('Standard') ?>" min="1" title="<?= $this->te('Eigene Warnschwelle in Tagen (optional)') ?>">
                    </div>
                </div>
                <div class="form-group">
                    <label for="ssl-add-tags"><?= $this->te('Tags') ?> <span class="form-hint"><?= $this->te('(durch Komma getrennt)') ?></span></label>
                    <input type="text" id="ssl-add-tags" name="tags" class="form-control" placeholder="<?= $this->te('z. B. intern, web') ?>">
                </div>
                <div id="sslAddMsg" class="ssl-add-msg"></div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-ssl-modal-close><?= $this->te('Abbrechen') ?></button>
                <button type="submit" class="btn btn-primary" id="sslAddBtn"><?= $this->te('Hinzufügen') ?></button>
            </div>
        </form>
    </div>
</div>
<?php endif; ?>