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.
1128 lines
35 KiB
1128 lines
35 KiB
1 year ago
|
<?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 to log audit records.
|
||
|
*/
|
||
|
class CAudit {
|
||
|
|
||
|
/**
|
||
|
* Audit actions.
|
||
|
*
|
||
|
* @var int
|
||
|
*/
|
||
|
public const ACTION_ADD = 0;
|
||
|
public const ACTION_UPDATE = 1;
|
||
|
public const ACTION_DELETE = 2;
|
||
|
public const ACTION_LOGOUT = 4;
|
||
|
public const ACTION_EXECUTE = 7;
|
||
|
public const ACTION_LOGIN_SUCCESS = 8;
|
||
|
public const ACTION_LOGIN_FAILED = 9;
|
||
|
public const ACTION_HISTORY_CLEAR = 10;
|
||
|
public const ACTION_CONFIG_REFRESH = 11;
|
||
|
public const ACTION_PUSH = 12;
|
||
|
|
||
|
/**
|
||
|
* Audit resources.
|
||
|
*
|
||
|
* @var int
|
||
|
*/
|
||
|
public const RESOURCE_USER = 0;
|
||
|
public const RESOURCE_MEDIA_TYPE = 3;
|
||
|
public const RESOURCE_HOST = 4;
|
||
|
public const RESOURCE_ACTION = 5;
|
||
|
public const RESOURCE_GRAPH = 6;
|
||
|
public const RESOURCE_USER_GROUP = 11;
|
||
|
public const RESOURCE_TRIGGER = 13;
|
||
|
public const RESOURCE_HOST_GROUP = 14;
|
||
|
public const RESOURCE_ITEM = 15;
|
||
|
public const RESOURCE_IMAGE = 16;
|
||
|
public const RESOURCE_VALUE_MAP = 17;
|
||
|
public const RESOURCE_IT_SERVICE = 18;
|
||
|
public const RESOURCE_MAP = 19;
|
||
|
public const RESOURCE_SCENARIO = 22;
|
||
|
public const RESOURCE_DISCOVERY_RULE = 23;
|
||
|
public const RESOURCE_SCRIPT = 25;
|
||
|
public const RESOURCE_PROXY = 26;
|
||
|
public const RESOURCE_MAINTENANCE = 27;
|
||
|
public const RESOURCE_REGEXP = 28;
|
||
|
public const RESOURCE_MACRO = 29;
|
||
|
public const RESOURCE_TEMPLATE = 30;
|
||
|
public const RESOURCE_TRIGGER_PROTOTYPE = 31;
|
||
|
public const RESOURCE_ICON_MAP = 32;
|
||
|
public const RESOURCE_DASHBOARD = 33;
|
||
|
public const RESOURCE_CORRELATION = 34;
|
||
|
public const RESOURCE_GRAPH_PROTOTYPE = 35;
|
||
|
public const RESOURCE_ITEM_PROTOTYPE = 36;
|
||
|
public const RESOURCE_HOST_PROTOTYPE = 37;
|
||
|
public const RESOURCE_AUTOREGISTRATION = 38;
|
||
|
public const RESOURCE_MODULE = 39;
|
||
|
public const RESOURCE_SETTINGS = 40;
|
||
|
public const RESOURCE_HOUSEKEEPING = 41;
|
||
|
public const RESOURCE_AUTHENTICATION = 42;
|
||
|
public const RESOURCE_TEMPLATE_DASHBOARD = 43;
|
||
|
public const RESOURCE_USER_ROLE = 44;
|
||
|
public const RESOURCE_AUTH_TOKEN = 45;
|
||
|
public const RESOURCE_SCHEDULED_REPORT = 46;
|
||
|
public const RESOURCE_HA_NODE = 47;
|
||
|
public const RESOURCE_SLA = 48;
|
||
|
public const RESOURCE_USERDIRECTORY = 49;
|
||
|
public const RESOURCE_TEMPLATE_GROUP = 50;
|
||
|
public const RESOURCE_CONNECTOR = 51;
|
||
|
public const RESOURCE_LLD_RULE = 52;
|
||
|
public const RESOURCE_HISTORY = 53;
|
||
|
|
||
|
/**
|
||
|
* Audit details actions.
|
||
|
*
|
||
|
* @var string
|
||
|
*/
|
||
|
public const DETAILS_ACTION_ADD = 'add';
|
||
|
public const DETAILS_ACTION_UPDATE = 'update';
|
||
|
public const DETAILS_ACTION_DELETE = 'delete';
|
||
|
|
||
|
/**
|
||
|
* Auditlog enabled value.
|
||
|
*
|
||
|
* @var int
|
||
|
*/
|
||
|
private const AUDITLOG_ENABLE = 1;
|
||
|
|
||
|
/**
|
||
|
* Table names of audit resources.
|
||
|
* resource => table name
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
private const TABLE_NAMES = [
|
||
|
self::RESOURCE_ACTION => 'actions',
|
||
|
self::RESOURCE_AUTHENTICATION => 'config',
|
||
|
self::RESOURCE_AUTH_TOKEN => 'token',
|
||
|
self::RESOURCE_AUTOREGISTRATION => 'config',
|
||
|
self::RESOURCE_CONNECTOR => 'connector',
|
||
|
self::RESOURCE_CORRELATION => 'correlation',
|
||
|
self::RESOURCE_DASHBOARD => 'dashboard',
|
||
|
self::RESOURCE_LLD_RULE => 'items',
|
||
|
self::RESOURCE_HOST => 'hosts',
|
||
|
self::RESOURCE_HOST_GROUP => 'hstgrp',
|
||
|
self::RESOURCE_HOST_PROTOTYPE => 'hosts',
|
||
|
self::RESOURCE_HOUSEKEEPING => 'config',
|
||
|
self::RESOURCE_ICON_MAP => 'icon_map',
|
||
|
self::RESOURCE_IMAGE => 'images',
|
||
|
self::RESOURCE_ITEM => 'items',
|
||
|
self::RESOURCE_ITEM_PROTOTYPE => 'items',
|
||
|
self::RESOURCE_IT_SERVICE => 'services',
|
||
|
self::RESOURCE_MACRO => 'globalmacro',
|
||
|
self::RESOURCE_MAINTENANCE => 'maintenances',
|
||
|
self::RESOURCE_MEDIA_TYPE => 'media_type',
|
||
|
self::RESOURCE_MODULE => 'module',
|
||
|
self::RESOURCE_PROXY => 'proxy',
|
||
|
self::RESOURCE_REGEXP => 'regexps',
|
||
|
self::RESOURCE_SCENARIO => 'httptest',
|
||
|
self::RESOURCE_SCHEDULED_REPORT => 'report',
|
||
|
self::RESOURCE_SCRIPT => 'scripts',
|
||
|
self::RESOURCE_SETTINGS => 'config',
|
||
|
self::RESOURCE_SLA => 'sla',
|
||
|
self::RESOURCE_TEMPLATE => 'hosts',
|
||
|
self::RESOURCE_TEMPLATE_DASHBOARD => 'dashboard',
|
||
|
self::RESOURCE_TEMPLATE_GROUP => 'hstgrp',
|
||
|
self::RESOURCE_USER => 'users',
|
||
|
self::RESOURCE_USERDIRECTORY => 'userdirectory',
|
||
|
self::RESOURCE_USER_GROUP => 'usrgrp'
|
||
|
];
|
||
|
|
||
|
/**
|
||
|
* ID field names of audit resources.
|
||
|
* resource => ID field name
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
private const ID_FIELD_NAMES = [
|
||
|
self::RESOURCE_PROXY => 'proxyid',
|
||
|
self::RESOURCE_TEMPLATE => 'templateid'
|
||
|
];
|
||
|
|
||
|
/**
|
||
|
* Name field names of audit resources.
|
||
|
* resource => name field
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
private const FIELD_NAMES = [
|
||
|
self::RESOURCE_ACTION => 'name',
|
||
|
self::RESOURCE_AUTHENTICATION => null,
|
||
|
self::RESOURCE_AUTH_TOKEN => 'name',
|
||
|
self::RESOURCE_AUTOREGISTRATION => null,
|
||
|
self::RESOURCE_CONNECTOR => 'name',
|
||
|
self::RESOURCE_CORRELATION => 'name',
|
||
|
self::RESOURCE_DASHBOARD => 'name',
|
||
|
self::RESOURCE_LLD_RULE => 'name',
|
||
|
self::RESOURCE_HOST => 'host',
|
||
|
self::RESOURCE_HOST_GROUP => 'name',
|
||
|
self::RESOURCE_HOST_PROTOTYPE => 'host',
|
||
|
self::RESOURCE_HOUSEKEEPING => null,
|
||
|
self::RESOURCE_ICON_MAP => 'name',
|
||
|
self::RESOURCE_IMAGE => 'name',
|
||
|
self::RESOURCE_ITEM => 'name',
|
||
|
self::RESOURCE_ITEM_PROTOTYPE => 'name',
|
||
|
self::RESOURCE_IT_SERVICE => 'name',
|
||
|
self::RESOURCE_MACRO => 'macro',
|
||
|
self::RESOURCE_MAINTENANCE => 'name',
|
||
|
self::RESOURCE_MEDIA_TYPE => 'name',
|
||
|
self::RESOURCE_MODULE => 'id',
|
||
|
self::RESOURCE_PROXY => 'name',
|
||
|
self::RESOURCE_REGEXP => 'name',
|
||
|
self::RESOURCE_SCENARIO => 'name',
|
||
|
self::RESOURCE_SCHEDULED_REPORT => 'name',
|
||
|
self::RESOURCE_SCRIPT => 'name',
|
||
|
self::RESOURCE_SETTINGS => null,
|
||
|
self::RESOURCE_SLA => 'name',
|
||
|
self::RESOURCE_TEMPLATE => 'host',
|
||
|
self::RESOURCE_TEMPLATE_DASHBOARD => 'name',
|
||
|
self::RESOURCE_TEMPLATE_GROUP => 'name',
|
||
|
self::RESOURCE_USER => 'username',
|
||
|
self::RESOURCE_USERDIRECTORY => 'name',
|
||
|
self::RESOURCE_USER_GROUP => 'name'
|
||
|
];
|
||
|
|
||
|
/**
|
||
|
* API names of audit resources.
|
||
|
* resource => API name
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
private const API_NAMES = [
|
||
|
self::RESOURCE_ACTION => 'action',
|
||
|
self::RESOURCE_AUTHENTICATION => 'authentication',
|
||
|
self::RESOURCE_AUTH_TOKEN => 'token',
|
||
|
self::RESOURCE_AUTOREGISTRATION => 'autoregistration',
|
||
|
self::RESOURCE_CONNECTOR => 'connector',
|
||
|
self::RESOURCE_CORRELATION => 'correlation',
|
||
|
self::RESOURCE_DASHBOARD => 'dashboard',
|
||
|
self::RESOURCE_LLD_RULE => 'discoveryrule',
|
||
|
self::RESOURCE_HOST => 'host',
|
||
|
self::RESOURCE_HOST_GROUP => 'hostgroup',
|
||
|
self::RESOURCE_HOST_PROTOTYPE => 'hostprototype',
|
||
|
self::RESOURCE_HOUSEKEEPING => 'housekeeping',
|
||
|
self::RESOURCE_ICON_MAP => 'iconmap',
|
||
|
self::RESOURCE_IMAGE => 'image',
|
||
|
self::RESOURCE_ITEM => 'item',
|
||
|
self::RESOURCE_ITEM_PROTOTYPE => 'itemprototype',
|
||
|
self::RESOURCE_IT_SERVICE => 'service',
|
||
|
self::RESOURCE_MACRO => 'usermacro',
|
||
|
self::RESOURCE_MAINTENANCE => 'maintenance',
|
||
|
self::RESOURCE_MEDIA_TYPE => 'mediatype',
|
||
|
self::RESOURCE_MODULE => 'module',
|
||
|
self::RESOURCE_PROXY => 'proxy',
|
||
|
self::RESOURCE_REGEXP => 'regexp',
|
||
|
self::RESOURCE_SCHEDULED_REPORT => 'report',
|
||
|
self::RESOURCE_SCRIPT => 'script',
|
||
|
self::RESOURCE_SETTINGS => 'settings',
|
||
|
self::RESOURCE_SLA => 'sla',
|
||
|
self::RESOURCE_TEMPLATE => 'template',
|
||
|
self::RESOURCE_TEMPLATE_DASHBOARD => 'templatedashboard',
|
||
|
self::RESOURCE_TEMPLATE_GROUP => 'templategroup',
|
||
|
self::RESOURCE_USER => 'user',
|
||
|
self::RESOURCE_USERDIRECTORY => 'userdirectory',
|
||
|
self::RESOURCE_USER_GROUP => 'usergroup'
|
||
|
];
|
||
|
|
||
|
/**
|
||
|
* Array of abstract paths that should be masked in audit details.
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
private const MASKED_PATHS = [
|
||
|
self::RESOURCE_AUTH_TOKEN => ['paths' => ['token.token']],
|
||
|
self::RESOURCE_AUTOREGISTRATION => [
|
||
|
'paths' => ['autoregistration.tls_psk_identity', 'autoregistration.tls_psk']
|
||
|
],
|
||
|
self::RESOURCE_CONNECTOR => [
|
||
|
[
|
||
|
'paths' => ['connector.password'],
|
||
|
'conditions' => [
|
||
|
'authtype' => [ZBX_HTTP_AUTH_BASIC, ZBX_HTTP_AUTH_NTLM, ZBX_HTTP_AUTH_KERBEROS,
|
||
|
ZBX_HTTP_AUTH_DIGEST
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
'paths' => ['connector.token'],
|
||
|
'conditions' => ['authtype' => ZBX_HTTP_AUTH_BEARER]
|
||
|
],
|
||
|
['paths' => ['connector.ssl_key_password']]
|
||
|
],
|
||
|
self::RESOURCE_LLD_RULE => [
|
||
|
[
|
||
|
'paths' => ['discoveryrule.password'],
|
||
|
'conditions' => [
|
||
|
[
|
||
|
'type' => [ITEM_TYPE_SIMPLE, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
|
||
|
ITEM_TYPE_JMX
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
'type' => ITEM_TYPE_HTTPAGENT,
|
||
|
'authtype' => [ZBX_HTTP_AUTH_BASIC, ZBX_HTTP_AUTH_NTLM, ZBX_HTTP_AUTH_KERBEROS,
|
||
|
ZBX_HTTP_AUTH_DIGEST
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
['paths' => ['discoveryrule.ssl_key_password'], 'conditions' => ['type' => ITEM_TYPE_HTTPAGENT]]
|
||
|
],
|
||
|
self::RESOURCE_HOST_PROTOTYPE => [
|
||
|
'paths' => ['hostprototype.macros.value'],
|
||
|
'conditions' => ['type' => ZBX_MACRO_TYPE_SECRET]
|
||
|
],
|
||
|
self::RESOURCE_ITEM => [
|
||
|
[
|
||
|
'paths' => ['item.password'],
|
||
|
'conditions' => [
|
||
|
[
|
||
|
'type' => [ITEM_TYPE_SIMPLE, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
|
||
|
ITEM_TYPE_JMX
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
'type' => ITEM_TYPE_HTTPAGENT,
|
||
|
'authtype' => [ZBX_HTTP_AUTH_BASIC, ZBX_HTTP_AUTH_NTLM, ZBX_HTTP_AUTH_KERBEROS,
|
||
|
ZBX_HTTP_AUTH_DIGEST
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
['paths' => ['item.ssl_key_password'], 'conditions' => ['type' => ITEM_TYPE_HTTPAGENT]]
|
||
|
],
|
||
|
self::RESOURCE_ITEM_PROTOTYPE => [
|
||
|
[
|
||
|
'paths' => ['itemprototype.password'],
|
||
|
'conditions' => [
|
||
|
[
|
||
|
'type' => [ITEM_TYPE_SIMPLE, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
|
||
|
ITEM_TYPE_JMX
|
||
|
]
|
||
|
],
|
||
|
[
|
||
|
'type' => ITEM_TYPE_HTTPAGENT,
|
||
|
'authtype' => [ZBX_HTTP_AUTH_BASIC, ZBX_HTTP_AUTH_NTLM, ZBX_HTTP_AUTH_KERBEROS,
|
||
|
ZBX_HTTP_AUTH_DIGEST
|
||
|
]
|
||
|
]
|
||
|
]
|
||
|
],
|
||
|
['paths' => ['itemprototype.ssl_key_password'], 'conditions' => ['type' => ITEM_TYPE_HTTPAGENT]]
|
||
|
],
|
||
|
self::RESOURCE_MACRO => [
|
||
|
'paths' => ['usermacro.value'],
|
||
|
'conditions' => ['type' => ZBX_MACRO_TYPE_SECRET]
|
||
|
],
|
||
|
self::RESOURCE_MEDIA_TYPE => ['paths' => ['mediatype.passwd']],
|
||
|
self::RESOURCE_PROXY => ['paths' => ['proxy.tls_psk_identity', 'proxy.tls_psk']],
|
||
|
self::RESOURCE_SCRIPT => ['paths' => ['script.password']],
|
||
|
self::RESOURCE_TEMPLATE => [
|
||
|
'paths' => ['template.macros.value'],
|
||
|
'conditions' => ['type' => ZBX_MACRO_TYPE_SECRET]
|
||
|
],
|
||
|
self::RESOURCE_USER => ['paths' => ['user.passwd']],
|
||
|
self::RESOURCE_USERDIRECTORY => ['paths' => ['userdirectory.bind_password']]
|
||
|
];
|
||
|
|
||
|
/**
|
||
|
* Table names of nested objects to check default values.
|
||
|
* abstract path => table name
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
private const NESTED_OBJECTS_TABLE_NAMES = [
|
||
|
'action.filter' => 'actions',
|
||
|
'action.filter.conditions' => 'conditions',
|
||
|
'action.operations' => 'operations',
|
||
|
'action.operations.opconditions' => 'opconditions',
|
||
|
'action.operations.opmessage' => 'opmessage',
|
||
|
'action.operations.opmessage_grp' => 'opmessage_grp',
|
||
|
'action.operations.opmessage_usr' => 'opmessage_usr',
|
||
|
'action.operations.opcommand' => 'opcommand',
|
||
|
'action.operations.opcommand_grp' => 'opcommand_grp',
|
||
|
'action.operations.opcommand_hst' => 'opcommand_hst',
|
||
|
'action.operations.opgroup' => 'opgroup',
|
||
|
'action.operations.optemplate' => 'optemplate',
|
||
|
'action.operations.opinventory' => 'opinventory',
|
||
|
'action.operations.optag' => 'optag',
|
||
|
'action.recovery_operations' => 'operations',
|
||
|
'action.recovery_operations.opmessage' => 'opmessage',
|
||
|
'action.recovery_operations.opmessage_grp' => 'opmessage_grp',
|
||
|
'action.recovery_operations.opmessage_usr' => 'opmessage_usr',
|
||
|
'action.recovery_operations.opcommand' => 'opcommand',
|
||
|
'action.recovery_operations.opcommand_grp' => 'opcommand_grp',
|
||
|
'action.recovery_operations.opcommand_hst' => 'opcommand_hst',
|
||
|
'action.update_operations' => 'operations',
|
||
|
'action.update_operations.opmessage' => 'opmessage',
|
||
|
'action.update_operations.opmessage_grp' => 'opmessage_grp',
|
||
|
'action.update_operations.opmessage_usr' => 'opmessage_usr',
|
||
|
'action.update_operations.opcommand' => 'opcommand',
|
||
|
'action.update_operations.opcommand_grp' => 'opcommand_grp',
|
||
|
'action.update_operations.opcommand_hst' => 'opcommand_hst',
|
||
|
'connector.tags' => 'connector_tag',
|
||
|
'correlation.filter' => 'correlation',
|
||
|
'correlation.filter.conditions' => 'corr_condition',
|
||
|
'correlation.operations' => 'corr_operation',
|
||
|
'dashboard.users' => 'dashboard_user',
|
||
|
'dashboard.userGroups' => 'dashboard_usrgrp',
|
||
|
'dashboard.pages' => 'dashboard_page',
|
||
|
'dashboard.pages.widgets' => 'widget',
|
||
|
'dashboard.pages.widgets.fields' => 'widget_field',
|
||
|
'discoveryrule.filter' => 'items',
|
||
|
'discoveryrule.filter.conditions' => 'item_condition',
|
||
|
'discoveryrule.lld_macro_paths' => 'lld_macro_path',
|
||
|
'discoveryrule.overrides' => 'lld_override',
|
||
|
'discoveryrule.overrides.filter' => 'lld_override',
|
||
|
'discoveryrule.overrides.filter.conditions' => 'lld_override_condition',
|
||
|
'discoveryrule.overrides.operations' => 'lld_override_operation',
|
||
|
'discoveryrule.overrides.operations.opdiscover' => 'lld_override_opdiscover',
|
||
|
'discoveryrule.overrides.operations.ophistory' => 'lld_override_ophistory',
|
||
|
'discoveryrule.overrides.operations.opinventory' => 'lld_override_opinventory',
|
||
|
'discoveryrule.overrides.operations.opperiod' => 'lld_override_opperiod',
|
||
|
'discoveryrule.overrides.operations.opseverity' => 'lld_override_opseverity',
|
||
|
'discoveryrule.overrides.operations.opstatus' => 'lld_override_opstatus',
|
||
|
'discoveryrule.overrides.operations.optag' => 'lld_override_optag',
|
||
|
'discoveryrule.overrides.operations.optemplate' => 'lld_override_optemplate',
|
||
|
'discoveryrule.overrides.operations.optrends' => 'lld_override_optrends',
|
||
|
'discoveryrule.parameters' => 'item_parameter',
|
||
|
'discoveryrule.preprocessing' => 'item_preproc',
|
||
|
'hostgroup.hosts' => 'hosts_groups',
|
||
|
'hostprototype.groupLinks' => 'group_prototype',
|
||
|
'hostprototype.groupPrototypes' => 'group_prototype',
|
||
|
'hostprototype.interfaces' => 'interface',
|
||
|
'hostprototype.interfaces.details' => 'interface_snmp',
|
||
|
'hostprototype.macros' => 'hostmacro',
|
||
|
'hostprototype.tags' => 'host_tag',
|
||
|
'hostprototype.templates' => 'hosts_templates',
|
||
|
'iconmap.mappings' => 'icon_mapping',
|
||
|
'item.parameters' => 'item_parameter',
|
||
|
'item.preprocessing' => 'item_preproc',
|
||
|
'item.tags' => 'item_tag',
|
||
|
'itemprototype.parameters' => 'item_parameter',
|
||
|
'itemprototype.preprocessing' => 'item_preproc',
|
||
|
'itemprototype.tags' => 'item_tag',
|
||
|
'maintenance.groups' => 'maintenances_groups',
|
||
|
'maintenance.hosts' => 'maintenances_hosts',
|
||
|
'maintenance.tags' => 'maintenance_tag',
|
||
|
'maintenance.timeperiods' => 'timeperiods',
|
||
|
'mediatype.message_templates' => 'media_type_message',
|
||
|
'mediatype.parameters' => 'media_type_param',
|
||
|
'proxy.hosts' => 'hosts',
|
||
|
'proxy.interface' => 'interface',
|
||
|
'regexp.expressions' => 'expressions',
|
||
|
'report.users' => 'report_user',
|
||
|
'report.user_groups' => 'report_usrgrp',
|
||
|
'service.children' => 'services_links',
|
||
|
'service.parents' => 'services_links',
|
||
|
'service.problem_tags' => 'service_problem_tag',
|
||
|
'service.status_rules' => 'service_status_rule',
|
||
|
'service.tags' => 'service_tag',
|
||
|
'sla.service_tags' => 'sla_service_tag',
|
||
|
'sla.schedule' => 'sla_schedule',
|
||
|
'sla.excluded_downtimes' => 'sla_excluded_downtime',
|
||
|
'script.parameters' => 'script_param',
|
||
|
'template.groups' => 'hosts_groups',
|
||
|
'template.macros' => 'hostmacro',
|
||
|
'template.tags' => 'host_tag',
|
||
|
'template.templates' => 'hosts_templates',
|
||
|
'template.templates_clear' => 'hosts_templates',
|
||
|
'templatedashboard.pages' => 'dashboard_page',
|
||
|
'templatedashboard.pages.widgets' => 'widget',
|
||
|
'templatedashboard.pages.widgets.fields' => 'widget_field',
|
||
|
'templategroup.templates' => 'hosts_groups',
|
||
|
'user.medias' => 'media',
|
||
|
'user.usrgrps' => 'users_groups',
|
||
|
'userdirectory.provision_media' => 'userdirectory_media',
|
||
|
'userdirectory.provision_groups' => 'userdirectory_idpgroup',
|
||
|
'userdirectory.provision_groups.user_groups' => 'userdirectory_usrgrp',
|
||
|
'usergroup.hostgroup_rights' => 'rights',
|
||
|
'usergroup.templategroup_rights' => 'rights',
|
||
|
'usergroup.tag_filters' => 'tag_filter',
|
||
|
'usergroup.users' => 'users_groups'
|
||
|
];
|
||
|
|
||
|
/**
|
||
|
* ID field names of nested objects that stored in a parent object properties containing an array of nested objects.
|
||
|
* abstract path => id field name
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
private const NESTED_OBJECTS_ID_FIELD_NAMES = [
|
||
|
'action.filter.conditions' => 'conditionid',
|
||
|
'action.operations' => 'operationid',
|
||
|
'action.operations.opconditions' => 'opconditionid',
|
||
|
'action.operations.opmessage_grp' => 'opmessage_grpid',
|
||
|
'action.operations.opmessage_usr' => 'opmessage_usrid',
|
||
|
'action.operations.opcommand_grp' => 'opcommand_grpid',
|
||
|
'action.operations.opcommand_hst' => 'opcommand_hstid',
|
||
|
'action.operations.opgroup' => 'opgroupid',
|
||
|
'action.operations.optemplate' => 'optemplateid',
|
||
|
'action.operations.optag' => 'optagid',
|
||
|
'action.recovery_operations' => 'operationid',
|
||
|
'action.recovery_operations.opmessage_grp' => 'opmessage_grpid',
|
||
|
'action.recovery_operations.opmessage_usr' => 'opmessage_usrid',
|
||
|
'action.recovery_operations.opcommand_grp' => 'opcommand_grpid',
|
||
|
'action.recovery_operations.opcommand_hst' => 'opcommand_hstid',
|
||
|
'action.update_operations' => 'operationid',
|
||
|
'action.update_operations.opmessage_grp' => 'opmessage_grpid',
|
||
|
'action.update_operations.opmessage_usr' => 'opmessage_usrid',
|
||
|
'action.update_operations.opcommand_grp' => 'opcommand_grpid',
|
||
|
'action.update_operations.opcommand_hst' => 'opcommand_hstid',
|
||
|
'connector.tags' => 'connector_tagid',
|
||
|
'correlation.filter.conditions' => 'corr_conditionid',
|
||
|
'correlation.operations' => 'corr_operationid',
|
||
|
'dashboard.users' => 'dashboard_userid',
|
||
|
'dashboard.userGroups' => 'dashboard_usrgrpid',
|
||
|
'dashboard.pages' => 'dashboard_pageid',
|
||
|
'dashboard.pages.widgets' => 'widgetid',
|
||
|
'dashboard.pages.widgets.fields' => 'widget_fieldid',
|
||
|
'discoveryrule.filter.conditions' => 'item_conditionid',
|
||
|
'discoveryrule.lld_macro_paths' => 'lld_macro_pathid',
|
||
|
'discoveryrule.overrides' => 'lld_overrideid',
|
||
|
'discoveryrule.overrides.filter.conditions' => 'lld_override_conditionid',
|
||
|
'discoveryrule.overrides.operations' => 'lld_override_operationid',
|
||
|
'discoveryrule.overrides.operations.optag' => 'lld_override_optagid',
|
||
|
'discoveryrule.overrides.operations.optemplate' => 'lld_override_optemplateid',
|
||
|
'discoveryrule.parameters' => 'item_parameterid',
|
||
|
'discoveryrule.preprocessing' => 'item_preprocid',
|
||
|
'hostgroup.hosts' => 'hostgroupid',
|
||
|
'hostprototype.groupLinks' => 'group_prototypeid',
|
||
|
'hostprototype.groupPrototypes' => 'group_prototypeid',
|
||
|
'hostprototype.interfaces' => 'interfaceid',
|
||
|
'hostprototype.macros' => 'hostmacroid',
|
||
|
'hostprototype.tags' => 'hosttagid',
|
||
|
'hostprototype.templates' => 'hosttemplateid',
|
||
|
'iconmap.mappings' => 'iconmappingid',
|
||
|
'item.parameters' => 'item_parameterid',
|
||
|
'item.preprocessing' => 'item_preprocid',
|
||
|
'item.tags' => 'itemtagid',
|
||
|
'itemprototype.parameters' => 'item_parameterid',
|
||
|
'itemprototype.preprocessing' => 'item_preprocid',
|
||
|
'itemprototype.tags' => 'itemtagid',
|
||
|
'maintenance.groups' => 'maintenance_groupid',
|
||
|
'maintenance.hosts' => 'maintenance_hostid',
|
||
|
'maintenance.tags' => 'maintenancetagid',
|
||
|
'maintenance.timeperiods' => 'timeperiodid',
|
||
|
'mediatype.message_templates' => 'mediatype_messageid',
|
||
|
'mediatype.parameters' => 'mediatype_paramid',
|
||
|
'proxy.hosts' => 'hostid',
|
||
|
'regexp.expressions' => 'expressionid',
|
||
|
'report.users' => 'reportuserid',
|
||
|
'report.user_groups' => 'reportusrgrpid',
|
||
|
'script.parameters' => 'script_paramid',
|
||
|
'service.children' => 'linkid',
|
||
|
'service.parents' => 'linkid',
|
||
|
'service.problem_tags' => 'service_problem_tagid',
|
||
|
'service.status_rules' => 'service_status_ruleid',
|
||
|
'service.tags' => 'servicetagid',
|
||
|
'sla.service_tags' => 'sla_service_tagid',
|
||
|
'sla.schedule' => 'sla_scheduleid',
|
||
|
'sla.excluded_downtimes' => 'sla_excluded_downtimeid',
|
||
|
'template.groups' => 'hostgroupid',
|
||
|
'template.macros' => 'hostmacroid',
|
||
|
'template.tags' => 'hosttagid',
|
||
|
'template.templates' => 'hosttemplateid',
|
||
|
'template.templates_clear' => 'hosttemplateid',
|
||
|
'templatedashboard.pages' => 'dashboard_pageid',
|
||
|
'templatedashboard.pages.widgets' => 'widgetid',
|
||
|
'templatedashboard.pages.widgets.fields' => 'widget_fieldid',
|
||
|
'templategroup.templates' => 'hostgroupid',
|
||
|
'user.medias' => 'mediaid',
|
||
|
'user.usrgrps' => 'id',
|
||
|
'userdirectory.provision_media' => 'userdirectory_mediaid',
|
||
|
'userdirectory.provision_groups' => 'userdirectory_idpgroupid',
|
||
|
'userdirectory.provision_groups.user_groups' => 'userdirectory_usrgrpid',
|
||
|
'usergroup.hostgroup_rights' => 'rightid',
|
||
|
'usergroup.templategroup_rights' => 'rightid',
|
||
|
'usergroup.tag_filters' => 'tag_filterid',
|
||
|
'usergroup.users' => 'id'
|
||
|
];
|
||
|
|
||
|
/**
|
||
|
* Array of abstract paths that should be skipped in audit details.
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
private const SKIP_FIELDS = ['token.creator_userid', 'token.created_at'];
|
||
|
|
||
|
/**
|
||
|
* Array of abstract paths that contain blob fields.
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
private const BLOB_FIELDS = ['image.image'];
|
||
|
|
||
|
/**
|
||
|
* Array of abstract paths that can only contain a data to delete.
|
||
|
*/
|
||
|
private const DELETE_ONLY_FIELDS = ['template.templates_clear'];
|
||
|
|
||
|
/**
|
||
|
* Add audit records.
|
||
|
*
|
||
|
* @param string|null $userid
|
||
|
* @param string $ip
|
||
|
* @param string $username
|
||
|
* @param int $action CAudit::ACTION_*
|
||
|
* @param int $resource CAudit::RESOURCE_*
|
||
|
* @param array $objects
|
||
|
* @param array $db_objects
|
||
|
*/
|
||
|
public static function log(?string $userid, string $ip, string $username, int $action, int $resource,
|
||
|
array $objects, array $db_objects): void {
|
||
|
if (!self::isAuditEnabled() && ($resource != self::RESOURCE_SETTINGS
|
||
|
|| !array_key_exists(CSettingsHelper::AUDITLOG_ENABLED, current($objects)))) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$auditlog = [];
|
||
|
$clock = time();
|
||
|
$ip = substr($ip, 0, DB::getFieldLength('auditlog', 'ip'));
|
||
|
$recordsetid = self::getRecordSetId();
|
||
|
|
||
|
switch ($action) {
|
||
|
case self::ACTION_LOGOUT:
|
||
|
case self::ACTION_LOGIN_SUCCESS:
|
||
|
case self::ACTION_LOGIN_FAILED:
|
||
|
$auditlog[] = [
|
||
|
'userid' => $userid,
|
||
|
'username' => $username,
|
||
|
'clock' => $clock,
|
||
|
'ip' => $ip,
|
||
|
'action' => $action,
|
||
|
'resourcetype' => $resource,
|
||
|
'resourceid' => $userid,
|
||
|
'resourcename' => '',
|
||
|
'recordsetid' => $recordsetid,
|
||
|
'details' => ''
|
||
|
];
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
$table_key = array_key_exists($resource, self::ID_FIELD_NAMES)
|
||
|
? self::ID_FIELD_NAMES[$resource]
|
||
|
: DB::getPk(self::TABLE_NAMES[$resource]);
|
||
|
|
||
|
foreach ($objects as $object) {
|
||
|
$resourceid = $object[$table_key];
|
||
|
$db_object = ($action == self::ACTION_UPDATE) ? $db_objects[$resourceid] : [];
|
||
|
$resource_name = self::getResourceName($resource, $action, $object, $db_object);
|
||
|
|
||
|
$diff = self::handleObjectDiff($resource, $action, $object, $db_object);
|
||
|
|
||
|
if ($action == self::ACTION_UPDATE && count($diff) === 0) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$auditlog[] = [
|
||
|
'userid' => $userid,
|
||
|
'username' => $username,
|
||
|
'clock' => $clock,
|
||
|
'ip' => $ip,
|
||
|
'action' => $action,
|
||
|
'resourcetype' => $resource,
|
||
|
'resourceid' => $resourceid,
|
||
|
'resourcename' => $resource_name,
|
||
|
'recordsetid' => $recordsetid,
|
||
|
'details' => (count($diff) == 0) ? '' : json_encode($diff)
|
||
|
];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DB::insertBatch('auditlog', $auditlog);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return recordsetid. Generate recordsetid if it has not been generated yet.
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
private static function getRecordSetId(): string {
|
||
|
static $recordsetid = null;
|
||
|
|
||
|
if ($recordsetid === null) {
|
||
|
$recordsetid = CCuid::generate();
|
||
|
}
|
||
|
|
||
|
return $recordsetid;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check audit logging is enabled.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
private static function isAuditEnabled(): bool {
|
||
|
return CSettingsHelper::get(CSettingsHelper::AUDITLOG_ENABLED) == self::AUDITLOG_ENABLE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return resource name of logging object.
|
||
|
*
|
||
|
* @param int $resource
|
||
|
* @param int $action
|
||
|
* @param array $object
|
||
|
* @param array $db_object
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
private static function getResourceName(int $resource, int $action, array $object, array $db_object): string {
|
||
|
$field_name = self::FIELD_NAMES[$resource];
|
||
|
$resource_name = ($field_name !== null)
|
||
|
? (($action == self::ACTION_UPDATE)
|
||
|
? $db_object[$field_name]
|
||
|
: $object[$field_name])
|
||
|
: '';
|
||
|
|
||
|
if (mb_strlen($resource_name) > 255) {
|
||
|
$resource_name = mb_substr($resource_name, 0, 252).'...';
|
||
|
}
|
||
|
|
||
|
return $resource_name;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Prepares the details for audit log.
|
||
|
*
|
||
|
* @param int $resource
|
||
|
* @param int $action
|
||
|
* @param array $object
|
||
|
* @param array $db_object
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private static function handleObjectDiff(int $resource, int $action, array $object, array $db_object): array {
|
||
|
if (!in_array($action, [self::ACTION_ADD, self::ACTION_UPDATE])) {
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
$api_name = self::API_NAMES[$resource];
|
||
|
$details = self::convertKeysToPaths($api_name, $object);
|
||
|
|
||
|
switch ($action) {
|
||
|
case self::ACTION_ADD:
|
||
|
return self::handleAdd($resource, $details);
|
||
|
|
||
|
case self::ACTION_UPDATE:
|
||
|
$db_details = self::convertKeysToPaths($api_name,
|
||
|
self::intersectObjects($api_name, $db_object, $object)
|
||
|
);
|
||
|
|
||
|
return self::handleUpdate($resource, $details, $db_details);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Computes the intersection of $db_object and $object using keys for comparison.
|
||
|
* Recursively removes $db_object properties if they are not present in $object.
|
||
|
*
|
||
|
* @param string $path
|
||
|
* @param array $db_object
|
||
|
* @param array $object
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private static function intersectObjects(string $path, array $db_object, array $object): array {
|
||
|
foreach ($db_object as $db_key => &$db_value) {
|
||
|
if (is_string($db_key) && !array_key_exists($db_key, $object)) {
|
||
|
unset($db_object[$db_key]);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (!is_array($db_value)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$key = $db_key;
|
||
|
$subpath = $path.'.'.$db_key;
|
||
|
|
||
|
if (is_int($db_key)) {
|
||
|
$key = null;
|
||
|
|
||
|
$pk = self::NESTED_OBJECTS_ID_FIELD_NAMES[$path];
|
||
|
|
||
|
foreach ($object as $i => $nested_object) {
|
||
|
if (bccomp($nested_object[$pk], $db_key) == 0) {
|
||
|
$key = $i;
|
||
|
$subpath = $path;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($key === null) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$db_value = self::intersectObjects($subpath, $db_value, $object[$key]);
|
||
|
}
|
||
|
unset($db_value);
|
||
|
|
||
|
return $db_object;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks by path, whether the value of the object should be masked.
|
||
|
*
|
||
|
* @param int $resource
|
||
|
* @param string $path
|
||
|
* @param array $object
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
private static function isValueToMask(int $resource, string $path, array $object): bool {
|
||
|
if (!array_key_exists($resource, self::MASKED_PATHS)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$object_path = self::getLastObjectPath($path);
|
||
|
$abstract_path = self::getAbstractPath($path);
|
||
|
|
||
|
$rules = [];
|
||
|
|
||
|
if (array_key_exists('paths', self::MASKED_PATHS[$resource])) {
|
||
|
if (in_array($abstract_path, self::MASKED_PATHS[$resource]['paths'])) {
|
||
|
$rules = self::MASKED_PATHS[$resource];
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
foreach (self::MASKED_PATHS[$resource] as $_rules) {
|
||
|
if (in_array($abstract_path, $_rules['paths'])) {
|
||
|
$rules = $_rules;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!$rules) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!array_key_exists('conditions', $rules)) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
$or_conditions = $rules['conditions'];
|
||
|
|
||
|
if (!array_key_exists(0, $or_conditions)) {
|
||
|
$or_conditions = [$or_conditions];
|
||
|
}
|
||
|
|
||
|
foreach ($or_conditions as $and_conditions) {
|
||
|
$all_conditions = count($and_conditions);
|
||
|
$true_conditions = 0;
|
||
|
|
||
|
foreach ($and_conditions as $condition_key => $value) {
|
||
|
$condition_path = $object_path.'.'.$condition_key;
|
||
|
|
||
|
if (array_key_exists($condition_path, $object)) {
|
||
|
$values = is_array($value) ? $value : [$value];
|
||
|
|
||
|
if (in_array($object[$condition_path], $values)) {
|
||
|
$true_conditions++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($true_conditions == $all_conditions) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Converts the object properties to the one-dimensional array where the key is a path.
|
||
|
*
|
||
|
* @param string $path Path to object or to array of objects.
|
||
|
* @param array $object The object or array of objects to convert.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private static function convertKeysToPaths(string $path, array $object): array {
|
||
|
$result = [];
|
||
|
|
||
|
$is_field_of_another_object = strpos($path, '.') !== false && !preg_match('/\[[0-9]+\]$/', $path);
|
||
|
$is_array_of_objects = false;
|
||
|
|
||
|
if ($is_field_of_another_object) {
|
||
|
$abstract_path = self::getAbstractPath($path);
|
||
|
$is_array_of_objects = array_key_exists($abstract_path, self::NESTED_OBJECTS_ID_FIELD_NAMES);
|
||
|
|
||
|
if ($is_array_of_objects) {
|
||
|
$id_field_name = self::NESTED_OBJECTS_ID_FIELD_NAMES[$abstract_path];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($is_array_of_objects) {
|
||
|
$objects = $object;
|
||
|
|
||
|
foreach ($objects as $object) {
|
||
|
$path_to_object = $path.'['.$object[$id_field_name].']';
|
||
|
|
||
|
$result += self::convertKeysToPaths($path_to_object, $object);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
foreach ($object as $field => $value) {
|
||
|
$path_to_field = $path.'.'.$field;
|
||
|
|
||
|
if (in_array(self::getAbstractPath($path_to_field), self::SKIP_FIELDS)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (is_array($value)) {
|
||
|
$result += self::convertKeysToPaths($path_to_field, $value);
|
||
|
}
|
||
|
else {
|
||
|
$result[$path_to_field] = (string) $value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks by path, whether the value is equal to default value from the database schema.
|
||
|
*
|
||
|
* @param int $resource
|
||
|
* @param string $path
|
||
|
* @param string $value
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
private static function isDefaultValue(int $resource, string $path, string $value): bool {
|
||
|
$object_path = self::getLastObjectPath($path);
|
||
|
$table_name = self::TABLE_NAMES[$resource];
|
||
|
|
||
|
if ($object_path !== self::API_NAMES[$resource]) {
|
||
|
$table_name = self::NESTED_OBJECTS_TABLE_NAMES[self::getAbstractPath($object_path)];
|
||
|
}
|
||
|
|
||
|
$schema_fields = DB::getSchema($table_name)['fields'];
|
||
|
$field_name = substr($path, strrpos($path, '.') + 1);
|
||
|
|
||
|
if (!array_key_exists($field_name, $schema_fields)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ($schema_fields[$field_name]['type'] === DB::FIELD_TYPE_ID && $schema_fields[$field_name]['null']
|
||
|
&& $value == 0) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (!array_key_exists('default', $schema_fields[$field_name])) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return $value == $schema_fields[$field_name]['default'];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks whether a path is path to nested object property.
|
||
|
*
|
||
|
* @param string $path
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
private static function isNestedObjectProperty(string $path): bool {
|
||
|
return (count(explode('.', $path)) > 2);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the path to the parent property object from the passed path.
|
||
|
*
|
||
|
* @param string $path
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
private static function getLastObjectPath(string $path): string {
|
||
|
return substr($path, 0, strrpos($path, '.'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the abstract path (without indexes).
|
||
|
*
|
||
|
* @param string $path
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
private static function getAbstractPath(string $path): string {
|
||
|
if (strpos($path, '[') !== false) {
|
||
|
$path = preg_replace('/\[[0-9]+\]/', '', $path);
|
||
|
}
|
||
|
|
||
|
return $path;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the paths to nested object properties from the paths of passing object.
|
||
|
*
|
||
|
* @param array $object
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private static function getNestedObjectsPaths(array $object): array {
|
||
|
$paths = [];
|
||
|
|
||
|
foreach ($object as $path => $foo) {
|
||
|
if (!self::isNestedObjectProperty($path)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$object_path = self::getLastObjectPath($path);
|
||
|
|
||
|
if (!in_array($object_path, $paths)) {
|
||
|
$paths[] = $object_path;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $paths;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Prepares the audit details for add action.
|
||
|
*
|
||
|
* @param int $resource
|
||
|
* @param array $object
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private static function handleAdd(int $resource, array $object): array {
|
||
|
$result = [];
|
||
|
|
||
|
foreach ($object as $path => $value) {
|
||
|
if (self::isNestedObjectProperty($path)) {
|
||
|
$result[self::getLastObjectPath($path)] = [self::DETAILS_ACTION_ADD];
|
||
|
}
|
||
|
|
||
|
if (self::isValueToMask($resource, $path, $object)) {
|
||
|
$result[$path] = [self::DETAILS_ACTION_ADD, ZBX_SECRET_MASK];
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (self::isDefaultValue($resource, $path, $value)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (in_array(self::getAbstractPath($path), self::BLOB_FIELDS)) {
|
||
|
$result[$path] = [self::DETAILS_ACTION_ADD];
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$result[$path] = [self::DETAILS_ACTION_ADD, $value];
|
||
|
}
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Prepares the audit details for update action.
|
||
|
*
|
||
|
* @param int $resource
|
||
|
* @param array $object
|
||
|
* @param array $db_object
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private static function handleUpdate(int $resource, array $object, array $db_object): array {
|
||
|
$result = [];
|
||
|
$nested_objects_paths = self::getNestedObjectsPaths($object);
|
||
|
$db_nested_objects_paths = self::getNestedObjectsPaths($db_object);
|
||
|
|
||
|
foreach ($db_nested_objects_paths as $path) {
|
||
|
if (!in_array($path, $nested_objects_paths)) {
|
||
|
$result[$path] = [self::DETAILS_ACTION_DELETE];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach ($nested_objects_paths as $path) {
|
||
|
if (!in_array($path, $db_nested_objects_paths)) {
|
||
|
if (in_array(self::getAbstractPath($path), self::DELETE_ONLY_FIELDS)) {
|
||
|
$result[$path] = [self::DETAILS_ACTION_DELETE];
|
||
|
|
||
|
foreach ($object as $object_path => $value) {
|
||
|
if (substr($object_path, 0, strlen($path)) === $path) {
|
||
|
unset($object[$object_path]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
$result[$path] = [self::DETAILS_ACTION_ADD];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach ($object as $path => $value) {
|
||
|
$is_value_to_mask = self::isValueToMask($resource, $path, $object);
|
||
|
$db_value = array_key_exists($path, $db_object) ? $db_object[$path] : null;
|
||
|
|
||
|
if ($db_value === null) {
|
||
|
$is_value_to_mask = self::isValueToMask($resource, $path, $object);
|
||
|
|
||
|
if ($is_value_to_mask) {
|
||
|
$result[$path] = [self::DETAILS_ACTION_ADD, ZBX_SECRET_MASK];
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (self::isDefaultValue($resource, $path, $value)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (in_array(self::getAbstractPath($path), self::BLOB_FIELDS)) {
|
||
|
$result[$path] = [self::DETAILS_ACTION_ADD];
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$result[$path] = [self::DETAILS_ACTION_ADD, $value];
|
||
|
}
|
||
|
else {
|
||
|
$is_db_value_to_mask = self::isValueToMask($resource, $path, $db_object);
|
||
|
|
||
|
if ($value != $db_value || $is_value_to_mask || $is_db_value_to_mask) {
|
||
|
if (self::isNestedObjectProperty($path)) {
|
||
|
$result[self::getLastObjectPath($path)] = [self::DETAILS_ACTION_UPDATE];
|
||
|
}
|
||
|
|
||
|
if (in_array(self::getAbstractPath($path), self::BLOB_FIELDS)) {
|
||
|
$result[$path] = [self::DETAILS_ACTION_UPDATE];
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$result[$path] = [
|
||
|
self::DETAILS_ACTION_UPDATE,
|
||
|
$is_value_to_mask ? ZBX_SECRET_MASK : $value,
|
||
|
$is_db_value_to_mask ? ZBX_SECRET_MASK : $db_value
|
||
|
];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
}
|