You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
399 lines
12 KiB
399 lines
12 KiB
<?php declare(strict_types = 0);
|
|
/*
|
|
** Zabbix
|
|
** Copyright (C) 2001-2023 Zabbix SIA
|
|
**
|
|
** This program is free software; you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation; either version 2 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software
|
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
**/
|
|
|
|
|
|
class CControllerAuditLogList extends CController {
|
|
|
|
/**
|
|
* @var string Time from.
|
|
*/
|
|
private $from;
|
|
|
|
/**
|
|
* @var string Time till.
|
|
*/
|
|
private $to;
|
|
|
|
protected function init() {
|
|
$this->disableCsrfValidation();
|
|
}
|
|
|
|
protected function checkInput(): bool {
|
|
$fields = [
|
|
'page' => 'ge 1',
|
|
'filter_actions' => 'array',
|
|
'filter_resourcetype' => 'in -1,'.implode(',', array_keys(self::getResourcesList())),
|
|
'filter_rst' => 'in 1',
|
|
'filter_set' => 'in 1',
|
|
'filter_userids' => 'array_db users.userid',
|
|
'filter_resourceid' => 'string',
|
|
'filter_recordsetid' => 'string',
|
|
'from' => 'range_time',
|
|
'to' => 'range_time'
|
|
];
|
|
|
|
$ret = $this->validateInput($fields);
|
|
|
|
if (!$ret) {
|
|
$this->setResponse(new CControllerResponseFatal());
|
|
}
|
|
else {
|
|
if ($this->hasInput('from') || $this->hasInput('to')) {
|
|
validateTimeSelectorPeriod(
|
|
$this->hasInput('from') ? $this->getInput('from') : null,
|
|
$this->hasInput('to') ? $this->getInput('to') : null
|
|
);
|
|
}
|
|
|
|
$this->from = $this->getInput('from', CProfile::get('web.auditlog.filter.from',
|
|
'now-'.CSettingsHelper::get(CSettingsHelper::PERIOD_DEFAULT)
|
|
));
|
|
$this->to = $this->getInput('to', CProfile::get('web.auditlog.filter.to', 'now'));
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
protected function checkPermissions(): bool {
|
|
return $this->checkAccess(CRoleHelper::UI_REPORTS_AUDIT);
|
|
}
|
|
|
|
protected function doAction(): void {
|
|
if ($this->hasInput('filter_set')) {
|
|
$this->updateProfiles();
|
|
}
|
|
elseif ($this->hasInput('filter_rst')) {
|
|
$this->deleteProfiles();
|
|
}
|
|
|
|
$data = [
|
|
'page' => $this->getInput('page', 1),
|
|
'userids' => CProfile::getArray('web.auditlog.filter.userids', []),
|
|
'resourcetype' => CProfile::get('web.auditlog.filter.resourcetype', -1),
|
|
'auditlog_actions' => CProfile::getArray('web.auditlog.filter.actions', []),
|
|
'resourceid' => CProfile::get('web.auditlog.filter.resourceid', ''),
|
|
'recordsetid' => CProfile::get('web.auditlog.filter.recordsetid', ''),
|
|
'action' => $this->getAction(),
|
|
'actions' => self::getActionsList(),
|
|
'resources' => self::getResourcesList(),
|
|
'timeline' => getTimeSelectorPeriod([
|
|
'profileIdx' => 'web.auditlog.filter',
|
|
'profileIdx2' => 0,
|
|
'from' => $this->from,
|
|
'to' => $this->to
|
|
]),
|
|
'auditlogs' => [],
|
|
'active_tab' => CProfile::get('web.auditlog.filter.active', 1)
|
|
];
|
|
$users = [];
|
|
$non_existent_userids = [0];
|
|
|
|
$filter = [
|
|
'action' => $data['auditlog_actions']
|
|
];
|
|
|
|
if ($data['resourcetype'] != -1) {
|
|
$filter['resourcetype'] = $data['resourcetype'];
|
|
}
|
|
|
|
if ($data['resourceid'] !== '') {
|
|
$filter['resourceid'] = $data['resourceid'];
|
|
}
|
|
|
|
if ($data['recordsetid'] !== '') {
|
|
$filter['recordsetid'] = $data['recordsetid'];
|
|
}
|
|
|
|
$params = [
|
|
'output' => ['auditid', 'userid', 'username', 'clock', 'action', 'resourcetype', 'ip', 'resourceid',
|
|
'resourcename', 'details', 'recordsetid'
|
|
],
|
|
'filter' => $filter,
|
|
'sortfield' => 'auditid',
|
|
'sortorder' => ZBX_SORT_DOWN,
|
|
'limit' => CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT) + 1
|
|
];
|
|
|
|
if ($data['timeline']['from_ts'] !== null) {
|
|
$params['time_from'] = $data['timeline']['from_ts'];
|
|
}
|
|
|
|
if ($data['timeline']['to_ts'] !== null) {
|
|
$params['time_till'] = $data['timeline']['to_ts'];
|
|
}
|
|
|
|
if ($data['userids']) {
|
|
$users = API::User()->get([
|
|
'output' => ['userid', 'username', 'name', 'surname'],
|
|
'userids' => $data['userids'],
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if (in_array('0', $data['userids'])) {
|
|
$users[0] = ['userid' => '0', 'username' => 'System', 'name' => '', 'surname' => ''];
|
|
}
|
|
|
|
$data['userids'] = $this->sanitizeUsersForMultiselect($users);
|
|
|
|
if ($users) {
|
|
$params['userids'] = array_column($users, 'userid');
|
|
}
|
|
|
|
$users = array_map(function(array $value): string {
|
|
return $value['username'];
|
|
}, $users);
|
|
}
|
|
|
|
$data['auditlogs'] = API::AuditLog()->get($params);
|
|
$data['paging'] = CPagerHelper::paginate($data['page'], $data['auditlogs'], ZBX_SORT_UP,
|
|
(new CUrl('zabbix.php'))->setArgument('action', $this->getAction())
|
|
);
|
|
|
|
$data['auditlogs'] = $this->sanitizeDetails($data['auditlogs']);
|
|
|
|
if (!$users && $data['auditlogs']) {
|
|
$db_users = API::User()->get([
|
|
'output' => ['username'],
|
|
'userids' => array_unique(array_column($data['auditlogs'], 'userid')),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$users = [];
|
|
foreach ($data['auditlogs'] as $auditlog) {
|
|
if (!array_key_exists($auditlog['userid'], $db_users)) {
|
|
$non_existent_userids[$auditlog['userid']] = true;
|
|
continue;
|
|
}
|
|
|
|
$users[$auditlog['userid']] = $db_users[$auditlog['userid']]['username'];
|
|
}
|
|
}
|
|
|
|
$data['users'] = $users;
|
|
$data['non_existent_userids'] = array_keys($non_existent_userids);
|
|
|
|
natsort($data['actions']);
|
|
natsort($data['resources']);
|
|
|
|
$data['resources'] = [-1 => _('All')] + $data['resources'];
|
|
|
|
$response = new CControllerResponseData($data);
|
|
$response->setTitle(_('Audit log'));
|
|
$this->setResponse($response);
|
|
}
|
|
|
|
/**
|
|
* Return associated list of available actions and labels.
|
|
*
|
|
* @return array
|
|
*/
|
|
private static function getActionsList(): array {
|
|
return [
|
|
CAudit::ACTION_LOGIN_SUCCESS => _('Login'),
|
|
CAudit::ACTION_LOGIN_FAILED => _('Failed login'),
|
|
CAudit::ACTION_LOGOUT => _('Logout'),
|
|
CAudit::ACTION_ADD => _('Add'),
|
|
CAudit::ACTION_UPDATE => _('Update'),
|
|
CAudit::ACTION_DELETE => _('Delete'),
|
|
CAudit::ACTION_EXECUTE => _('Execute'),
|
|
CAudit::ACTION_HISTORY_CLEAR => _('History clear'),
|
|
CAudit::ACTION_CONFIG_REFRESH => _('Configuration refresh'),
|
|
CAudit::ACTION_PUSH => _('Push')
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Return associated list of available resources and labels.
|
|
*
|
|
* @return array
|
|
*/
|
|
private static function getResourcesList(): array {
|
|
return [
|
|
CAudit::RESOURCE_ACTION => _('Action'),
|
|
CAudit::RESOURCE_AUTH_TOKEN => _('API token'),
|
|
CAudit::RESOURCE_AUTHENTICATION => _('Authentication'),
|
|
CAudit::RESOURCE_AUTOREGISTRATION => _('Autoregistration'),
|
|
CAudit::RESOURCE_CONNECTOR => _('Connector'),
|
|
CAudit::RESOURCE_CORRELATION => _('Event correlation'),
|
|
CAudit::RESOURCE_DASHBOARD => _('Dashboard'),
|
|
CAudit::RESOURCE_DISCOVERY_RULE => _('Discovery rule'),
|
|
CAudit::RESOURCE_GRAPH => _('Graph'),
|
|
CAudit::RESOURCE_GRAPH_PROTOTYPE => _('Graph prototype'),
|
|
CAudit::RESOURCE_HA_NODE => _('High availability node'),
|
|
CAudit::RESOURCE_HISTORY => _('History'),
|
|
CAudit::RESOURCE_HOST => _('Host'),
|
|
CAudit::RESOURCE_HOST_GROUP => _('Host group'),
|
|
CAudit::RESOURCE_HOST_PROTOTYPE => _('Host prototype'),
|
|
CAudit::RESOURCE_HOUSEKEEPING => _('Housekeeping'),
|
|
CAudit::RESOURCE_ICON_MAP => _('Icon mapping'),
|
|
CAudit::RESOURCE_IMAGE => _('Image'),
|
|
CAudit::RESOURCE_IT_SERVICE => _('Service'),
|
|
CAudit::RESOURCE_ITEM => _('Item'),
|
|
CAudit::RESOURCE_ITEM_PROTOTYPE => _('Item prototype'),
|
|
CAudit::RESOURCE_LLD_RULE => _('LLD rule'),
|
|
CAudit::RESOURCE_MACRO => _('Macro'),
|
|
CAudit::RESOURCE_MAINTENANCE => _('Maintenance'),
|
|
CAudit::RESOURCE_MAP => _('Map'),
|
|
CAudit::RESOURCE_MEDIA_TYPE => _('Media type'),
|
|
CAudit::RESOURCE_MODULE => _('Module'),
|
|
CAudit::RESOURCE_PROXY => _('Proxy'),
|
|
CAudit::RESOURCE_REGEXP => _('Regular expression'),
|
|
CAudit::RESOURCE_SCENARIO => _('Web scenario'),
|
|
CAudit::RESOURCE_SCHEDULED_REPORT => _('Scheduled report'),
|
|
CAudit::RESOURCE_SCRIPT => _('Script'),
|
|
CAudit::RESOURCE_SETTINGS => _('Settings'),
|
|
CAudit::RESOURCE_SLA => _('SLA'),
|
|
CAudit::RESOURCE_TEMPLATE => _('Template'),
|
|
CAudit::RESOURCE_TEMPLATE_DASHBOARD => _('Template dashboard'),
|
|
CAudit::RESOURCE_TRIGGER => _('Trigger'),
|
|
CAudit::RESOURCE_TRIGGER_PROTOTYPE => _('Trigger prototype'),
|
|
CAudit::RESOURCE_TEMPLATE_GROUP => _('Template group'),
|
|
CAudit::RESOURCE_USER => _('User'),
|
|
CAudit::RESOURCE_USERDIRECTORY => _('User directory'),
|
|
CAudit::RESOURCE_USER_GROUP => _('User group'),
|
|
CAudit::RESOURCE_USER_ROLE => _('User role'),
|
|
CAudit::RESOURCE_VALUE_MAP => _('Value map')
|
|
];
|
|
}
|
|
|
|
private function updateProfiles(): void {
|
|
CProfile::updateArray('web.auditlog.filter.userids', $this->getInput('filter_userids', []), PROFILE_TYPE_ID);
|
|
CProfile::updateArray('web.auditlog.filter.actions', $this->getInput('filter_actions', []), PROFILE_TYPE_INT);
|
|
CProfile::update('web.auditlog.filter.resourcetype', $this->getInput('filter_resourcetype', -1),
|
|
PROFILE_TYPE_INT
|
|
);
|
|
CProfile::update('web.auditlog.filter.resourceid', $this->getInput('filter_resourceid', ''), PROFILE_TYPE_STR);
|
|
CProfile::update('web.auditlog.filter.recordsetid', $this->getInput('filter_recordsetid', ''),
|
|
PROFILE_TYPE_STR
|
|
);
|
|
CProfile::update('web.auditlog.filter.from', $this->from, PROFILE_TYPE_STR);
|
|
CProfile::update('web.auditlog.filter.to', $this->to, PROFILE_TYPE_STR);
|
|
}
|
|
|
|
private function deleteProfiles(): void {
|
|
CProfile::deleteIdx('web.auditlog.filter.userids');
|
|
CProfile::deleteIdx('web.auditlog.filter.actions');
|
|
CProfile::delete('web.auditlog.filter.resourcetype');
|
|
CProfile::delete('web.auditlog.filter.resourceid');
|
|
CProfile::delete('web.auditlog.filter.recordsetid');
|
|
}
|
|
|
|
private function sanitizeUsersForMultiselect(array $users): array {
|
|
$users = array_map(function(array $value): array {
|
|
return ['id' => $value['userid'], 'name' => getUserFullname($value)];
|
|
}, $users);
|
|
|
|
CArrayHelper::sort($users, ['name']);
|
|
|
|
return $users;
|
|
}
|
|
|
|
private function sanitizeDetails(array $auditlogs): array {
|
|
foreach ($auditlogs as &$auditlog) {
|
|
$auditlog['short_details'] = '';
|
|
$auditlog['details_button'] = 0;
|
|
|
|
if ($auditlog['resourcename'] != '') {
|
|
$auditlog['short_details'] .= _('Description').': '.$auditlog['resourcename'];
|
|
}
|
|
|
|
if (!in_array($auditlog['action'], [CAudit::ACTION_ADD, CAudit::ACTION_UPDATE, CAudit::ACTION_EXECUTE,
|
|
CAudit::ACTION_PUSH])) {
|
|
continue;
|
|
}
|
|
|
|
$details = json_decode($auditlog['details'], true);
|
|
|
|
if (!is_array($details) || count($details) == 0) {
|
|
$auditlog['details'] = '';
|
|
continue;
|
|
}
|
|
|
|
// Add space after description string.
|
|
if ($auditlog['short_details'] != '') {
|
|
$auditlog['short_details'] .= "\n\n";
|
|
}
|
|
|
|
$details = $this->formatDetails($details);
|
|
$short_details = array_slice($details, 0, 2);
|
|
|
|
// We cut string and show "Details" button if audit detail string more than 255 symbols.
|
|
foreach ($short_details as &$detail) {
|
|
// Remove all line breaks from short details.
|
|
$detail = str_replace(["\r\n", "\n"], " ", $detail);
|
|
|
|
if (mb_strlen($detail) > 255) {
|
|
$detail = mb_substr($detail, 0, 252).'...';
|
|
$auditlog['details_button'] = 1;
|
|
}
|
|
}
|
|
unset($detail);
|
|
|
|
$auditlog['details'] = implode("\n", $details);
|
|
$auditlog['short_details'] .= implode("\n", $short_details);
|
|
|
|
if (!$auditlog['details_button'] && count($details) > 2) {
|
|
$auditlog['details_button'] = 1;
|
|
}
|
|
|
|
$details = json_decode($auditlog['details'], true);
|
|
|
|
if (!is_array($details) || count($details) == 0) {
|
|
continue;
|
|
}
|
|
|
|
$details = $this->formatDetails($details);
|
|
|
|
$auditlog['details'] = implode("\n", $details);
|
|
}
|
|
unset($auditlog);
|
|
|
|
return $auditlogs;
|
|
}
|
|
|
|
private function formatDetails(array $details): array {
|
|
ksort($details);
|
|
|
|
$new_details = [];
|
|
foreach ($details as $key => $detail) {
|
|
$new_details[] = $this->makeDetailString($key, $detail);
|
|
}
|
|
|
|
return $new_details;
|
|
}
|
|
|
|
private function makeDetailString(string $key, array $detail): string {
|
|
switch ($detail[0]) {
|
|
case CAudit::DETAILS_ACTION_ADD:
|
|
return array_key_exists(1, $detail)
|
|
? $key.': '.$detail[1]
|
|
: $key.': '._('Added');
|
|
|
|
case CAudit::DETAILS_ACTION_DELETE:
|
|
return $key.': '._('Deleted');
|
|
|
|
case CAudit::DETAILS_ACTION_UPDATE:
|
|
return array_key_exists(1, $detail)
|
|
? sprintf('%s: %s => %s', $key, $detail[2], $detail[1])
|
|
: $key.': '._('Updated');
|
|
}
|
|
}
|
|
}
|