1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (at your option) any later version. 10** 11** This program is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22/** 23 * Class containing methods for operations with auditlog records. 24 */ 25class CAuditLog extends CApiService { 26 27 public const ACCESS_RULES = [ 28 'get' => ['min_user_type' => USER_TYPE_SUPER_ADMIN] 29 ]; 30 31 /** 32 * @var string Database table name. 33 */ 34 protected $tableName = 'auditlog'; 35 36 /** 37 * @var string Database table name alias. 38 */ 39 protected $tableAlias = 'a'; 40 41 /** 42 * @var array Database fields list allowed for sort operation. 43 */ 44 protected $sortColumns = ['auditid', 'userid', 'clock']; 45 46 /** 47 * @var array Database table with auditlog details supported fields list. 48 */ 49 protected $details_fields = ['table_name', 'field_name', 'oldvalue', 'newvalue']; 50 51 /** 52 * Method auditlog.get, returns audit log records according filtering criteria. 53 * 54 * @param array $options Array of API request options. 55 * @param int|array $options['auditids'] Filter by auditids. 56 * @param int|array $options['userids'] Filter by userids. 57 * @param int $options['time_from'] Filter by timestamp, range start time, inclusive. 58 * @param int $options['time_till'] Filter by timestamp, range end time, inclusive. 59 * @param string|array $options['selectDetails'] Select additional details from auditlog_details. 60 * @param string $options['sortfield'] Sorting field: auditid, userid, clock. 61 * @param string $options['sortorder'] Sorting direction. 62 * @param array $options['filter'] Filter by fields value, exact match. 63 * @param array $options['search'] Filter by fields value, case insensitive search of substring. 64 * @param bool $options['countOutput'] 65 * @param bool $options['excludeSearch'] 66 * @param int $options['limit'] 67 * @param string|array $options['output'] 68 * @param bool $options['preservekeys'] 69 * @param bool $options['searchByAny'] 70 * @param bool $options['searchWildcardsEnabled'] 71 * @param bool $options['startSearch'] 72 * 73 * @throws APIException 74 * 75 * @return array|int 76 */ 77 public function get(array $options) { 78 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) { 79 self::exception(ZBX_API_ERROR_PERMISSIONS, _('You do not have permission to perform this operation.')); 80 } 81 82 $result = []; 83 $fields = array_keys($this->getTableSchema($this->tableName())['fields']); 84 $actions = [ 85 AUDIT_ACTION_ADD, AUDIT_ACTION_UPDATE, AUDIT_ACTION_DELETE, AUDIT_ACTION_LOGIN, AUDIT_ACTION_LOGOUT, 86 AUDIT_ACTION_ENABLE, AUDIT_ACTION_DISABLE, AUDIT_ACTION_EXECUTE 87 ]; 88 $resourcetype = [ 89 AUDIT_RESOURCE_USER, AUDIT_RESOURCE_ZABBIX_CONFIG, AUDIT_RESOURCE_MEDIA_TYPE, AUDIT_RESOURCE_HOST, 90 AUDIT_RESOURCE_ACTION, AUDIT_RESOURCE_GRAPH, AUDIT_RESOURCE_GRAPH_ELEMENT, AUDIT_RESOURCE_USER_GROUP, 91 AUDIT_RESOURCE_TRIGGER, AUDIT_RESOURCE_HOST_GROUP, AUDIT_RESOURCE_ITEM, 92 AUDIT_RESOURCE_IMAGE, AUDIT_RESOURCE_VALUE_MAP, AUDIT_RESOURCE_IT_SERVICE, AUDIT_RESOURCE_MAP, 93 AUDIT_RESOURCE_SCENARIO, AUDIT_RESOURCE_DISCOVERY_RULE, AUDIT_RESOURCE_SCRIPT, AUDIT_RESOURCE_PROXY, 94 AUDIT_RESOURCE_MAINTENANCE, AUDIT_RESOURCE_REGEXP, AUDIT_RESOURCE_MACRO, AUDIT_RESOURCE_TEMPLATE, 95 AUDIT_RESOURCE_TRIGGER_PROTOTYPE, AUDIT_RESOURCE_ICON_MAP, AUDIT_RESOURCE_DASHBOARD, 96 AUDIT_RESOURCE_CORRELATION, AUDIT_RESOURCE_GRAPH_PROTOTYPE, AUDIT_RESOURCE_ITEM_PROTOTYPE, 97 AUDIT_RESOURCE_HOST_PROTOTYPE, AUDIT_RESOURCE_AUTOREGISTRATION, AUDIT_RESOURCE_MODULE, 98 AUDIT_RESOURCE_SETTINGS, AUDIT_RESOURCE_HOUSEKEEPING, AUDIT_RESOURCE_AUTHENTICATION, 99 AUDIT_RESOURCE_TEMPLATE_DASHBOARD, AUDIT_RESOURCE_AUTH_TOKEN, AUDIT_RESOURCE_SCHEDULED_REPORT 100 ]; 101 102 $api_input_rules = ['type' => API_OBJECT, 'fields' => [ 103 // filter 104 'auditids' => ['type' => API_IDS, 'flags' => API_ALLOW_NULL | API_NORMALIZE, 'default' => null], 105 'userids' => ['type' => API_IDS, 'flags' => API_ALLOW_NULL | API_NORMALIZE, 'default' => null], 106 'filter' => ['type' => API_OBJECT, 'flags' => API_ALLOW_NULL, 'default' => null, 'fields' => [ 107 'auditid' => ['type' => API_IDS, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 108 'userid' => ['type' => API_IDS, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 109 'clock' => ['type' => API_INTS32, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 110 'action' => ['type' => API_INTS32, 'flags' => API_ALLOW_NULL | API_NORMALIZE, 'in' => implode(',', $actions)], 111 'resourcetype' => ['type' => API_INTS32, 'flags' => API_ALLOW_NULL | API_NORMALIZE, 'in' => implode(',', $resourcetype)], 112 'note' => ['type' => API_STRINGS_UTF8, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 113 'ip' => ['type' => API_STRINGS_UTF8, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 114 'resourceid' => ['type' => API_IDS, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 115 'resourcename' => ['type' => API_STRINGS_UTF8, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 116 'table_name' => ['type' => API_STRINGS_UTF8, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 117 'field_name' => ['type' => API_STRINGS_UTF8, 'flags' => API_ALLOW_NULL | API_NORMALIZE] 118 ]], 119 'search' => ['type' => API_OBJECT, 'flags' => API_ALLOW_NULL, 'default' => null, 'fields' => [ 120 'note' => ['type' => API_STRINGS_UTF8, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 121 'ip' => ['type' => API_STRINGS_UTF8, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 122 'resourcename' => ['type' => API_STRINGS_UTF8, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 123 'oldvalue' => ['type' => API_STRINGS_UTF8, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 124 'newvalue' => ['type' => API_STRINGS_UTF8, 'flags' => API_ALLOW_NULL | API_NORMALIZE] 125 ]], 126 'time_from' => ['type' => API_INT32, 'flags' => API_ALLOW_NULL, 'default' => null], 127 'time_till' => ['type' => API_INT32, 'flags' => API_ALLOW_NULL, 'default' => null], 128 'selectDetails' => ['type' => API_OUTPUT, 'flags' => API_ALLOW_NULL, 'in' => implode(',', $this->details_fields), 'default' => null], 129 'searchByAny' => ['type' => API_BOOLEAN, 'default' => false], 130 'startSearch' => ['type' => API_FLAG, 'default' => false], 131 'excludeSearch' => ['type' => API_FLAG, 'default' => false], 132 'searchWildcardsEnabled' => ['type' => API_BOOLEAN, 'default' => false], 133 // output 134 'output' => ['type' => API_OUTPUT, 'in' => implode(',', $fields), 'default' => $fields], 135 'countOutput' => ['type' => API_FLAG, 'default' => false], 136 // sort and limit 137 'sortfield' => ['type' => API_STRINGS_UTF8, 'flags' => API_NORMALIZE, 'in' => implode(',', $this->sortColumns), 'uniq' => true, 'default' => []], 138 'sortorder' => ['type' => API_SORTORDER, 'default' => []], 139 'limit' => ['type' => API_INT32, 'flags' => API_ALLOW_NULL, 'in' => '1:'.ZBX_MAX_INT32, 'default' => null], 140 // flags 141 'preservekeys' => ['type' => API_BOOLEAN, 'default' => false] 142 ]]; 143 144 if (!CApiInputValidator::validate($api_input_rules, $options, '/', $error)) { 145 self::exception(ZBX_API_ERROR_PARAMETERS, $error); 146 } 147 148 $sql_parts = [ 149 'select' => ['auditlog' => 'a.auditid'], 150 'from' => ['auditlog' => 'auditlog a'], 151 'where' => [], 152 'order' => [], 153 'limit' => null 154 ]; 155 156 if ($options['output'] === API_OUTPUT_EXTEND) { 157 $options['output'] = $fields; 158 } 159 160 if ($options['userids'] !== null) { 161 $sql_parts['where']['userid'] = dbConditionId('a.userid', $options['userids']); 162 } 163 164 if ($options['time_from'] !== null) { 165 $sql_parts['where'][] = 'a.clock>='.zbx_dbstr($options['time_from']); 166 } 167 168 if ($options['time_till'] !== null) { 169 $sql_parts['where'][] = 'a.clock<='.zbx_dbstr($options['time_till']); 170 } 171 172 $sql_parts = $this->applyQueryFilterOptions($this->tableName, $this->tableAlias, $options, $sql_parts); 173 $sql_parts = $this->applyQueryOutputOptions($this->tableName, $this->tableAlias, $options, $sql_parts); 174 $sql_parts = $this->applyQuerySortOptions($this->tableName, $this->tableAlias, $options, $sql_parts); 175 $res = DBselect(self::createSelectQueryFromParts($sql_parts), $options['limit']); 176 177 while ($audit = DBfetch($res)) { 178 if (!$options['countOutput']) { 179 $result[$audit['auditid']] = $audit; 180 continue; 181 } 182 183 $result = $audit['rowscount']; 184 } 185 186 if ($options['countOutput']) { 187 return $result; 188 } 189 190 if ($result && $options['selectDetails'] !== null) { 191 $result = $this->addRelatedObjects($options, $result); 192 } 193 194 if (!$options['preservekeys']) { 195 $result = array_values($result); 196 } 197 198 return $this->unsetExtraFields($result, ['auditid'], $options['output']); 199 } 200 201 /** 202 * Add related objects from auditlog_details table if requested. 203 * 204 * @param array $options Array of API request options. 205 * @param array $result Associative array of selected auditlog data, key is auditid property. 206 * 207 * @return array 208 */ 209 protected function addRelatedObjects(array $options, array $result): array { 210 $fields = []; 211 212 foreach ($this->details_fields as $field) { 213 if ($this->outputIsRequested($field, $options['selectDetails'])) { 214 $fields[] = $field; 215 } 216 }; 217 218 foreach ($result as &$row) { 219 $row['details'] = []; 220 } 221 unset($row); 222 223 if ($fields) { 224 $relation_fields = ['auditid', 'auditdetailid']; 225 $auditlog_details = API::getApiService()->select('auditlog_details', [ 226 'output' => array_merge($fields, $relation_fields), 227 'filter' => ['auditid' => array_keys($result)], 228 'preservekeys' => true 229 ]); 230 231 $relation_map = $this->createRelationMap($auditlog_details, 'auditid', 'auditdetailid'); 232 $auditlog_details = $this->unsetExtraFields($auditlog_details, $relation_fields, []); 233 $result = $relation_map->mapMany($result, $auditlog_details, 'details'); 234 } 235 236 return $result; 237 } 238 239 /** 240 * Apply filter and search options to $sql_parts query. Also add auditlog_details alias if filter or search requires 241 * field from auditlog_details table. 242 * 243 * @param string $table Table name. 244 * @param string $alias Table alias. 245 * @param array $options Request options. 246 * @param array $sql_parts Array of SQL query parts to be modified. 247 * 248 * @return array 249 */ 250 protected function applyQueryFilterOptions($table, $alias, array $options, array $sql_parts): array { 251 $filter = ($options['filter'] !== null) 252 ? array_intersect_key($options['filter'], array_flip($this->details_fields)) 253 : []; 254 $search = ($options['search'] !== null) 255 ? array_intersect_key($options['search'], array_flip(['oldvalue', 'newvalue'])) 256 : []; 257 258 if ($filter || $search) { 259 $details_options = ['filter' => $filter, 'search' => $search] + $options; 260 $sql_parts['where']['aad'] = 'a.auditid=ad.auditid'; 261 $sql_parts['from']['auditlog_details'] = 'auditlog_details ad'; 262 $sql_parts = parent::applyQueryFilterOptions('auditlog_details', 'ad', $details_options, $sql_parts); 263 } 264 265 return parent::applyQueryFilterOptions($table, $alias, $options, $sql_parts); 266 } 267} 268