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 user groups. 24 */ 25class CUserGroup extends CApiService { 26 27 public const ACCESS_RULES = [ 28 'get' => ['min_user_type' => USER_TYPE_ZABBIX_USER], 29 'create' => ['min_user_type' => USER_TYPE_SUPER_ADMIN], 30 'update' => ['min_user_type' => USER_TYPE_SUPER_ADMIN], 31 'delete' => ['min_user_type' => USER_TYPE_SUPER_ADMIN] 32 ]; 33 34 protected $tableName = 'usrgrp'; 35 protected $tableAlias = 'g'; 36 protected $sortColumns = ['usrgrpid', 'name']; 37 38 /** 39 * Get user groups. 40 * 41 * @param array $options 42 * @param array $options['usrgrpids'] 43 * @param array $options['userids'] 44 * @param bool $options['status'] 45 * @param bool $options['selectUsers'] 46 * @param int $options['count'] 47 * @param string $options['pattern'] 48 * @param int $options['limit'] 49 * @param string $options['order'] 50 * 51 * @return array 52 */ 53 public function get($options = []) { 54 $result = []; 55 56 $sqlParts = [ 57 'select' => ['usrgrp' => 'g.usrgrpid'], 58 'from' => ['usrgrp' => 'usrgrp g'], 59 'where' => [], 60 'order' => [], 61 'limit' => null 62 ]; 63 64 $defOptions = [ 65 'usrgrpids' => null, 66 'userids' => null, 67 'status' => null, 68 // filter 69 'filter' => null, 70 'search' => null, 71 'searchByAny' => null, 72 'startSearch' => false, 73 'excludeSearch' => false, 74 'searchWildcardsEnabled' => null, 75 // output 76 'editable' => false, 77 'output' => API_OUTPUT_EXTEND, 78 'selectUsers' => null, 79 'selectRights' => null, 80 'selectTagFilters' => null, 81 'countOutput' => false, 82 'preservekeys' => false, 83 'sortfield' => '', 84 'sortorder' => '', 85 'limit' => null 86 ]; 87 88 $options = zbx_array_merge($defOptions, $options); 89 90 // permissions 91 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) { 92 if (!$options['editable']) { 93 $sqlParts['where'][] = 'g.usrgrpid IN ('. 94 'SELECT uug.usrgrpid'. 95 ' FROM users_groups uug'. 96 ' WHERE uug.userid='.self::$userData['userid']. 97 ')'; 98 } 99 else { 100 return []; 101 } 102 } 103 104 // usrgrpids 105 if (!is_null($options['usrgrpids'])) { 106 zbx_value2array($options['usrgrpids']); 107 108 $sqlParts['where'][] = dbConditionInt('g.usrgrpid', $options['usrgrpids']); 109 } 110 111 // userids 112 if (!is_null($options['userids'])) { 113 zbx_value2array($options['userids']); 114 115 $sqlParts['from']['users_groups'] = 'users_groups ug'; 116 $sqlParts['where'][] = dbConditionInt('ug.userid', $options['userids']); 117 $sqlParts['where']['gug'] = 'g.usrgrpid=ug.usrgrpid'; 118 } 119 120 // status 121 if (!is_null($options['status'])) { 122 $sqlParts['where'][] = 'g.users_status='.zbx_dbstr($options['status']); 123 } 124 125 // filter 126 if (is_array($options['filter'])) { 127 $this->dbFilter('usrgrp g', $options, $sqlParts); 128 } 129 130 // search 131 if (is_array($options['search'])) { 132 zbx_db_search('usrgrp g', $options, $sqlParts); 133 } 134 135 // limit 136 if (zbx_ctype_digit($options['limit']) && $options['limit']) { 137 $sqlParts['limit'] = $options['limit']; 138 } 139 140 $sqlParts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); 141 $sqlParts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); 142 $res = DBselect(self::createSelectQueryFromParts($sqlParts), $sqlParts['limit']); 143 while ($usrgrp = DBfetch($res)) { 144 if ($options['countOutput']) { 145 $result = $usrgrp['rowscount']; 146 } 147 else { 148 $result[$usrgrp['usrgrpid']] = $usrgrp; 149 } 150 } 151 152 if ($options['countOutput']) { 153 return $result; 154 } 155 156 if ($result) { 157 $result = $this->addRelatedObjects($options, $result); 158 } 159 160 // removing keys (hash -> array) 161 if (!$options['preservekeys']) { 162 $result = zbx_cleanHashes($result); 163 } 164 165 return $result; 166 } 167 168 /** 169 * @param array $usrgrps 170 * 171 * @return array 172 */ 173 public function create(array $usrgrps) { 174 $this->validateCreate($usrgrps); 175 176 $ins_usrgrps = []; 177 178 foreach ($usrgrps as $usrgrp) { 179 unset($usrgrp['rights'], $usrgrp['userids']); 180 $ins_usrgrps[] = $usrgrp; 181 } 182 $usrgrpids = DB::insert('usrgrp', $ins_usrgrps); 183 184 foreach ($usrgrps as $index => &$usrgrp) { 185 $usrgrp['usrgrpid'] = $usrgrpids[$index]; 186 } 187 unset($usrgrp); 188 189 $this->updateRights($usrgrps, __FUNCTION__); 190 $this->updateTagFilters($usrgrps, __FUNCTION__); 191 $this->updateUsersGroups($usrgrps, __FUNCTION__); 192 193 $this->addAuditBulk(AUDIT_ACTION_ADD, AUDIT_RESOURCE_USER_GROUP, $usrgrps); 194 195 return ['usrgrpids' => $usrgrpids]; 196 } 197 198 /** 199 * @param array $usrgrps 200 * 201 * @throws APIException if the input is invalid. 202 */ 203 private function validateCreate(array &$usrgrps) { 204 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) { 205 self::exception(ZBX_API_ERROR_PERMISSIONS, _('Only Super Admins can create user groups.')); 206 } 207 208 $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['name']], 'fields' => [ 209 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('usrgrp', 'name')], 210 'debug_mode' => ['type' => API_INT32, 'in' => implode(',', [GROUP_DEBUG_MODE_DISABLED, GROUP_DEBUG_MODE_ENABLED])], 211 'gui_access' => ['type' => API_INT32, 'in' => implode(',', [GROUP_GUI_ACCESS_SYSTEM, GROUP_GUI_ACCESS_INTERNAL, GROUP_GUI_ACCESS_LDAP, GROUP_GUI_ACCESS_DISABLED])], 212 'users_status' => ['type' => API_INT32, 'in' => implode(',', [GROUP_STATUS_ENABLED, GROUP_STATUS_DISABLED])], 213 'rights' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['id']], 'fields' => [ 214 'id' => ['type' => API_ID, 'flags' => API_REQUIRED], 215 'permission' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [PERM_DENY, PERM_READ, PERM_READ_WRITE])] 216 ]], 217 'tag_filters' => ['type' => API_OBJECTS, 'uniq' => [['groupid', 'tag', 'value']], 'fields' => [ 218 'groupid' => ['type' => API_ID, 'flags' => API_REQUIRED], 219 'tag' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('tag_filter', 'tag'), 'default' => DB::getDefault('tag_filter', 'tag')], 220 'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('tag_filter', 'value'), 'default' => DB::getDefault('tag_filter', 'value')] 221 ]], 222 'userids' => ['type' => API_IDS, 'flags' => API_NORMALIZE] 223 ]]; 224 if (!CApiInputValidator::validate($api_input_rules, $usrgrps, '/', $error)) { 225 self::exception(ZBX_API_ERROR_PARAMETERS, $error); 226 } 227 228 $this->checkDuplicates(zbx_objectValues($usrgrps, 'name')); 229 $this->checkUsers($usrgrps); 230 $this->checkHimself($usrgrps, __FUNCTION__); 231 $this->checkHostGroups($usrgrps); 232 $this->checkTagFilters($usrgrps); 233 } 234 235 /** 236 * @param array $usrgrps 237 * 238 * @return array 239 */ 240 public function update($usrgrps) { 241 $this->validateUpdate($usrgrps, $db_usrgrps); 242 243 $upd_usrgrps = []; 244 245 foreach ($usrgrps as $usrgrp) { 246 $db_usrgrp = $db_usrgrps[$usrgrp['usrgrpid']]; 247 248 $upd_usrgrp = []; 249 250 if (array_key_exists('name', $usrgrp) && $usrgrp['name'] !== $db_usrgrp['name']) { 251 $upd_usrgrp['name'] = $usrgrp['name']; 252 } 253 if (array_key_exists('debug_mode', $usrgrp) && $usrgrp['debug_mode'] != $db_usrgrp['debug_mode']) { 254 $upd_usrgrp['debug_mode'] = $usrgrp['debug_mode']; 255 } 256 if (array_key_exists('gui_access', $usrgrp) && $usrgrp['gui_access'] != $db_usrgrp['gui_access']) { 257 $upd_usrgrp['gui_access'] = $usrgrp['gui_access']; 258 } 259 if (array_key_exists('users_status', $usrgrp) && $usrgrp['users_status'] != $db_usrgrp['users_status']) { 260 $upd_usrgrp['users_status'] = $usrgrp['users_status']; 261 } 262 263 if ($upd_usrgrp) { 264 $upd_usrgrps[] = [ 265 'values' => $upd_usrgrp, 266 'where' => ['usrgrpid' => $usrgrp['usrgrpid']] 267 ]; 268 } 269 } 270 271 if ($upd_usrgrps) { 272 DB::update('usrgrp', $upd_usrgrps); 273 } 274 275 $this->updateRights($usrgrps, __FUNCTION__); 276 $this->updateTagFilters($usrgrps, __FUNCTION__); 277 $this->updateUsersGroups($usrgrps, __FUNCTION__); 278 279 $this->addAuditBulk(AUDIT_ACTION_UPDATE, AUDIT_RESOURCE_USER_GROUP, $usrgrps, $db_usrgrps); 280 281 return ['usrgrpids'=> zbx_objectValues($usrgrps, 'usrgrpid')]; 282 } 283 284 /** 285 * @param array $usrgrps 286 * @param array $db_usrgrps 287 * 288 * @throws APIException if the input is invalid. 289 */ 290 private function validateUpdate(array &$usrgrps, array &$db_usrgrps = null) { 291 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) { 292 self::exception(ZBX_API_ERROR_PERMISSIONS, _('Only Super Admins can update user groups.')); 293 } 294 295 $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['usrgrpid'], ['name']], 'fields' => [ 296 'usrgrpid' => ['type' => API_ID, 'flags' => API_REQUIRED], 297 'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('usrgrp', 'name')], 298 'debug_mode' => ['type' => API_INT32, 'in' => implode(',', [GROUP_DEBUG_MODE_DISABLED, GROUP_DEBUG_MODE_ENABLED])], 299 'gui_access' => ['type' => API_INT32, 'in' => implode(',', [GROUP_GUI_ACCESS_SYSTEM, GROUP_GUI_ACCESS_INTERNAL, GROUP_GUI_ACCESS_LDAP, GROUP_GUI_ACCESS_DISABLED])], 300 'users_status' => ['type' => API_INT32, 'in' => implode(',', [GROUP_STATUS_ENABLED, GROUP_STATUS_DISABLED])], 301 'rights' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['id']], 'fields' => [ 302 'id' => ['type' => API_ID, 'flags' => API_REQUIRED], 303 'permission' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [PERM_DENY, PERM_READ, PERM_READ_WRITE])] 304 ]], 305 'tag_filters' => ['type' => API_OBJECTS, 'uniq' => [['groupid', 'tag', 'value']], 'fields' => [ 306 'groupid' => ['type' => API_ID, 'flags' => API_REQUIRED], 307 'tag' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('tag_filter', 'tag'), 'default' => DB::getDefault('tag_filter', 'tag')], 308 'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('tag_filter', 'value'), 'default' => DB::getDefault('tag_filter', 'value')] 309 ]], 310 'userids' => ['type' => API_IDS, 'flags' => API_NORMALIZE] 311 ]]; 312 if (!CApiInputValidator::validate($api_input_rules, $usrgrps, '/', $error)) { 313 self::exception(ZBX_API_ERROR_PARAMETERS, $error); 314 } 315 316 // Check user group names. 317 $db_usrgrps = DB::select('usrgrp', [ 318 'output' => ['usrgrpid', 'name', 'debug_mode', 'gui_access', 'users_status'], 319 'usrgrpids' => zbx_objectValues($usrgrps, 'usrgrpid'), 320 'preservekeys' => true 321 ]); 322 323 $names = []; 324 325 foreach ($usrgrps as $usrgrp) { 326 // Check if this user group exists. 327 if (!array_key_exists($usrgrp['usrgrpid'], $db_usrgrps)) { 328 self::exception(ZBX_API_ERROR_PERMISSIONS, 329 _('No permissions to referred object or it does not exist!') 330 ); 331 } 332 333 $db_usrgrp = $db_usrgrps[$usrgrp['usrgrpid']]; 334 335 if (array_key_exists('name', $usrgrp) && $usrgrp['name'] !== $db_usrgrp['name']) { 336 $names[] = $usrgrp['name']; 337 } 338 } 339 340 if ($names) { 341 $this->checkDuplicates($names); 342 } 343 $this->checkUsers($usrgrps); 344 $this->checkHimself($usrgrps, __FUNCTION__, $db_usrgrps); 345 $this->checkUsersWithoutGroups($usrgrps); 346 $this->checkHostGroups($usrgrps); 347 $this->checkTagFilters($usrgrps); 348 } 349 350 /** 351 * Check for duplicated user groups. 352 * 353 * @param array $names 354 * 355 * @throws APIException if user group already exists. 356 */ 357 private function checkDuplicates(array $names) { 358 $db_usrgrps = DB::select('usrgrp', [ 359 'output' => ['name'], 360 'filter' => ['name' => $names], 361 'limit' => 1 362 ]); 363 364 if ($db_usrgrps) { 365 self::exception(ZBX_API_ERROR_PARAMETERS, _s('User group "%1$s" already exists.', $db_usrgrps[0]['name'])); 366 } 367 } 368 369 /** 370 * Check for valid users. 371 * 372 * @param array $usrgrps 373 * @param array $usrgrps[]['userids'] (optional) 374 * 375 * @throws APIException 376 */ 377 private function checkUsers(array $usrgrps) { 378 $userids = []; 379 380 foreach ($usrgrps as $usrgrp) { 381 if (array_key_exists('userids', $usrgrp)) { 382 foreach ($usrgrp['userids'] as $userid) { 383 $userids[$userid] = true; 384 } 385 } 386 } 387 388 if (!$userids) { 389 return; 390 } 391 392 $userids = array_keys($userids); 393 394 $db_users = DB::select('users', [ 395 'output' => [], 396 'userids' => $userids, 397 'preservekeys' => true 398 ]); 399 400 foreach ($userids as $userid) { 401 if (!array_key_exists($userid, $db_users)) { 402 self::exception(ZBX_API_ERROR_PARAMETERS, _s('User with ID "%1$s" is not available.', $userid)); 403 } 404 } 405 } 406 407 /** 408 * Check for valid host grups. 409 * 410 * @param array $usrgrps 411 * @param array $usrgrps[]['rights'] (optional) 412 * 413 * @throws APIException 414 */ 415 private function checkHostGroups(array $usrgrps) { 416 $groupids = []; 417 418 foreach ($usrgrps as $usrgrp) { 419 if (array_key_exists('rights', $usrgrp)) { 420 foreach ($usrgrp['rights'] as $right) { 421 $groupids[$right['id']] = true; 422 } 423 } 424 if (array_key_exists('tag_filters', $usrgrp)) { 425 foreach ($usrgrp['tag_filters'] as $tag_filter) { 426 $groupids[$tag_filter['groupid']] = true; 427 } 428 } 429 } 430 431 if (!$groupids) { 432 return; 433 } 434 435 $groupids = array_keys($groupids); 436 437 $db_groups = DB::select('hstgrp', [ 438 'output' => [], 439 'groupids' => $groupids, 440 'preservekeys' => true 441 ]); 442 443 foreach ($groupids as $groupid) { 444 if (!array_key_exists($groupid, $db_groups)) { 445 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Host group with ID "%1$s" is not available.', $groupid)); 446 } 447 } 448 } 449 450 /** 451 * Tag filter validation. 452 * 453 * @param array $usrgrps 454 * 455 * @throws APIException 456 */ 457 private function checkTagFilters(array $usrgrps) { 458 foreach ($usrgrps as $usrgrp) { 459 if (array_key_exists('tag_filters', $usrgrp)) { 460 foreach ($usrgrp['tag_filters'] as $tag_filter) { 461 if ($tag_filter['tag'] === '' && $tag_filter['value'] !== '') { 462 self::exception(ZBX_API_ERROR_PARAMETERS, 463 _s('Incorrect value for field "%1$s": %2$s.', _('tag'), _('cannot be empty')) 464 ); 465 } 466 } 467 } 468 } 469 } 470 471 /** 472 * Auxiliary function for checkHimself(). 473 * Returns true if user group has GROUP_GUI_ACCESS_DISABLED or GROUP_STATUS_DISABLED states. 474 * 475 * @param array $usrgrp 476 * @param string $method 477 * @param array $db_usrgrps 478 * 479 * @return bool 480 */ 481 private static function userGroupDisabled(array $usrgrp, $method, array $db_usrgrps = null) { 482 $gui_access = array_key_exists('gui_access', $usrgrp) 483 ? $usrgrp['gui_access'] 484 : ($method === 'validateCreate' ? GROUP_GUI_ACCESS_SYSTEM : $db_usrgrps[$usrgrp['usrgrpid']]['gui_access']); 485 $users_status = array_key_exists('users_status', $usrgrp) 486 ? $usrgrp['users_status'] 487 : ($method === 'validateCreate' ? GROUP_STATUS_ENABLED : $db_usrgrps[$usrgrp['usrgrpid']]['users_status']); 488 489 return ($gui_access == GROUP_GUI_ACCESS_DISABLED || $users_status == GROUP_STATUS_DISABLED); 490 } 491 492 /** 493 * Additional check to exclude an opportunity to deactivate himself. 494 * 495 * @param array $usrgrps 496 * @param string $method 497 * @param array $db_usrgrps 498 * 499 * @throws APIException 500 */ 501 private function checkHimself(array $usrgrps, $method, array $db_usrgrps = null) { 502 if ($method === 'validateUpdate') { 503 $groups_users = []; 504 505 foreach ($usrgrps as $usrgrp) { 506 if (self::userGroupDisabled($usrgrp, $method, $db_usrgrps) && !array_key_exists('userids', $usrgrp)) { 507 $groups_users[$usrgrp['usrgrpid']] = []; 508 } 509 } 510 511 if ($groups_users) { 512 $db_users_groups = DB::select('users_groups', [ 513 'output' => ['usrgrpid', 'userid'], 514 'filter' => ['usrgrpid' => array_keys($groups_users)] 515 ]); 516 517 foreach ($db_users_groups as $db_user_group) { 518 $groups_users[$db_user_group['usrgrpid']][] = $db_user_group['userid']; 519 } 520 521 foreach ($usrgrps as &$usrgrp) { 522 if (self::userGroupDisabled($usrgrp, $method, $db_usrgrps) 523 && !array_key_exists('userids', $usrgrp)) { 524 $usrgrp['userids'] = $groups_users[$usrgrp['usrgrpid']]; 525 } 526 } 527 unset($usrgrp); 528 } 529 } 530 531 foreach ($usrgrps as $usrgrp) { 532 if (self::userGroupDisabled($usrgrp, $method, $db_usrgrps) 533 && array_key_exists('userids', $usrgrp) 534 && uint_in_array(self::$userData['userid'], $usrgrp['userids'])) { 535 self::exception(ZBX_API_ERROR_PARAMETERS, 536 _('User cannot add himself to a disabled group or a group with disabled GUI access.') 537 ); 538 } 539 } 540 } 541 542 /** 543 * Check to exclude an opportunity to leave user without user groups. 544 * 545 * @param array $usrgrps 546 * @param array $usrgrps[]['usrgrpid'] 547 * @param array $usrgrps[]['userids'] (optional) 548 * 549 * @throws APIException 550 */ 551 private function checkUsersWithoutGroups(array $usrgrps) { 552 $users_groups = []; 553 554 foreach ($usrgrps as $usrgrp) { 555 if (array_key_exists('userids', $usrgrp)) { 556 $users_groups[$usrgrp['usrgrpid']] = []; 557 558 foreach ($usrgrp['userids'] as $userid) { 559 $users_groups[$usrgrp['usrgrpid']][$userid] = true; 560 } 561 } 562 } 563 564 if (!$users_groups) { 565 return; 566 } 567 568 $db_users_groups = DB::select('users_groups', [ 569 'output' => ['usrgrpid', 'userid'], 570 'filter' => ['usrgrpid' => array_keys($users_groups)] 571 ]); 572 573 $ins_userids = []; 574 $del_userids = []; 575 576 foreach ($db_users_groups as $db_user_group) { 577 if (array_key_exists($db_user_group['userid'], $users_groups[$db_user_group['usrgrpid']])) { 578 unset($users_groups[$db_user_group['usrgrpid']][$db_user_group['userid']]); 579 } 580 else { 581 if (!array_key_exists($db_user_group['userid'], $del_userids)) { 582 $del_userids[$db_user_group['userid']] = 0; 583 } 584 $del_userids[$db_user_group['userid']]++; 585 } 586 } 587 588 foreach ($users_groups as $usrgrpid => $userids) { 589 foreach (array_keys($userids) as $userid) { 590 $ins_userids[$userid] = true; 591 } 592 } 593 594 $del_userids = array_diff_key($del_userids, $ins_userids); 595 596 if (!$del_userids) { 597 return; 598 } 599 600 $db_users = DBselect( 601 'SELECT u.userid,u.username,count(ug.usrgrpid) as usrgrp_num'. 602 ' FROM users u,users_groups ug'. 603 ' WHERE u.userid=ug.userid'. 604 ' AND '.dbConditionInt('u.userid', array_keys($del_userids)). 605 ' GROUP BY u.userid,u.username' 606 ); 607 608 while ($db_user = DBfetch($db_users)) { 609 if ($db_user['usrgrp_num'] == $del_userids[$db_user['userid']]) { 610 self::exception(ZBX_API_ERROR_PARAMETERS, 611 _s('User "%1$s" cannot be without user group.', $db_user['username']) 612 ); 613 } 614 } 615 } 616 617 /** 618 * Update table "rights". 619 * 620 * @param array $usrgrps 621 * @param string $method 622 */ 623 private function updateRights(array $usrgrps, $method) { 624 $rights = []; 625 626 foreach ($usrgrps as $usrgrp) { 627 if (array_key_exists('rights', $usrgrp)) { 628 $rights[$usrgrp['usrgrpid']] = []; 629 630 foreach ($usrgrp['rights'] as $right) { 631 $rights[$usrgrp['usrgrpid']][$right['id']] = $right['permission']; 632 } 633 } 634 } 635 636 if (!$rights) { 637 return; 638 } 639 640 $db_rights = ($method === 'update') 641 ? DB::select('rights', [ 642 'output' => ['rightid', 'groupid', 'id', 'permission'], 643 'filter' => ['groupid' => array_keys($rights)] 644 ]) 645 : []; 646 647 $ins_rights = []; 648 $upd_rights = []; 649 $del_rightids = []; 650 651 foreach ($db_rights as $db_right) { 652 if (array_key_exists($db_right['groupid'], $rights) 653 && array_key_exists($db_right['id'], $rights[$db_right['groupid']])) { 654 if ($db_right['permission'] != $rights[$db_right['groupid']][$db_right['id']]) { 655 $upd_rights[] = [ 656 'values' => ['permission' => $rights[$db_right['groupid']][$db_right['id']]], 657 'where' => ['rightid' => $db_right['rightid']] 658 ]; 659 } 660 unset($rights[$db_right['groupid']][$db_right['id']]); 661 } 662 else { 663 $del_rightids[] = $db_right['rightid']; 664 } 665 } 666 667 foreach ($rights as $groupid => $usrgrp_rights) { 668 foreach ($usrgrp_rights as $id => $permission) { 669 $ins_rights[] = [ 670 'groupid' => $groupid, 671 'id' => $id, 672 'permission' => $permission 673 ]; 674 } 675 } 676 677 if ($ins_rights) { 678 DB::insertBatch('rights', $ins_rights); 679 } 680 681 if ($upd_rights) { 682 DB::update('rights', $upd_rights); 683 } 684 685 if ($del_rightids) { 686 DB::delete('rights', ['rightid' => $del_rightids]); 687 } 688 } 689 690 /** 691 * Update table "tag_filter". 692 * 693 * @param array $usrgrps 694 * @param string $method 695 */ 696 private function updateTagFilters(array $usrgrps, $method) { 697 $tag_filters = []; 698 699 foreach ($usrgrps as $usrgrp) { 700 if (array_key_exists('tag_filters', $usrgrp)) { 701 $tag_filters[$usrgrp['usrgrpid']] = []; 702 703 foreach ($usrgrp['tag_filters'] as $tag_filter) { 704 $tag_filter['usrgrpid'] = $usrgrp['usrgrpid']; 705 $tag_filters[$usrgrp['usrgrpid']][] = $tag_filter; 706 } 707 CArrayHelper::sort($tag_filters[$usrgrp['usrgrpid']], ['groupid', 'tag', 'value']); 708 } 709 } 710 711 if (!$tag_filters) { 712 return; 713 } 714 715 $db_tag_filters = ($method === 'update') 716 ? DB::select('tag_filter', [ 717 'output' => ['tag_filterid', 'usrgrpid', 'groupid', 'tag', 'value'], 718 'filter' => ['usrgrpid' => array_keys($tag_filters)] 719 ]) 720 : []; 721 CArrayHelper::sort($db_tag_filters, ['usrgrpid', 'groupid', 'tag', 'value']); 722 723 $ins_tag_filters = []; 724 $upd_tag_filters = []; 725 $del_tag_filterids = []; 726 727 foreach ($db_tag_filters as $db_tag_filter) { 728 if ($tag_filters[$db_tag_filter['usrgrpid']]) { 729 $tag_filter = array_shift($tag_filters[$db_tag_filter['usrgrpid']]); 730 731 $upd_tag_filter = []; 732 733 if (bccomp($tag_filter['groupid'], $db_tag_filter['groupid']) != 0) { 734 $upd_tag_filter['groupid'] = $tag_filter['groupid']; 735 } 736 if ($tag_filter['tag'] !== $db_tag_filter['tag']) { 737 $upd_tag_filter['tag'] = $tag_filter['tag']; 738 } 739 if ($tag_filter['value'] !== $db_tag_filter['value']) { 740 $upd_tag_filter['value'] = $tag_filter['value']; 741 } 742 743 if ($upd_tag_filter) { 744 $upd_tag_filters[] = [ 745 'values' => $upd_tag_filter, 746 'where' => ['tag_filterid' => $db_tag_filter['tag_filterid']] 747 ]; 748 } 749 } 750 else { 751 $del_tag_filterids[] = $db_tag_filter['tag_filterid']; 752 } 753 } 754 755 foreach ($usrgrps as $usrgrp) { 756 $ins_tag_filters = array_merge($ins_tag_filters, $tag_filters[$usrgrp['usrgrpid']]); 757 } 758 759 if ($ins_tag_filters) { 760 DB::insertBatch('tag_filter', array_values($ins_tag_filters)); 761 } 762 763 if ($upd_tag_filters) { 764 DB::update('tag_filter', array_values($upd_tag_filters)); 765 } 766 767 if ($del_tag_filterids) { 768 DB::delete('tag_filter', ['tag_filterid' => $del_tag_filterids]); 769 } 770 } 771 772 /** 773 * Update table "users_groups". 774 * 775 * @param array $usrgrps 776 * @param string $method 777 */ 778 private function updateUsersGroups(array $usrgrps, $method) { 779 $users_groups = []; 780 781 foreach ($usrgrps as $usrgrp) { 782 if (array_key_exists('userids', $usrgrp)) { 783 $users_groups[$usrgrp['usrgrpid']] = []; 784 785 foreach ($usrgrp['userids'] as $userid) { 786 $users_groups[$usrgrp['usrgrpid']][$userid] = true; 787 } 788 } 789 } 790 791 if (!$users_groups) { 792 return; 793 } 794 795 $db_users_groups = ($method === 'update') 796 ? DB::select('users_groups', [ 797 'output' => ['id', 'usrgrpid', 'userid'], 798 'filter' => ['usrgrpid' => array_keys($users_groups)] 799 ]) 800 : []; 801 802 $ins_users_groups = []; 803 $del_ids = []; 804 805 foreach ($db_users_groups as $db_user_group) { 806 if (array_key_exists($db_user_group['userid'], $users_groups[$db_user_group['usrgrpid']])) { 807 unset($users_groups[$db_user_group['usrgrpid']][$db_user_group['userid']]); 808 } 809 else { 810 $del_ids[] = $db_user_group['id']; 811 } 812 } 813 814 foreach ($users_groups as $usrgrpid => $userids) { 815 foreach (array_keys($userids) as $userid) { 816 $ins_users_groups[] = [ 817 'usrgrpid' => $usrgrpid, 818 'userid' => $userid 819 ]; 820 } 821 } 822 823 if ($ins_users_groups) { 824 DB::insertBatch('users_groups', $ins_users_groups); 825 } 826 827 if ($del_ids) { 828 DB::delete('users_groups', ['id' => $del_ids]); 829 } 830 } 831 832 /** 833 * @param array $usrgrpids 834 * 835 * @return array 836 */ 837 public function delete(array $usrgrpids) { 838 $this->validateDelete($usrgrpids, $db_usrgrps); 839 840 DB::delete('rights', ['groupid' => $usrgrpids]); 841 DB::delete('users_groups', ['usrgrpid' => $usrgrpids]); 842 DB::delete('usrgrp', ['usrgrpid' => $usrgrpids]); 843 844 $this->addAuditBulk(AUDIT_ACTION_DELETE, AUDIT_RESOURCE_USER_GROUP, $db_usrgrps); 845 846 return ['usrgrpids' => $usrgrpids]; 847 } 848 849 /** 850 * @throws APIException 851 * 852 * @param array $usrgrpids 853 * @param array $db_usrgrps 854 */ 855 protected function validateDelete(array &$usrgrpids, array &$db_usrgrps = null) { 856 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) { 857 self::exception(ZBX_API_ERROR_PERMISSIONS, _('Only Super Admins can delete user groups.')); 858 } 859 860 $api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true]; 861 if (!CApiInputValidator::validate($api_input_rules, $usrgrpids, '/', $error)) { 862 self::exception(ZBX_API_ERROR_PARAMETERS, $error); 863 } 864 865 $db_usrgrps = DB::select('usrgrp', [ 866 'output' => ['usrgrpid', 'name'], 867 'usrgrpids' => $usrgrpids, 868 'preservekeys' => true 869 ]); 870 871 $usrgrps = []; 872 873 foreach ($usrgrpids as $usrgrpid) { 874 // Check if this user group exists. 875 if (!array_key_exists($usrgrpid, $db_usrgrps)) { 876 self::exception(ZBX_API_ERROR_PERMISSIONS, 877 _('No permissions to referred object or it does not exist!') 878 ); 879 } 880 881 $usrgrps[] = [ 882 'usrgrpid' => $usrgrpid, 883 'userids' => [] 884 ]; 885 } 886 887 // Check if user groups are used in actions. 888 $db_actions = DBselect( 889 'SELECT a.name,og.usrgrpid'. 890 ' FROM opmessage_grp og,operations o,actions a'. 891 ' WHERE og.operationid=o.operationid'. 892 ' AND o.actionid=a.actionid'. 893 ' AND '.dbConditionInt('og.usrgrpid', $usrgrpids), 894 1 895 ); 896 897 if ($db_action = DBfetch($db_actions)) { 898 self::exception(ZBX_API_ERROR_PARAMETERS, _s('User group "%1$s" is used in "%2$s" action.', 899 $db_usrgrps[$db_action['usrgrpid']]['name'], $db_action['name'] 900 )); 901 } 902 903 // Check if user groups are used in scripts. 904 $db_scripts = DB::select('scripts', [ 905 'output' => ['name', 'usrgrpid'], 906 'filter' => ['usrgrpid' => $usrgrpids], 907 'limit' => 1 908 ]); 909 910 if ($db_scripts) { 911 self::exception(ZBX_API_ERROR_PARAMETERS, _s('User group "%1$s" is used in script "%2$s".', 912 $db_usrgrps[$db_scripts[0]['usrgrpid']]['name'], $db_scripts[0]['name'] 913 )); 914 } 915 916 // Check if user group are used in config. 917 if (array_key_exists(CSettingsHelper::get(CSettingsHelper::ALERT_USRGRPID), $db_usrgrps)) { 918 self::exception(ZBX_API_ERROR_PARAMETERS, 919 _s('User group "%1$s" is used in configuration for database down messages.', $db_usrgrps[CSettingsHelper::get(CSettingsHelper::ALERT_USRGRPID)]['name']) 920 ); 921 } 922 923 // Check if user groups are used in scheduled reports. 924 $db_reports = DBselect( 925 'SELECT r.name,rug.usrgrpid'. 926 ' FROM report r,report_usrgrp rug'. 927 ' WHERE r.reportid=rug.reportid'. 928 ' AND '.dbConditionInt('rug.usrgrpid', $usrgrpids), 929 1 930 ); 931 932 if ($db_report = DBfetch($db_reports)) { 933 self::exception(ZBX_API_ERROR_PARAMETERS, _s('User group "%1$s" is report "%2$s" recipient.', 934 $db_usrgrps[$db_report['usrgrpid']]['name'], $db_report['name'] 935 )); 936 } 937 938 $this->checkUsersWithoutGroups($usrgrps); 939 } 940 941 protected function addRelatedObjects(array $options, array $result) { 942 $result = parent::addRelatedObjects($options, $result); 943 944 // adding users 945 if ($options['selectUsers'] !== null && $options['selectUsers'] != API_OUTPUT_COUNT) { 946 $dbUsers = []; 947 $relationMap = $this->createRelationMap($result, 'usrgrpid', 'userid', 'users_groups'); 948 $related_ids = $relationMap->getRelatedIds(); 949 950 if ($related_ids) { 951 $get_access = ($this->outputIsRequested('gui_access', $options['selectUsers']) 952 || $this->outputIsRequested('debug_mode', $options['selectUsers']) 953 || $this->outputIsRequested('users_status', $options['selectUsers'])) ? true : null; 954 955 $dbUsers = API::User()->get([ 956 'output' => $options['selectUsers'], 957 'userids' => $related_ids, 958 'getAccess' => $get_access, 959 'preservekeys' => true 960 ]); 961 } 962 963 $result = $relationMap->mapMany($result, $dbUsers, 'users'); 964 } 965 966 // adding usergroup rights 967 if ($options['selectRights'] !== null && $options['selectRights'] != API_OUTPUT_COUNT) { 968 $db_rights = []; 969 $relationMap = $this->createRelationMap($result, 'groupid', 'rightid', 'rights'); 970 $related_ids = $relationMap->getRelatedIds(); 971 972 if ($related_ids) { 973 if (is_array($options['selectRights'])) { 974 $pk_field = $this->pk('rights'); 975 976 $output_fields = [ 977 $pk_field => $this->fieldId($pk_field, 'r') 978 ]; 979 980 foreach ($options['selectRights'] as $field) { 981 if ($this->hasField($field, 'rights')) { 982 $output_fields[$field] = $this->fieldId($field, 'r'); 983 } 984 } 985 986 $output_fields = implode(',', $output_fields); 987 } 988 else { 989 $output_fields = 'r.*'; 990 } 991 992 $db_rights = DBfetchArray(DBselect( 993 'SELECT '.$output_fields. 994 ' FROM rights r'. 995 ' WHERE '.dbConditionInt('r.rightid', $related_ids). 996 ((self::$userData['type'] == USER_TYPE_SUPER_ADMIN) ? '' : ' AND r.permission>'.PERM_DENY) 997 )); 998 $db_rights = zbx_toHash($db_rights, 'rightid'); 999 1000 foreach ($db_rights as &$db_right) { 1001 unset($db_right['rightid'], $db_right['groupid']); 1002 } 1003 unset($db_right); 1004 } 1005 1006 $result = $relationMap->mapMany($result, $db_rights, 'rights'); 1007 } 1008 1009 // Adding usergroup tag filters. 1010 if ($options['selectTagFilters'] !== null && $options['selectTagFilters'] != API_OUTPUT_COUNT) { 1011 foreach ($result as &$usrgrp) { 1012 $usrgrp['tag_filters'] = []; 1013 } 1014 unset($usrgrp); 1015 1016 if (is_array($options['selectTagFilters'])) { 1017 $output_fields = []; 1018 1019 foreach ($this->outputExtend($options['selectTagFilters'], ['usrgrpid']) as $field) { 1020 if ($this->hasField($field, 'tag_filter')) { 1021 $output_fields[$field] = $this->fieldId($field, 't'); 1022 } 1023 } 1024 1025 $output_fields = implode(',', $output_fields); 1026 } 1027 else { 1028 $output_fields = 't.*'; 1029 } 1030 1031 $db_tag_filters = DBselect( 1032 'SELECT '.$output_fields. 1033 ' FROM tag_filter t'. 1034 ' WHERE '.dbConditionInt('t.usrgrpid', array_keys($result)) 1035 ); 1036 1037 while ($db_tag_filter = DBfetch($db_tag_filters)) { 1038 $usrgrpid = $db_tag_filter['usrgrpid']; 1039 unset($db_tag_filter['tag_filterid'], $db_tag_filter['usrgrpid']); 1040 1041 $result[$usrgrpid]['tag_filters'][] = $db_tag_filter; 1042 } 1043 } 1044 1045 return $result; 1046 } 1047} 1048