OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

rules.php

17 KB
<?php $this->layout('layouts/admin', [
    'page_title'         => 'XDR-Regeln',
    'current_admin_page' => 'xdr-rules',
]); ?>
<?php
$rules         = $rules ?? [];
$actions       = $actions ?? [];
$matchStates   = $match_states ?? [];
$providerList  = $providers ?? [];
$alertTypes    = $alert_types ?? [];
$users         = $users ?? [];
$ticketsModule = $tickets_module ?? false;
$view          = $this;
$severities    = ['critical' => 'Kritisch', 'high' => 'Hoch', 'medium' => 'Mittel', 'low' => 'Niedrig', 'info' => 'Info'];

/** Bedingungen einer Regel als lesbare Aufzählung. */
$bedingungen = static function (array $r) use ($providerList, $alertTypes, $severities, $matchStates): array {
    $teile = [];
    if (!empty($r['match_provider'])) {
        $teile[] = 'Anbieter: ' . ($providerList[$r['match_provider']] ?? $r['match_provider']);
    }
    if (!empty($r['match_alert_type'])) {
        $teile[] = 'Art: ' . ($alertTypes[$r['match_alert_type']] ?? $r['match_alert_type']);
    }
    if (!empty($r['match_min_severity'])) {
        $teile[] = 'ab ' . ($severities[$r['match_min_severity']] ?? $r['match_min_severity']);
    }
    if (($r['match_state'] ?? 'any') !== 'any') {
        $teile[] = $matchStates[$r['match_state']] ?? $r['match_state'];
    }
    if (!empty($r['match_title'])) {
        $teile[] = 'Titel ~ „' . $r['match_title'] . '"';
    }
    if (!empty($r['match_endpoint'])) {
        $teile[] = 'Gerät ~ „' . $r['match_endpoint'] . '"';
    }
    return $teile ?: ['Alle Warnungen'];
};
?>

<div class="settings-page">
    <section class="settings-card">
        <div class="settings-card-header">
            <span class="settings-card-icon">&#129529;</span>
            <div>
                <h2><?= $this->te('Regeln für eingehende Warnungen') ?></h2>
                <p class="settings-card-desc">
                    <?= $this->te('Jede neue Warnung wird der Reihe nach gegen diese Regeln geprüft – die erste zutreffende gewinnt. Bereits vorhandene Warnungen bleiben unberührt.') ?>
                </p>
            </div>
        </div>
        <div class="settings-card-body">
            <p style="margin-top:0;">
                <button type="button" class="btn btn-primary" onclick="xdrRuleOpen()">
                    <i class="fas fa-plus"></i> <?= $this->te('Regel anlegen') ?>
                </button>
                <a href="/admin/xdr/settings" class="btn btn-default"><?= $this->te('Zu den Einstellungen') ?></a>
            </p>

            <?php if (empty($rules)): ?>
                <div class="empty-state">
                    <i class="fas fa-filter"></i>
                    <p><?= $this->te('Noch keine Regel angelegt. Ohne Regeln werden alle Warnungen unverändert übernommen.') ?></p>
                </div>
            <?php else: ?>
                <div class="table-wrapper">
                    <table class="data-table">
                        <thead>
                            <tr>
                                <th scope="col" style="width:5rem;"><?= $this->te('Folge') ?></th>
                                <th scope="col"><?= $this->te('Regel') ?></th>
                                <th scope="col"><?= $this->te('Trifft auf') ?></th>
                                <th scope="col"><?= $this->te('Aktion') ?></th>
                                <th scope="col" style="width:9rem;"><?= $this->te('Treffer') ?></th>
                                <th scope="col" style="width:9rem;"></th>
                            </tr>
                        </thead>
                        <tbody>
                        <?php foreach ($rules as $r): ?>
                            <tr<?= empty($r['enabled']) ? ' style="opacity:.55;"' : '' ?>>
                                <td><?= (int)$r['sort_order'] ?></td>
                                <td>
                                    <strong><?= $view->e((string)$r['name']) ?></strong><br>
                                    <span class="status-pill <?= !empty($r['enabled']) ? 'status-pill--success' : 'status-pill--neutral' ?>">
                                        <?= !empty($r['enabled']) ? 'Aktiv' : 'Inaktiv' ?>
                                    </span>
                                </td>
                                <td style="font-size:.85rem;">
                                    <?= $view->e(implode(' · ', $bedingungen($r))) ?>
                                </td>
                                <td>
                                    <?= $view->e($actions[$r['action']] ?? (string)$r['action']) ?>
                                    <?php if ($r['action'] === 'assign' && !empty($r['action_user_name'])): ?>
                                        <br><small><?= $view->e((string)$r['action_user_name']) ?></small>
                                    <?php elseif ($r['action'] === 'ticket' && !$ticketsModule): ?>
                                        <br><small class="text-danger"><?= $this->te('Tickets-Modul ist aus') ?></small>
                                    <?php endif; ?>
                                </td>
                                <td>
                                    <?= (int)$r['applied_count'] ?>
                                    <?php if (!empty($r['last_applied_at'])): ?>
                                        <br><small>zuletzt <?= $view->e(date('d.m.Y H:i', strtotime((string)$r['last_applied_at']))) ?></small>
                                    <?php endif; ?>
                                </td>
                                <td style="text-align:right; white-space:nowrap;">
                                    <button aria-label="<?= $this->te('Bearbeiten') ?>" type="button" class="btn btn-default btn-sm"
                                            onclick='xdrRuleOpen(<?= json_encode($r, JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG) ?>)'>
                                        <i class="fas fa-pen"></i>
                                    </button>
                                    <form method="post" action="/admin/xdr/rules/<?= (int)$r['id'] ?>/delete" style="display:inline;"
                                          onsubmit="return confirm('Regel &bdquo;<?= $view->e((string)$r['name']) ?>&ldquo; wirklich löschen?');">
                                        <input type="hidden" name="_csrf" value="<?= $view->e($view->csrf()) ?>">
                                        <button aria-label="<?= $this->te('Löschen') ?>" type="submit" class="btn btn-danger btn-sm"><i class="fas fa-trash"></i></button>
                                    </form>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
            <?php endif; ?>
        </div>
    </section>
</div>

<!-- Regel anlegen / bearbeiten -->
<div class="modal-overlay" id="xdrRuleModal" style="display:none;">
    <div class="modal-card">
        <form method="post" action="/admin/xdr/rules">
            <input type="hidden" name="_csrf" value="<?= $view->e($view->csrf()) ?>">
            <input type="hidden" name="id" id="xr-id" value="">

            <div class="modal-header">
                <h3 id="xr-title"><?= $this->te('Regel anlegen') ?></h3>
                <button aria-label="<?= $this->te('Schließen') ?>" type="button" class="modal-close" onclick="xdrRuleClose()">&times;</button>
            </div>

            <div class="modal-body">
                <div class="form-group">
                    <label for="xr-name"><?= $this->te('Name') ?> <span class="text-danger">*</span></label>
                    <input type="text" id="xr-name" name="name" class="form-control" maxlength="160" required>
                </div>

                <div class="form-group">
                    <label for="xr-order"><?= $this->te('Reihenfolge') ?></label>
                    <input type="number" id="xr-order" name="sort_order" class="form-control form-control-sm" min="0" max="9999" value="100">
                    <small class="form-hint"><?= $this->te('Kleinere Zahlen werden zuerst geprüft.') ?></small>
                </div>

                <label class="settings-toggle" style="margin-bottom:1rem;">
                    <input type="checkbox" name="enabled" id="xr-enabled" value="1" checked>
                    <span class="toggle-text"><strong><?= $this->te('Regel aktiv') ?></strong><small><?= $this->te('Inaktive Regeln werden übersprungen.') ?></small></span>
                </label>

                <hr>
                <p style="font-weight:600; margin:.25rem 0 .75rem;"><?= $this->te('Trifft auf') ?></p>
                <p class="form-hint" style="margin-top:0;"><?= $this->te('Leere Felder bedeuten „alles". Alle ausgefüllten Bedingungen müssen zutreffen.') ?></p>

                <div class="form-group">
                    <label for="xr-provider"><?= $this->te('Anbieter') ?></label>
                    <select id="xr-provider" name="match_provider" class="form-control form-control-sm">
                        <option value=""><?= $this->te('Alle') ?></option>
                        <?php foreach ($providerList as $k => $lbl): ?>
                            <option value="<?= $view->e((string)$k) ?>"><?= $view->e((string)$lbl) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>

                <div class="form-group">
                    <label for="xr-type"><?= $this->te('Art der Warnung') ?></label>
                    <select id="xr-type" name="match_alert_type" class="form-control form-control-sm">
                        <option value=""><?= $this->te('Alle') ?></option>
                        <?php foreach ($alertTypes as $k => $lbl): ?>
                            <option value="<?= $view->e((string)$k) ?>"><?= $view->e((string)$lbl) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>

                <div class="form-group">
                    <label for="xr-sev"><?= $this->te('Mindest-Schweregrad') ?></label>
                    <select id="xr-sev" name="match_min_severity" class="form-control form-control-sm">
                        <option value=""><?= $this->te('Alle') ?></option>
                        <?php foreach ($severities as $k => $lbl): ?>
                            <option value="<?= $k ?>"><?= $lbl ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>

                <div class="form-group">
                    <label for="xr-state"><?= $this->te('Zuordnung zum Inventar') ?></label>
                    <select id="xr-state" name="match_state" class="form-control form-control-sm">
                        <?php foreach ($matchStates as $k => $lbl): ?>
                            <option value="<?= $view->e((string)$k) ?>"><?= $view->e((string)$lbl) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>

                <div class="form-group">
                    <label for="xr-title-match"><?= $this->te('Titel enthält') ?></label>
                    <input type="text" id="xr-title-match" name="match_title" class="form-control form-control-sm" maxlength="255"
                           placeholder="<?= $this->te('z. B. Test-Signatur') ?>">
                    <small class="form-hint">
                        <?= $this->t('Groß-/Kleinschreibung spielt keine Rolle. Mit <code>*</code> (beliebig viele Zeichen) oder <code>?</code> (ein Zeichen) beschreibt das Muster den <em>gesamten</em> Titel.') ?>
                    </small>
                </div>

                <div class="form-group">
                    <label for="xr-endpoint"><?= $this->te('Gerätename enthält') ?></label>
                    <input type="text" id="xr-endpoint" name="match_endpoint" class="form-control form-control-sm" maxlength="255"
                           placeholder="<?= $this->te('z. B. LAB-*') ?>">
                </div>

                <hr>
                <p style="font-weight:600; margin:.25rem 0 .75rem;"><?= $this->te('Dann') ?></p>

                <div class="form-group">
                    <label for="xr-action"><?= $this->te('Aktion') ?> <span class="text-danger">*</span></label>
                    <select id="xr-action" name="action" class="form-control" required onchange="xdrRuleActionChanged()">
                        <?php foreach ($actions as $k => $lbl): ?>
                            <option value="<?= $view->e((string)$k) ?>"><?= $view->e((string)$lbl) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>

                <div class="form-group" id="xr-user-group" hidden>
                    <label for="xr-user"><?= $this->te('Zuständige Person') ?> <span class="text-danger">*</span></label>
                    <select id="xr-user" name="action_user_id" class="form-control form-control-sm">
                        <option value=""><?= $this->te('– bitte wählen –') ?></option>
                        <?php foreach ($users as $u): ?>
                            <option value="<?= (int)$u['id'] ?>">
                                <?= $view->e(trim((string)($u['display_name'] ?? '')) !== '' ? (string)$u['display_name'] : (string)$u['email']) ?>
                            </option>
                        <?php endforeach; ?>
                    </select>
                    <small class="form-hint"><?= $this->te('Nur diese Person wird benachrichtigt – unabhängig vom Schweregrad.') ?></small>
                </div>

                <div class="alert alert-info" id="xr-ticket-hint" hidden style="margin-bottom:0;">
                    <?php if ($ticketsModule): ?>
                        <?= $this->t('Das Ticket entsteht beim nächsten Lauf der Aufgabe <code>alert_to_ticket</code> – auch dann, wenn die Schwellwert-Automation für XDR ausgeschaltet ist.') ?>
                    <?php else: ?>
                        <?= $this->te('Das Tickets-Modul ist derzeit deaktiviert; diese Aktion bleibt dann wirkungslos.') ?>
                    <?php endif; ?>
                </div>

                <div class="alert alert-info" id="xr-suppress-hint" hidden style="margin-bottom:0;">
                    <?= $this->te('Die Warnung wird sofort als verworfen geführt: keine Benachrichtigung, kein automatisches Ticket. Aufgezeichnet und im Board unter „Verworfen" auffindbar bleibt sie trotzdem.') ?>
                </div>
            </div>

            <div class="modal-footer">
                <button type="button" class="btn btn-default" onclick="xdrRuleClose()"><?= $this->te('Abbrechen') ?></button>
                <button type="submit" class="btn btn-primary"><?= $this->te('Speichern') ?></button>
            </div>
        </form>
    </div>
</div>

<script>
(function () {
    const modal = document.getElementById('xdrRuleModal');

    function set(id, value) {
        const el = document.getElementById(id);
        if (el) { el.value = value === null || value === undefined ? '' : String(value); }
    }

    window.xdrRuleActionChanged = function () {
        const aktion = document.getElementById('xr-action').value;
        document.getElementById('xr-user-group').hidden     = aktion !== 'assign';
        document.getElementById('xr-ticket-hint').hidden    = aktion !== 'ticket';
        document.getElementById('xr-suppress-hint').hidden  = aktion !== 'suppress';
    };

    window.xdrRuleOpen = function (regel) {
        document.getElementById('xr-title').textContent = regel ? 'Regel bearbeiten' : 'Regel anlegen';

        set('xr-id',          regel ? regel.id : '');
        set('xr-name',        regel ? regel.name : '');
        set('xr-order',       regel ? regel.sort_order : 100);
        set('xr-provider',    regel ? regel.match_provider : '');
        set('xr-type',        regel ? regel.match_alert_type : '');
        set('xr-sev',         regel ? regel.match_min_severity : '');
        set('xr-state',       regel ? (regel.match_state || 'any') : 'any');
        set('xr-title-match', regel ? regel.match_title : '');
        set('xr-endpoint',    regel ? regel.match_endpoint : '');
        set('xr-action',      regel ? regel.action : 'suppress');
        set('xr-user',        regel && regel.action_user_id ? regel.action_user_id : '');

        document.getElementById('xr-enabled').checked = regel ? Number(regel.enabled) === 1 : true;

        window.xdrRuleActionChanged();
        modal.style.display = 'flex';
    };

    window.xdrRuleClose = function () {
        modal.style.display = 'none';
    };

    modal.addEventListener('click', function (e) {
        if (e.target === modal) { window.xdrRuleClose(); }
    });
    document.addEventListener('keydown', function (e) {
        if (e.key === 'Escape' && modal.style.display !== 'none') { window.xdrRuleClose(); }
    });
})();
</script>