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