first sync
This commit is contained in:
706
inc/wazuhalert.class.php
Normal file
706
inc/wazuhalert.class.php
Normal file
@@ -0,0 +1,706 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Classe de gestion des alertes Wazuh
|
||||
*/
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
class PluginSiemWazuhAlert extends CommonDBTM {
|
||||
|
||||
public $dohistory = true;
|
||||
|
||||
static $rightname = 'plugin_siem_wazuh_alert';
|
||||
|
||||
const MATRIX_FIELD = '';
|
||||
const SEARCHOPTIONS_INHERITANCE = true;
|
||||
|
||||
/**
|
||||
* Return the localized name of the current Type
|
||||
*/
|
||||
static function getTypeName($nb = 0) {
|
||||
return _n('Wazuh Alert', 'Wazuh Alerts', $nb, 'siem-wazuh');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define search options
|
||||
*/
|
||||
function rawSearchOptions() {
|
||||
$tab = [];
|
||||
|
||||
$tab[] = [
|
||||
'id' => 'common',
|
||||
'name' => __('Characteristics')
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '1',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'alert_id',
|
||||
'name' => __('Alert ID', 'siem-wazuh'),
|
||||
'datatype' => 'itemlink',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '2',
|
||||
'table' => 'glpi_plugin_siem_wazuh_servers',
|
||||
'field' => 'name',
|
||||
'name' => __('Wazuh Server', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'wazuh_server_id'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '3',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'rule_id',
|
||||
'name' => __('Rule ID', 'siem-wazuh'),
|
||||
'datatype' => 'integer',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '4',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'rule_level',
|
||||
'name' => __('Rule Level', 'siem-wazuh'),
|
||||
'datatype' => 'integer',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '5',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'rule_description',
|
||||
'name' => __('Rule Description', 'siem-wazuh'),
|
||||
'datatype' => 'text',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '6',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'agent_name',
|
||||
'name' => __('Agent Name', 'siem-wazuh'),
|
||||
'datatype' => 'string',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '7',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'agent_ip',
|
||||
'name' => __('Agent IP', 'siem-wazuh'),
|
||||
'datatype' => 'string',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '8',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'timestamp',
|
||||
'name' => __('Alert Timestamp', 'siem-wazuh'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '9',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'status',
|
||||
'name' => __('Status'),
|
||||
'datatype' => 'specific',
|
||||
'searchtype' => 'equals',
|
||||
'massiveaction' => true
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '10',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'severity',
|
||||
'name' => __('Severity', 'siem-wazuh'),
|
||||
'datatype' => 'specific',
|
||||
'searchtype' => 'equals',
|
||||
'massiveaction' => true
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '11',
|
||||
'table' => 'glpi_computers',
|
||||
'field' => 'name',
|
||||
'name' => __('Associated Computer', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'computer_id'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '12',
|
||||
'table' => 'glpi_networkequipments',
|
||||
'field' => 'name',
|
||||
'name' => __('Associated Network Equipment', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'networkequipment_id'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '13',
|
||||
'table' => 'glpi_tickets',
|
||||
'field' => 'name',
|
||||
'name' => __('Associated Ticket', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'ticket_id'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '30',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'date_creation',
|
||||
'name' => __('Creation date'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '31',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'date_mod',
|
||||
'name' => __('Last update'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
return $tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display specific values
|
||||
*/
|
||||
static function getSpecificValueToDisplay($field, $values, array $options = []) {
|
||||
if (!is_array($values)) {
|
||||
$values = [$field => $values];
|
||||
}
|
||||
|
||||
switch ($field) {
|
||||
case 'status':
|
||||
$statuses = self::getStatusArray();
|
||||
return $statuses[$values[$field]] ?? $values[$field];
|
||||
|
||||
case 'severity':
|
||||
$severities = self::getSeverityArray();
|
||||
return $severities[$values[$field]] ?? $values[$field];
|
||||
}
|
||||
|
||||
return parent::getSpecificValueToDisplay($field, $values, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status array
|
||||
*/
|
||||
static function getStatusArray() {
|
||||
return [
|
||||
'new' => __('New', 'siem-wazuh'),
|
||||
'processed' => __('Processed', 'siem-wazuh'),
|
||||
'ignored' => __('Ignored', 'siem-wazuh'),
|
||||
'ticket_created' => __('Ticket Created', 'siem-wazuh')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get severity array
|
||||
*/
|
||||
static function getSeverityArray() {
|
||||
return [
|
||||
'low' => __('Low', 'siem-wazuh'),
|
||||
'medium' => __('Medium', 'siem-wazuh'),
|
||||
'high' => __('High', 'siem-wazuh'),
|
||||
'critical' => __('Critical', 'siem-wazuh')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Display form
|
||||
*/
|
||||
function showForm($ID, $options = []) {
|
||||
$this->initForm($ID, $options);
|
||||
$this->showFormHeader($options);
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Alert ID', 'siem-wazuh')."</td>";
|
||||
echo "<td>".$this->fields["alert_id"]."</td>";
|
||||
echo "<td>".__('Rule ID', 'siem-wazuh')."</td>";
|
||||
echo "<td>".$this->fields["rule_id"]."</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Rule Level', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
echo $this->fields["rule_level"];
|
||||
if ($this->fields["rule_level"]) {
|
||||
$severity = self::getSeverityFromLevel($this->fields["rule_level"]);
|
||||
$severities = self::getSeverityArray();
|
||||
echo " <span class='badge severity-$severity'>".$severities[$severity]."</span>";
|
||||
}
|
||||
echo "</td>";
|
||||
echo "<td>".__('Agent Name', 'siem-wazuh')."</td>";
|
||||
echo "<td>".$this->fields["agent_name"]."</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Agent IP', 'siem-wazuh')."</td>";
|
||||
echo "<td>".$this->fields["agent_ip"]."</td>";
|
||||
echo "<td>".__('Timestamp', 'siem-wazuh')."</td>";
|
||||
echo "<td>".Html::convDateTime($this->fields["timestamp"])."</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Status')."</td>";
|
||||
echo "<td>";
|
||||
$statuses = self::getStatusArray();
|
||||
Dropdown::showFromArray('status', $statuses, ['value' => $this->fields["status"]]);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Severity', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
$severities = self::getSeverityArray();
|
||||
Dropdown::showFromArray('severity', $severities, ['value' => $this->fields["severity"]]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Rule Description', 'siem-wazuh')."</td>";
|
||||
echo "<td colspan='3'>";
|
||||
echo nl2br(Html::entities_deep($this->fields["rule_description"]));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
// Affichage des associations
|
||||
if ($this->fields["computer_id"]) {
|
||||
$computer = new Computer();
|
||||
$computer->getFromDB($this->fields["computer_id"]);
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Associated Computer')."</td>";
|
||||
echo "<td>";
|
||||
echo $computer->getLink();
|
||||
echo "</td>";
|
||||
echo "<td></td><td></td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
if ($this->fields["networkequipment_id"]) {
|
||||
$netequip = new NetworkEquipment();
|
||||
$netequip->getFromDB($this->fields["networkequipment_id"]);
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Associated Network Equipment')."</td>";
|
||||
echo "<td>";
|
||||
echo $netequip->getLink();
|
||||
echo "</td>";
|
||||
echo "<td></td><td></td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
if ($this->fields["ticket_id"]) {
|
||||
$ticket = new Ticket();
|
||||
$ticket->getFromDB($this->fields["ticket_id"]);
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Associated Ticket')."</td>";
|
||||
echo "<td>";
|
||||
echo $ticket->getLink();
|
||||
echo "</td>";
|
||||
echo "<td></td><td></td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
// Affichage des données brutes
|
||||
if (!empty($this->fields["raw_data"])) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td colspan='4'>";
|
||||
echo "<details>";
|
||||
echo "<summary>".__('Raw Alert Data', 'siem-wazuh')."</summary>";
|
||||
echo "<pre class='json-data'>";
|
||||
echo json_encode(json_decode($this->fields["raw_data"]), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
echo "</pre>";
|
||||
echo "</details>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
$this->showFormButtons($options);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process alerts from Wazuh API
|
||||
*/
|
||||
function processAlerts($server_id, $alerts_data) {
|
||||
$processed = 0;
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
|
||||
foreach ($alerts_data as $alert_data) {
|
||||
$alert_id = $alert_data['id'] ?? $alert_data['_id'] ?? null;
|
||||
if (!$alert_id) continue;
|
||||
|
||||
// Vérifier si l'alerte existe déjà
|
||||
if ($this->getFromDBByCrit([
|
||||
'wazuh_server_id' => $server_id,
|
||||
'alert_id' => $alert_id
|
||||
])) {
|
||||
continue; // Alerte déjà traitée
|
||||
}
|
||||
|
||||
// Extraire les informations de l'alerte
|
||||
$rule_id = $alert_data['rule']['id'] ?? null;
|
||||
$rule_level = $alert_data['rule']['level'] ?? 0;
|
||||
$rule_description = $alert_data['rule']['description'] ?? '';
|
||||
$agent_name = $alert_data['agent']['name'] ?? '';
|
||||
$agent_ip = $alert_data['agent']['ip'] ?? '';
|
||||
$timestamp = $alert_data['timestamp'] ?? $alert_data['@timestamp'] ?? date('Y-m-d H:i:s');
|
||||
|
||||
// Convertir le timestamp au format MySQL
|
||||
if (strpos($timestamp, 'T') !== false) {
|
||||
$timestamp = str_replace('T', ' ', substr($timestamp, 0, 19));
|
||||
}
|
||||
|
||||
// Déterminer la sévérité
|
||||
$severity = self::getSeverityFromLevel($rule_level);
|
||||
|
||||
// Créer l'alerte
|
||||
$alert_input = [
|
||||
'wazuh_server_id' => $server_id,
|
||||
'alert_id' => $alert_id,
|
||||
'rule_id' => $rule_id,
|
||||
'rule_level' => $rule_level,
|
||||
'rule_description' => $rule_description,
|
||||
'agent_id' => $alert_data['agent']['id'] ?? '',
|
||||
'agent_name' => $agent_name,
|
||||
'agent_ip' => $agent_ip,
|
||||
'timestamp' => $timestamp,
|
||||
'raw_data' => json_encode($alert_data),
|
||||
'severity' => $severity,
|
||||
'status' => 'new'
|
||||
];
|
||||
|
||||
// Tenter d'associer avec un asset
|
||||
$this->associateWithAsset($alert_input, $agent_name, $agent_ip);
|
||||
|
||||
if ($alert_id = $this->add($alert_input)) {
|
||||
$processed++;
|
||||
|
||||
// Créer automatiquement un ticket si configuré
|
||||
if ($config->getConfiguration('auto_create_ticket', 1)) {
|
||||
$this->createTicketFromAlert($alert_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $processed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate alert with GLPI asset
|
||||
*/
|
||||
private function associateWithAsset(&$alert_input, $agent_name, $agent_ip) {
|
||||
global $DB;
|
||||
|
||||
// Recherche par nom d'agent
|
||||
if (!empty($agent_name)) {
|
||||
// Recherche dans les ordinateurs
|
||||
$computer = $DB->request([
|
||||
'SELECT' => ['id'],
|
||||
'FROM' => 'glpi_computers',
|
||||
'WHERE' => [
|
||||
'name' => $agent_name,
|
||||
'is_deleted' => 0
|
||||
],
|
||||
'LIMIT' => 1
|
||||
])->current();
|
||||
|
||||
if ($computer) {
|
||||
$alert_input['computer_id'] = $computer['id'];
|
||||
return;
|
||||
}
|
||||
|
||||
// Recherche dans les équipements réseau
|
||||
$netequip = $DB->request([
|
||||
'SELECT' => ['id'],
|
||||
'FROM' => 'glpi_networkequipments',
|
||||
'WHERE' => [
|
||||
'name' => $agent_name,
|
||||
'is_deleted' => 0
|
||||
],
|
||||
'LIMIT' => 1
|
||||
])->current();
|
||||
|
||||
if ($netequip) {
|
||||
$alert_input['networkequipment_id'] = $netequip['id'];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Recherche par adresse IP
|
||||
if (!empty($agent_ip)) {
|
||||
// Recherche dans les ports réseau
|
||||
$port = $DB->request([
|
||||
'SELECT' => ['items_id', 'itemtype'],
|
||||
'FROM' => 'glpi_networkports',
|
||||
'INNER JOIN' => [
|
||||
'glpi_ipaddresses' => [
|
||||
'ON' => [
|
||||
'glpi_networkports' => 'id',
|
||||
'glpi_ipaddresses' => 'items_id'
|
||||
]
|
||||
]
|
||||
],
|
||||
'WHERE' => [
|
||||
'glpi_ipaddresses.name' => $agent_ip,
|
||||
'glpi_ipaddresses.itemtype' => 'NetworkPort'
|
||||
],
|
||||
'LIMIT' => 1
|
||||
])->current();
|
||||
|
||||
if ($port) {
|
||||
if ($port['itemtype'] == 'Computer') {
|
||||
$alert_input['computer_id'] = $port['items_id'];
|
||||
} elseif ($port['itemtype'] == 'NetworkEquipment') {
|
||||
$alert_input['networkequipment_id'] = $port['items_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ticket from alert
|
||||
*/
|
||||
function createTicketFromAlert($alert_id) {
|
||||
if (!$this->getFromDB($alert_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$server = new PluginSiemWazuhServer();
|
||||
if (!$server->getFromDB($this->fields['wazuh_server_id'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ticket = new Ticket();
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
|
||||
// Déterminer la priorité basée sur le niveau de règle
|
||||
$priority = self::getPriorityFromLevel($this->fields['rule_level']);
|
||||
|
||||
$ticket_input = [
|
||||
'name' => sprintf(__('Wazuh Alert: %s', 'siem-wazuh'), $this->fields['rule_description']),
|
||||
'content' => $this->generateTicketContent(),
|
||||
'type' => $server->fields['ticket_type'] ?: 1,
|
||||
'itilcategories_id' => $server->fields['ticket_category'],
|
||||
'priority' => $priority,
|
||||
'urgency' => $priority,
|
||||
'impact' => $priority,
|
||||
'status' => CommonITILObject::INCOMING,
|
||||
'_add_validation' => 0
|
||||
];
|
||||
|
||||
// Associer le ticket à l'asset si trouvé
|
||||
if ($this->fields['computer_id']) {
|
||||
$ticket_input['_link']['items_id'] = $this->fields['computer_id'];
|
||||
$ticket_input['_link']['itemtype'] = 'Computer';
|
||||
} elseif ($this->fields['networkequipment_id']) {
|
||||
$ticket_input['_link']['items_id'] = $this->fields['networkequipment_id'];
|
||||
$ticket_input['_link']['itemtype'] = 'NetworkEquipment';
|
||||
}
|
||||
|
||||
$ticket_id = $ticket->add($ticket_input);
|
||||
|
||||
if ($ticket_id) {
|
||||
// Mettre à jour l'alerte
|
||||
$this->update([
|
||||
'id' => $alert_id,
|
||||
'ticket_id' => $ticket_id,
|
||||
'status' => 'ticket_created'
|
||||
]);
|
||||
|
||||
return $ticket_id;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ticket content from alert
|
||||
*/
|
||||
private function generateTicketContent() {
|
||||
$content = "[WAZUH-ALERT]\n\n";
|
||||
$content .= sprintf(__('Alert ID: %s', 'siem-wazuh'), $this->fields['alert_id']) . "\n";
|
||||
$content .= sprintf(__('Rule ID: %s', 'siem-wazuh'), $this->fields['rule_id']) . "\n";
|
||||
$content .= sprintf(__('Rule Level: %s', 'siem-wazuh'), $this->fields['rule_level']) . "\n";
|
||||
$content .= sprintf(__('Agent: %s (%s)', 'siem-wazuh'), $this->fields['agent_name'], $this->fields['agent_ip']) . "\n";
|
||||
$content .= sprintf(__('Timestamp: %s', 'siem-wazuh'), $this->fields['timestamp']) . "\n\n";
|
||||
$content .= sprintf(__('Description: %s', 'siem-wazuh'), $this->fields['rule_description']) . "\n\n";
|
||||
|
||||
if ($this->fields['raw_data']) {
|
||||
$content .= __('Raw Alert Data:', 'siem-wazuh') . "\n";
|
||||
$content .= "```json\n";
|
||||
$content .= json_encode(json_decode($this->fields['raw_data']), JSON_PRETTY_PRINT);
|
||||
$content .= "\n```";
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get severity from rule level
|
||||
*/
|
||||
static function getSeverityFromLevel($level) {
|
||||
if ($level >= 12) return 'critical';
|
||||
if ($level >= 10) return 'high';
|
||||
if ($level >= 7) return 'medium';
|
||||
return 'low';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get priority from rule level
|
||||
*/
|
||||
static function getPriorityFromLevel($level) {
|
||||
if ($level >= 12) return 5; // Very high
|
||||
if ($level >= 10) return 4; // High
|
||||
if ($level >= 7) return 3; // Medium
|
||||
if ($level >= 5) return 2; // Low
|
||||
return 1; // Very low
|
||||
}
|
||||
|
||||
/**
|
||||
* Show alerts for a server
|
||||
*/
|
||||
static function showForServer($server_id) {
|
||||
global $DB;
|
||||
|
||||
$iterator = $DB->request([
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_alerts',
|
||||
'WHERE' => ['wazuh_server_id' => $server_id],
|
||||
'ORDER' => 'timestamp DESC',
|
||||
'LIMIT' => 50
|
||||
]);
|
||||
|
||||
echo "<div class='spaced'>";
|
||||
echo "<table class='tab_cadre_fixehov'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th>".__('Alert ID', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Rule', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Level', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Agent', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Timestamp', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Status')."</th>";
|
||||
echo "<th>".__('Ticket')."</th>";
|
||||
echo "</tr>";
|
||||
|
||||
$statuses = self::getStatusArray();
|
||||
|
||||
foreach ($iterator as $row) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td><a href='".PluginSiemWazuhAlert::getFormURLWithID($row['id'])."'>".$row['alert_id']."</a></td>";
|
||||
echo "<td>".$row['rule_id']." - ".Html::clean($row['rule_description'])."</td>";
|
||||
echo "<td>".$row['rule_level']."</td>";
|
||||
echo "<td>".$row['agent_name']." (".$row['agent_ip'].")</td>";
|
||||
echo "<td>".Html::convDateTime($row['timestamp'])."</td>";
|
||||
echo "<td>".$statuses[$row['status']]."</td>";
|
||||
echo "<td>";
|
||||
if ($row['ticket_id']) {
|
||||
$ticket = new Ticket();
|
||||
if ($ticket->getFromDB($row['ticket_id'])) {
|
||||
echo $ticket->getLink();
|
||||
}
|
||||
}
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Cron function to sync alerts
|
||||
*/
|
||||
static function cronSyncAlerts($task = null) {
|
||||
global $DB;
|
||||
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
if (!$config->getConfiguration('sync_enabled', 1)) {
|
||||
return 0; // Synchronisation désactivée
|
||||
}
|
||||
|
||||
$iterator = $DB->request([
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_servers',
|
||||
'WHERE' => ['is_active' => 1]
|
||||
]);
|
||||
|
||||
$total_processed = 0;
|
||||
|
||||
foreach ($iterator as $server_data) {
|
||||
$server = new PluginSiemWazuhServer();
|
||||
$server->fields = $server_data;
|
||||
|
||||
$result = $server->syncAlerts();
|
||||
if ($result['success']) {
|
||||
$matches = [];
|
||||
if (preg_match('/(\d+)/', $result['message'], $matches)) {
|
||||
$total_processed += (int)$matches[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($task) {
|
||||
$task->addVolume($total_processed);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return $total_processed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cron function to cleanup old alerts
|
||||
*/
|
||||
static function cronCleanupOldAlerts($task = null) {
|
||||
global $DB;
|
||||
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
$retention_days = $config->getConfiguration('alert_retention_days', 90);
|
||||
|
||||
$query = "DELETE FROM glpi_plugin_siem_wazuh_alerts
|
||||
WHERE date_creation < DATE_SUB(NOW(), INTERVAL $retention_days DAY)";
|
||||
|
||||
$result = $DB->query($query);
|
||||
$deleted = $DB->affectedRows();
|
||||
|
||||
if ($task) {
|
||||
$task->addVolume($deleted);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu content
|
||||
*/
|
||||
static function getMenuContent() {
|
||||
$menu = [];
|
||||
if (Session::haveRight(static::$rightname, READ)) {
|
||||
$menu['title'] = self::getMenuName();
|
||||
$menu['page'] = "/plugins/siem-wazuh/front/wazuhalert.php";
|
||||
$menu['links']['search'] = "/plugins/siem-wazuh/front/wazuhalert.php";
|
||||
}
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu name
|
||||
*/
|
||||
static function getMenuName() {
|
||||
return self::getTypeName(Session::getPluralNumber());
|
||||
}
|
||||
}
|
515
inc/wazuhapi.class.php
Normal file
515
inc/wazuhapi.class.php
Normal file
@@ -0,0 +1,515 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Classe de gestion de l'API Wazuh
|
||||
*/
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
class PluginSiemWazuhAPI {
|
||||
|
||||
private $server;
|
||||
private $token;
|
||||
private $config;
|
||||
|
||||
const API_VERSION = 'v1';
|
||||
const TIMEOUT = 30;
|
||||
const MAX_RETRIES = 3;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct(PluginSiemWazuhServer $server) {
|
||||
$this->server = $server;
|
||||
$this->config = new PluginSiemWazuhConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test connection to Wazuh server
|
||||
*/
|
||||
public function testConnection() {
|
||||
try {
|
||||
$result = $this->authenticate();
|
||||
if ($result['success']) {
|
||||
// Test basic API call
|
||||
$info = $this->makeRequest('GET', '/');
|
||||
if ($info['success']) {
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => __('Connection successful', 'siem-wazuh'),
|
||||
'data' => $info['data']
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => __('Connection failed:', 'siem-wazuh') . ' ' . $info['message']
|
||||
];
|
||||
}
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => __('Authentication failed:', 'siem-wazuh') . ' ' . $result['message']
|
||||
];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => __('Connection error:', 'siem-wazuh') . ' ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate with Wazuh API
|
||||
*/
|
||||
private function authenticate() {
|
||||
if (!empty($this->token)) {
|
||||
return ['success' => true, 'token' => $this->token];
|
||||
}
|
||||
|
||||
$url = rtrim($this->server->fields['wazuh_url'], '/') . ':' . $this->server->fields['wazuh_port'] . '/security/user/authenticate';
|
||||
|
||||
$credentials = base64_encode($this->server->fields['wazuh_login'] . ':' . $this->server->getPassword());
|
||||
|
||||
$headers = [
|
||||
'Authorization: Basic ' . $credentials,
|
||||
'Content-Type: application/json'
|
||||
];
|
||||
|
||||
try {
|
||||
$response = $this->httpRequest('POST', $url, null, $headers);
|
||||
|
||||
if ($response['success'] && isset($response['data']['data']['token'])) {
|
||||
$this->token = $response['data']['data']['token'];
|
||||
return ['success' => true, 'token' => $this->token];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $response['message'] ?? __('Authentication failed', 'siem-wazuh')
|
||||
];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get alerts from Wazuh
|
||||
*/
|
||||
public function getAlerts($params = []) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
$default_params = [
|
||||
'offset' => 0,
|
||||
'limit' => $this->config->getConfiguration('max_alerts_per_sync', 100),
|
||||
'sort' => '-timestamp',
|
||||
'rule.level' => '>=' . $this->config->getConfiguration('min_rule_level', 5)
|
||||
];
|
||||
|
||||
// Merge with provided parameters
|
||||
$params = array_merge($default_params, $params);
|
||||
|
||||
// Construire l'URL avec les paramètres
|
||||
$query_string = http_build_query($params);
|
||||
$endpoint = '/alerts?' . $query_string;
|
||||
|
||||
return $this->makeRequest('GET', $endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get alert by ID
|
||||
*/
|
||||
public function getAlert($alert_id) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
return $this->makeRequest('GET', '/alerts/' . $alert_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get agents
|
||||
*/
|
||||
public function getAgents($params = []) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
$default_params = [
|
||||
'offset' => 0,
|
||||
'limit' => 500,
|
||||
'sort' => '+name'
|
||||
];
|
||||
|
||||
$params = array_merge($default_params, $params);
|
||||
$query_string = http_build_query($params);
|
||||
$endpoint = '/agents?' . $query_string;
|
||||
|
||||
return $this->makeRequest('GET', $endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get agent by ID
|
||||
*/
|
||||
public function getAgent($agent_id) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
return $this->makeRequest('GET', '/agents/' . $agent_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rules
|
||||
*/
|
||||
public function getRules($params = []) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
$default_params = [
|
||||
'offset' => 0,
|
||||
'limit' => 500,
|
||||
'sort' => '+id'
|
||||
];
|
||||
|
||||
$params = array_merge($default_params, $params);
|
||||
$query_string = http_build_query($params);
|
||||
$endpoint = '/rules?' . $query_string;
|
||||
|
||||
return $this->makeRequest('GET', $endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get decoders
|
||||
*/
|
||||
public function getDecoders($params = []) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
$default_params = [
|
||||
'offset' => 0,
|
||||
'limit' => 500
|
||||
];
|
||||
|
||||
$params = array_merge($default_params, $params);
|
||||
$query_string = http_build_query($params);
|
||||
$endpoint = '/decoders?' . $query_string;
|
||||
|
||||
return $this->makeRequest('GET', $endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cluster status
|
||||
*/
|
||||
public function getClusterStatus() {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
return $this->makeRequest('GET', '/cluster/status');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get manager information
|
||||
*/
|
||||
public function getManagerInfo() {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
return $this->makeRequest('GET', '/manager/info');
|
||||
}
|
||||
|
||||
/**
|
||||
* Make API request
|
||||
*/
|
||||
private function makeRequest($method, $endpoint, $data = null, $extra_headers = []) {
|
||||
if (!$this->token) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
}
|
||||
|
||||
$url = rtrim($this->server->fields['wazuh_url'], '/') . ':' . $this->server->fields['wazuh_port'] . $endpoint;
|
||||
|
||||
$headers = array_merge([
|
||||
'Authorization: Bearer ' . $this->token,
|
||||
'Content-Type: application/json'
|
||||
], $extra_headers);
|
||||
|
||||
return $this->httpRequest($method, $url, $data, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make HTTP request with retry logic
|
||||
*/
|
||||
private function httpRequest($method, $url, $data = null, $headers = []) {
|
||||
$retries = 0;
|
||||
$last_error = null;
|
||||
|
||||
while ($retries < self::MAX_RETRIES) {
|
||||
try {
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => self::TIMEOUT,
|
||||
CURLOPT_CONNECTTIMEOUT => 10,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSL_VERIFYHOST => false,
|
||||
CURLOPT_CUSTOMREQUEST => $method,
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
CURLOPT_USERAGENT => 'GLPI-SIEM-Wazuh-Plugin/1.0'
|
||||
]);
|
||||
|
||||
if ($data !== null) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, is_array($data) ? json_encode($data) : $data);
|
||||
}
|
||||
|
||||
// Logging for debug mode
|
||||
if ($this->config->getConfiguration('debug_mode', 0)) {
|
||||
$this->logDebug("HTTP Request: $method $url", [
|
||||
'headers' => $headers,
|
||||
'data' => $data,
|
||||
'retry' => $retries
|
||||
]);
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_error($ch);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception("cURL Error: $curl_error");
|
||||
}
|
||||
|
||||
$decoded_response = json_decode($response, true);
|
||||
|
||||
if ($this->config->getConfiguration('debug_mode', 0)) {
|
||||
$this->logDebug("HTTP Response: $http_code", [
|
||||
'response' => $decoded_response,
|
||||
'raw_response' => substr($response, 0, 1000)
|
||||
]);
|
||||
}
|
||||
|
||||
if ($http_code >= 200 && $http_code < 300) {
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => $decoded_response,
|
||||
'http_code' => $http_code
|
||||
];
|
||||
} elseif ($http_code == 401) {
|
||||
// Token expired, clear it and retry authentication
|
||||
$this->token = null;
|
||||
if ($retries < self::MAX_RETRIES - 1) {
|
||||
$retries++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$error_message = "HTTP $http_code";
|
||||
if ($decoded_response && isset($decoded_response['detail'])) {
|
||||
$error_message .= ": " . $decoded_response['detail'];
|
||||
} elseif ($decoded_response && isset($decoded_response['message'])) {
|
||||
$error_message .= ": " . $decoded_response['message'];
|
||||
}
|
||||
|
||||
throw new Exception($error_message);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$last_error = $e;
|
||||
$retries++;
|
||||
|
||||
if ($retries < self::MAX_RETRIES) {
|
||||
sleep(pow(2, $retries)); // Exponential backoff
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->logError("HTTP Request failed after $retries retries", [
|
||||
'url' => $url,
|
||||
'method' => $method,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $last_error ? $last_error->getMessage() : __('Request failed after maximum retries', 'siem-wazuh')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Query Wazuh Indexer (OpenSearch/Elasticsearch)
|
||||
*/
|
||||
public function queryIndexer($index, $query = [], $params = []) {
|
||||
if (empty($this->server->fields['indexer_url'])) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => __('Indexer not configured', 'siem-wazuh')
|
||||
];
|
||||
}
|
||||
|
||||
$url = rtrim($this->server->fields['indexer_url'], '/') . ':' . $this->server->fields['indexer_port'] . '/' . $index . '/_search';
|
||||
|
||||
$headers = [
|
||||
'Content-Type: application/json'
|
||||
];
|
||||
|
||||
if (!empty($this->server->fields['indexer_login'])) {
|
||||
$credentials = base64_encode($this->server->fields['indexer_login'] . ':' . $this->server->getPassword('indexer_password'));
|
||||
$headers[] = 'Authorization: Basic ' . $credentials;
|
||||
}
|
||||
|
||||
$default_query = [
|
||||
'size' => $this->config->getConfiguration('max_alerts_per_sync', 100),
|
||||
'sort' => [
|
||||
['timestamp' => ['order' => 'desc']]
|
||||
]
|
||||
];
|
||||
|
||||
$final_query = array_merge($default_query, $query);
|
||||
|
||||
return $this->httpRequest('POST', $url, $final_query, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent alerts from indexer
|
||||
*/
|
||||
public function getRecentAlertsFromIndexer($minutes = 60) {
|
||||
$query = [
|
||||
'query' => [
|
||||
'bool' => [
|
||||
'must' => [
|
||||
[
|
||||
'range' => [
|
||||
'timestamp' => [
|
||||
'gte' => 'now-' . $minutes . 'm',
|
||||
'lt' => 'now'
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$min_level = $this->config->getConfiguration('min_rule_level', 5);
|
||||
if ($min_level > 0) {
|
||||
$query['query']['bool']['must'][] = [
|
||||
'range' => [
|
||||
'rule.level' => [
|
||||
'gte' => $min_level
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return $this->queryIndexer('wazuh-alerts-*', $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log debug message
|
||||
*/
|
||||
private function logDebug($message, $context = []) {
|
||||
if ($this->config->getConfiguration('debug_mode', 0)) {
|
||||
$this->log('debug', $message, $context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log error message
|
||||
*/
|
||||
private function logError($message, $context = []) {
|
||||
$this->log('error', $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log message
|
||||
*/
|
||||
private function log($level, $message, $context = []) {
|
||||
global $DB;
|
||||
|
||||
$log_levels = ['debug', 'info', 'warning', 'error', 'critical'];
|
||||
$current_log_level = $this->config->getConfiguration('log_level', 'info');
|
||||
$current_level_index = array_search($current_log_level, $log_levels);
|
||||
$message_level_index = array_search($level, $log_levels);
|
||||
|
||||
// Only log if message level is equal or higher than configured level
|
||||
if ($message_level_index >= $current_level_index) {
|
||||
try {
|
||||
$DB->insert('glpi_plugin_siem_wazuh_logs', [
|
||||
'wazuh_server_id' => $this->server->getID(),
|
||||
'level' => $level,
|
||||
'message' => $message,
|
||||
'context' => json_encode($context),
|
||||
'date_creation' => $_SESSION['glpi_currenttime']
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
// Ignore logging errors to prevent recursion
|
||||
error_log("SIEM-Wazuh Plugin: Failed to log message - " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API statistics
|
||||
*/
|
||||
public function getStatistics() {
|
||||
global $DB;
|
||||
|
||||
$stats = [];
|
||||
|
||||
// Requests count by server
|
||||
$iterator = $DB->request([
|
||||
'SELECT' => ['COUNT' => '* as count'],
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_logs',
|
||||
'WHERE' => [
|
||||
'wazuh_server_id' => $this->server->getID(),
|
||||
'date_creation' => ['>=', date('Y-m-d 00:00:00')]
|
||||
]
|
||||
]);
|
||||
|
||||
$stats['requests_today'] = $iterator->current()['count'];
|
||||
|
||||
// Error rate
|
||||
$iterator = $DB->request([
|
||||
'SELECT' => ['COUNT' => '* as count'],
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_logs',
|
||||
'WHERE' => [
|
||||
'wazuh_server_id' => $this->server->getID(),
|
||||
'level' => ['IN', ['error', 'critical']],
|
||||
'date_creation' => ['>=', date('Y-m-d 00:00:00')]
|
||||
]
|
||||
]);
|
||||
|
||||
$stats['errors_today'] = $iterator->current()['count'];
|
||||
$stats['error_rate'] = $stats['requests_today'] > 0 ?
|
||||
round(($stats['errors_today'] / $stats['requests_today']) * 100, 2) : 0;
|
||||
|
||||
return $stats;
|
||||
}
|
||||
}
|
423
inc/wazuhconfig.class.php
Normal file
423
inc/wazuhconfig.class.php
Normal file
@@ -0,0 +1,423 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Classe de gestion de la configuration
|
||||
*/
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
class PluginSiemWazuhConfig extends CommonDBTM {
|
||||
|
||||
public $dohistory = true;
|
||||
|
||||
static $rightname = 'plugin_siem_wazuh_config';
|
||||
|
||||
const MATRIX_FIELD = '';
|
||||
const SEARCHOPTIONS_INHERITANCE = true;
|
||||
|
||||
/**
|
||||
* Return the localized name of the current Type
|
||||
*/
|
||||
static function getTypeName($nb = 0) {
|
||||
return __('SIEM Wazuh Configuration', 'siem-wazuh');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration value
|
||||
*/
|
||||
function getConfiguration($name, $default = null, $context = 'global') {
|
||||
global $DB;
|
||||
|
||||
$iterator = $DB->request([
|
||||
'FROM' => $this->getTable(),
|
||||
'WHERE' => [
|
||||
'name' => $name,
|
||||
'context' => $context
|
||||
],
|
||||
'LIMIT' => 1
|
||||
]);
|
||||
|
||||
if (count($iterator) > 0) {
|
||||
$row = $iterator->current();
|
||||
return $row['value'];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set configuration value
|
||||
*/
|
||||
function setConfiguration($name, $value, $context = 'global') {
|
||||
global $DB;
|
||||
|
||||
// Vérifier si la configuration existe
|
||||
$iterator = $DB->request([
|
||||
'FROM' => $this->getTable(),
|
||||
'WHERE' => [
|
||||
'name' => $name,
|
||||
'context' => $context
|
||||
],
|
||||
'LIMIT' => 1
|
||||
]);
|
||||
|
||||
if (count($iterator) > 0) {
|
||||
// Mettre à jour
|
||||
$row = $iterator->current();
|
||||
return $this->update([
|
||||
'id' => $row['id'],
|
||||
'value' => $value
|
||||
]);
|
||||
} else {
|
||||
// Créer
|
||||
return $this->add([
|
||||
'name' => $name,
|
||||
'value' => $value,
|
||||
'context' => $context
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display configuration form
|
||||
*/
|
||||
function showConfigForm() {
|
||||
global $CFG_GLPI;
|
||||
|
||||
echo "<form name='form' method='post' action='" . Toolbox::getItemTypeFormURL(__CLASS__) . "'>";
|
||||
echo "<div class='spaced' id='tabsbody'>";
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
|
||||
echo "<tr><th colspan='4'>" . __('General Configuration', 'siem-wazuh') . "</th></tr>";
|
||||
|
||||
// Configuration générale
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td width='30%'>" . __('Enable synchronization', 'siem-wazuh') . "</td>";
|
||||
echo "<td width='20%'>";
|
||||
Dropdown::showYesNo("sync_enabled", $this->getConfiguration('sync_enabled', 1));
|
||||
echo "</td>";
|
||||
echo "<td width='30%'>" . __('Auto-create tickets', 'siem-wazuh') . "</td>";
|
||||
echo "<td width='20%'>";
|
||||
Dropdown::showYesNo("auto_create_ticket", $this->getConfiguration('auto_create_ticket', 1));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Maximum alerts per sync', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showNumber("max_alerts_per_sync", [
|
||||
'value' => $this->getConfiguration('max_alerts_per_sync', 100),
|
||||
'min' => 10,
|
||||
'max' => 1000,
|
||||
'step' => 10
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Alert retention (days)', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showNumber("alert_retention_days", [
|
||||
'value' => $this->getConfiguration('alert_retention_days', 90),
|
||||
'min' => 7,
|
||||
'max' => 365,
|
||||
'step' => 7
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Minimum rule level', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showNumber("min_rule_level", [
|
||||
'value' => $this->getConfiguration('min_rule_level', 5),
|
||||
'min' => 0,
|
||||
'max' => 15
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Enable notifications', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("notification_enabled", $this->getConfiguration('notification_enabled', 1));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
// Configuration des tickets par défaut
|
||||
echo "<tr><th colspan='4'>" . __('Default Ticket Settings', 'siem-wazuh') . "</th></tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Default Priority', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Ticket::dropdownPriority('default_ticket_priority', $this->getConfiguration('default_ticket_priority', 3));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Default Urgency', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Ticket::dropdownUrgency('default_ticket_urgency', $this->getConfiguration('default_ticket_urgency', 3));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Default Impact', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Ticket::dropdownImpact('default_ticket_impact', $this->getConfiguration('default_ticket_impact', 3));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Auto-assign tickets', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("auto_assign_tickets", $this->getConfiguration('auto_assign_tickets', 0));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
if ($this->getConfiguration('auto_assign_tickets', 0)) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Default Assignee', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
User::dropdown([
|
||||
'name' => 'default_assignee',
|
||||
'value' => $this->getConfiguration('default_assignee', 0),
|
||||
'right' => 'own_ticket'
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Default Group', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Group::dropdown([
|
||||
'name' => 'default_group',
|
||||
'value' => $this->getConfiguration('default_group', 0)
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
// Configuration de mapping
|
||||
echo "<tr><th colspan='4'>" . __('Asset Mapping Configuration', 'siem-wazuh') . "</th></tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Match by hostname', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("match_by_hostname", $this->getConfiguration('match_by_hostname', 1));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Match by IP address', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("match_by_ip", $this->getConfiguration('match_by_ip', 1));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Case sensitive matching', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("case_sensitive_matching", $this->getConfiguration('case_sensitive_matching', 0));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Create unknown assets', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("create_unknown_assets", $this->getConfiguration('create_unknown_assets', 0));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
// Configuration de debug
|
||||
echo "<tr><th colspan='4'>" . __('Debug Configuration', 'siem-wazuh') . "</th></tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Enable debug mode', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("debug_mode", $this->getConfiguration('debug_mode', 0));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Log level', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
$log_levels = [
|
||||
'error' => __('Error only', 'siem-wazuh'),
|
||||
'warning' => __('Warning and above', 'siem-wazuh'),
|
||||
'info' => __('Info and above', 'siem-wazuh'),
|
||||
'debug' => __('All messages', 'siem-wazuh')
|
||||
];
|
||||
Dropdown::showFromArray('log_level', $log_levels, [
|
||||
'value' => $this->getConfiguration('log_level', 'info')
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Keep debug logs (days)', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showNumber("debug_retention_days", [
|
||||
'value' => $this->getConfiguration('debug_retention_days', 30),
|
||||
'min' => 1,
|
||||
'max' => 90
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "<td></td><td></td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<td class='center' colspan='4'>";
|
||||
echo "<input type='submit' name='update_config' value='" . _sx('button', 'Save') . "' class='submit'>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
Html::closeForm();
|
||||
|
||||
// Affichage des statistiques
|
||||
$this->showStatistics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process configuration form
|
||||
*/
|
||||
function processConfigForm() {
|
||||
if (isset($_POST['update_config'])) {
|
||||
$config_fields = [
|
||||
'sync_enabled', 'auto_create_ticket', 'max_alerts_per_sync', 'alert_retention_days',
|
||||
'min_rule_level', 'notification_enabled', 'default_ticket_priority',
|
||||
'default_ticket_urgency', 'default_ticket_impact', 'auto_assign_tickets',
|
||||
'default_assignee', 'default_group', 'match_by_hostname', 'match_by_ip',
|
||||
'case_sensitive_matching', 'create_unknown_assets', 'debug_mode',
|
||||
'log_level', 'debug_retention_days'
|
||||
];
|
||||
|
||||
foreach ($config_fields as $field) {
|
||||
if (isset($_POST[$field])) {
|
||||
$this->setConfiguration($field, $_POST[$field]);
|
||||
}
|
||||
}
|
||||
|
||||
Session::addMessageAfterRedirect(__('Configuration updated successfully', 'siem-wazuh'));
|
||||
Html::back();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show statistics
|
||||
*/
|
||||
function showStatistics() {
|
||||
global $DB;
|
||||
|
||||
echo "<div class='spaced'>";
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
echo "<tr><th colspan='4'>" . __('Statistics', 'siem-wazuh') . "</th></tr>";
|
||||
|
||||
// Statistiques des serveurs
|
||||
$servers_count = countElementsInTable('glpi_plugin_siem_wazuh_servers');
|
||||
$active_servers = countElementsInTable('glpi_plugin_siem_wazuh_servers', ['is_active' => 1]);
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Total servers', 'siem-wazuh') . "</td>";
|
||||
echo "<td>$servers_count</td>";
|
||||
echo "<td>" . __('Active servers', 'siem-wazuh') . "</td>";
|
||||
echo "<td>$active_servers</td>";
|
||||
echo "</tr>";
|
||||
|
||||
// Statistiques des alertes
|
||||
$total_alerts = countElementsInTable('glpi_plugin_siem_wazuh_alerts');
|
||||
$new_alerts = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['status' => 'new']);
|
||||
$processed_alerts = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['status' => 'processed']);
|
||||
$tickets_created = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['status' => 'ticket_created']);
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Total alerts', 'siem-wazuh') . "</td>";
|
||||
echo "<td>$total_alerts</td>";
|
||||
echo "<td>" . __('New alerts', 'siem-wazuh') . "</td>";
|
||||
echo "<td>$new_alerts</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Processed alerts', 'siem-wazuh') . "</td>";
|
||||
echo "<td>$processed_alerts</td>";
|
||||
echo "<td>" . __('Tickets created', 'siem-wazuh') . "</td>";
|
||||
echo "<td>$tickets_created</td>";
|
||||
echo "</tr>";
|
||||
|
||||
// Alertes par sévérité
|
||||
$severities = PluginSiemWazuhAlert::getSeverityArray();
|
||||
foreach ($severities as $severity => $label) {
|
||||
$count = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['severity' => $severity]);
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . sprintf(__('%s alerts', 'siem-wazuh'), $label) . "</td>";
|
||||
echo "<td>$count</td>";
|
||||
if (next($severities) !== false) {
|
||||
$next_severity = key($severities);
|
||||
$next_label = current($severities);
|
||||
$next_count = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['severity' => $next_severity]);
|
||||
echo "<td>" . sprintf(__('%s alerts', 'siem-wazuh'), $next_label) . "</td>";
|
||||
echo "<td>$next_count</td>";
|
||||
next($severities);
|
||||
} else {
|
||||
echo "<td></td><td></td>";
|
||||
}
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
// Dernière synchronisation
|
||||
$last_sync = $DB->request([
|
||||
'SELECT' => ['MAX' => 'last_sync AS last_sync'],
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_servers',
|
||||
'WHERE' => ['is_active' => 1]
|
||||
])->current();
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Last global sync', 'siem-wazuh') . "</td>";
|
||||
echo "<td colspan='3'>";
|
||||
echo $last_sync['last_sync'] ? Html::convDateTime($last_sync['last_sync']) : __('Never');
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu content
|
||||
*/
|
||||
static function getMenuContent() {
|
||||
$menu = [];
|
||||
if (Session::haveRight(static::$rightname, READ)) {
|
||||
$menu['title'] = self::getMenuName();
|
||||
$menu['page'] = "/plugins/siem-wazuh/front/wazuhconfig.php";
|
||||
$menu['links']['config'] = "/plugins/siem-wazuh/front/wazuhconfig.php";
|
||||
}
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu name
|
||||
*/
|
||||
static function getMenuName() {
|
||||
return self::getTypeName(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default configurations
|
||||
*/
|
||||
static function getDefaultConfigurations() {
|
||||
return [
|
||||
'sync_enabled' => 1,
|
||||
'auto_create_ticket' => 1,
|
||||
'max_alerts_per_sync' => 100,
|
||||
'alert_retention_days' => 90,
|
||||
'min_rule_level' => 5,
|
||||
'notification_enabled' => 1,
|
||||
'default_ticket_priority' => 3,
|
||||
'default_ticket_urgency' => 3,
|
||||
'default_ticket_impact' => 3,
|
||||
'auto_assign_tickets' => 0,
|
||||
'default_assignee' => 0,
|
||||
'default_group' => 0,
|
||||
'match_by_hostname' => 1,
|
||||
'match_by_ip' => 1,
|
||||
'case_sensitive_matching' => 0,
|
||||
'create_unknown_assets' => 0,
|
||||
'debug_mode' => 0,
|
||||
'log_level' => 'info',
|
||||
'debug_retention_days' => 30
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Install default configuration
|
||||
*/
|
||||
static function installDefaultConfiguration() {
|
||||
$config = new self();
|
||||
$defaults = self::getDefaultConfigurations();
|
||||
|
||||
foreach ($defaults as $name => $value) {
|
||||
$config->setConfiguration($name, $value);
|
||||
}
|
||||
}
|
||||
}
|
411
inc/wazuhserver.class.php
Normal file
411
inc/wazuhserver.class.php
Normal file
@@ -0,0 +1,411 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Classe de gestion des serveurs Wazuh
|
||||
*/
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
class PluginSiemWazuhServer extends CommonDBTM {
|
||||
|
||||
public $dohistory = true;
|
||||
|
||||
static protected $forward_entity_to = ['PluginSiemWazuhAlert'];
|
||||
|
||||
static $rightname = 'plugin_siem_wazuh_server';
|
||||
|
||||
const MATRIX_FIELD = '';
|
||||
const SEARCHOPTIONS_INHERITANCE = true;
|
||||
|
||||
/**
|
||||
* Return the localized name of the current Type
|
||||
*/
|
||||
static function getTypeName($nb = 0) {
|
||||
return _n('Wazuh Server', 'Wazuh Servers', $nb, 'siem-wazuh');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define tab name
|
||||
*/
|
||||
function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) {
|
||||
if (!$withtemplate) {
|
||||
$nb = 0;
|
||||
switch ($item->getType()) {
|
||||
case 'PluginSiemWazuhServer':
|
||||
if ($_SESSION['glpishow_count_on_tabs']) {
|
||||
$nb = countElementsInTable('glpi_plugin_siem_wazuh_alerts',
|
||||
"`wazuh_server_id` = '".$item->getID()."'");
|
||||
}
|
||||
return self::createTabEntry(__('Alerts', 'siem-wazuh'), $nb);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Display tab content
|
||||
*/
|
||||
static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) {
|
||||
if ($item->getType() == 'PluginSiemWazuhServer') {
|
||||
PluginSiemWazuhAlert::showForServer($item->getID());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define search options
|
||||
*/
|
||||
function rawSearchOptions() {
|
||||
$tab = [];
|
||||
|
||||
$tab[] = [
|
||||
'id' => 'common',
|
||||
'name' => __('Characteristics')
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '1',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'name',
|
||||
'name' => __('Name'),
|
||||
'datatype' => 'itemlink',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '2',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'wazuh_url',
|
||||
'name' => __('Wazuh URL', 'siem-wazuh'),
|
||||
'datatype' => 'string',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '3',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'wazuh_port',
|
||||
'name' => __('Wazuh Port', 'siem-wazuh'),
|
||||
'datatype' => 'integer',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '4',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'is_active',
|
||||
'name' => __('Active'),
|
||||
'datatype' => 'bool',
|
||||
'massiveaction' => true
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '5',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'sync_interval',
|
||||
'name' => __('Sync Interval (seconds)', 'siem-wazuh'),
|
||||
'datatype' => 'integer',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '6',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'last_sync',
|
||||
'name' => __('Last Sync', 'siem-wazuh'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '7',
|
||||
'table' => 'glpi_itilcategories',
|
||||
'field' => 'name',
|
||||
'name' => __('Default Ticket Category', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'ticket_category'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '30',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'date_creation',
|
||||
'name' => __('Creation date'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '31',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'date_mod',
|
||||
'name' => __('Last update'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
return $tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display form
|
||||
*/
|
||||
function showForm($ID, $options = []) {
|
||||
$this->initForm($ID, $options);
|
||||
$this->showFormHeader($options);
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Name')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "name", ['size' => 50]);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Active')."</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("is_active", $this->fields["is_active"]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Wazuh Server URL', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "wazuh_url", ['size' => 50, 'placeholder' => 'https://wazuh-server.domain.com']);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Wazuh API Port', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "wazuh_port", ['size' => 10, 'value' => ($this->fields["wazuh_port"] ?: 55000)]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Wazuh API Login', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "wazuh_login", ['size' => 30]);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Wazuh API Password', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
echo "<input type='password' name='wazuh_password' value='' size='30' autocomplete='new-password'>";
|
||||
if (!empty($this->fields["wazuh_password"])) {
|
||||
echo "<br><small>".__('Leave empty to keep current password', 'siem-wazuh')."</small>";
|
||||
}
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Indexer Server URL', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "indexer_url", ['size' => 50, 'placeholder' => 'https://wazuh-indexer.domain.com']);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Indexer API Port', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "indexer_port", ['size' => 10, 'value' => ($this->fields["indexer_port"] ?: 9200)]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Indexer API Login', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "indexer_login", ['size' => 30]);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Indexer API Password', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
echo "<input type='password' name='indexer_password' value='' size='30' autocomplete='new-password'>";
|
||||
if (!empty($this->fields["indexer_password"])) {
|
||||
echo "<br><small>".__('Leave empty to keep current password', 'siem-wazuh')."</small>";
|
||||
}
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Sync Interval (seconds)', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "sync_interval", ['size' => 10, 'value' => ($this->fields["sync_interval"] ?: 300)]);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Default Ticket Type', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Ticket::dropdownType('ticket_type', $this->fields["ticket_type"]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Default Ticket Category', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
ITILCategory::dropdown(['name' => 'ticket_category', 'value' => $this->fields["ticket_category"]]);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Last Sync', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
echo ($this->fields["last_sync"] ? Html::convDateTime($this->fields["last_sync"]) : __('Never'));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
// Bouton de test de connexion
|
||||
if ($ID > 0) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td colspan='4' class='center'>";
|
||||
echo "<button type='button' class='btn btn-primary' onclick='testWazuhConnection($ID)'>";
|
||||
echo __('Test Connection', 'siem-wazuh');
|
||||
echo "</button>";
|
||||
echo "<div id='test-result-$ID' class='mt-2'></div>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
$this->showFormButtons($options);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare input for add
|
||||
*/
|
||||
function prepareInputForAdd($input) {
|
||||
if (!empty($input['wazuh_password'])) {
|
||||
$input['wazuh_password'] = Toolbox::encrypt($input['wazuh_password'], GLPIKEY);
|
||||
}
|
||||
if (!empty($input['indexer_password'])) {
|
||||
$input['indexer_password'] = Toolbox::encrypt($input['indexer_password'], GLPIKEY);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare input for update
|
||||
*/
|
||||
function prepareInputForUpdate($input) {
|
||||
if (!empty($input['wazuh_password'])) {
|
||||
$input['wazuh_password'] = Toolbox::encrypt($input['wazuh_password'], GLPIKEY);
|
||||
} else {
|
||||
unset($input['wazuh_password']);
|
||||
}
|
||||
|
||||
if (!empty($input['indexer_password'])) {
|
||||
$input['indexer_password'] = Toolbox::encrypt($input['indexer_password'], GLPIKEY);
|
||||
} else {
|
||||
unset($input['indexer_password']);
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get decrypted password
|
||||
*/
|
||||
function getPassword($field = 'wazuh_password') {
|
||||
if (!empty($this->fields[$field])) {
|
||||
return Toolbox::decrypt($this->fields[$field], GLPIKEY);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test connection to Wazuh server
|
||||
*/
|
||||
function testConnection() {
|
||||
if (empty($this->fields['wazuh_url']) || empty($this->fields['wazuh_login'])) {
|
||||
return ['success' => false, 'message' => __('Missing connection parameters', 'siem-wazuh')];
|
||||
}
|
||||
|
||||
$api = new PluginSiemWazuhAPI($this);
|
||||
return $api->testConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize alerts from this server
|
||||
*/
|
||||
function syncAlerts() {
|
||||
if (!$this->fields['is_active']) {
|
||||
return ['success' => false, 'message' => __('Server is not active', 'siem-wazuh')];
|
||||
}
|
||||
|
||||
$api = new PluginSiemWazuhAPI($this);
|
||||
$result = $api->getAlerts();
|
||||
|
||||
if ($result['success']) {
|
||||
$alert = new PluginSiemWazuhAlert();
|
||||
$processed = $alert->processAlerts($this->getID(), $result['data']);
|
||||
|
||||
// Mise à jour de la dernière synchronisation
|
||||
$this->update([
|
||||
'id' => $this->getID(),
|
||||
'last_sync' => $_SESSION['glpi_currenttime']
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => sprintf(__('%d alerts processed', 'siem-wazuh'), $processed)
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get servers for dropdown
|
||||
*/
|
||||
static function getDropdownValues($post) {
|
||||
global $DB;
|
||||
|
||||
$where = ['is_active' => 1];
|
||||
if (isset($post['searchText']) && !empty($post['searchText'])) {
|
||||
$where[] = ['name' => ['LIKE', '%' . $post['searchText'] . '%']];
|
||||
}
|
||||
|
||||
$iterator = $DB->request([
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_servers',
|
||||
'WHERE' => $where,
|
||||
'ORDER' => 'name'
|
||||
]);
|
||||
|
||||
$values = [];
|
||||
foreach ($iterator as $row) {
|
||||
$values[] = [
|
||||
'id' => $row['id'],
|
||||
'text' => $row['name']
|
||||
];
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu content
|
||||
*/
|
||||
static function getMenuContent() {
|
||||
$menu = [];
|
||||
if (Session::haveRight(static::$rightname, READ)) {
|
||||
$menu['title'] = self::getMenuName();
|
||||
$menu['page'] = "/plugins/siem-wazuh/front/wazuhserver.php";
|
||||
$menu['links']['search'] = "/plugins/siem-wazuh/front/wazuhserver.php";
|
||||
if (Session::haveRight(static::$rightname, CREATE)) {
|
||||
$menu['links']['add'] = "/plugins/siem-wazuh/front/wazuhserver.form.php";
|
||||
}
|
||||
}
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu name
|
||||
*/
|
||||
static function getMenuName() {
|
||||
return self::getTypeName(Session::getPluralNumber());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean database relations
|
||||
*/
|
||||
function cleanDBonPurge() {
|
||||
$this->deleteChildrenAndRelationsFromDb(
|
||||
[
|
||||
PluginSiemWazuhAlert::class
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get used configuration
|
||||
*/
|
||||
static function getUsedConfig() {
|
||||
return ['plugin_siem_wazuh_server'];
|
||||
}
|
||||
}
|
454
inc/wazuhtab.class.php
Normal file
454
inc/wazuhtab.class.php
Normal file
@@ -0,0 +1,454 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Classe de gestion des onglets Wazuh sur les assets
|
||||
*/
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
class PluginSiemWazuhTab extends CommonGLPI {
|
||||
|
||||
static $rightname = 'plugin_siem_wazuh_alert';
|
||||
|
||||
/**
|
||||
* Get tab name for item
|
||||
*/
|
||||
function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) {
|
||||
if (!$withtemplate && Session::haveRight(static::$rightname, READ)) {
|
||||
if (in_array($item->getType(), ['Computer', 'NetworkEquipment', 'Peripheral', 'Phone', 'Printer'])) {
|
||||
$nb = 0;
|
||||
if ($_SESSION['glpishow_count_on_tabs']) {
|
||||
$nb = self::countForItem($item);
|
||||
}
|
||||
return self::createTabEntry(__('Wazuh Alerts', 'siem-wazuh'), $nb);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Display tab content
|
||||
*/
|
||||
static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) {
|
||||
if (in_array($item->getType(), ['Computer', 'NetworkEquipment', 'Peripheral', 'Phone', 'Printer'])) {
|
||||
self::showForItem($item);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count alerts for item
|
||||
*/
|
||||
static function countForItem(CommonGLPI $item) {
|
||||
global $DB;
|
||||
|
||||
$itemtype = $item->getType();
|
||||
$items_id = $item->getID();
|
||||
|
||||
$field_map = [
|
||||
'Computer' => 'computer_id',
|
||||
'NetworkEquipment' => 'networkequipment_id',
|
||||
'Peripheral' => 'peripheral_id',
|
||||
'Phone' => 'phone_id',
|
||||
'Printer' => 'printer_id'
|
||||
];
|
||||
|
||||
if (!isset($field_map[$itemtype])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return countElementsInTable('glpi_plugin_siem_wazuh_alerts', [
|
||||
$field_map[$itemtype] => $items_id
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show alerts for item
|
||||
*/
|
||||
static function showForItem(CommonGLPI $item) {
|
||||
global $DB, $CFG_GLPI;
|
||||
|
||||
$itemtype = $item->getType();
|
||||
$items_id = $item->getID();
|
||||
|
||||
$field_map = [
|
||||
'Computer' => 'computer_id',
|
||||
'NetworkEquipment' => 'networkequipment_id',
|
||||
'Peripheral' => 'peripheral_id',
|
||||
'Phone' => 'phone_id',
|
||||
'Printer' => 'printer_id'
|
||||
];
|
||||
|
||||
if (!isset($field_map[$itemtype])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$field = $field_map[$itemtype];
|
||||
$canupdate = Session::haveRight(static::$rightname, UPDATE);
|
||||
|
||||
// Récupération des alertes
|
||||
$iterator = $DB->request([
|
||||
'SELECT' => [
|
||||
'glpi_plugin_siem_wazuh_alerts.*',
|
||||
'glpi_plugin_siem_wazuh_servers.name AS server_name'
|
||||
],
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_alerts',
|
||||
'LEFT JOIN' => [
|
||||
'glpi_plugin_siem_wazuh_servers' => [
|
||||
'ON' => [
|
||||
'glpi_plugin_siem_wazuh_alerts' => 'wazuh_server_id',
|
||||
'glpi_plugin_siem_wazuh_servers' => 'id'
|
||||
]
|
||||
]
|
||||
],
|
||||
'WHERE' => [$field => $items_id],
|
||||
'ORDER' => ['timestamp DESC', 'rule_level DESC'],
|
||||
'LIMIT' => 100
|
||||
]);
|
||||
|
||||
$alerts = iterator_to_array($iterator);
|
||||
|
||||
echo "<div class='spaced'>";
|
||||
|
||||
// Résumé des alertes
|
||||
self::showAlertsSummary($alerts);
|
||||
|
||||
if (count($alerts) > 0) {
|
||||
// Filtres
|
||||
self::showAlertsFilters($item, $field);
|
||||
|
||||
// Tableau des alertes
|
||||
self::showAlertsTable($alerts, $canupdate);
|
||||
|
||||
// Actions de masse
|
||||
if ($canupdate && count($alerts) > 0) {
|
||||
self::showMassActions($item, $field);
|
||||
}
|
||||
} else {
|
||||
echo "<div class='center'>";
|
||||
echo "<p>" . __('No Wazuh alerts found for this item', 'siem-wazuh') . "</p>";
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Show alerts summary
|
||||
*/
|
||||
static function showAlertsSummary($alerts) {
|
||||
$stats = [
|
||||
'total' => count($alerts),
|
||||
'new' => 0,
|
||||
'processed' => 0,
|
||||
'ticket_created' => 0,
|
||||
'ignored' => 0,
|
||||
'critical' => 0,
|
||||
'high' => 0,
|
||||
'medium' => 0,
|
||||
'low' => 0
|
||||
];
|
||||
|
||||
foreach ($alerts as $alert) {
|
||||
$stats[$alert['status']]++;
|
||||
$stats[$alert['severity']]++;
|
||||
}
|
||||
|
||||
echo "<div class='alert-summary'>";
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th colspan='8'>" . __('Alerts Summary', 'siem-wazuh') . "</th>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td class='center'><strong>" . $stats['total'] . "</strong><br>" . __('Total', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center status-new'><strong>" . $stats['new'] . "</strong><br>" . __('New', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center status-processed'><strong>" . $stats['processed'] . "</strong><br>" . __('Processed', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center status-ticket'><strong>" . $stats['ticket_created'] . "</strong><br>" . __('Ticket Created', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center severity-critical'><strong>" . $stats['critical'] . "</strong><br>" . __('Critical', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center severity-high'><strong>" . $stats['high'] . "</strong><br>" . __('High', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center severity-medium'><strong>" . $stats['medium'] . "</strong><br>" . __('Medium', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center severity-low'><strong>" . $stats['low'] . "</strong><br>" . __('Low', 'siem-wazuh') . "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
echo "<br>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Show alerts filters
|
||||
*/
|
||||
static function showAlertsFilters($item, $field) {
|
||||
echo "<div class='alert-filters'>";
|
||||
echo "<form method='get' action=''>";
|
||||
echo "<input type='hidden' name='id' value='" . $item->getID() . "'>";
|
||||
echo "<input type='hidden' name='_glpi_tab' value='PluginSiemWazuhTab$1'>";
|
||||
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th colspan='4'>" . __('Filters', 'siem-wazuh') . "</th>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Status') . "</td>";
|
||||
echo "<td>";
|
||||
$statuses = ['all' => __('All')] + PluginSiemWazuhAlert::getStatusArray();
|
||||
Dropdown::showFromArray('filter_status', $statuses, [
|
||||
'value' => $_GET['filter_status'] ?? 'all'
|
||||
]);
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>" . __('Severity', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
$severities = ['all' => __('All')] + PluginSiemWazuhAlert::getSeverityArray();
|
||||
Dropdown::showFromArray('filter_severity', $severities, [
|
||||
'value' => $_GET['filter_severity'] ?? 'all'
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Date from', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Html::showDateField('filter_date_from', [
|
||||
'value' => $_GET['filter_date_from'] ?? ''
|
||||
]);
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>" . __('Date to', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Html::showDateField('filter_date_to', [
|
||||
'value' => $_GET['filter_date_to'] ?? ''
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td colspan='4' class='center'>";
|
||||
echo "<input type='submit' value='" . _sx('button', 'Filter') . "' class='submit'>";
|
||||
echo " ";
|
||||
echo "<input type='button' value='" . _sx('button', 'Reset') . "' class='submit' onclick='window.location.href=\"" . $item->getLinkURL() . "&_glpi_tab=PluginSiemWazuhTab$1\"'>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "</table>";
|
||||
echo "</form>";
|
||||
echo "</div>";
|
||||
echo "<br>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Show alerts table
|
||||
*/
|
||||
static function showAlertsTable($alerts, $canupdate) {
|
||||
echo "<div class='alert-list'>";
|
||||
echo "<form method='post' name='alerts_form' id='alerts_form' action='" . Plugin::getWebDir('siem-wazuh') . "/front/wazuhalert.form.php'>";
|
||||
echo "<table class='tab_cadre_fixehov'>";
|
||||
|
||||
$header = "<tr class='tab_bg_2'>";
|
||||
if ($canupdate) {
|
||||
$header .= "<th width='10'>";
|
||||
$header .= "<input type='checkbox' name='select_all' onclick='checkAll(this)'>";
|
||||
$header .= "</th>";
|
||||
}
|
||||
$header .= "<th>" . __('Alert ID', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Server', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Rule', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Level', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Agent', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Timestamp', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Status') . "</th>";
|
||||
$header .= "<th>" . __('Severity', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Ticket') . "</th>";
|
||||
$header .= "</tr>";
|
||||
|
||||
echo $header;
|
||||
|
||||
if (count($alerts) == 0) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td colspan='" . ($canupdate ? "10" : "9") . "' class='center'>";
|
||||
echo __('No alerts found', 'siem-wazuh');
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
$statuses = PluginSiemWazuhAlert::getStatusArray();
|
||||
$severities = PluginSiemWazuhAlert::getSeverityArray();
|
||||
|
||||
foreach ($alerts as $alert) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
|
||||
if ($canupdate) {
|
||||
echo "<td>";
|
||||
echo "<input type='checkbox' name='selected_alerts[]' value='" . $alert['id'] . "'>";
|
||||
echo "</td>";
|
||||
}
|
||||
|
||||
echo "<td>";
|
||||
echo "<a href='" . PluginSiemWazuhAlert::getFormURLWithID($alert['id']) . "'>";
|
||||
echo $alert['alert_id'];
|
||||
echo "</a>";
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>" . $alert['server_name'] . "</td>";
|
||||
|
||||
echo "<td>";
|
||||
echo "<strong>" . $alert['rule_id'] . "</strong><br>";
|
||||
echo "<small>" . Html::clean($alert['rule_description']) . "</small>";
|
||||
echo "</td>";
|
||||
|
||||
echo "<td class='center'>";
|
||||
echo "<span class='rule-level-" . $alert['rule_level'] . "'>";
|
||||
echo $alert['rule_level'];
|
||||
echo "</span>";
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>";
|
||||
echo $alert['agent_name'] . "<br>";
|
||||
echo "<small>" . $alert['agent_ip'] . "</small>";
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>" . Html::convDateTime($alert['timestamp']) . "</td>";
|
||||
|
||||
echo "<td>";
|
||||
echo "<span class='status-badge status-" . $alert['status'] . "'>";
|
||||
echo $statuses[$alert['status']];
|
||||
echo "</span>";
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>";
|
||||
echo "<span class='severity-badge severity-" . $alert['severity'] . "'>";
|
||||
echo $severities[$alert['severity']];
|
||||
echo "</span>";
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>";
|
||||
if ($alert['ticket_id']) {
|
||||
$ticket = new Ticket();
|
||||
if ($ticket->getFromDB($alert['ticket_id'])) {
|
||||
echo $ticket->getLink();
|
||||
}
|
||||
} else {
|
||||
echo "-";
|
||||
}
|
||||
echo "</td>";
|
||||
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
|
||||
if ($canupdate && count($alerts) > 0) {
|
||||
echo "<div class='center' style='margin-top: 10px;'>";
|
||||
echo "<input type='submit' name='mark_processed' value='" . __('Mark as Processed', 'siem-wazuh') . "' class='submit'>";
|
||||
echo " ";
|
||||
echo "<input type='submit' name='mark_ignored' value='" . __('Mark as Ignored', 'siem-wazuh') . "' class='submit'>";
|
||||
echo " ";
|
||||
echo "<input type='submit' name='create_tickets' value='" . __('Create Tickets', 'siem-wazuh') . "' class='submit'>";
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
echo Html::closeForm();
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Show mass actions
|
||||
*/
|
||||
static function showMassActions($item, $field) {
|
||||
echo "<br>";
|
||||
echo "<div class='mass-actions'>";
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th colspan='2'>" . __('Mass Actions', 'siem-wazuh') . "</th>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Sync new alerts', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
echo "<button type='button' class='btn btn-primary' onclick='syncAlertsForItem(" . $item->getID() . ", \"" . $item->getType() . "\")'>";
|
||||
echo __('Sync Now', 'siem-wazuh');
|
||||
echo "</button>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Export alerts', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
echo "<a href='" . Plugin::getWebDir('siem-wazuh') . "/front/wazuhalert.php?export=1&" . $field . "=" . $item->getID() . "' class='btn'>";
|
||||
echo __('Export CSV', 'siem-wazuh');
|
||||
echo "</a>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get search options for item
|
||||
*/
|
||||
static function getSearchOptionsForItem($itemtype) {
|
||||
$tab = [];
|
||||
|
||||
$tab[5150]['table'] = 'glpi_plugin_siem_wazuh_alerts';
|
||||
$tab[5150]['field'] = 'id';
|
||||
$tab[5150]['name'] = __('Wazuh Alerts', 'siem-wazuh');
|
||||
$tab[5150]['forcegroupby'] = true;
|
||||
$tab[5150]['usehaving'] = true;
|
||||
$tab[5150]['datatype'] = 'count';
|
||||
$tab[5150]['massiveaction'] = false;
|
||||
|
||||
$field_map = [
|
||||
'Computer' => 'computer_id',
|
||||
'NetworkEquipment' => 'networkequipment_id',
|
||||
'Peripheral' => 'peripheral_id',
|
||||
'Phone' => 'phone_id',
|
||||
'Printer' => 'printer_id'
|
||||
];
|
||||
|
||||
if (isset($field_map[$itemtype])) {
|
||||
$tab[5150]['linkfield'] = $field_map[$itemtype];
|
||||
}
|
||||
|
||||
return $tab;
|
||||
}
|
||||
}
|
||||
|
||||
// JavaScript pour les fonctionnalités interactives
|
||||
echo "<script type='text/javascript'>
|
||||
function checkAll(checkbox) {
|
||||
var checkboxes = document.querySelectorAll('input[name=\"selected_alerts[]\"]');
|
||||
for (var i = 0; i < checkboxes.length; i++) {
|
||||
checkboxes[i].checked = checkbox.checked;
|
||||
}
|
||||
}
|
||||
|
||||
function syncAlertsForItem(itemId, itemType) {
|
||||
if (confirm('" . __('Are you sure you want to sync alerts for this item?', 'siem-wazuh') . "')) {
|
||||
// AJAX call to sync alerts
|
||||
fetch('" . Plugin::getWebDir('siem-wazuh') . "/ajax/sync_alerts.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: 'item_id=' + itemId + '&item_type=' + itemType
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert('" . __('Sync completed successfully', 'siem-wazuh') . "');
|
||||
location.reload();
|
||||
} else {
|
||||
alert('" . __('Sync failed:', 'siem-wazuh') . " ' + data.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('" . __('An error occurred during sync', 'siem-wazuh') . "');
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>";
|
Reference in New Issue
Block a user